/**
 * @fileOverview 所有 Geometry 的基类
 * @author dxq613@gmail.com
 */
const Attr = require('@antv/attr/lib');
const Adjust = require('@antv/adjust/lib');
const Base = require('../base');
const Util = require('../util');
const Global = require('../global');
const Labels = require('./label/index');
const Shape = require('./shape/shape');
const TooltipMixin = require('./mixin/tooltip');
const ActiveMixin = require('./mixin/active');
const SelectMixin = require('./mixin/select');
const parseFields = require('./util/parse-fields');

const GROUP_ATTRS = [ 'color', 'shape', 'size' ];
const FIELD_ORIGIN = '_origin';

// 转换成对象的数组 [{type: 'adjust'}]
function parseAdjusts(adjusts) {
  // 如果是字符串或者对象转换成数组
  if (Util.isString(adjusts) || Util.isPlainObject(adjusts)) {
    adjusts = [ adjusts ];
  }
  Util.each(adjusts, (adjust, index) => {
    if (!Util.isObject(adjust)) {
      adjusts[index] = { type: adjust };
    }
  });
  return adjusts;
}

/**
 * 几何标记
 * @class Geom
 */
class GeomBase extends Base {

  /**
   * 获取默认的配置属性
   * @protected
   * @return {Object} 默认属性
   */
  getDefaultCfg() {
    return {
      /**
       * 标记 _id 用于区分执行动画
       * @type {String}
       */
      _id: null,
      /**
       * 类型
       * @type {String}
       */
      type: 'base',

      /**
       * 坐标系
       * @type {Object}
       */
      coord: null,

      /**
       * 属性映射集
       * @protected
       * @type {Object}
       */
      attrs: {},

      /**
       * 所属的View
       * @type {View}
       */
      view: null,

      /**
       * 几何标记显示的数据
       * @type {Array}
       */
      data: [],

      /**
       * 相关的度量
       * @type {Object}
       */
      scales: {},

      /**
       * 绘图容器
       * @type {Object}
       */
      container: null,

      /**
       * 文本容器
       * @type {Object}
       */
      labelContainer: null,

      /**
       * 图形容器
       * @type {Object}
       */
      shapeContainer: null,

      /**
       * 几何标记的一些配置项，用于延迟生成图表
       * @type {Object}
       */
      attrOptions: {

      },
      // 样式配置项
      styleOptions: null,
      // 选中时的配置项
      selectedOptions: null,
      // active 时的配置项
      activedOptions: null,
      /**
       * 某些类存在默认的adjust，不能更改 adjust
       * @type {Boolean}
       */
      hasDefaultAdjust: false,
      // 数据调整类型
      adjusts: null,
      /**
       * 使用形状的类型
       * @protected
       * @type {String}
       */
      shapeType: null,
      /**
       * 是否生成多个点来绘制图形
       * @protected
       * @type {Boolean}
       */
      generatePoints: false,

      /**
       * 数据是否进行排序
       * @type {Boolean}
       */
      sortable: false,

      labelCfg: null,
      /**
       * 是否共享 tooltip
       * @type {Boolean}
       */
      shareTooltip: true,
      tooltipCfg: null,
      /**
       * 是否执行动画，默认执行
       * @type {Boolean}
       */
      animate: true,
      /**
       * 动画配置
       * @type {[type]}
       */
      animateCfg: null,
      visible: true
    };
  }

  constructor(cfg) {
    super(cfg);
    this.viewTheme = this.get('viewTheme');
    Util.assign(this, TooltipMixin, ActiveMixin, SelectMixin);
    if (this.get('container')) {
      this._initContainer();
    }
    this._initOptions();
  }

  // 初始化时对配置项的格式化
  _initOptions() {
    let adjusts = this.get('adjusts');
    if (adjusts) {
      adjusts = parseAdjusts(adjusts);
      this.set('adjusts', adjusts);
    }
  }

  _createScale(field, data) {
    const scales = this.get('scales');
    let scale = scales[field];
    if (!scale) {
      scale = this.get('view').createScale(field, data);
      scales[field] = scale;
    }
    return scale;
  }

