// CodeMirror, copyright © by Marijn Haverbeke and others // Distributed under an MIT license: codemirror.net/LICENSE

/*

*      Pig Latin Mode for CodeMirror 2
*      @author Prasanth Jayachandran
*      @link   https://github.com/prasanthj/pig-codemirror-2
*  This implementation is adapted from PL/SQL mode in CodeMirror 2.
*/

(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(“pig”, function(_config, parserConfig) {

var keywords = parserConfig.keywords,
builtins = parserConfig.builtins,
types = parserConfig.types,
multiLineStrings = parserConfig.multiLineStrings;

var isOperatorChar = /[*+\-%<>=&?:\/!|]/;

function chain(stream, state, f) {
  state.tokenize = f;
  return f(stream, state);
}

function tokenComment(stream, state) {
  var isEnd = false;
  var ch;
  while(ch = stream.next()) {
    if(ch == "/" && isEnd) {
      state.tokenize = tokenBase;
      break;
    }
    isEnd = (ch == "*");
  }
  return "comment";
}

function tokenString(quote) {
  return function(stream, state) {
    var escaped = false, next, end = false;
    while((next = stream.next()) != null) {
      if (next == quote && !escaped) {
        end = true; break;
      }
      escaped = !escaped && next == "\\";
    }
    if (end || !(escaped || multiLineStrings))
      state.tokenize = tokenBase;
    return "error";
  };
}

function tokenBase(stream, state) {
  var ch = stream.next();

  // is a start of string?
  if (ch == '"' || ch == "'")
    return chain(stream, state, tokenString(ch));
  // is it one of the special chars
  else if(/[\[\]{}\(\),;\.]/.test(ch))
    return null;
  // is it a number?
  else if(/\d/.test(ch)) {
    stream.eatWhile(/[\w\.]/);
    return "number";
  }
  // multi line comment or operator
  else if (ch == "/") {
    if (stream.eat("*")) {
      return chain(stream, state, tokenComment);
    }
    else {
      stream.eatWhile(isOperatorChar);
      return "operator";
    }
  }
  // single line comment or operator
  else if (ch=="-") {
    if(stream.eat("-")){
      stream.skipToEnd();
      return "comment";
    }
    else {
      stream.eatWhile(isOperatorChar);
      return "operator";
    }
  }
  // is it an operator
  else if (isOperatorChar.test(ch)) {
    stream.eatWhile(isOperatorChar);
    return "operator";
  }
  else {
    // get the while word
    stream.eatWhile(/[\w\$_]/);
    // is it one of the listed keywords?
    if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) {
      //keywords can be used as variables like flatten(group), group.$0 etc..
      if (!stream.eat(")") && !stream.eat("."))
        return "keyword";
    }
    // is it one of the builtin functions?
    if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase()))
      return "variable-2";
    // is it one of the listed types?
    if (types && types.propertyIsEnumerable(stream.current().toUpperCase()))
      return "variable-3";
    // default is a 'variable'
    return "variable";
  }
}

// Interface
return {
  startState: function() {
    return {
      tokenize: tokenBase,
      startOfLine: true
    };
  },

  token: function(stream, state) {
    if(stream.eatSpace()) return null;
    var style = state.tokenize(stream, state);
    return style;
  }
};

});

(function() {

function keywords(str) {
  var obj = {}, words = str.split(" ");
  for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
  return obj;
}

// builtin funcs taken from trunk revision 1303237
var pBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL "
  + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS "
  + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG "
  + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN "
  + "INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER "
  + "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS "
  + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA  "
  + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE "
  + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG "
  + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER ";

// taken from QueryLexer.g
var pKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP "
  + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL "
  + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE "
  + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE "
  + "NEQ MATCHES TRUE FALSE DUMP";

// data types
var pTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP ";

CodeMirror.defineMIME("text/x-pig", {
  name: "pig",
  builtins: keywords(pBuiltins),
  keywords: keywords(pKeywords),
  types: keywords(pTypes)
});

CodeMirror.registerHelper("hintWords", "pig", (pBuiltins + pTypes + pKeywords).split(" "));

}());

});