'use strict';

var util = require('../../../../../../zrender@6.0.0/node_modules/zrender/lib/core/util.js');
var Point = require('../../../../../../zrender@6.0.0/node_modules/zrender/lib/core/Point.js');
var OrdinalMeta = require('../../data/OrdinalMeta.js');
var Ordinal = require('../../scale/Ordinal.js');
var graphic = require('../../util/graphic.js');
var model = require('../../util/model.js');
var matrixCoordHelper = require('./matrixCoordHelper.js');
var log = require('../../util/log.js');
var number = require('../../util/number.js');

/**
 * Lifetime: the same with `MatrixModel`, but different from `coord/Matrix`.
 */
var MatrixDim = /** @class */function () {
  function MatrixDim(dim, dimModel) {
    // Under the current definition, every leave corresponds a unit cell,
    // and leaves can serve as the locator of cells.
    // Therefore make sure:
    //  - The first `_leavesCount` elements in `_cells` are leaves.
    //  - `_cells[leaf.id[XY[this.dimIdx]]]` is the leaf itself.
    //  - Leaves of each subtree are placed together, that is, the leaves of a dimCell are:
    //    `this._cells.slice(dimCell.firstLeafLocator, dimCell.span[XY[this.dimIdx]])`
    this._cells = [];
    // Can be visited by `_levels[cell.level]` or `_levels[cell.id[1 - dimIdx] + _levels.length]`.
    // Items are never be null/undefined after initialized.
    this._levels = [];
    this.dim = dim;
    this.dimIdx = dim === 'x' ? 0 : 1;
    this._model = dimModel;
    this._uniqueValueGen = createUniqueValueGenerator(dim);
    var dimModelData = dimModel.get('data', true);
    if (dimModelData != null && !util.isArray(dimModelData)) {
      if (process.env.NODE_ENV !== 'production') {
        log.error("Illegal echarts option - matrix." + this.dim + ".data must be an array if specified.");
      }
      dimModelData = [];
    }
    if (dimModelData) {
      this._initByDimModelData(dimModelData);
    } else {
      this._initBySeriesData();
    }
  }
  MatrixDim.prototype._initByDimModelData = function (dimModelData) {
    var self = this;
    var _cells = self._cells;
    var _levels = self._levels;
    var sameLocatorCellsLists = []; // Save for sorting.
    var _cellCount = 0;
    self._leavesCount = traverseInitCells(dimModelData, 0, 0);
    postInitCells();
    return;
    function traverseInitCells(dimModelData, firstLeafLocator, level) {
      var totalSpan = 0;
      if (!dimModelData) {
        return totalSpan;
      }
      util.each(dimModelData, function (option, optionIdx) {
        var invalidOption = false;
        var cellOption;
        if (util.isString(option)) {
          cellOption = {
            value: option
          };
        } else if (util.isObject(option)) {
          cellOption = option;
          if (option.value != null && !util.isString(option.value)) {
            invalidOption = true;
            cellOption = {
              value: null
            };
          }
        } else {
          cellOption = {
            value: null
          };
          if (option != null) {
            invalidOption = true;
          }
        }
        if (invalidOption) {
          if (process.env.NODE_ENV !== 'production') {
            log.error("Illegal echarts option - matrix." + self.dim + ".data[" + optionIdx + "]" + ' must be `string | {value: string}`.');
          }
        }
        var cell = {
          type: matrixCoordHelper.MatrixCellLayoutInfoType.nonLeaf,
          ordinal: NaN,
          level: level,
          firstLeafLocator: firstLeafLocator,
          id: new Point.default(),
          span: matrixCoordHelper.setDimXYValue(new Point.default(), self.dimIdx, 1, 1),
          option: cellOption,
          xy: NaN,
          wh: NaN,
          dim: self,
          rect: matrixCoordHelper.createNaNRectLike()
        };
        _cellCount++;
        (sameLocatorCellsLists[firstLeafLocator] || (sameLocatorCellsLists[firstLeafLocator] = [])).push(cell);
        if (!_levels[level]) {
          // Create a level only if at least one cell exists.
          _levels[level] = {
            type: matrixCoordHelper.MatrixCellLayoutInfoType.level,
            xy: NaN,
            wh: NaN,
            option: null,
            id: new Point.default(),
            dim: self
          };
        }
        var childrenSpan = traverseInitCells(cellOption.children, firstLeafLocator, level + 1);
        var subSpan = Math.max(1, childrenSpan);
        cell.span[graphic.XY[self.dimIdx]] = subSpan;
        totalSpan += subSpan;
        firstLeafLocator += subSpan;
      });
      return totalSpan;
    }
    function postInitCells() {
      // Sort to make sure the leaves are at the beginning, so that
      // they can be used as the locator of body cells.
      var categories = [];
      while (_cells.length < _cellCount) {
        for (var locator = 0; locator < sameLocatorCellsLists.length; locator++) {
          var cell = sameLocatorCellsLists[locator].pop();
          if (cell) {
            cell.ordinal = categories.length;
            var val = cell.option.value;
            categories.push(val);
            _cells.push(cell);
            self._uniqueValueGen.calcDupBase(val);
          }
        }
      }
      self._uniqueValueGen.ensureValueUnique(categories, _cells);
      var ordinalMeta = self._ordinalMeta = new OrdinalMeta.default({
        categories: categories,
        needCollect: false,
        deduplication: false
      });
      self._scale = new Ordinal.default({
        ordinalMeta: ordinalMeta
      });
      for (var idx = 0; idx < self._leavesCount; idx++) {
        var leaf = self._cells[idx];
        leaf.type = matrixCoordHelper.MatrixCellLayoutInfoType.leaf;
        // Handle the tree level variation: enlarge the span of the leaves to reach the body cells.
        leaf.span[graphic.XY[1 - self.dimIdx]] = self._levels.length - leaf.level;
      }
      self._initCellsId();
      self._initLevelIdOptions();
    }
  };
  MatrixDim.prototype._initBySeriesData = function () {
    var self = this;
    self._leavesCount = 0;
    self._levels = [{
      type: matrixCoordHelper.MatrixCellLayoutInfoType.level,
      xy: NaN,
      wh: NaN,
      option: null,
      id: new Point.default(),
      dim: self
    }];
    self._initLevelIdOptions();
    var ordinalMeta = self._ordinalMeta = new OrdinalMeta.default({
      needCollect: true,
      deduplication: true,
      onCollect: function (value, ordinalNumber) {
        var cell = self._cells[ordinalNumber] = {
          type: matrixCoordHelper.MatrixCellLayoutInfoType.leaf,
          ordinal: ordinalNumber,
          level: 0,
          firstLeafLocator: ordinalNumber,
          id: new Point.default(),
          span: matrixCoordHelper.setDimXYValue(new Point.default(), self.dimIdx, 1, 1),
          // Theoretically `value` is from `dataset` or `series.data`, so it may be any type.
          // Do not restrict this case for user's convenience, and here simply convert it to
          // string for display.
          option: {
            value: value + ''
          },
          xy: NaN,
          wh: NaN,
          dim: self,
          rect: matrixCoordHelper.createNaNRectLike()
        };
        self._leavesCount++;
        self._setCellId(cell);
      }
    });
    self._scale = new Ordinal.default({
      ordinalMeta: ordinalMeta
    });
  };
  MatrixDim.prototype._setCellId = function (cell) {
    var levelsLen = this._levels.length;
    var dimIdx = this.dimIdx;
    matrixCoordHelper.setDimXYValue(cell.id, dimIdx, cell.firstLeafLocator, cell.level - levelsLen);
  };
  MatrixDim.prototype._initCellsId = function () {
    var levelsLen = this._levels.length;
    var dimIdx = this.dimIdx;
    util.each(this._cells, function (cell) {
      matrixCoordHelper.setDimXYValue(cell.id, dimIdx, cell.firstLeafLocator, cell.level - levelsLen);
    });
  };
  MatrixDim.prototype._initLevelIdOptions = function () {
    var levelsLen = this._levels.length;
    var dimIdx = this.dimIdx;
    var levelOptionList = this._model.get('levels', true);
    levelOptionList = util.isArray(levelOptionList) ? levelOptionList : [];
    util.each(this._levels, function (levelCfg, level) {
      matrixCoordHelper.setDimXYValue(levelCfg.id, dimIdx, 0, level - levelsLen);
      levelCfg.option = levelOptionList[level];
    });
  };
  MatrixDim.prototype.shouldShow = function () {
    return !!this._model.getShallow('show', true);
  };
  /**
   * Iterate leaves (they are layout units) if dimIdx === this.dimIdx.
   * Iterate levels if dimIdx !== this.dimIdx.
   */
  MatrixDim.prototype.resetLayoutIterator = function (it, dimIdx, startLocator, count) {
    it = it || new model.ListIterator();
    if (dimIdx === this.dimIdx) {
      var len = this._leavesCount;
      var startIdx = startLocator != null ? Math.max(0, startLocator) : 0;
      count = count != null ? Math.min(count, len) : len;
      it.reset(this._cells, startIdx, startIdx + count);
    } else {
      var len = this._levels.length;
      // Corner locator is from `-this._levels.length` to `-1`.
      var startIdx = startLocator != null ? Math.max(0, startLocator + len) : 0;
      count = count != null ? Math.min(count, len) : len;
      it.reset(this._levels, startIdx, startIdx + count);
    }
    return it;
  };
  MatrixDim.prototype.resetCellIterator = function (it) {
    return (it || new model.ListIterator()).reset(this._cells, 0);
  };
  MatrixDim.prototype.resetLevelIterator = function (it) {
    return (it || new model.ListIterator()).reset(this._levels, 0);
  };
  MatrixDim.prototype.getLayout = function (outRect, dimIdx, locator) {
    var layout = this.getUnitLayoutInfo(dimIdx, locator);
    outRect[graphic.XY[dimIdx]] = layout ? layout.xy : NaN;
    outRect[graphic.WH[dimIdx]] = layout ? layout.wh : NaN;
  };
  /**
   * Get leaf cell or get level info.
   * Should be able to return null/undefined if not found on x or y, thus input `dimIdx` is needed.
   */
  MatrixDim.prototype.getUnitLayoutInfo = function (dimIdx, locator) {
    return dimIdx === this.dimIdx ? locator < this._leavesCount ? this._cells[locator] : undefined : this._levels[locator + this._levels.length];
  };
  /**
   * Get dimension cell by data, including leaves and non-leaves.
   */
  MatrixDim.prototype.getCell = function (value) {
    var ordinal = this._scale.parse(value);
    return util.eqNaN(ordinal) ? undefined : this._cells[ordinal];
  };
  /**
   * Get leaf count or get level count.
   */
  MatrixDim.prototype.getLocatorCount = function (dimIdx) {
    return dimIdx === this.dimIdx ? this._leavesCount : this._levels.length;
  };
  MatrixDim.prototype.getOrdinalMeta = function () {
    return this._ordinalMeta;
  };
  return MatrixDim;
}();
function createUniqueValueGenerator(dim) {
  var dimUpper = dim.toUpperCase();
  var defaultValReg = new RegExp("^" + dimUpper + "([0-9]+)$");
  var dupBase = 0;
  function calcDupBase(val) {
    var matchResult;
    if (val != null && (matchResult = val.match(defaultValReg))) {
      dupBase = number.mathMax(dupBase, +matchResult[1] + 1);
    }
  }
  function makeUniqueValue() {
    return "" + dimUpper + dupBase++;
  }
  // Duplicated value is allowed, because the `matrix.x/y.data` can be a tree and it's reasonable
  // that leaves in different subtrees has the same text. But only the first one is allowed to be
  // queried by the text, and the other ones can only be queried by index.
  // Additionally, `matrix.x/y.data: [null, null, ...]` is allowed.
  function ensureValueUnique(categories, cells) {
    // A simple way to deduplicate or handle illegal or not specified values to avoid unexpected behaviors.
    // The tree structure should not be broken even if duplicated.
    var cateMap = util.createHashMap();
    for (var idx = 0; idx < categories.length; idx++) {
      var value = categories[idx];
      // value may be set to NullUndefined by users or if illegal.
      if (value == null || cateMap.get(value) != null) {
        // Still display the original option.value if duplicated, but loose the ability to query by text.
        categories[idx] = value = makeUniqueValue();
        cells[idx].option = util.defaults({
          value: value
        }, cells[idx].option);
      }
      cateMap.set(value, true);
    }
  }
  return {
    calcDupBase: calcDupBase,
    ensureValueUnique: ensureValueUnique
  };
}

exports.MatrixDim = MatrixDim;
