'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = parse;

var _generalError;

function _load_generalError() {
  return _generalError = _interopRequireDefault(require('../error/general-error'));
}

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var token = {
  CODE: 'CODE',
  DESC_DELIMITER: 'DESC_DELIMITER',
  RETURNS_DELIMITER: 'RETURNS_DELIMITER',
  COMMENT: 'COMMENT'
};

var status = {
  IN_CODE: 'IN_CODE',
  IN_DESCRIPTION: 'IN_DESCRIPTION',
  IN_RETURNS: 'IN_RETURNS',
  NONE: 'NONE'
};

function isComment(str) {
  return str.startsWith('//');
}

function isCode(str) {
  return !isComment(str);
}

function isDescriptionDelimiter(str) {
  return str === '//-';
}

function isReturnsDelimiter(str) {
  return str === '//=>';
}

function tokenize(str) {
  if (isCode(str)) return token.CODE;
  if (isDescriptionDelimiter(str)) return token.DESC_DELIMITER;
  if (isReturnsDelimiter(str)) return token.RETURNS_DELIMITER;
  return token.COMMENT;
}

function stripComment(str) {
  return isComment(str) ? str.replace('//', '') : str;
}

function updateExample(line, field, example) {
  line = stripComment(line).trim();
  if (example[field]) {
    example[field] += `\n${line}`;
  } else {
    example[field] = line;
  }
}

function parseToken(currentToken, line, currentStatus, example) {
  switch (currentToken) {
    case token.DESC_DELIMITER:
      if (currentStatus === status.NONE) currentStatus = status.IN_DESCRIPTION;else if (currentStatus === status.IN_DESCRIPTION) {
        currentStatus = status.NONE;
      } else {
        throw new (_generalError || _load_generalError()).default(`${currentToken // end desc block
        } can't appear after code or returns block`);
      }
      break;
    case token.RETURNS_DELIMITER:
      if (currentStatus === status.NONE || currentStatus === status.IN_CODE) currentStatus = status.IN_RETURNS;else if (currentStatus === status.IN_RETURNS) {
        currentStatus = status.NONE;
      } else {
        throw new (_generalError || _load_generalError()).default(`${currentToken // end returns block
        } must appear after code block`);
      }
      break;
    case token.CODE:
      if (currentStatus === status.NONE || currentStatus === status.IN_CODE) updateExample(line, 'code', example);else throw new (_generalError || _load_generalError()).default(`${currentToken} can't appear inside desc or returns block`);
      break;
    case token.COMMENT:
      if (currentStatus === status.IN_DESCRIPTION) updateExample(line, 'description', example);else if (currentStatus === status.IN_RETURNS) updateExample(line, 'returns', example);else throw new (_generalError || _load_generalError()).default(`${currentToken} must appear inside desc or returns block`);
      break;
    default:
      throw new (_generalError || _load_generalError()).default('Unrecognized token');
  }
  return currentStatus;
}

/**
 * An example of an "example" tag
 *
 * * @example
 * //-
 * // Adds two numbers
 * //-
 * add(2, 3);
 * //=>
 * // 5
 * //=>
 *
 * @returns {Example} Returns the parsed "example" tag
 * {
 *   raw: '//-\n// Adds two numbers\n//-\nadd(2, 3);\n//=>\n// 5\n//=>',
 *   description: 'Adds two numbers',
 *   code: 'add(2, 3);',
 *   returns: '5'
 * }
 */
function parse(exampleRaw) {
  var example = {};
  var currentStatus = status.NONE;
  example.raw = exampleRaw;
  try {
    var lines = exampleRaw.split('\n');
    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;

    try {
      for (var _iterator = lines[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
        var line = _step.value;

        line = line.trim();
        if (!line) continue;
        var currentToken = tokenize(line);
        currentStatus = parseToken(currentToken, line, currentStatus, example);
      }
    } catch (err) {
      _didIteratorError = true;
      _iteratorError = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion && _iterator.return) {
          _iterator.return();
        }
      } finally {
        if (_didIteratorError) {
          throw _iteratorError;
        }
      }
    }
  } catch (e) {
    // That's fine. The example probably doesn't comply with our standard
  }

  return example;
}