  _setAttrOptions(attrName, attrCfg) {
    const options = this.get('attrOptions');
    options[attrName] = attrCfg;
  }
  _createAttrOption(attrName, field, cfg, defaultValues) {
    const attrCfg = {};
    attrCfg.field = field;
    if (cfg) {
      if (Util.isFunction(cfg)) {
        attrCfg.callback = cfg;
      } else {
        attrCfg.values = cfg;
      }
    } else if (attrName !== 'color') {
      attrCfg.values = defaultValues;
    }
    this._setAttrOptions(attrName, attrCfg);
  }

  /**
   * 位置属性映射
   * @chainable
   * @param  {String} field 字段名
   * @return {Geom} geom 当前几何标记
   */
  position(field) {
    this._setAttrOptions('position', {
      field
    });
    return this;
  }

  /**
   * 颜色属性映射
   * @chainable
   * @param  {String} field 字段名
   * @param  {Array|Function} values 颜色的数组或者回调函数
   * @return {Geom} geom 当前几何标记
   */
  color(field, values) {
    const viewTheme = this.viewTheme || Global;
    this._createAttrOption('color', field, values, viewTheme.colors);
    return this;
  }

  /**
   * 大小属性映射
   * @chainable
   * @param  {String} field 字段名
   * @param  {Array|Function} values 大小的数组或者回调函数
   * @return {Geom} geom 当前几何标记
   */
  size(field, values) {
    const viewTheme = this.viewTheme || Global;
    this._createAttrOption('size', field, values, viewTheme.sizes);
    return this;
  }

  /**
   * 形状属性映射
   * @chainable
   * @param  {String} field 字段名
   * @param  {Array|Function} values 大小的数组或者回调函数
   * @return {Geom} geom 当前几何标记
   */
  shape(field, values) {
    const viewTheme = this.viewTheme || Global;
    const type = this.get('type');
    const shapes = viewTheme.shapes[type] || [];
    this._createAttrOption('shape', field, values, shapes);
    return this;
  }

  /**
   * 透明度属性映射
   * @chainable
   * @param  {String} field 字段名
   * @param  {Array|Function} values 透明度的数组或者回调函数
   * @return {Geom} geom 当前几何标记
   */
  opacity(field, values) {
    const viewTheme = this.viewTheme || Global;
    this._createAttrOption('opacity', field, values, viewTheme.opacities);
    return this;
  }

  style(field, cfg) {
    let styleOptions = this.get('styleOptions');
    if (!styleOptions) {
      styleOptions = {};
      this.set('styleOptions', styleOptions);
    }
    if (Util.isObject(field)) {
      cfg = field;
      field = null;
    }
    let fields;
    if (field) {
      fields = parseFields(field);
    }
    styleOptions.fields = fields;
    styleOptions.style = cfg;
    return this;
  }

  label(field, callback, cfg) {
    const self = this;
    let labelCfg = self.get('labelCfg');
    // const scales = Util.map(self.get('labelCfg').fields, field => self._createScale(field));
    if (!labelCfg) {
      labelCfg = {};
      self.set('labelCfg', labelCfg);
    }
    let fields;
    if (field) {
      fields = parseFields(field);
    }
    labelCfg.fields = fields;
    // 如果存在回调函数
    if (Util.isFunction(callback)) {
      if (!cfg) {
        cfg = {};
      }
      labelCfg.callback = callback;
    } else if (Util.isObject(callback)) { // 如果没有设置回调函数
      cfg = callback;
    }
    labelCfg.globalCfg = cfg;
    return this;
  }

  tooltip(field, cfg) {
    let tooltipCfg = this.get('tooltipCfg');
    if (!tooltipCfg) {
      tooltipCfg = {};
    }
    if (field === false) { // geom 关闭 tooltip
      this.set('tooltipCfg', false);
    } else {
      let tooltipFields;
      if (field) {
        tooltipFields = parseFields(field);
      }
      tooltipCfg.fields = tooltipFields;
      tooltipCfg.cfg = cfg;
    }

    this.set('tooltipCfg', tooltipCfg);
    return this;
  }

  animate(cfg) {
    this.set('animateCfg', cfg);
    return this;
  }

