(function() {
  var Grammar, GrammarRegistry, NullGrammar, fs, _;

  _ = require('underscore');

  fs = require('fs');

  Grammar = require('./grammar');

  NullGrammar = require('./null-grammar');

  module.exports = GrammarRegistry = (function() {
    function GrammarRegistry(options) {
      var _ref;
      if (options == null) {
        options = {};
      }
      this.maxTokensPerLine = (_ref = options.maxTokensPerLine) != null ? _ref : Infinity;
      this.grammars = [];
      this.grammarsByScopeName = {};
      this.injectionGrammars = [];
      this.grammarOverridesByPath = {};
      this.scopeIdCounter = -1;
      this.idsByScope = {};
      this.scopesById = {};
      this.nullGrammar = new NullGrammar(this);
      this.addGrammar(this.nullGrammar);
    }


    /*
    Section: Managing Grammars
     */

    GrammarRegistry.prototype.getGrammars = function() {
      return _.clone(this.grammars);
    };

    GrammarRegistry.prototype.grammarForScopeName = function(scopeName) {
      return this.grammarsByScopeName[scopeName];
    };

    GrammarRegistry.prototype.addGrammar = function(grammar) {
      this.grammars.push(grammar);
      this.grammarsByScopeName[grammar.scopeName] = grammar;
      if (grammar.injectionSelector != null) {
        this.injectionGrammars.push(grammar);
      }
      return void 0;
    };

    GrammarRegistry.prototype.removeGrammar = function(grammar) {
      var index;
      index = this.grammars.indexOf(grammar);
      if (index >= 0) {
        this.grammars.splice(index, 1);
      }
      delete this.grammarsByScopeName[grammar.scopeName];
      index = this.injectionGrammars.indexOf(grammar);
      if (index >= 0) {
        this.injectionGrammars.splice(index, 1);
      }
      return void 0;
    };

    GrammarRegistry.prototype.removeGrammarForScopeName = function(scopeName) {
      var grammar;
      grammar = this.grammarForScopeName(scopeName);
      if (grammar != null) {
        this.removeGrammar(grammar);
      }
      return grammar;
    };

    GrammarRegistry.prototype.readGrammarSync = function(grammarPath) {
      var contents, grammar;
      contents = fs.readFileSync(grammarPath.replace(/\.cson$/, '.json'), 'utf8');
      grammar = null;
      if (contents.trim().length !== 0) {
        grammar = JSON.parse(contents);
      }
      if (typeof grammar.scopeName === 'string' && grammar.scopeName.length > 0) {
        return this.createGrammar(grammarPath, grammar);
      } else {
        throw new Error("Grammar missing required scopeName property: " + grammarPath);
      }
    };

    GrammarRegistry.prototype.loadGrammarSync = function(grammarPath) {
      var grammar;
      grammar = this.readGrammarSync(grammarPath);
      this.addGrammar(grammar);
      return grammar;
    };

    GrammarRegistry.prototype.grammarOverrideForPath = function(filePath) {
      return this.grammarOverridesByPath[filePath];
    };

    GrammarRegistry.prototype.setGrammarOverrideForPath = function(filePath, scopeName) {
      if (filePath) {
        return this.grammarOverridesByPath[filePath] = scopeName;
      }
    };

    GrammarRegistry.prototype.clearGrammarOverrideForPath = function(filePath) {
      delete this.grammarOverridesByPath[filePath];
      return void 0;
    };

    GrammarRegistry.prototype.clearGrammarOverrides = function() {
      this.grammarOverridesByPath = {};
      return void 0;
    };

    GrammarRegistry.prototype.selectGrammar = function(filePath, fileContents) {
      return _.max(this.grammars, function(grammar) {
        return grammar.getScore(filePath, fileContents);
      });
    };

    GrammarRegistry.prototype.startIdForScope = function(scope) {
      var id;
      if (!(id = this.idsByScope[scope])) {
        id = this.scopeIdCounter;
        this.scopeIdCounter -= 2;
        this.idsByScope[scope] = id;
        this.scopesById[id] = scope;
      }
      return id;
    };

    GrammarRegistry.prototype.endIdForScope = function(scope) {
      return this.startIdForScope(scope) - 1;
    };

    GrammarRegistry.prototype.scopeForId = function(id) {
      if ((id % 2) === -1) {
        return this.scopesById[id];
      } else {
        return this.scopesById[id + 1];
      }
    };

    GrammarRegistry.prototype.createGrammar = function(grammarPath, object) {
      var grammar;
      if (object.maxTokensPerLine == null) {
        object.maxTokensPerLine = this.maxTokensPerLine;
      }
      grammar = new Grammar(this, object);
      grammar.path = grammarPath;
      return grammar;
    };

    GrammarRegistry.prototype.decodeTokens = function(lineText, tags, scopeTags, fn) {
      var expectedScopeName, index, offset, poppedScopeName, scopeNames, tag, token, tokens, _i, _len;
      if (scopeTags == null) {
        scopeTags = [];
      }
      offset = 0;
      scopeNames = scopeTags.map((function(_this) {
        return function(tag) {
          return _this.scopeForId(tag);
        };
      })(this));
      tokens = [];
      for (index = _i = 0, _len = tags.length; _i < _len; index = ++_i) {
        tag = tags[index];
        if (tag >= 0) {
          token = {
            value: lineText.substring(offset, offset + tag),
            scopes: scopeNames.slice()
          };
          if (fn != null) {
            token = fn(token, index);
          }
          tokens.push(token);
          offset += tag;
        } else if ((tag % 2) === -1) {
          scopeTags.push(tag);
          scopeNames.push(this.scopeForId(tag));
        } else {
          scopeTags.pop();
          expectedScopeName = this.scopeForId(tag + 1);
          poppedScopeName = scopeNames.pop();
          if (poppedScopeName !== expectedScopeName) {
            throw new Error("Expected popped scope to be " + expectedScopeName + ", but it was " + poppedScopeName);
          }
        }
      }
      return tokens;
    };

    return GrammarRegistry;

  })();

}).call(this);
