var React = require("react");
var _ = require("lodash");

var MessageUtils = require("../../flow/utils.js").MessageUtils;
var utils = require("../../utils.js");

var image_regex = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i;
var ViewImage = React.createClass({
    statics: {
        matches: function (message) {
            return image_regex.test(MessageUtils.getContentType(message));
        }
    },
    render: function () {
        var url = MessageUtils.getContentURL(this.props.flow, this.props.message);
        return <div className="flowview-image">
            <img src={url} alt="preview" className="img-thumbnail"/>
        </div>;
    }
});

var RawMixin = {
    getInitialState: function () {
        return {
            content: undefined,
            request: undefined
        }
    },
    requestContent: function (nextProps) {
        if (this.state.request) {
            this.state.request.abort();
        }
        var request = MessageUtils.getContent(nextProps.flow, nextProps.message);
        this.setState({
            content: undefined,
            request: request
        });
        request.done(function (data) {
            this.setState({content: data});
        }.bind(this)).fail(function (jqXHR, textStatus, errorThrown) {
            if (textStatus === "abort") {
                return;
            }
            this.setState({content: "AJAX Error: " + textStatus + "\r\n" + errorThrown});
        }.bind(this)).always(function () {
            this.setState({request: undefined});
        }.bind(this));

    },
    componentWillMount: function () {
        this.requestContent(this.props);
    },
    componentWillReceiveProps: function (nextProps) {
        if (nextProps.message !== this.props.message) {
            this.requestContent(nextProps);
        }
    },
    componentWillUnmount: function () {
        if (this.state.request) {
            this.state.request.abort();
        }
    },
    render: function () {
        if (!this.state.content) {
            return <div className="text-center">
                <i className="fa fa-spinner fa-spin"></i>
            </div>;
        }
        return this.renderContent();
    }
};

var ViewRaw = React.createClass({
    mixins: [RawMixin],
    statics: {
        matches: function (message) {
            return true;
        }
    },
    renderContent: function () {
        return <pre>{this.state.content}</pre>;
    }
});

var json_regex = /^application\/json$/i;
var ViewJSON = React.createClass({
    mixins: [RawMixin],
    statics: {
        matches: function (message) {
            return json_regex.test(MessageUtils.getContentType(message));
        }
    },
    renderContent: function () {
        var json = this.state.content;
        try {
            json = JSON.stringify(JSON.parse(json), null, 2);
        } catch (e) {
        }
        return <pre>{json}</pre>;
    }
});

var ViewAuto = React.createClass({
    statics: {
        matches: function () {
            return false; // don't match itself
        },
        findView: function (message) {
            for (var i = 0; i < all.length; i++) {
                if (all[i].matches(message)) {
                    return all[i];
                }
            }
            return all[all.length - 1];
        }
    },
    render: function () {
        var View = ViewAuto.findView(this.props.message);
        return <View {...this.props}/>;
    }
});

var all = [ViewAuto, ViewImage, ViewJSON, ViewRaw];


var ContentEmpty = React.createClass({
    render: function () {
        var message_name = this.props.flow.request === this.props.message ? "request" : "response";
        return <div className="alert alert-info">No {message_name} content.</div>;
    }
});

var ContentMissing = React.createClass({
    render: function () {
        var message_name = this.props.flow.request === this.props.message ? "Request" : "Response";
        return <div className="alert alert-info">{message_name} content missing.</div>;
    }
});

var TooLarge = React.createClass({
    statics: {
        isTooLarge: function (message) {
            var max_mb = ViewImage.matches(message) ? 10 : 0.2;
            return message.contentLength > 1024 * 1024 * max_mb;
        }
    },
    render: function () {
        var size = utils.formatSize(this.props.message.contentLength);
        return <div className="alert alert-warning">
            <button onClick={this.props.onClick} className="btn btn-xs btn-warning pull-right">Display anyway</button>
            {size} content size.
        </div>;
    }
});

var ViewSelector = React.createClass({
    render: function () {
        var views = [];
        for (var i = 0; i < all.length; i++) {
            var view = all[i];
            var className = "btn btn-default";
            if (view === this.props.active) {
                className += " active";
            }
            var text;
            if (view === ViewAuto) {
                text = "auto: " + ViewAuto.findView(this.props.message).displayName.toLowerCase().replace("view", "");
            } else {
                text = view.displayName.toLowerCase().replace("view", "");
            }
            views.push(
                <button
                    key={view.displayName}
                    onClick={this.props.selectView.bind(null, view)}
                    className={className}>
                    {text}
                </button>
            );
        }

        return <div className="view-selector btn-group btn-group-xs">{views}</div>;
    }
});

var ContentView = React.createClass({
    getInitialState: function () {
        return {
            displayLarge: false,
            View: ViewAuto
        };
    },
    propTypes: {
        // It may seem a bit weird at the first glance:
        // Every view takes the flow and the message as props, e.g.
        // <Auto flow={flow} message={flow.request}/>
        flow: React.PropTypes.object.isRequired,
        message: React.PropTypes.object.isRequired,
    },
    selectView: function (view) {
        this.setState({
            View: view
        });
    },
    displayLarge: function () {
        this.setState({displayLarge: true});
    },
    componentWillReceiveProps: function (nextProps) {
        if (nextProps.message !== this.props.message) {
            this.setState(this.getInitialState());
        }
    },
    render: function () {
        var message = this.props.message;
        if (message.contentLength === 0) {
            return <ContentEmpty {...this.props}/>;
        } else if (message.contentLength === null) {
            return <ContentMissing {...this.props}/>;
        } else if (!this.state.displayLarge && TooLarge.isTooLarge(message)) {
            return <TooLarge {...this.props} onClick={this.displayLarge}/>;
        }

        var downloadUrl = MessageUtils.getContentURL(this.props.flow, message);

        return <div>
            <this.state.View {...this.props} />
            <div className="view-options text-center">
                <ViewSelector selectView={this.selectView} active={this.state.View} message={message}/>
            &nbsp;
                <a className="btn btn-default btn-xs" href={downloadUrl}>
                    <i className="fa fa-download"/>
                </a>
            </div>
        </div>;
    }
});

module.exports = ContentView;