  /**
   * 是否允许使用默认的图形激活交互
   * @param  {Boolean} enable 是否允许激活开关
   * @param {Object} cfg 激活的配置项
   * @return {Geom}    返回 geom 自身
   */
  active(enable, cfg) {
    if (enable === false) {
      this.set('allowActive', false);
    } else if (Util.isObject(enable)) {
      this.set('allowActive', true);
      this.set('activedOptions', enable);
    } else {
      this.set('allowActive', true);
      this.set('activedOptions', cfg);
    }
    return this;
  }

  /**
   * 对 geometry 进行数据调整
   * @chainable
   * @param  {String|Array|null} adjusts 数据调整的类型
   * @return {Object} geometry 对象
   */
  adjust(adjusts) {
    if (!this.get('hasDefaultAdjust')) {
      if (adjusts) {
        adjusts = parseAdjusts(adjusts);
      }
      this.set('adjusts', adjusts);
    }
    return this;
  }

  /**
   * 设置图形的选中模式
   * @param  {Boolean|Object} enable 布尔类型用于模式开关，对象类型用于配置
   * @param  {Object} cfg    选中配置项
   * @return {Geom}          返回 geom 自身
   */
  select(enable, cfg) {
    if (enable === false) {
      this.set('allowSelect', false);
    } else if (Util.isObject(enable)) {
      this.set('allowSelect', true);
      this.set('selectedOptions', enable);
    } else {
      this.set('allowSelect', true);
      this.set('selectedOptions', cfg);
    }

    return this;
  }

  hasAdjust(adjustType) {
    const self = this;
    const adjusts = self.get('adjusts');
    if (!adjustType) {
      return false;
    }
    let rst = false;
    Util.each(adjusts, adjust => {
      if (adjust.type === adjustType) {
        rst = true;
        return false;
      }
    });
    return rst;
  }

  hasStack() {
    let isStacked = this.get('isStacked');
    if (Util.isNil(isStacked)) {
      isStacked = this.hasAdjust('stack');
      this.set('isStacked', isStacked);
    }
    return isStacked;
  }

  isInCircle() {
    const coord = this.get('coord');
    return coord && coord.isPolar;
  }

  _initContainer() {
    const self = this;
    let shapeContainer = self.get('shapeContainer');
    if (!shapeContainer) {
      const container = self.get('container');
      const view = self.get('view');
      const viewId = view && view.get('_id');
      shapeContainer = container.addGroup({
        viewId,
        visible: self.get('visible')
      });
      self.set('shapeContainer', shapeContainer);
    }
  }

  init() {
    const self = this;
    self._initContainer();
    self._initAttrs();
    if (self.get('tooltipCfg') && self.get('tooltipCfg').fields) {
      const tooltipFields = self.get('tooltipCfg').fields;
      Util.each(tooltipFields, field => {
        self._createScale(field);
      });
    }
    const dataArray = self._processData();
    if (self.get('adjusts')) {
      self._adjust(dataArray);
    }
    self.set('dataArray', dataArray);
  }

  // step 1: init attrs
  _initAttrs() {
    const self = this;
    const attrs = self.get('attrs');
    const attrOptions = self.get('attrOptions');
    const coord = self.get('coord');
    const viewTheme = self.viewTheme || Global;
    let isPie = false;

    for (const type in attrOptions) {
      if (attrOptions.hasOwnProperty(type)) {
        const option = attrOptions[type];
        const className = Util.upperFirst(type);
        const fields = parseFields(option.field);
        if (type === 'position') {
          option.coord = coord;
          // 饼图坐标系下，填充一维
          if (fields.length === 1 && coord.type === 'theta') {
            fields.unshift('1');
            isPie = true;
          }
        }
        const scales = [];
        for (let i = 0; i < fields.length; i++) {
          const field = fields[i];
          const scale = self._createScale(field);
          if (type === 'color' && Util.isNil(option.values)) { // 设置 color 的默认色值
            if (scale.values.length <= 8) {
              option.values = isPie ? viewTheme.colors_pie : viewTheme.colors;
            } else if (scale.values.length <= 16) {
              option.values = isPie ? viewTheme.colors_pie_16 : viewTheme.colors_16;
            } else {
              option.values = viewTheme.colors_24;
            }

            if (Util.isNil(option.values)) {
              option.values = viewTheme.colors; // 防止主题没有声明诸如 colors_pie 的属性
            }
          }
          scales.push(scale);
        }
        // 饼图需要填充满整个空间
        if (coord.type === 'theta' && type === 'position' && scales.length > 1) {
          const yScale = scales[1];
          const min = 0;
          let max = Math.max.apply(null, yScale.values);
          if (!isFinite(max)) {
            max = 1;
          }
          yScale.change({
            nice: false,
            min,
            max
          });
        }
        option.scales = scales;
        const attr = new Attr[className](option);
        attrs[type] = attr;
      }
    }
  }
  // step 2: 处理数据
  _processData() {
    const self = this;
    const data = this.get('data');
    const dataArray = [];
    const groupedArray = this._groupData(data);
    for (let i = 0; i < groupedArray.length; i++) {
      const subData = groupedArray[i];
      const tempData = self._saveOrigin(subData);

      dataArray.push(self._numberic(tempData));
    }
    return dataArray;
  }

