
const Util = require('../util');
const Interaction = require('./base');
const filterData = require('./helper/filter-data');
const getColDef = require('./helper/get-col-def');
const getLimitRange = require('./helper/get-limit-range');
const DAY_TIMESTAMPS = 86400000;
// const G2 = require('../core.js');

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

class Drag extends Interaction {
  getDefaultCfg() {
    const cfg = super.getDefaultCfg();
    return Util.mix({}, cfg, {
      type: DEFAULT_TYPE,
      stepRatio: 0.05,
      limitRange: {},
      stepByField: {},
      threshold: 20,
      originScaleDefsByField: {},
      previousPoint: null,
      isDragging: false
    });
  }

  _disableTooltip() {
    const me = this;
    const { chart } = me;
    const tooltipController = chart.get('tooltipController');
    if (tooltipController) {
      me._showTooltip = true;
      chart.tooltip(false);
    }
  }

  _enableTooltip(ev) {
    const me = this;
    const { chart } = me;
    if (me._showTooltip) {
      chart.tooltip(true);
      chart.showTooltip(ev);
    }
  }

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

    // pre process
    filterData(chart);

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

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

    me._disableTooltip();
  }

  // onDragstart() { }
  // onDrag() { }
  // onDragend() { }

  _applyTranslate(scale, offset = 0, total) {
    const me = this;
    if (scale.isLinear) {
      me._translateLinearScale(scale, offset, total);
    } else {
      me._translateCatScale(scale, offset, total);
    }
  }

  _translateCatScale(scale, offset, total) {
    const me = this;
    const chart = me.chart;
    const { type, field, values, ticks } = scale;
    const colDef = getColDef(chart, field);

    const originValues = me.limitRange[field];
    const ratio = offset / total;
    const valueLength = values.length;
    const deltaCount = Math.max(1, Math.abs(parseInt(ratio * valueLength)));

    let firstIndex = originValues.indexOf(values[0]);
    let lastIndex = originValues.indexOf(values[valueLength - 1]);
    if (offset > 0 && firstIndex >= 0) { // right
      for (let i = 0; i < deltaCount && firstIndex > 0; i++) {
        firstIndex -= 1;
        lastIndex -= 1;
      }
      const newValues = originValues.slice(firstIndex, lastIndex + 1);
      let newTicks = null;
      if (type === 'timeCat') {
        const tickGap = ticks.length > 2 ? ticks[1] - ticks[0] : DAY_TIMESTAMPS;
        for (let i = ticks[0] - tickGap; i >= newValues[0]; i -= tickGap) {
          ticks.unshift(i);
        }
        newTicks = ticks;
      }

      chart.scale(field, Util.mix({}, colDef, {
        values: newValues,
        ticks: newTicks
      }));
    } else if (offset < 0 && lastIndex <= originValues.length - 1) { // left
      for (let i = 0; i < deltaCount && lastIndex < originValues.length - 1; i++) {
        firstIndex += 1;
        lastIndex += 1;
      }
      const newValues = originValues.slice(firstIndex, lastIndex + 1);

      let newTicks = null;
      if (type === 'timeCat') {
        const tickGap = ticks.length > 2 ? ticks[1] - ticks[0] : DAY_TIMESTAMPS;
        for (let i = ticks[ticks.length - 1] + tickGap; i <= newValues[newValues.length - 1]; i += tickGap) {
          ticks.push(i);
        }
        newTicks = ticks;
      }

      chart.scale(field, Util.mix({}, colDef, {
        values: newValues,
        ticks: newTicks
      }));
    }
  }

  _translateLinearScale(scale, offset, total) {
    const me = this;
    const { chart, limitRange } = me;
    // linear / cat
    const { min, max, field } = scale;

    if (min === limitRange[field].min && max === limitRange[field].max) return;

    const ratio = offset / total;
    const range = max - min;
    const colDef = getColDef(chart, field);
    chart.scale(field, Util.mix({}, colDef, {
      nice: false,
      min: min + ratio * range,
      max: max + ratio * range
    }));
  }

  start(ev) {
    const me = this;
    const { canvas } = me;
    const canvasDOM = canvas.get('canvasDOM');
    canvasDOM.style.cursor = 'pointer';
    // const coord = chart.get('coord');
    me.isDragging = true;
    me.previousPoint = {
      x: ev.x,
      y: ev.y
    };
    me._disableTooltip();
  }
  process(ev) {
    const me = this;
    if (me.isDragging) {
      const { chart, type, canvas, coord, threshold } = me;
      const canvasDOM = canvas.get('canvasDOM');
      canvasDOM.style.cursor = 'move';
      // const coord = chart.get('coord');
      const previousPoint = me.previousPoint;
      const currentPoint = ev;
      const deltaX = currentPoint.x - previousPoint.x;
      const deltaY = currentPoint.y - previousPoint.y;
      let modified = false;
      if (Math.abs(deltaX) > threshold && type.indexOf('X') > -1) {
        modified = true;
        const xScale = chart.getXScale();
        me._applyTranslate(xScale, xScale.isLinear ? -deltaX : deltaX, coord.width);
      }
      if (Math.abs(deltaY) > threshold && type.indexOf('Y') > -1) {
        modified = true;
        const yScales = chart.getYScales();
        yScales.forEach(yScale => {
          me._applyTranslate(yScale, currentPoint.y - previousPoint.y, coord.height);
        });
      }
      if (modified) {
        me.previousPoint = currentPoint;
        chart.repaint();
      }
    }
  }

  end(ev) {
    const me = this;
    me.isDragging = false;
    const { canvas } = me;
    const canvasDOM = canvas.get('canvasDOM');
    canvasDOM.style.cursor = 'default';
    me._enableTooltip(ev);
  }

  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();
    me._disableTooltip();
  }
}

module.exports = Drag;
