"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.chainLinkAsync = exports.chainLink = exports.chainAsync = exports.chain = void 0;
var _index = require("../../util/index.js");
var _logger = _interopRequireDefault(require("../../logger.js"));
var _register = require("./register.js");
var _type = require("./type.js");
var _data = require("./data.js");
var _graph = require("./graph.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function prepareParseGraph(graph) {
  return graph.reduce((array, next) => {
    const last = array[array.length - 1];
    if (last && last.type === next.type) {
      last.count = last.count + 1 || 2;
    } else {
      array.push(next);
    }
    return array;
  }, []).map(element => (element.count > 1 ? element.count + 'x ' : '') + element.type).join(' -> ');
}
class ChainParser {
  constructor(input, options = {}) {
    this.options = Object.assign({
      generateGraph: true,
      forceType: (0, _type.type)(input),
      maxChainLength: 10,
      strict: true,
      target: '@csl/list+object'
    }, options);
    this.type = this.options.forceType;
    this.data = typeof input === 'object' ? (0, _index.deepCopy)(input) : input;
    this.graph = [{
      type: this.type,
      data: input
    }];
    this.iteration = 0;
  }
  iterate() {
    if (this.iteration !== 0) {
      const typeInfo = (0, _register.get)(this.type);
      if (typeInfo && typeInfo.outputs) {
        this.type = typeInfo.outputs;
      } else {
        this.type = (0, _type.type)(this.data);
      }
      this.graph.push({
        type: this.type
      });
    }
    if (this.error || this.type === this.options.target) {
      return false;
    } else if (this.iteration >= this.options.maxChainLength) {
      this.error = new RangeError(`Max. number of parsing iterations reached (${prepareParseGraph(this.graph)})`);
      return false;
    } else {
      this.iteration++;
      return true;
    }
  }
  end() {
    if (this.error) {
      _logger.default.error('[core]', this.error.message);
      if (this.options.strict !== false) {
        throw this.error;
      } else {
        return [];
      }
    } else if (this.options.target === '@csl/list+object') {
      return (0, _index.upgradeCsl)(this.data).map(this.options.generateGraph ? entry => (0, _graph.applyGraph)(entry, this.graph) : _graph.removeGraph);
    } else {
      return this.data;
    }
  }
}
const chain = (...args) => {
  const chain = new ChainParser(...args);
  while (chain.iterate()) {
    try {
      chain.data = (0, _data.data)(chain.data, chain.type);
    } catch (e) {
      chain.error = e;
    }
  }
  return chain.end();
};
exports.chain = chain;
const chainLink = input => {
  const type = (0, _type.type)(input);
  const output = type.match(/array|object/) ? (0, _index.deepCopy)(input) : input;
  return (0, _data.data)(output, type);
};
exports.chainLink = chainLink;
const chainAsync = async (...args) => {
  const chain = new ChainParser(...args);
  while (chain.iterate()) {
    chain.data = await (0, _data.dataAsync)(chain.data, chain.type).catch(e => {
      chain.error = e;
    });
  }
  return chain.end();
};
exports.chainAsync = chainAsync;
const chainLinkAsync = async input => {
  const type = (0, _type.type)(input);
  const output = type.match(/array|object/) ? (0, _index.deepCopy)(input) : input;
  return (0, _data.dataAsync)(output, type);
};
exports.chainLinkAsync = chainLinkAsync;