  // step 2.1 数据分组
  _groupData(data) {
    const groupScales = this._getGroupScales();
    const fields = groupScales.map(scale => scale.field);

    return Util.Array.group(data, fields);
  }

  // step 2.2 数据调整前保存原始数据
  _saveOrigin(data) {
    const rst = [];
    for (let i = 0; i < data.length; i++) {
      const origin = data[i];
      const obj = {};
      for (const k in origin) {
        obj[k] = origin[k];
      }
      // const obj = Util.mix({}, origin);
      obj[FIELD_ORIGIN] = origin;
      rst.push(obj);
    }
    return rst;
  }

  // step 2.3 将分类数据翻译成数据, 仅对位置相关的度量进行数字化处理
  _numberic(data) {
    const positionAttr = this.getAttr('position');
    const scales = positionAttr.scales;
    const result = [];

    for (let j = 0; j < data.length; j++) {
      const obj = data[j];
      let isValidate = true;

      for (let i = 0; i < Math.min(2, scales.length); i++) {
        const scale = scales[i];
        if (scale.isCategory) {
          const field = scale.field;
          obj[field] = scale.translate(obj[field]);

          if (Number.isNaN(obj[field])) {
            // 当分类为 NaN 时，说明该条数据不在定义域内，需要过滤掉
            isValidate = false;
          }
        }
      }

      if (isValidate) {
        result.push(obj);
      }
    }

    return result;
  }

  _getGroupScales() {
    const self = this;
    let scales = self.get('groupScales');
    if (!scales) {
      scales = [];
      const attrs = self.get('attrs');
      Util.each(attrs, attr => {
        if (GROUP_ATTRS.includes(attr.type)) {
          const attrScales = attr.scales;
          Util.each(attrScales, scale => {
            if (scale.isCategory && Util.indexOf(scales, scale) === -1) {
              scales.push(scale);
            }
          });
        }
      });
      self.set('groupScales', scales);
    }
    return scales;
  }

  _updateStackRange(field, scale, dataArray) {
    const mergeArray = Util.Array.merge(dataArray);
    let min = scale.min;
    let max = scale.max;
    for (let i = 0; i < mergeArray.length; i++) {
      const obj = mergeArray[i];

      // 过滤掉非法数据
      if (!Util.isArray(obj[field])) {
        continue;
      }
      const tmpMin = Math.min.apply(null, obj[field]);
      const tmpMax = Math.max.apply(null, obj[field]);
      if (tmpMin < min) {
        min = tmpMin;
      }
      if (tmpMax > max) {
        max = tmpMax;
      }
    }
    if (min < scale.min || max > scale.max) {
      scale.change({
        min,
        max
      });
    }
  }

