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

(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('tiki', function(config) {

function inBlock(style, terminator, returnTokenizer) {
  return function(stream, state) {
    while (!stream.eol()) {
      if (stream.match(terminator)) {
        state.tokenize = inText;
        break;
      }
      stream.next();
    }

    if (returnTokenizer) state.tokenize = returnTokenizer;

    return style;
  };
}

function inLine(style) {
  return function(stream, state) {
    while(!stream.eol()) {
      stream.next();
    }
    state.tokenize = inText;
    return style;
  };
}

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

  var sol = stream.sol();
  var ch = stream.next();

  //non start of line
  switch (ch) { //switch is generally much faster than if, so it is used here
  case "{": //plugin
    stream.eat("/");
    stream.eatSpace();
    var tagName = "";
    var c;
    while ((c = stream.eat(/[^\s\u00a0=\"\'\/?(}]/))) tagName += c;
    state.tokenize = inPlugin;
    return "tag";
    break;
  case "_": //bold
    if (stream.eat("_")) {
      return chain(inBlock("strong", "__", inText));
    }
    break;
  case "'": //italics
    if (stream.eat("'")) {
      // Italic text
      return chain(inBlock("em", "''", inText));
    }
    break;
  case "(":// Wiki Link
    if (stream.eat("(")) {
      return chain(inBlock("variable-2", "))", inText));
    }
    break;
  case "[":// Weblink
    return chain(inBlock("variable-3", "]", inText));
    break;
  case "|": //table
    if (stream.eat("|")) {
      return chain(inBlock("comment", "||"));
    }
    break;
  case "-":
    if (stream.eat("=")) {//titleBar
      return chain(inBlock("header string", "=-", inText));
    } else if (stream.eat("-")) {//deleted
      return chain(inBlock("error tw-deleted", "--", inText));
    }
    break;
  case "=": //underline
    if (stream.match("==")) {
      return chain(inBlock("tw-underline", "===", inText));
    }
    break;
  case ":":
    if (stream.eat(":")) {
      return chain(inBlock("comment", "::"));
    }
    break;
  case "^": //box
    return chain(inBlock("tw-box", "^"));
    break;
  case "~": //np
    if (stream.match("np~")) {
      return chain(inBlock("meta", "~/np~"));
    }
    break;
  }

  //start of line types
  if (sol) {
    switch (ch) {
    case "!": //header at start of line
      if (stream.match('!!!!!')) {
        return chain(inLine("header string"));
      } else if (stream.match('!!!!')) {
        return chain(inLine("header string"));
      } else if (stream.match('!!!')) {
        return chain(inLine("header string"));
      } else if (stream.match('!!')) {
        return chain(inLine("header string"));
      } else {
        return chain(inLine("header string"));
      }
      break;
    case "*": //unordered list line item, or <li /> at start of line
    case "#": //ordered list line item, or <li /> at start of line
    case "+": //ordered list line item, or <li /> at start of line
      return chain(inLine("tw-listitem bracket"));
      break;
    }
  }

  //stream.eatWhile(/[&{]/); was eating up plugins, turned off to act less like html and more like tiki
  return null;
}

var indentUnit = config.indentUnit;

// Return variables for tokenizers
var pluginName, type;
function inPlugin(stream, state) {
  var ch = stream.next();
  var peek = stream.peek();

  if (ch == "}") {
    state.tokenize = inText;
    //type = ch == ")" ? "endPlugin" : "selfclosePlugin"; inPlugin
    return "tag";
  } else if (ch == "(" || ch == ")") {
    return "bracket";
  } else if (ch == "=") {
    type = "equals";

    if (peek == ">") {
      ch = stream.next();
      peek = stream.peek();
    }

    //here we detect values directly after equal character with no quotes
    if (!/[\'\"]/.test(peek)) {
      state.tokenize = inAttributeNoQuote();
    }
    //end detect values

    return "operator";
  } else if (/[\'\"]/.test(ch)) {
    state.tokenize = inAttribute(ch);
    return state.tokenize(stream, state);
  } else {
    stream.eatWhile(/[^\s\u00a0=\"\'\/?]/);
    return "keyword";
  }
}

function inAttribute(quote) {
  return function(stream, state) {
    while (!stream.eol()) {
      if (stream.next() == quote) {
        state.tokenize = inPlugin;
        break;
      }
    }
    return "string";
  };
}

function inAttributeNoQuote() {
  return function(stream, state) {
    while (!stream.eol()) {
      var ch = stream.next();
      var peek = stream.peek();
      if (ch == " " || ch == "," || /[ )}]/.test(peek)) {
    state.tokenize = inPlugin;
    break;
  }
}
return "string";

};

}

var curState, setStyle; function pass() {

for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);

}

function cont() {

pass.apply(null, arguments);
return true;

}

function pushContext(pluginName, startOfLine) {

var noIndent = curState.context && curState.context.noIndent;
curState.context = {
  prev: curState.context,
  pluginName: pluginName,
  indent: curState.indented,
  startOfLine: startOfLine,
  noIndent: noIndent
};

}

function popContext() {

if (curState.context) curState.context = curState.context.prev;

}

function element(type) {

if (type == "openPlugin") {curState.pluginName = pluginName; return cont(attributes, endplugin(curState.startOfLine));}
else if (type == "closePlugin") {
  var err = false;
  if (curState.context) {
    err = curState.context.pluginName != pluginName;
    popContext();
  } else {
    err = true;
  }
  if (err) setStyle = "error";
  return cont(endcloseplugin(err));
}
else if (type == "string") {
  if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
  if (curState.tokenize == inText) popContext();
  return cont();
}
else return cont();

}

function endplugin(startOfLine) {

return function(type) {
  if (
    type == "selfclosePlugin" ||
      type == "endPlugin"
  )
    return cont();
  if (type == "endPlugin") {pushContext(curState.pluginName, startOfLine); return cont();}
  return cont();
};

}

function endcloseplugin(err) {

return function(type) {
  if (err) setStyle = "error";
  if (type == "endPlugin") return cont();
  return pass();
};

}

function attributes(type) {

if (type == "keyword") {setStyle = "attribute"; return cont(attributes);}
if (type == "equals") return cont(attvalue, attributes);
return pass();

} function attvalue(type) {

if (type == "keyword") {setStyle = "string"; return cont();}
if (type == "string") return cont(attvaluemaybe);
return pass();

} function attvaluemaybe(type) {

if (type == "string") return cont(attvaluemaybe);
else return pass();

} return {

startState: function() {
  return {tokenize: inText, cc: [], indented: 0, startOfLine: true, pluginName: null, context: null};
},
token: function(stream, state) {
  if (stream.sol()) {
    state.startOfLine = true;
    state.indented = stream.indentation();
  }
  if (stream.eatSpace()) return null;

  setStyle = type = pluginName = null;
  var style = state.tokenize(stream, state);
  if ((style || type) && style != "comment") {
    curState = state;
    while (true) {
      var comb = state.cc.pop() || element;
      if (comb(type || style)) break;
    }
  }
  state.startOfLine = false;
  return setStyle || style;
},
indent: function(state, textAfter) {
  var context = state.context;
  if (context && context.noIndent) return 0;
  if (context && /^{\//.test(textAfter))
      context = context.prev;
      while (context && !context.startOfLine)
        context = context.prev;
      if (context) return context.indent + indentUnit;
      else return 0;
     },
  electricChars: "/"
};

});

CodeMirror.defineMIME(“text/tiki”, “tiki”);

});