/*
 * r_highlight_rules.js
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * 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
 *
 */
define(function (require, exports, module) {

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

    var RHighlightRules = function () {

        var keywords = lang.arrayToMap(
            ("function|if|in|break|next|repeat|else|for|return|switch|while|try|tryCatch|stop|warning|require|library|attach|detach|source|setMethod|setGeneric|setGroupGeneric|setClass")
                .split("|")
        );

        var buildinConstants = lang.arrayToMap(
            ("NULL|NA|TRUE|FALSE|T|F|Inf|NaN|NA_integer_|NA_real_|NA_character_|" +
            "NA_complex_").split("|")
        );

        // regexp must not have capturing parentheses. Use (?:) instead.
        // regexps are ordered -> the first match is used

        this.$rules = {
            "start": [
                {
                    // Roxygen
                    token: "comment.sectionhead",
                    regex: "#+(?!').*(?:----|====|####)\\s*$"
                },
                {
                    // Roxygen
                    token: "comment",
                    regex: "#+'",
                    next: "rd-start"
                },
                {
                    token: "comment",
                    regex: "#.*$"
                },
                {
                    token: "string", // multi line string start
                    regex: '["]',
                    next: "qqstring"
                },
                {
                    token: "string", // multi line string start
                    regex: "[']",
                    next: "qstring"
                },
                {
                    token: "constant.numeric", // hex
                    regex: "0[xX][0-9a-fA-F]+[Li]?\\b"
                },
                {
                    token: "constant.numeric", // explicit integer
                    regex: "\\d+L\\b"
                },
                {
                    token: "constant.numeric", // number
                    regex: "\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b"
                },
                {
                    token: "constant.numeric", // number with leading decimal
                    regex: "\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b"
                },
                {
                    token: "constant.language.boolean",
                    regex: "(?:TRUE|FALSE|T|F)\\b"
                },
                {
                    token: "identifier",
                    regex: "`.*?`"
                },
                {
                    onMatch: function (value) {
                        if (keywords[value])
                            return "keyword";
                        else if (buildinConstants[value])
                            return "constant.language";
                        else if (value == '...' || value.match(/^\.\.\d+$/))
                            return "variable.language";
                        else
                            return "identifier";
                    },
                    regex: "[a-zA-Z.][a-zA-Z0-9._]*\\b"
                },
                {
                    token: "keyword.operator",
                    regex: "%%|>=|<=|==|!=|\\->|<\\-|\\|\\||&&|=|\\+|\\-|\\*|/|\\^|>|<|!|&|\\||~|\\$|:"
                },
                {
                    token: "keyword.operator", // infix operators
                    regex: "%.*?%"
                },
                {
                    // Obviously these are neither keywords nor operators, but
                    // labelling them as such was the easiest way to get them
                    // to be colored distinctly from regular text
                    token: "paren.keyword.operator",
                    regex: "[[({]"
                },
                {
                    // Obviously these are neither keywords nor operators, but
                    // labelling them as such was the easiest way to get them
                    // to be colored distinctly from regular text
                    token: "paren.keyword.operator",
                    regex: "[\\])}]"
                },
                {
                    token: "text",
                    regex: "\\s+"
                }
            ],
            "qqstring": [
                {
                    token: "string",
                    regex: '(?:(?:\\\\.)|(?:[^"\\\\]))*?"',
                    next: "start"
                },
                {
                    token: "string",
                    regex: '.+'
                }
            ],
            "qstring": [
                {
                    token: "string",
                    regex: "(?:(?:\\\\.)|(?:[^'\\\\]))*?'",
                    next: "start"
                },
                {
                    token: "string",
                    regex: '.+'
                }
            ]
        };

        var rdRules = new TexHighlightRules("comment").getRules();

        // Make all embedded TeX virtual-comment so they don't interfere with
        // auto-indent.
        for (var i = 0; i < rdRules["start"].length; i++) {
            rdRules["start"][i].token += ".virtual-comment";
        }

        this.addRules(rdRules, "rd-");
        this.$rules["rd-start"].unshift({
            token: "text",
            regex: "^",
            next: "start"
        });
        this.$rules["rd-start"].unshift({
            token: "keyword",
            regex: "@(?!@)[^ ]*"
        });
        this.$rules["rd-start"].unshift({
            token: "comment",
            regex: "@@"
        });
        this.$rules["rd-start"].push({
            token: "comment",
            regex: "[^%\\\\[({\\])}]+"
        });
    };

    oop.inherits(RHighlightRules, TextHighlightRules);

    exports.RHighlightRules = RHighlightRules;
});