  // step 2.2 调整数据
  _adjust(dataArray) {
    // 当数据为空的时候，就不需要对数据进行调整了
    if (!dataArray || !dataArray.length) { return; }
    const self = this;
    const adjusts = self.get('adjusts');
    const viewTheme = this.viewTheme || Global;

    const yScale = self.getYScale();
    const xScale = self.getXScale();
    const xField = xScale.field;
    const yField = yScale ? yScale.field : null;
    Util.each(adjusts, adjust => {
      const adjustCfg = Util.mix({
        xField,
        yField
      }, adjust);
      const adjustType = Util.upperFirst(adjust.type);
      if (adjustType === 'Dodge') {
        const adjustNames = [];
        if (xScale.isCategory || xScale.isIdentity) {
          adjustNames.push('x');
        } else if (!yScale) {
          adjustNames.push('y');
        } else {
          throw new Error('dodge is not support linear attribute, please use category attribute!');
        }
        adjustCfg.adjustNames = adjustNames;
        adjustCfg.dodgeRatio = adjustCfg.dodgeRatio || viewTheme.widthRatio.column;
        /* if (self.isInCircle()) {
          adjustCfg.dodgeRatio = 1;
          adjustCfg.marginRatio = 0;
        }*/
      } else if (adjustType === 'Stack') {
        const coord = self.get('coord');
        if (!yScale) {
          // 一维的情况下获取高度和默认size
          adjustCfg.height = coord.getHeight();
          const size = self.getDefaultValue('size') || 3;
          adjustCfg.size = size;
        }
        // 不进行 transpose 时，用户又没有设置这个参数时，默认从上向下
        if (!coord.isTransposed && Util.isNil(adjustCfg.reverseOrder)) {
          adjustCfg.reverseOrder = true;
        }
      }
      const adjustElement = new Adjust[adjustType](adjustCfg);
      adjustElement.processAdjust(dataArray);
      if (adjustType === 'Stack' && yScale) {
        self._updateStackRange(yField, yScale, dataArray);
      }
    });
  }

  /**
   * @internal 设置coord，通常外部容器变化时，coord 会发生变化
   * @param {Object} coord 坐标系
   */
  setCoord(coord) {
    this.set('coord', coord);
    const position = this.getAttr('position');
    const shapeContainer = this.get('shapeContainer');
    shapeContainer.setMatrix(coord.matrix);
    if (position) {
      position.coord = coord;
    }
  }

  // step 3 绘制
  paint() {
    const self = this;
    const dataArray = self.get('dataArray');
    const mappedArray = [];
    const shapeFactory = self.getShapeFactory();
    shapeFactory.setCoord(self.get('coord'));
    self.set('shapeFactory', shapeFactory);
    const shapeContainer = self.get('shapeContainer');
    self._beforeMapping(dataArray);
    for (let i = 0; i < dataArray.length; i++) {
      let data = dataArray[i];
      const index = i;
      data = self._mapping(data);
      mappedArray.push(data);
      self.draw(data, shapeContainer, shapeFactory, index);
    }
    if (self.get('labelCfg')) {
      self._addLabels(Util.union.apply(null, mappedArray), shapeContainer.get('children'));
    }

    if (!self.get('sortable')) {
      self._sort(mappedArray); // 便于数据的查找，需要对数据进行排序，用于 geom.findPoint()
    } else {
      self.set('dataArray', mappedArray);
    }
  }

  _sort(mappedArray) {
    const self = this;
    const xScale = self.getXScale();
    const xField = xScale.field;
    Util.each(mappedArray, itemArr => {
      itemArr.sort((obj1, obj2) => {
        return xScale.translate(obj1[FIELD_ORIGIN][xField]) - xScale.translate(obj2[FIELD_ORIGIN][xField]);
      });
    });

    self.set('dataArray', mappedArray);
  }

  // step 3.1 before mapping
  _beforeMapping(dataArray) {
    const self = this;
    if (self.get('sortable')) {
      const xScale = self.getXScale();
      const field = xScale.field;
      Util.each(dataArray, data => {
        data.sort((v1, v2) => xScale.translate(v1[field]) - xScale.translate(v2[field]));
      });
    }
    if (self.get('generatePoints')) {
      Util.each(dataArray, data => {
        self._generatePoints(data);
      });
      Util.each(dataArray, (data, index) => {
        const nextData = dataArray[index + 1];
        if (nextData) {
          data[0].nextPoints = nextData[0].points;
        }
      });
    }
  }

