'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var tslib_es6 = require('../../../../../../tslib@2.3.0/node_modules/tslib/tslib.es6.js');
var util = require('../../../../../../zrender@6.0.0/node_modules/zrender/lib/core/util.js');
var visualDefault = require('../../visual/visualDefault.js');
var VisualMapping = require('../../visual/VisualMapping.js');
var visualSolution = require('../../visual/visualSolution.js');
var model = require('../../util/model.js');
var number = require('../../util/number.js');
var Component = require('../../model/Component.js');
var tokens = require('../../visual/tokens.js');

var mapVisual = VisualMapping.default.mapVisual;
var eachVisual = VisualMapping.default.eachVisual;
var isArray = util.isArray;
var each = util.each;
var asc = number.asc;
var linearMap = number.linearMap;
var VisualMapModel = /** @class */function (_super) {
  tslib_es6.__extends(VisualMapModel, _super);
  function VisualMapModel() {
    var _this = _super !== null && _super.apply(this, arguments) || this;
    _this.type = VisualMapModel.type;
    _this.stateList = ['inRange', 'outOfRange'];
    _this.replacableOptionKeys = ['inRange', 'outOfRange', 'target', 'controller', 'color'];
    _this.layoutMode = {
      type: 'box',
      ignoreSize: true
    };
    /**
     * [lowerBound, upperBound]
     */
    _this.dataBound = [-Infinity, Infinity];
    _this.targetVisuals = {};
    _this.controllerVisuals = {};
    return _this;
  }
  VisualMapModel.prototype.init = function (option, parentModel, ecModel) {
    this.mergeDefaultAndTheme(option, ecModel);
  };
  /**
   * @protected
   */
  VisualMapModel.prototype.optionUpdated = function (newOption, isInit) {
    var thisOption = this.option;
    !isInit && visualSolution.replaceVisualOption(thisOption, newOption, this.replacableOptionKeys);
    this.textStyleModel = this.getModel('textStyle');
    this.resetItemSize();
    this.completeVisualOption();
  };
  /**
   * @protected
   */
  VisualMapModel.prototype.resetVisual = function (supplementVisualOption) {
    var stateList = this.stateList;
    supplementVisualOption = util.bind(supplementVisualOption, this);
    this.controllerVisuals = visualSolution.createVisualMappings(this.option.controller, stateList, supplementVisualOption);
    this.targetVisuals = visualSolution.createVisualMappings(this.option.target, stateList, supplementVisualOption);
  };
  /**
   * @public
   */
  VisualMapModel.prototype.getItemSymbol = function () {
    return null;
  };
  /**
   * @return An array of series indices.
   */
  VisualMapModel.prototype.getTargetSeriesIndices = function () {
    var optionSeriesId = this.option.seriesId;
    var optionSeriesIndex = this.option.seriesIndex;
    if (optionSeriesIndex == null && optionSeriesId == null) {
      optionSeriesIndex = 'all';
    }
    var seriesModels = model.queryReferringComponents(this.ecModel, 'series', {
      index: optionSeriesIndex,
      id: optionSeriesId
    }, {
      useDefault: false,
      enableAll: true,
      enableNone: false
    }).models;
    return util.map(seriesModels, function (seriesModel) {
      return seriesModel.componentIndex;
    });
  };
  /**
   * @public
   */
  VisualMapModel.prototype.eachTargetSeries = function (callback, context) {
    util.each(this.getTargetSeriesIndices(), function (seriesIndex) {
      var seriesModel = this.ecModel.getSeriesByIndex(seriesIndex);
      if (seriesModel) {
        callback.call(context, seriesModel);
      }
    }, this);
  };
  /**
   * @pubilc
   */
  VisualMapModel.prototype.isTargetSeries = function (seriesModel) {
    var is = false;
    this.eachTargetSeries(function (model) {
      model === seriesModel && (is = true);
    });
    return is;
  };
  /**
   * @example
   * this.formatValueText(someVal); // format single numeric value to text.
   * this.formatValueText(someVal, true); // format single category value to text.
   * this.formatValueText([min, max]); // format numeric min-max to text.
   * this.formatValueText([this.dataBound[0], max]); // using data lower bound.
   * this.formatValueText([min, this.dataBound[1]]); // using data upper bound.
   *
   * @param value Real value, or this.dataBound[0 or 1].
   * @param isCategory Only available when value is number.
   * @param edgeSymbols Open-close symbol when value is interval.
   * @protected
   */
  VisualMapModel.prototype.formatValueText = function (value, isCategory, edgeSymbols) {
    var option = this.option;
    var precision = option.precision;
    var dataBound = this.dataBound;
    var formatter = option.formatter;
    var isMinMax;
    edgeSymbols = edgeSymbols || ['<', '>'];
    if (util.isArray(value)) {
      value = value.slice();
      isMinMax = true;
    }
    var textValue = isCategory ? value // Value is string when isCategory
    : isMinMax ? [toFixed(value[0]), toFixed(value[1])] : toFixed(value);
    if (util.isString(formatter)) {
      return formatter.replace('{value}', isMinMax ? textValue[0] : textValue).replace('{value2}', isMinMax ? textValue[1] : textValue);
    } else if (util.isFunction(formatter)) {
      return isMinMax ? formatter(value[0], value[1]) : formatter(value);
    }
    if (isMinMax) {
      if (value[0] === dataBound[0]) {
        return edgeSymbols[0] + ' ' + textValue[1];
      } else if (value[1] === dataBound[1]) {
        return edgeSymbols[1] + ' ' + textValue[0];
      } else {
        return textValue[0] + ' - ' + textValue[1];
      }
    } else {
      // Format single value (includes category case).
      return textValue;
    }
    function toFixed(val) {
      return val === dataBound[0] ? 'min' : val === dataBound[1] ? 'max' : (+val).toFixed(Math.min(precision, 20));
    }
  };
  /**
   * @protected
   */
  VisualMapModel.prototype.resetExtent = function () {
    var thisOption = this.option;
    // Can not calculate data extent by data here.
    // Because series and data may be modified in processing stage.
    // So we do not support the feature "auto min/max".
    var extent = asc([thisOption.min, thisOption.max]);
    this._dataExtent = extent;
  };
  /**
   * PENDING:
   * delete this method if no outer usage.
   *
   * Return  Concrete dimension. If null/undefined is returned, no dimension is used.
   */
  // getDataDimension(data: SeriesData) {
  //     const optDim = this.option.dimension;
  //     if (optDim != null) {
  //         return data.getDimension(optDim);
  //     }
  //     const dimNames = data.dimensions;
  //     for (let i = dimNames.length - 1; i >= 0; i--) {
  //         const dimName = dimNames[i];
  //         const dimInfo = data.getDimensionInfo(dimName);
  //         if (!dimInfo.isCalculationCoord) {
  //             return dimName;
  //         }
  //     }
  // }
  VisualMapModel.prototype.getDataDimensionIndex = function (data) {
    var optDim = this.option.dimension;
    if (optDim != null) {
      return data.getDimensionIndex(optDim);
    }
    var dimNames = data.dimensions;
    for (var i = dimNames.length - 1; i >= 0; i--) {
      var dimName = dimNames[i];
      var dimInfo = data.getDimensionInfo(dimName);
      if (!dimInfo.isCalculationCoord) {
        return dimInfo.storeDimIndex;
      }
    }
  };
  VisualMapModel.prototype.getExtent = function () {
    return this._dataExtent.slice();
  };
  VisualMapModel.prototype.completeVisualOption = function () {
    var ecModel = this.ecModel;
    var thisOption = this.option;
    var base = {
      inRange: thisOption.inRange,
      outOfRange: thisOption.outOfRange
    };
    var target = thisOption.target || (thisOption.target = {});
    var controller = thisOption.controller || (thisOption.controller = {});
    util.merge(target, base); // Do not override
    util.merge(controller, base); // Do not override
    var isCategory = this.isCategory();
    completeSingle.call(this, target);
    completeSingle.call(this, controller);
    completeInactive.call(this, target, 'inRange', 'outOfRange');
    // completeInactive.call(this, target, 'outOfRange', 'inRange');
    completeController.call(this, controller);
    function completeSingle(base) {
      // Compatible with ec2 dataRange.color.
      // The mapping order of dataRange.color is: [high value, ..., low value]
      // whereas inRange.color and outOfRange.color is [low value, ..., high value]
      // Notice: ec2 has no inverse.
      if (isArray(thisOption.color)
      // If there has been inRange: {symbol: ...}, adding color is a mistake.
      // So adding color only when no inRange defined.
      && !base.inRange) {
        base.inRange = {
          color: thisOption.color.slice().reverse()
        };
      }
      // Compatible with previous logic, always give a default color, otherwise
      // simple config with no inRange and outOfRange will not work.
      // Originally we use visualMap.color as the default color, but setOption at
      // the second time the default color will be erased. So we change to use
      // constant DEFAULT_COLOR.
      // If user do not want the default color, set inRange: {color: null}.
      base.inRange = base.inRange || {
        color: ecModel.get('gradientColor')
      };
    }
    function completeInactive(base, stateExist, stateAbsent) {
      var optExist = base[stateExist];
      var optAbsent = base[stateAbsent];
      if (optExist && !optAbsent) {
        optAbsent = base[stateAbsent] = {};
        each(optExist, function (visualData, visualType) {
          if (!VisualMapping.default.isValidType(visualType)) {
            return;
          }
          var defa = visualDefault.default.get(visualType, 'inactive', isCategory);
          if (defa != null) {
            optAbsent[visualType] = defa;
            // Compatibable with ec2:
            // Only inactive color to rgba(0,0,0,0) can not
            // make label transparent, so use opacity also.
            if (visualType === 'color' && !optAbsent.hasOwnProperty('opacity') && !optAbsent.hasOwnProperty('colorAlpha')) {
              optAbsent.opacity = [0, 0];
            }
          }
        });
      }
    }
    function completeController(controller) {
      var symbolExists = (controller.inRange || {}).symbol || (controller.outOfRange || {}).symbol;
      var symbolSizeExists = (controller.inRange || {}).symbolSize || (controller.outOfRange || {}).symbolSize;
      var inactiveColor = this.get('inactiveColor');
      var itemSymbol = this.getItemSymbol();
      var defaultSymbol = itemSymbol || 'roundRect';
      each(this.stateList, function (state) {
        var itemSize = this.itemSize;
        var visuals = controller[state];
        // Set inactive color for controller if no other color
        // attr (like colorAlpha) specified.
        if (!visuals) {
          visuals = controller[state] = {
            color: isCategory ? inactiveColor : [inactiveColor]
          };
        }
        // Consistent symbol and symbolSize if not specified.
        if (visuals.symbol == null) {
          visuals.symbol = symbolExists && util.clone(symbolExists) || (isCategory ? defaultSymbol : [defaultSymbol]);
        }
        if (visuals.symbolSize == null) {
          visuals.symbolSize = symbolSizeExists && util.clone(symbolSizeExists) || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]);
        }
        // Filter none
        visuals.symbol = mapVisual(visuals.symbol, function (symbol) {
          return symbol === 'none' ? defaultSymbol : symbol;
        });
        // Normalize symbolSize
        var symbolSize = visuals.symbolSize;
        if (symbolSize != null) {
          var max_1 = -Infinity;
          // symbolSize can be object when categories defined.
          eachVisual(symbolSize, function (value) {
            value > max_1 && (max_1 = value);
          });
          visuals.symbolSize = mapVisual(symbolSize, function (value) {
            return linearMap(value, [0, max_1], [0, itemSize[0]], true);
          });
        }
      }, this);
    }
  };
  VisualMapModel.prototype.resetItemSize = function () {
    this.itemSize = [parseFloat(this.get('itemWidth')), parseFloat(this.get('itemHeight'))];
  };
  VisualMapModel.prototype.isCategory = function () {
    return !!this.option.categories;
  };
  /**
   * @public
   * @abstract
   */
  VisualMapModel.prototype.setSelected = function (selected) {};
  VisualMapModel.prototype.getSelected = function () {
    return null;
  };
  /**
   * @public
   * @abstract
   */
  VisualMapModel.prototype.getValueState = function (value) {
    return null;
  };
  /**
   * FIXME
   * Do not publish to thirt-part-dev temporarily
   * util the interface is stable. (Should it return
   * a function but not visual meta?)
   *
   * @pubilc
   * @abstract
   * @param getColorVisual
   *        params: value, valueState
   *        return: color
   * @return {Object} visualMeta
   *        should includes {stops, outerColors}
   *        outerColor means [colorBeyondMinValue, colorBeyondMaxValue]
   */
  VisualMapModel.prototype.getVisualMeta = function (getColorVisual) {
    return null;
  };
  VisualMapModel.type = 'visualMap';
  VisualMapModel.dependencies = ['series'];
  VisualMapModel.defaultOption = {
    show: true,
    // zlevel: 0,
    z: 4,
    // seriesIndex: 'all',
    min: 0,
    max: 200,
    left: 0,
    right: null,
    top: null,
    bottom: 0,
    itemWidth: null,
    itemHeight: null,
    inverse: false,
    orient: 'vertical',
    backgroundColor: tokens.default.color.transparent,
    borderColor: tokens.default.color.borderTint,
    contentColor: tokens.default.color.theme[0],
    inactiveColor: tokens.default.color.disabled,
    borderWidth: 0,
    padding: tokens.default.size.m,
    // 接受数组分别设定上右下左边距，同css
    textGap: 10,
    precision: 0,
    textStyle: {
      color: tokens.default.color.secondary // 值域文字颜色
    }
  };
  return VisualMapModel;
}(Component.default);

exports.default = VisualMapModel;
