import React from 'react';
import PropTypes from 'prop-types';
import { ScaleUtils, AbstractSeries } from 'react-vis';
import { ORIENTATION, SCALE } from './utils/chart_utils';
const { HORIZONTAL, VERTICAL, BOTH } = ORIENTATION;

const DEFAULT_AREAS = {
  areaSize: 0,
  drawArea: {
    x0: 0,
    x1: 0,
    y0: 0,
    y1: 0,
  },
  rectArea: {
    x: 0,
    y: 0,
    width: 0,
    height: 0,
  }
};

export class EuiSelectionBrush extends AbstractSeries {
  state = {
    drawing: false,
    ...DEFAULT_AREAS,
  }

  onParentMouseDown(e) {
    this._startDrawing(e);
  }

  onParentMouseMove(e) {
    this._brushing(e);
  }

  onParentMouseUp() {
    this._stopDrawing();
  }

  onParentMouseLeave() {
    this._stopDrawing();
  }

  _getDrawArea(offsetX, offsetY, isStartingPoint) {
    const { orientation, marginTop, marginLeft, innerHeight, innerWidth } = this.props;
    const yLocation = offsetY - marginTop;
    const xLocation = offsetX - marginLeft;
    let x0;
    let y0;
    if (isStartingPoint) {
      x0 = orientation === VERTICAL ? 0 : xLocation;
      y0 = orientation === HORIZONTAL ? 0 : yLocation;
    } else {
      x0 = this.state.drawArea.x0;
      y0 = this.state.drawArea.y0;
    }
    const x1 = orientation === VERTICAL ? innerWidth : xLocation;
    const y1 = orientation === HORIZONTAL ? innerHeight : yLocation;
    const areaSize = Math.abs(x0 - x1) * Math.abs(y0 - y1);
    return {
      areaSize,
      drawArea: {
        x0,
        x1,
        y0,
        y1,
      },
      rectArea: {
        x: x0 < x1 ? x0 : x1,
        y: y0 < y1 ? y0 : y1,
        width: x0 < x1 ? (x1 - x0) : (x0 - x1),
        height: y0 < y1 ? (y1 - y0) : (y0 - y1),
      }
    };
  }

  _getScaledValue(scale, scaleType, value0, value1) {
    switch(scaleType) {
      case SCALE.ORDINAL:
        return [0, 0];
      default:
        return [
          scale.invert(value0 < value1 ? value0 : value1),
          scale.invert(value0 < value1 ? value1 : value0),
        ];
        break;

    }
  }

  _startDrawing = (e) => {
    const { onBrushStart } = this.props;
    const { offsetX, offsetY } = e.nativeEvent;
    const drawAndRectAreas = this._getDrawArea(offsetX, offsetY, true);
    this.setState(() => ({
      drawing: true,
      ...drawAndRectAreas,
    }));

    if (onBrushStart) {
      onBrushStart(drawAndRectAreas);
    }
  }

  _brushing = (e) => {
    const { onBrushing } = this.props;
    const { drawing } = this.state;
    const { offsetX, offsetY } = e.nativeEvent;
    if (drawing) {
      const drawAndRectAreas = this._getDrawArea(offsetX, offsetY);
      this.setState(() => ({
        ...drawAndRectAreas
      }));

      if (onBrushing) {
        onBrushing(drawAndRectAreas);
      }
    } else {
      this.setState(() => ({
        drawing: false,
        ...DEFAULT_AREAS,
      }));
    }
  }

  _stopDrawing = () => {
    // Quickly short-circuit if the user isn't drawing in our component
    const { drawing } = this.state;
    if (!drawing) {
      return;
    }

    // Clear the draw area
    this.setState(() => ({
      drawing: false,
      ...DEFAULT_AREAS,
    }));


    // Don't invoke the callback if the selected area was < 25 square px.
    // This is a click not a select
    const { areaSize } = this.state;
    if (areaSize < 25) {
      return;
    }
    const { drawArea } = this.state;
    const { x0, y0, x1, y1 } = drawArea;
    const { xType, yType, onBrushEnd } = this.props;
    const xScale = ScaleUtils.getAttributeScale(this.props, 'x');
    const yScale = ScaleUtils.getAttributeScale(this.props, 'y');

    const xValues = this._getScaledValue(xScale, xType, x0, x1);
    const yValues = this._getScaledValue(yScale, yType, y0, y1);

    // Compute the corresponding domain drawn
    const domainArea = {
      startX: xValues[0],
      endX: xValues[1],
      startY: yValues[1],
      endY: yValues[0],
    };

    if (onBrushEnd) {
      onBrushEnd({
        domainArea,
        drawArea,
      });
    }
  }

  render() {
    const { marginLeft, marginTop, color, opacity } = this.props;
    const { rectArea: { x, y, width, height } } = this.state;
    return (
      <g
        transform={`translate(${marginLeft}, ${marginTop})`}
        style={{
          pointerEvents: 'none',
        }}
      >
        <rect
          opacity={opacity}
          fill={color}
          x={x}
          y={y}
          width={width}
          height={height}
        />
      </g>
    );
  }
}

EuiSelectionBrush.displayName = 'EuiSelectionBrush';

EuiSelectionBrush.propTypes = {
  /** Specify the brush orientation */
  orientation: PropTypes.oneOf([ HORIZONTAL, VERTICAL, BOTH ]),
  /** Callback on brush start event. */
  onBrushStart: PropTypes.func,
  /** Callback on every mouse move event. */
  onBrushing: PropTypes.func,
  /** Callback on brush end event. */
  onBrushEnd: PropTypes.func.isRequired,
  /** The color of the brush rectangle */
  color: PropTypes.string,
  /** The opacity of the brush rectangle*/
  opacity: PropTypes.number,
};

EuiSelectionBrush.defaultProps = {
  orientation: HORIZONTAL,
  color: 'black',
  opacity: 0.2,
};