  // step 3.2 add labels
  _addLabels(points, shapes) {
    const self = this;
    const type = self.get('type');
    const viewTheme = self.get('viewTheme') || Global;
    const coord = self.get('coord');
    const C = Labels.getLabelsClass(coord.type, type);
    const container = self.get('container');
    const scales = Util.map(self.get('labelCfg').fields, field => self._createScale(field));
    const labelContainer = container.addGroup(C, {
      _id: this.get('_id'),
      labelCfg: Util.mix({
        scales
      }, self.get('labelCfg')),
      coord,
      geom: self,
      geomType: type,
      yScale: self.getYScale(),
      viewTheme,
      visible: self.get('visible')
    });

    labelContainer.showLabels(points, shapes);
    self.set('labelContainer', labelContainer);
  }

  /**
   * @protected
   * 获取图形的工厂类
   * @return {Object} 工厂类对象
   */
  getShapeFactory() {
    let shapeFactory = this.get('shapeFactory');
    if (!shapeFactory) {
      const shapeType = this.get('shapeType');
      shapeFactory = Shape.getShapeFactory(shapeType);
      this.set('shapeFactory', shapeFactory);
    }
    return shapeFactory;
  }

  // step 3.2 generate points
  _generatePoints(data) {
    const self = this;
    const shapeFactory = self.getShapeFactory();
    const shapeAttr = self.getAttr('shape');
    for (let i = 0; i < data.length; i++) {
      const obj = data[i];
      const cfg = self.createShapePointsCfg(obj);
      const shape = shapeAttr ? self._getAttrValues(shapeAttr, obj) : null;
      const points = shapeFactory.getShapePoints(shape, cfg);
      obj.points = points;
    }
  }

  /**
   * 获取图形对应点的配置项
   * @protected
   * @param  {Object} obj 数据对象
   * @return {Object} cfg 获取图形对应点的配置项
   */
  createShapePointsCfg(obj) {
    const xScale = this.getXScale();
    const yScale = this.getYScale();
    const x = this._normalizeValues(obj[xScale.field], xScale);
    let y; // 存在没有 y 的情况

    if (yScale) {
      y = this._normalizeValues(obj[yScale.field], yScale);
    } else {
      y = obj.y ? obj.y : 0.1;
    }

    return {
      x,
      y,
      y0: yScale ? yScale.scale(this.getYMinValue()) : undefined
    };
  }

  /**
   * @protected
   * 如果y轴的最小值小于0则返回0，否则返回最小值
   * @return {Number} y轴上的最小值
   */
  getYMinValue() {
    const yScale = this.getYScale();
    const { min, max } = yScale;
    let value;

    if (min >= 0) {
      value = min;
    } else if (max <= 0) { // 当值全位于负区间时，需要保证 ymin 在区域内，不可为 0
      value = max;
    } else {
      value = 0;
    }
    return value;
  }

  // 将数据归一化
  _normalizeValues(values, scale) {
    let rst = [];
    if (Util.isArray(values)) {
      for (let i = 0; i < values.length; i++) {
        const v = values[i];
        rst.push(scale.scale(v));
      }
    } else {
      rst = scale.scale(values);
    }
    return rst;
  }

  // step 3.2 mapping
  _mapping(data) {
    const self = this;
    const attrs = self.get('attrs');
    const mappedData = [];
    for (let i = 0; i < data.length; i++) {
      const record = data[i];
      const newRecord = {};
      newRecord[FIELD_ORIGIN] = record[FIELD_ORIGIN];
      newRecord.points = record.points;
      newRecord.nextPoints = record.nextPoints;
      for (const k in attrs) {
        if (attrs.hasOwnProperty(k)) {
          const attr = attrs[k];
          const names = attr.names;
          const values = self._getAttrValues(attr, record);
          if (names.length > 1) { // position 之类的生成多个字段的属性
            for (let j = 0; j < values.length; j++) {
              const val = values[j];
              const name = names[j];
              newRecord[name] = (Util.isArray(val) && val.length === 1) ? val[0] : val; // 只有一个值时返回第一个属性值
            }
          } else {
            newRecord[names[0]] = values.length === 1 ? values[0] : values;
          }
        }
      }
      mappedData.push(newRecord);
    }

    return mappedData;
  }

