'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Argument = require('./argument');
var TreeRegexp = require('./tree_regexp');

var _require = require('./errors'),
    UndefinedParameterTypeError = _require.UndefinedParameterTypeError;

var CucumberExpression = function () {
  /**
   * @param expression
   * @param parameterTypeRegistry
   */
  function CucumberExpression(expression, parameterTypeRegistry) {
    _classCallCheck(this, CucumberExpression);

    // Does not include (){} characters because they have special meaning
    var ESCAPE_REGEXP = /([\\^[$.|?*+])/g;
    var PARAMETER_REGEXP = /{([^}]+)}/g;
    var OPTIONAL_REGEXP = /\(([^)]+)\)/g;
    var ALTERNATIVE_NON_WHITESPACE_TEXT_REGEXP = /([^\s^/]+)((\/[^\s^/]+)+)/g;

    this._expression = expression;
    this._parameterTypes = [];
    var regexp = '^';
    var match = void 0;
    var matchOffset = 0;

    // Does not include (){} because they have special meaning

    expression = expression.replace(ESCAPE_REGEXP, '\\$1');

    // Create non-capturing, optional capture groups from parenthesis
    expression = expression.replace(OPTIONAL_REGEXP, '(?:$1)?');

    expression = expression.replace(ALTERNATIVE_NON_WHITESPACE_TEXT_REGEXP, function (_, p1, p2) {
      return '(?:' + p1 + p2.replace(/\//g, '|') + ')';
    });

    while ((match = PARAMETER_REGEXP.exec(expression)) !== null) {
      var typeName = match[1];

      var parameterType = parameterTypeRegistry.lookupByTypeName(typeName);
      if (!parameterType) throw new UndefinedParameterTypeError(typeName);
      this._parameterTypes.push(parameterType);

      var text = expression.slice(matchOffset, match.index);
      var captureRegexp = buildCaptureRegexp(parameterType.regexps);
      matchOffset = PARAMETER_REGEXP.lastIndex;
      regexp += text;
      regexp += captureRegexp;
    }
    regexp += expression.slice(matchOffset);
    regexp += '$';
    this._treeRegexp = new TreeRegexp(regexp);
  }

  _createClass(CucumberExpression, [{
    key: 'match',
    value: function match(text) {
      return Argument.build(this._treeRegexp, text, this._parameterTypes);
    }
  }, {
    key: 'regexp',
    get: function get() {
      return this._treeRegexp.regexp;
    }
  }, {
    key: 'source',
    get: function get() {
      return this._expression;
    }
  }]);

  return CucumberExpression;
}();

function buildCaptureRegexp(regexps) {
  if (regexps.length === 1) {
    return '(' + regexps[0] + ')';
  }

  var captureGroups = regexps.map(function (group) {
    return '(?:' + group + ')';
  });

  return '(' + captureGroups.join('|') + ')';
}

module.exports = CucumberExpression;