/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function (require, exports, module) {
    "use strict";

    var oop = require("../lib/oop");
    var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;

    var LuaHighlightRules = function () {

        var keywords = (
            "break|do|else|elseif|end|for|function|if|in|local|repeat|" +
            "return|then|until|while|or|and|not"
        );

        var builtinConstants = ("true|false|nil|_G|_VERSION");

        var functions = (
            // builtinFunctions
            "string|xpcall|package|tostring|print|os|unpack|require|" +
            "getfenv|setmetatable|next|assert|tonumber|io|rawequal|" +
            "collectgarbage|getmetatable|module|rawset|math|debug|" +
            "pcall|table|newproxy|type|coroutine|_G|select|gcinfo|" +
            "pairs|rawget|loadstring|ipairs|_VERSION|dofile|setfenv|" +
            "load|error|loadfile|" +

            "sub|upper|len|gfind|rep|find|match|char|dump|gmatch|" +
            "reverse|byte|format|gsub|lower|preload|loadlib|loaded|" +
            "loaders|cpath|config|path|seeall|exit|setlocale|date|" +
            "getenv|difftime|remove|time|clock|tmpname|rename|execute|" +
            "lines|write|close|flush|open|output|type|read|stderr|" +
            "stdin|input|stdout|popen|tmpfile|log|max|acos|huge|" +
            "ldexp|pi|cos|tanh|pow|deg|tan|cosh|sinh|random|randomseed|" +
            "frexp|ceil|floor|rad|abs|sqrt|modf|asin|min|mod|fmod|log10|" +
            "atan2|exp|sin|atan|getupvalue|debug|sethook|getmetatable|" +
            "gethook|setmetatable|setlocal|traceback|setfenv|getinfo|" +
            "setupvalue|getlocal|getregistry|getfenv|setn|insert|getn|" +
            "foreachi|maxn|foreach|concat|sort|remove|resume|yield|" +
            "status|wrap|create|running|" +
            // metatableMethods
            "__add|__sub|__mod|__unm|__concat|__lt|__index|__call|__gc|__metatable|" +
            "__mul|__div|__pow|__len|__eq|__le|__newindex|__tostring|__mode|__tonumber"
        );

        var stdLibaries = ("string|package|os|io|math|debug|table|coroutine");

        var deprecatedIn5152 = ("setn|foreach|foreachi|gcinfo|log10|maxn");

        var keywordMapper = this.createKeywordMapper({
            "keyword": keywords,
            "support.function": functions,
            "keyword.deprecated": deprecatedIn5152,
            "constant.library": stdLibaries,
            "constant.language": builtinConstants,
            "variable.language": "self"
        }, "identifier");

        var decimalInteger = "(?:(?:[1-9]\\d*)|(?:0))";
        var hexInteger = "(?:0[xX][\\dA-Fa-f]+)";
        var integer = "(?:" + decimalInteger + "|" + hexInteger + ")";

        var fraction = "(?:\\.\\d+)";
        var intPart = "(?:\\d+)";
        var pointFloat = "(?:(?:" + intPart + "?" + fraction + ")|(?:" + intPart + "\\.))";
        var floatNumber = "(?:" + pointFloat + ")";

        this.$rules = {
            "start": [{
                stateName: "bracketedComment",
                onMatch: function (value, currentState, stack) {
                    stack.unshift(this.next, value.length - 2, currentState);
                    return "comment";
                },
                regex: /\-\-\[=*\[/,
                next: [
                    {
                        onMatch: function (value, currentState, stack) {
                            if (value.length == stack[1]) {
                                stack.shift();
                                stack.shift();
                                this.next = stack.shift();
                            } else {
                                this.next = "";
                            }
                            return "comment";
                        },
                        regex: /\]=*\]/,
                        next: "start"
                    }, {
                        defaultToken: "comment"
                    }
                ]
            },

                {
                    token: "comment",
                    regex: "\\-\\-.*$"
                },
                {
                    stateName: "bracketedString",
                    onMatch: function (value, currentState, stack) {
                        stack.unshift(this.next, value.length, currentState);
                        return "comment";
                    },
                    regex: /\[=*\[/,
                    next: [
                        {
                            onMatch: function (value, currentState, stack) {
                                if (value.length == stack[1]) {
                                    stack.shift();
                                    stack.shift();
                                    this.next = stack.shift();
                                } else {
                                    this.next = "";
                                }
                                return "comment";
                            },

                            regex: /\]=*\]/,
                            next: "start"
                        }, {
                            defaultToken: "comment"
                        }
                    ]
                },
                {
                    token: "string",           // " string
                    regex: '"(?:[^\\\\]|\\\\.)*?"'
                }, {
                    token: "string",           // ' string
                    regex: "'(?:[^\\\\]|\\\\.)*?'"
                }, {
                    token: "constant.numeric", // float
                    regex: floatNumber
                }, {
                    token: "constant.numeric", // integer
                    regex: integer + "\\b"
                }, {
                    token: keywordMapper,
                    regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
                }, {
                    token: "keyword.operator",
                    regex: "\\+|\\-|\\*|\\/|%|\\#|\\^|~|<|>|<=|=>|==|~=|=|\\:|\\.\\.\\.|\\.\\."
                }, {
                    token: "paren.lparen",
                    regex: "[\\[\\(\\{]"
                }, {
                    token: "paren.rparen",
                    regex: "[\\]\\)\\}]"
                }, {
                    token: "text",
                    regex: "\\s+|\\w+"
                }]
        };

        this.normalizeRules();
    }

    oop.inherits(LuaHighlightRules, TextHighlightRules);

    exports.LuaHighlightRules = LuaHighlightRules;
});