  // 获取属性映射的值
  _getAttrValues(attr, record) {
    const scales = attr.scales;
    const params = [];
    for (let i = 0; i < scales.length; i++) {
      const scale = scales[i];
      const field = scale.field;
      if (scale.type === 'identity') {
        params.push(scale.value);
      } else {
        params.push(record[field]);
      }
    }
    const values = attr.mapping(...params);
    return values;
  }

  getAttrValue(attrName, record) {
    const attr = this.getAttr(attrName);
    let rst = null;
    if (attr) {
      const values = this._getAttrValues(attr, record);
      rst = values[0];
    }
    return rst;
  }

  getDefaultValue(attrName) {
    let value = this.get(attrName);
    const attr = this.getAttr(attrName);
    if (attr) {
      const scale = attr.getScale(attrName);
      if (scale.type === 'identity') {
        value = scale.value;
      }
    }
    return value;
  }

  /**
   * step 3.3 draw
   * @protected
   * @param  {Array} data 绘制图形
   * @param {Object} container 绘图容器
   * @param {Object} shapeFactory 绘制图形的工厂类
   * @param {Number} index 每个 shape 的索引值
   */
  draw(data, container, shapeFactory, index) {
    const self = this;
    for (let i = 0; i < data.length; i++) {
      const obj = data[i];
      self.drawPoint(obj, container, shapeFactory, index + i);
    }
  }

  getCallbackCfg(fields, cfg, origin) {
    if (!fields) {
      return cfg;
    }
    const tmpCfg = {};
    const params = fields.map(field => origin[field]);
    Util.each(cfg, (v, k) => {
      if (Util.isFunction(v)) {
        tmpCfg[k] = v.apply(null, params);
      } else {
        tmpCfg[k] = v;
      }
    });
    return tmpCfg;
  }

  _getShapeId(dataObj) {
    let id = this.get('_id');
    const keyFields = this.get('keyFields');
    if (keyFields && keyFields.length > 0) {
      Util.each(keyFields, key => {
        id += '-' + dataObj[key];
      });
    } else {
      const type = this.get('type');
      const xScale = this.getXScale();
      const yScale = this.getYScale();
      const xField = xScale.field || 'x';
      const yField = yScale.field || 'y';
      const yVal = dataObj[yField];
      let xVal;
      if (xScale.isIdentity) {
        xVal = xScale.value;
      } else {
        xVal = dataObj[xField];
      }

      if (type === 'interval' || type === 'schema') {
        id += '-' + xVal;
      } else if (type === 'line' || type === 'area' || type === 'path') {
        id += '-' + type;
      } else {
        id += '-' + xVal + '-' + yVal;
      }

      const groupScales = this._getGroupScales();
      if (!Util.isEmpty(groupScales)) {
        Util.each(groupScales, groupScale => {
          const field = groupScale.field;
          if (groupScale.type !== 'identity') {
            id += '-' + dataObj[field];
          }
        });
      }
    }

    return id;
  }

  getDrawCfg(obj) {
    const self = this;
    const cfg = {
      origin: obj,
      x: obj.x,
      y: obj.y,
      color: obj.color,
      size: obj.size,
      shape: obj.shape,
      isInCircle: self.isInCircle(),
      opacity: obj.opacity
    };
    const styleOptions = self.get('styleOptions');
    if (styleOptions && styleOptions.style) {
      cfg.style = self.getCallbackCfg(styleOptions.fields, styleOptions.style, obj[FIELD_ORIGIN]);
    }
    if (self.get('generatePoints')) {
      cfg.points = obj.points;
      cfg.nextPoints = obj.nextPoints;
    }
    if (self.get('animate')) { // _id 字段仅用于动画
      cfg._id = self._getShapeId(obj[FIELD_ORIGIN]);
    }
    return cfg;
  }

  appendShapeInfo(shape, index) {
    if (shape) {
      shape.setSilent('index', index);
      shape.setSilent('coord', this.get('coord'));

      if (this.get('animate') && this.get('animateCfg')) {
        shape.setSilent('animateCfg', this.get('animateCfg'));
      }
    }
  }

