/* 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("uicomponents", function () {

        var simpleCategories = ['title','subtitle','owners','titre','soustitre','redacteurs','liage','parentage','lang'];
        var withNameCategories = ['prop','propriete','info','information','section','comment','corpus','thesaurus','album','addenda','data'];
        var withModeCategories = ['corpus','thesaurus','album','addenda'];
        var subsetCategories = ['liage','parentage','corpus','thesaurus','album','addenda'];

        var ON_CATEGORY = 1;
        var ON_NAME = 2;
        var ON_MODE = 3;
        var ON_WEIGHT = 4;
        var ON_DETAIL = 5;
        var ON_MASTER_CATEGORY = 6;
        var ON_ERROR = -1;

        function errorToken (stream, state) {
            stream.skipToEnd();
            state.position = ON_ERROR;
            return "error";
        }
        
        function categoryToken (stream, state) {
            if (state.position !== ON_MASTER_CATEGORY) {
                if (stream.match("_master_")) {
                    state.position = ON_MASTER_CATEGORY;
                    return masterClass();
                }
            }
            stream.eatWhile(/[^_\s(]/);
            var cur = stream.current();
            if ((state.position === ON_MASTER_CATEGORY) && (cur !== 'corpus') && (cur !== 'thesaurus')) {
                return errorToken(stream, state);
            }
            var isSimple = (simpleCategories.indexOf(cur) !== -1);
            if (stream.eol()) {
                if (isSimple) {
                    return categoryClass(cur);
                } else {
                    return errorToken(stream, state);
                }
            } else {
                var peek = stream.peek();
                if (peek !== '_') {
                    if (isSimple) {
                        if ((peek === ' ') || (peek === '(')) {
                            state.position = ON_DETAIL;
                            return categoryClass(cur);
                        } else {
                            return errorToken(stream, state);
                        }
                    } else {
                        return errorToken(stream, state);
                    }
                } else if (withNameCategories.indexOf(cur) === -1) {
                    return errorToken(stream, state);
                } else {
                    state.position = ON_NAME;
                    state.withMode = (withModeCategories.indexOf(cur) !== -1);
                    return categoryClass(cur);
                }
            }
        }
        
        function nameToken (stream, state) {
            stream.next();
            stream.eatWhile(/[^_\s(]/);
            var cur = stream.current().substring(1);
            if (!/^[a-z]([a-z0-9]*)$/.test(cur)) {
                return errorToken(stream, state);
            } else if (stream.eol()) {
                return nameClass();
            } else {
                var peek = stream.peek();
                if (peek !== '_') {
                    if ((peek === ' ') || (peek === '(')) {
                        state.position = ON_DETAIL;
                        return nameClass()
                    } else {
                        return errorToken(stream, state);
                    }
                } else {
                    if (state.withMode) {
                        state.position = ON_MODE;
                        return nameClass();
                    } else {
                        return errorToken(stream, state);
                    }
                }
            }
        }
        
        function modeToken (stream, state) {
            stream.next();
            stream.eatWhile(/[^_\s(]/);
            var cur = stream.current().substring(1);
            if (/^([0-9]+)$/.test(cur)) {
                if (stream.eol()) {
                    return weightClass();
                } else {
                    var peek = stream.peek();
                    if (peek !== '_') {
                        if ((peek === ' ') || (peek === '(')) {
                            state.position = ON_DETAIL;
                            return weightClass();
                        } else {
                            return errorToken(stream, state);
                        }
                    }
                }
            } else if (!/^[a-z]([a-z0-9]*)$/.test(cur)) {
                return errorToken(stream, state);
            } else if (stream.eol()) {
                return modeClass();
            } else {
                var peek = stream.peek();
                if (peek !== '_') {
                    if ((peek === ' ') || (peek === '(')) {
                        state.position = ON_DETAIL;
                        return modeClass();
                    } else {
                        return errorToken(stream, state);
                    }
                } else {
                    state.position = ON_WEIGHT;
                    return modeClass();
                }
            }
        }
        
         function weightToken (stream, state) {
            stream.next();
            stream.eatWhile(/[^\s(]/);
            var cur = stream.current().substring(1);
            if (!/^([0-9])+$/.test(cur)) {
                return errorToken(stream, state);
            } else if (stream.eol()) {
                return weightClass();
            } else {
                var peek = stream.peek();
                if ((peek === ' ') || (peek === '(')) {
                    state.position = ON_DETAIL;
                    return weightClass();
                } else {
                    return errorToken(stream, state);
                }
            }
         }
        
        function detailToken (stream, state) {
            stream.skipToEnd();
            return detailClass();
        }
        
        function categoryClass (token) {
            if (token === "comment") {
                return "component-comment";
            } else if (token === "data") {
                return "component-data";
            } else if (subsetCategories.indexOf(token) !== - 1) {
                return "component-subset";
            } else {
                return "component-field";
            }
        }
        
        function nameClass () {
            return "quote";
        }
        
        function modeClass () {
            return "def";
        }
        
        function weightClass () {
            return "number";
        }
        
        function detailClass () {
            return "comment";
        }
        
        function masterClass () {
            return "qualifier";
        }

        return {
            token: function (stream, state) {
                if (stream.sol()) {
                    state.position = ON_CATEGORY;
                    if (stream.eatSpace()) {
                        return null;
                    }
                }
                switch(state.position) {
                    case ON_CATEGORY:
                    case ON_MASTER_CATEGORY:
                        return categoryToken(stream, state);
                    case ON_NAME:
                        return nameToken(stream, state);
                    case ON_ERROR:
                        return errorToken(stream, state);
                    case ON_MODE:
                        return modeToken(stream, state);
                    case ON_WEIGHT:
                        return weightToken(stream, state);
                    case ON_DETAIL:
                        return detailToken(stream, state);
                    default :
                        throw new Error("Unknown position: " + state.position);
                }
            },
            startState: function () {
                return {
                    position: ON_CATEGORY,
                    withMode: false
                };
            }

        };
    });

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

});
