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

// mode(s) for the sequence chart dsl's mscgen, xù and msgenny // For more information on mscgen, see the site of the original author: // www.mcternan.me.uk/mscgen // // This mode for mscgen and the two derivative languages were // originally made for use in the mscgen_js interpreter // (sverweij.github.io/mscgen_js)

(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";

var languages = {
  mscgen: {
    "keywords" : ["msc"],
    "options" : ["hscale", "width", "arcgradient", "wordwraparcs"],
    "constants" : ["true", "false", "on", "off"],
    "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip"],
    "brackets" : ["\\{", "\\}"], // [ and  ] are brackets too, but these get handled in with lists
    "arcsWords" : ["note", "abox", "rbox", "box"],
    "arcsOthers" : ["\\|\\|\\|", "\\.\\.\\.", "---", "--", "<->", "==", "<<=>>", "<=>", "\\.\\.", "<<>>", "::", "<:>", "->", "=>>", "=>", ">>", ":>", "<-", "<<=", "<=", "<<", "<:", "x-", "-x"],
    "singlecomment" : ["//", "#"],
    "operators" : ["="]
  },
  xu: {
    "keywords" : ["msc", "xu"],
    "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "watermark"],
    "constants" : ["true", "false", "on", "off", "auto"],
    "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip"],
    "brackets" : ["\\{", "\\}"],  // [ and  ] are brackets too, but these get handled in with lists
    "arcsWords" : ["note", "abox", "rbox", "box", "alt", "else", "opt", "break", "par", "seq", "strict", "neg", "critical", "ignore", "consider", "assert", "loop", "ref", "exc"],
    "arcsOthers" : ["\\|\\|\\|", "\\.\\.\\.", "---", "--", "<->", "==", "<<=>>", "<=>", "\\.\\.", "<<>>", "::", "<:>", "->", "=>>", "=>", ">>", ":>", "<-", "<<=", "<=", "<<", "<:", "x-", "-x"],
    "singlecomment" : ["//", "#"],
    "operators" : ["="]
  },
  msgenny: {
    "keywords" : null,
    "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "watermark"],
    "constants" : ["true", "false", "on", "off", "auto"],
    "attributes" : null,
    "brackets" : ["\\{", "\\}"],
    "arcsWords" : ["note", "abox", "rbox", "box", "alt", "else", "opt", "break", "par", "seq", "strict", "neg", "critical", "ignore", "consider", "assert", "loop", "ref", "exc"],
    "arcsOthers" : ["\\|\\|\\|", "\\.\\.\\.", "---", "--", "<->", "==", "<<=>>", "<=>", "\\.\\.", "<<>>", "::", "<:>", "->", "=>>", "=>", ">>", ":>", "<-", "<<=", "<=", "<<", "<:", "x-", "-x"],
    "singlecomment" : ["//", "#"],
    "operators" : ["="]
  }
}

CodeMirror.defineMode("mscgen", function(_, modeConfig) {
  var language = languages[modeConfig && modeConfig.language || "mscgen"]
  return {
    startState: startStateFn,
    copyState: copyStateFn,
    token: produceTokenFunction(language),
    lineComment : "#",
    blockCommentStart : "/*",
    blockCommentEnd : "*/"
  };
});

CodeMirror.defineMIME("text/x-mscgen", "mscgen");
CodeMirror.defineMIME("text/x-xu", {name: "mscgen", language: "xu"});
CodeMirror.defineMIME("text/x-msgenny", {name: "mscgen", language: "msgenny"});

function wordRegexpBoundary(pWords) {
  return new RegExp("\\b(" + pWords.join("|") + ")\\b", "i");
}

function wordRegexp(pWords) {
  return new RegExp("(" + pWords.join("|") + ")", "i");
}

function startStateFn() {
  return {
    inComment : false,
    inString : false,
    inAttributeList : false,
    inScript : false
  };
}

function copyStateFn(pState) {
  return {
    inComment : pState.inComment,
    inString : pState.inString,
    inAttributeList : pState.inAttributeList,
    inScript : pState.inScript
  };
}

function produceTokenFunction(pConfig) {

  return function(pStream, pState) {
    if (pStream.match(wordRegexp(pConfig.brackets), true, true)) {
      return "bracket";
    }
    /* comments */
    if (!pState.inComment) {
      if (pStream.match(/\/\*[^\*\/]*/, true, true)) {
        pState.inComment = true;
        return "comment";
      }
      if (pStream.match(wordRegexp(pConfig.singlecomment), true, true)) {
        pStream.skipToEnd();
        return "comment";
      }
    }
    if (pState.inComment) {
      if (pStream.match(/[^\*\/]*\*\//, true, true))
        pState.inComment = false;
      else
        pStream.skipToEnd();
      return "comment";
    }
    /* strings */
    if (!pState.inString && pStream.match(/\"(\\\"|[^\"])*/, true, true)) {
      pState.inString = true;
      return "string";
    }
    if (pState.inString) {
      if (pStream.match(/[^\"]*\"/, true, true))
        pState.inString = false;
      else
        pStream.skipToEnd();
      return "string";
    }
    /* keywords & operators */
    if (!!pConfig.keywords && pStream.match(wordRegexpBoundary(pConfig.keywords), true, true))
      return "keyword";

    if (pStream.match(wordRegexpBoundary(pConfig.options), true, true))
      return "keyword";

    if (pStream.match(wordRegexpBoundary(pConfig.arcsWords), true, true))
      return "keyword";

    if (pStream.match(wordRegexp(pConfig.arcsOthers), true, true))
      return "keyword";

    if (!!pConfig.operators && pStream.match(wordRegexp(pConfig.operators), true, true))
      return "operator";

    if (!!pConfig.constants && pStream.match(wordRegexp(pConfig.constants), true, true))
      return "variable";

    /* attribute lists */
    if (!pConfig.inAttributeList && !!pConfig.attributes && pStream.match(/\[/, true, true)) {
      pConfig.inAttributeList = true;
      return "bracket";
    }
    if (pConfig.inAttributeList) {
      if (pConfig.attributes !== null && pStream.match(wordRegexpBoundary(pConfig.attributes), true, true)) {
        return "attribute";
      }
      if (pStream.match(/]/, true, true)) {
        pConfig.inAttributeList = false;
        return "bracket";
      }
    }

    pStream.next();
    return "base";
  };
}

});