
const Util = require('../util');
const Interaction = require('./base');
const getColDef = require('./helper/get-col-def');
const getLimitRange = require('./helper/get-limit-range');

const ZOOMING_TYPES = [ 'X', 'Y', 'XY' ];
const DEFAULT_TYPE = 'X';

class Zoom extends Interaction {
  getDefaultCfg() {
    const cfg = super.getDefaultCfg();
    return Util.mix({}, cfg, {
      processEvent: 'mousewheel',
      type: DEFAULT_TYPE,
      stepRatio: 0.05,
      stepByField: {},
      minScale: 1,
      maxScale: 4,
      catStep: 2,
      limitRange: {},
      originScaleDefsByField: {}
    });
  }

  constructor(cfg, chart) {
    super(cfg, chart);
    const me = this;
    me.chart = chart;
    me.type = me.type.toUpperCase();
    const data = me.data = chart.get('data');

    const scales = chart.getYScales();
    const xScale = chart.getXScale();
    scales.push(xScale);
    const scaleController = chart.get('scaleController');
    scales.forEach(scale => {
      const field = scale.field;
      const def = scaleController.defs[field] || {};
      me.limitRange[field] = getLimitRange(data, scale);
      me.originScaleDefsByField[field] = Util.mix(def, {
        nice: !!def.nice
      });
      if (scale.isLinear) {
        me.stepByField[field] = (scale.max - scale.min) * me.stepRatio;
      } else {
        me.stepByField[field] = me.catStep;
      }
    });

    if (!ZOOMING_TYPES.includes(me.type)) {
      me.type = DEFAULT_TYPE;
    }
  }

  // onZoom() { }
  // onZoomin() { }
  // onZoomout() { }

  _applyScale(scale, delta, minOffset = 0, center) {
    const me = this;
    const { chart, stepByField } = me;
    if (scale.isLinear) {
      const { min, max, field } = scale;
      const maxOffset = 1 - minOffset;
      const step = stepByField[field] * delta;
      const newMin = min + step * minOffset;
      const newMax = max - step * maxOffset;
      if (newMax > newMin) {
        const colDef = getColDef(chart, field);
        // @2019-02-28 by blue.lb 这里需要将原始scale的配置整合新算出的最大及最小值
        chart.scale(field, Util.mix({}, colDef, {
          nice: false,
          min: newMin,
          max: newMax
        }));
      }
    } else {
      const { field, values } = scale;
      const chart = me.chart;
      const coord = chart.get('coord');
      const colDef = getColDef(chart, field);

      const originValues = me.limitRange[field];
      const originValuesLen = originValues.length;
      const maxScale = me.maxScale;
      const minScale = me.minScale;
      const minCount = originValuesLen / maxScale;
      const maxCount = originValuesLen / minScale;

      const valuesLength = values.length;
      const offsetPoint = coord.invertPoint(center);
      const percent = offsetPoint.x;
      const deltaCount = valuesLength - delta * this.catStep;
      const minDelta = parseInt(deltaCount * (percent));
      const maxDelta = deltaCount + minDelta;

      if (delta > 0 && valuesLength >= minCount) { // zoom out
        let min = minDelta;
        let max = maxDelta;
        if (maxDelta > valuesLength) {
          max = valuesLength - 1;
          min = valuesLength - deltaCount;
        }
        const newValues = values.slice(min, max);
        chart.scale(field, Util.mix({}, colDef, {
          values: newValues
        }));
      } else if (delta < 0 && valuesLength <= maxCount) { // zoom in
        const firstIndex = originValues.indexOf(values[0]);
        const lastIndex = originValues.indexOf(values[valuesLength - 1]);
        const minIndex = Math.max(0, firstIndex - minDelta);
        const maxIndex = Math.min(lastIndex + maxDelta, originValuesLen);
        const newValues = originValues.slice(minIndex, maxIndex);
        chart.scale(field, Util.mix({}, colDef, {
          values: newValues
        }));
      }
    }
  }

  process(ev) {
    const me = this;
    const { chart, type } = me;
    const coord = chart.get('coord');
    const deltaY = ev.deltaY;
    const offsetPoint = coord.invertPoint(ev);
    if (deltaY) {
      me.onZoom && me.onZoom(deltaY, offsetPoint, me);
      if (deltaY > 0) {
        me.onZoomin && me.onZoomin(deltaY, offsetPoint, me);
      } else {
        me.onZoomout && me.onZoomout(deltaY, offsetPoint, me);
      }
      const delta = deltaY / Math.abs(deltaY);
      if (type.indexOf('X') > -1) {
        me._applyScale(chart.getXScale(), delta, offsetPoint.x, ev);
      }
      if (type.indexOf('Y') > -1) {
        const yScales = chart.getYScales();
        yScales.forEach(yScale => {
          me._applyScale(yScale, delta, offsetPoint.y, ev);
        });
      }
    }
    chart.repaint();
  }

  reset() {
    const me = this;
    const { view, originScaleDefsByField } = me;
    const scales = view.getYScales();
    const xScale = view.getXScale();
    scales.push(xScale);
    scales.forEach(scale => {
      if (scale.isLinear) {
        const field = scale.field;
        view.scale(field, originScaleDefsByField[field]);
      }
    });
    view.repaint();
  }
}

// G2.registerInteraction('zoom', Zoom);
// G2.registerInteraction('Zoom', Zoom);

module.exports = Zoom;