  _applyViewThemeShapeStyle(cfg, shape, shapeFactory) {
    // applying view theme
    const self = this;
    const viewTheme = self.viewTheme || Global;
    let shapeName = shapeFactory.name;
    if (shape) {
      if (shape && (shape.indexOf('hollow') > -1 || shape.indexOf('liquid') > -1)) {
        shapeName = `hollow${Util.upperFirst(shapeName)}`;
      }
    } else if (shapeFactory.defaultShapeType.indexOf('hollow') > -1) {
      shapeName = `hollow${Util.upperFirst(shapeName)}`;
    }
    const defaultStyle = viewTheme.shape[shapeName] || {};
    cfg.style = Util.mix({}, defaultStyle, cfg.style);
  }

  drawPoint(obj, container, shapeFactory, index) {
    const self = this;
    const shape = obj.shape;
    const cfg = self.getDrawCfg(obj);

    self._applyViewThemeShapeStyle(cfg, shape, shapeFactory);

    const geomShape = shapeFactory.drawShape(shape, cfg, container);
    self.appendShapeInfo(geomShape, index);
  }

  /**
   * 获取属性
   * @protected
   * @param {String} name 属性名
   * @return {Scale} 度量
   */
  getAttr(name) {
    return this.get('attrs')[name];
  }

  /**
   * 获取 x 对应的度量
   * @return {Scale} x 对应的度量
   */
  getXScale() {
    return this.getAttr('position').scales[0];
  }

  /**
   * 获取 y 对应的度量
   * @return {Scale} y 对应的度量
   */
  getYScale() {
    return this.getAttr('position').scales[1];
  }

  getShapes() {
    const result = [];
    const shapeContainer = this.get('shapeContainer');
    const children = shapeContainer.get('children');
    Util.each(children, child => {
      if (child.get('origin')) { // 过滤 label
        result.push(child);
      }
    });
    return result;
  }

  getAttrsForLegend() {
    const attrs = this.get('attrs');
    const rst = [];
    Util.each(attrs, attr => {
      if (GROUP_ATTRS.includes(attr.type)) {
        rst.push(attr);
      }
    });
    return rst;
  }

  getFieldsForLegend() {
    let fields = [];
    const attrOptions = this.get('attrOptions');
    Util.each(GROUP_ATTRS, attrName => {
      const attrCfg = attrOptions[attrName];
      if (attrCfg && attrCfg.field && Util.isString(attrCfg.field)) {
        fields = fields.concat(attrCfg.field.split('*'));
      }
    });
    return Util.uniq(fields);
  }

  changeVisible(visible, stopDraw) {
    const me = this;
    me.set('visible', visible);
    const shapeContainer = this.get('shapeContainer');
    if (shapeContainer) {
      shapeContainer.set('visible', visible);
    }
    const labelContainer = this.get('labelContainer');
    if (labelContainer) {
      labelContainer.set('visible', visible);
    }
    if (!stopDraw && shapeContainer) {
      const canvas = shapeContainer.get('canvas');
      canvas.draw();
    }
  }

  reset() {
    this.set('attrOptions', {});
    this.clearInner();
  }

  clearInner() {
    this.clearActivedShapes();
    this.clearSelected();
    const shapeContainer = this.get('shapeContainer');
    shapeContainer && shapeContainer.clear();

    // 由于 Labels 对应的模块需要生成group，所以这个地方需要删除
    const labelContainer = this.get('labelContainer');
    labelContainer && labelContainer.remove();
    this.set('attrs', {});
    this.set('groupScales', null);
    // if (!this.get('hasDefaultAdjust')) {
    //   this.set('adjusts', null);
    // }
    this.set('labelContainer', null);
    this.set('xDistance', null);
    this.set('isStacked', null);
  }

  clear() {
    this.clearInner();
    this.set('scales', {});
  }

  destroy() {
    this.clear();
    const shapeContainer = this.get('shapeContainer');
    shapeContainer && shapeContainer.remove();
    this.offEvents();
    super.destroy();
  }

  bindEvents() {
    if (this.get('view')) {
      this._bindActiveAction();
      this._bindSelectedAction();
    }
  }

  offEvents() {
    if (this.get('view')) {
      this._offActiveAction();
      this._offSelectedAction();
    }
  }
}

module.exports = GeomBase;
