define(['react', 'lodash', 'jquery', './libs/codemirror-4.8/lib/codemirror',
        './CMLint',
        './libs/codemirror-4.8/mode/javascript/javascript',
        './libs/codemirror-4.8/addon/hint/html-hint',
        './libs/codemirror-4.8/addon/hint/show-hint',
        './libs/codemirror-4.8/addon/hint/xml-hint',
        './libs/codemirror-4.8/addon/hint/html-hint',
        //'./libs/codemirror-4.8/addon/display/panel',
        './libs/codemirror-4.8/mode/xml/xml',
        //'./libs/codemirror-4.8/mode/css/css',
        './libs/codemirror-4.8/addon/runmode/runmode'
        //'./libs/codemirror-4.8/addon/display/placeholder'
], function (React, _, $, CodeMirror, CMLint) {
    'use strict';
    //codeMirror: 'libs/codemirror-4.8/lib/codemirror',
    //htmlmixed: 'libs/codemirror-4.8/mode/htmlmixed/htmlmixed',
    //javascript: 'libs/codemirror-4.8/mode/javascript/javascript'

    var rtSchema = {
        div: {
            attrs: {
                'rt-props': null,
                'rt-if': null,
                'rt-repeat': null,
                'rt-class': null,
                'rt-scope': null,
                valueLink: null,
                key: null,
                ref: null,
                dangerouslySetInnerHTML: null
            }
        }
    };
    var tags = CodeMirror.htmlSchema;
    Object.keys(CodeMirror.htmlSchema).forEach(function (i) {
        tags[i].attrs = _.defaults(rtSchema.div.attrs, tags[i].attrs);
    });

    function completeAfter(cm, pred) {
        //var cur = cm.getCursor();
        if (!pred || pred()) {
            setTimeout(function () {
                if (!cm.state.completionActive) {
                    cm.showHint({completeSingle: false});
                }
            }, 100);
        }
        return CodeMirror.Pass;
    }

    function completeIfAfterLt(cm) {
        return completeAfter(cm, function () {
            var cur = cm.getCursor();
            /*eslint new-cap:0*/
            return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) === '<';
        });
    }

    function completeIfInTag(cm) {
        return completeAfter(cm, function () {
            var tok = cm.getTokenAt(cm.getCursor());
            if (tok.type === 'string' && (!/['"]/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length === 1)) {
                return false;
            }
            var inner = CodeMirror.innerMode(cm.getMode(), tok.state).state;
            return inner.tagName;
        });
    }

    var editor = React.createClass({
        displayName: 'CodeMirrorEditor',
        propTypes: {
            id: React.PropTypes.string,
            readOnly: React.PropTypes.bool,
            runMode: React.PropTypes.bool,
            mode: React.PropTypes.string,
            value: React.PropTypes.string,
            valueLink: React.PropTypes.string,
            onChange: React.PropTypes.func
        },
        getDefaultProps: function () {
            return {
                readOnly: false,
                mode: 'html'
            };
        },
        getInitialState: function () {
            return {
                editorId: _.uniqueId()
            };
        },
        //componentWillMount: function () {
        //},
        render: function () {
            var props = _.omit(this.props, ['ref', 'key', 'value', 'valueLink', 'onChange']);
            props.id = this.props.id || this.state.editorId;
            props.defaultValue = this.props.valueLink ? this.props.valueLink() : this.props.value;
            return React.DOM.textarea(props);
        },
        componentWillUpdate: function (nextProps/*, nextState*/) {
            var value = nextProps.valueLink ? nextProps.valueLink() : nextProps.value;
            if (this.editor && this.editor.getValue() !== value) {
                this.editor.setValue(value || '');
            }
        },
        componentDidMount: function () {
            var value = this.props.valueLink ? this.props.valueLink() : this.props.value;
            var options = {
                readOnly: this.props.readOnly,
                lineWrapping: true,
                smartIndent: true,
                matchBrackets: true,
                value: value,
                lineNumbers: true,
                mode: 'javascript',
                gutters: ['CodeMirror-linenumbers', 'rt-annotations'],
                theme: 'solarized' //solarized_light solarized-light
            };

            if (this.props.mode === 'html') {
                options.mode = 'text/html';
                options.extraKeys = {
                    "'<'": completeAfter,
                    "'/'": completeIfAfterLt,
                    "' '": completeIfInTag,
                    "'='": completeIfInTag,
                    'Ctrl-Space': 'autocomplete'
                };
                options.hintOptions = {schemaInfo: tags};
                //options.gutters = ['CodeMirror-lint-markers'];
                //options.lint = true;
            } else {
                options.mode = 'javascript';
                //options.gutters = ['CodeMirror-lint-markers'];
                //options.lint = true;
            }

            this.editor = CodeMirror.fromTextArea(this.getDOMNode(), options);

            if (!this.props.readOnly) {
                this.editor.on('change', function (/*e*/) {
                    if (this.props.valueLink) {
                        this.props.valueLink(this.editor.getValue());
                    } else if (this.props.onChange) {
                        this.props.onChange({target: {value: this.editor.getValue()}});
                    }
                }.bind(this));
            }
        },
        //showMessage: function (msg) {
        //    //var anOption = document.createElement('div');
        //    //anOption.innerText = msg;
        //    //anOption.setAttribute('class', 'error-panel');
        //    //if (this.panel) {
        //    //    this.panel.clear();
        //    //}
        //    //this.panel = this.editor.addPanel(anOption, {height: 22}); // {position: 'bottom'}
        //},
        //clearMessage: function () {
        //    if (this.panel) {
        //        this.panel.clear();
        //        this.panel = null;
        //    }
        //},
        annotate: function (annot) {
            CMLint.annotate(this.editor, annot);
        },
        clearAnnotations: function () {
            CMLint.clearMarks(this.editor);
        },
        componentWillUnmount: function () {
            this.editor.toTextArea();
        }
    });

    return editor;
});