/* global CodeMirror */
(function (mod) {
    if (typeof exports == "object" && typeof module == "object") // CommonJS
        mod(require("../../lib/codemirror"));
    else if (typeof define == "function" && define.amd) // AMD
        define(["../../lib/codemirror"], mod);
    else // Plain browser env
        mod(CodeMirror);
})(function (CodeMirror) {
    "use strict";

    CodeMirror.defineMode("subsettree", function () {

        var subsetCategories = ['corpus','thesaurus','album','addenda', 'sphere'];

        var ON_CATEGORY = 1;
        var ON_NAME = 2;
        var ON_GROUP = 3;
        var ON_DETAIL = 4;
        var ON_ERROR = -1;

        function errorToken (stream, state) {
            stream.skipToEnd();
            state.position = ON_ERROR;
            return "error";
        }
        
        function categoryToken (stream, state) {
            stream.eatWhile(/[^_]/);
            var cur = stream.current();
            if (stream.eol()) {
               return errorToken(stream, state);
            } else if (subsetCategories.indexOf(cur) === -1) {
                return errorToken(stream, state);
            } else {
                state.position = ON_NAME;
                return categoryClass();
            }
        };
        
        function nameToken (stream, state) {
            stream.next();
            stream.eatWhile(/[^(]/);
            var cur = stream.current().substring(1);
            if (!/^[a-z]([a-z0-9]*)(\s*)$/.test(cur)) {
                return errorToken(stream, state);
            } else {
                state.position = ON_DETAIL;
                return nameClass();
            }
        };
        
        function detailToken (stream, state) {
            stream.skipToEnd();
            return detailClass();
        }
        
        function groupToken (stream, state) {
            stream.eatWhile(/[^(]/);
            var cur = stream.current().substring(1);
            if (!/^[a-z]([a-z0-9]*)(\s*)$/.test(cur)) {
                return errorToken(stream, state);
            } else {
                state.position = ON_DETAIL;
                return groupClass();
            }
        }

        
        function categoryClass() {
            return "component-subset";
        }
        
        function nameClass() {
            return "quote";
        }
        
        function detailClass() {
            return "comment";
        }
        
        function groupClass() {
            return "component-group";
        }

        return {
            token: function (stream, state) {
                if (stream.sol() || state.sol) {
                    state.position = ON_CATEGORY;
                    if (stream.eatSpace()) {
                        state.sol = true;
                        return null;
                    }
                    state.sol = false;
                    if (!stream.eol()) {
                        var ch = stream.peek();
                        if (ch === '+') {
                            stream.eatWhile(/[+\s]/);
                            state.position = ON_GROUP;
                            state.level++;
                            return "operator";
                        } else if (ch === '-') {
                            if (state.level < 1) {
                                return errorToken(stream, state);
                            }
                            stream.eatWhile(/[-\s]/);
                            state.position = ON_GROUP;
                            state.level--;
                            return "operator";
                        } else {
                            state.position = ON_CATEGORY;
                        }
                    }
                }
                switch(state.position) {
                    case ON_CATEGORY:
                        return categoryToken(stream, state);
                    case ON_NAME:
                        return nameToken(stream, state);
                    case ON_ERROR:
                        return errorToken(stream, state);
                    case ON_DETAIL:
                        return detailToken(stream, state);
                    case ON_GROUP:
                        return groupToken(stream, state);
                    default :
                        throw new Error("Unknown position: " + state.position);
                }
            },
            startState: function () {
                return {
                    position: ON_CATEGORY,
                    sol: true,
                    level: 0
                };
            }

        };
    });

    CodeMirror.defineMIME("text/plain", "subsettree");

});
