import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ChartData, DataPoints } from './ChartBox';
// import d3
import * as d3 from 'd3';

interface LineChartProps {
    data: ChartData;
    width: number;
    height: number;
}

interface SelectedDataPoint {
    x: number;
    y: number;
    elem: d3.Selection<d3.BaseType, unknown, SVGGElement, unknown>;
}

@observer
export class LineChart extends React.Component<LineChartProps> {
    private _dataReactionDisposer: IReactionDisposer | undefined = undefined;
    @observable private _x: number = 0;
    @observable private _y: number = 0;
    @observable private _currSelected: SelectedDataPoint | undefined = undefined;

    componentDidMount() {
        console.log('Getting to line chart');
        this.drawChart();
        this._dataReactionDisposer = reaction(() => this.props.data, this.drawChart);
    }

    componentWillUnmount() {
        if (this._dataReactionDisposer) {
            this._dataReactionDisposer();
        }
    }

    tooltipContent(data: DataPoints) {
        return `<b>x: ${data.x} y: ${data.y}</b>`;
    }

    drawChart() {
        // clearing tooltip
        d3.select('#chart-container').select('svg').remove();
        d3.select('#chart-container').select('.tooltip').remove();

        const margin = { top: 50, right: 50, bottom: 50, left: 50 };
        const { data } = this.props;

        const yMin = d3.min(data.data, d => d.y);
        const yMax = d3.max(data.data, d => d.y);

        // TODO: nda - modify data.x to support strings

        const xMin = d3.min(data.data, d => d.x);
        const xMax = d3.max(data.data, d => d.x);

        // adding svg
        const svg = d3.select('#chart-container').append('svg').attr('width', '100%').attr('height', '100%').append('g').attr('transform', `translate(${margin.left}, ${margin.top})`);

        // adding tooltip
        // const tooltip = d3.select('#chart-container').append('div').attr('class', 'tooltip');

        if (!xMin || !xMax || !yMin || !yMax) {
            // TODO: nda - error handle
            return;
        }
        // adding x axis
        const xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, this.props.width]);

        const yScale = d3.scaleLinear().domain([0, yMax]).range([this.props.height, 0]);

        // create a line function that takes in the data.data.x and data.data.y
        // TODO: nda - fix the types for the d here
        const lineGen = d3
            .line<DataPoints>()
            .x(d => xScale(d.x))
            .y(d => yScale(d.y))
            .curve(d3.curveMonotoneX);

        // adding the line to the svg
        svg.append('g')
            .attr('class', 'grid')
            .attr('transform', `translate(0,${this.props.height})`)
            .call(
                d3
                    .axisBottom(xScale)
                    .tickSize(-this.props.height)
                    .tickFormat((a, b) => '')
            );
        svg.append('g')
            .attr('class', 'grid')
            .call(
                d3
                    .axisLeft(yScale)
                    .tickSize(-this.props.width)
                    .tickFormat((a, b) => '')
            );
        svg.append('g').attr('class', 'x-axis').attr('transform', `translate(0,${this.props.height})`).call(d3.axisBottom(xScale).tickSize(15));
        svg.append('g').attr('class', 'y-axis').call(d3.axisLeft(yScale));

        // draw the line
        svg.append('path').datum(data.data).attr('fill', 'none').attr('stroke', 'rgba(53, 162, 235, 0.5)').attr('stroke-width', 2).attr('class', 'line').attr('d', lineGen);

        // draw the datapoint circles
        svg.selectAll('.circle-d1')
            .data(data.data)
            .join('circle') // enter append
            .attr('class', 'circle-d1')
            .attr('r', '3') // radius
            .attr('cx', d => xScale(d.x))
            .attr('cy', d => yScale(d.y))
            .attr('data-x', d => d.x)
            .attr('data-y', d => d.y);

        // this.setupDatapointClickHandlers();

        const focus = svg.append('g').attr('class', 'focus').style('display', 'none');
        focus.append('circle').attr('r', 5).attr('class', 'circle');
        const tooltip = d3
            .select('#chart-container')
            .append('div')
            .attr('class', 'tooltip')
            .style('opacity', 0)
            .style('position', 'absolute')
            // .style('z-index', '10')
            .style('background', '#fff')
            .style('border', '1px solid #ccc')
            .style('padding', '5px')
            .style('font-size', '12px');
        // add all the tooltipContent to the tooltip
        // @action
        const mousemove = action((e: any) => {
            const bisect = d3.bisector((d: DataPoints) => d.x).left;
            const xPos = d3.pointer(e)[0];
            const x0 = bisect(data.data, xScale.invert(xPos));
            const d0 = data.data[x0];
            this._x = d0.x;
            this._y = d0.y;
            focus.attr('transform', `translate(${xScale(d0.x)},${yScale(d0.y)})`);
            // TODO: nda - implement tooltips
            // tooltip.transition().duration(300).style('opacity', 0.9);
            // tooltip.html(() => this.tooltipContent(d0)).attr('transform', `translate(${xScale(d0.x) + 30}px,${yScale(d0.y) + 30}px)`);
        });

        const onPointClick = action((e: any) => {
            const bisect = d3.bisector((d: DataPoints) => d.x).left;
            const xPos = d3.pointer(e)[0];
            const x0 = bisect(data.data, xScale.invert(xPos));
            const d0 = data.data[x0];
            this._x = d0.x;
            this._y = d0.y;
            // find .circle-d1 with data-x = d0.x and data-y = d0.y
            const selected = svg.selectAll('.circle-d1').filter((d: any) => d['data-x'] === d0.x && d['data-y'] === d0.y);
            this._currSelected = { x: d0.x, y: d0.y, elem: selected };
            console.log('Getting here');
            console.log(this._currSelected);
        });

        svg.append('rect')
            .attr('class', 'overlay')
            .attr('width', this.props.width)
            .attr('height', this.props.height)
            .style('opacity', 0)
            .on('mouseover', () => {
                focus.style('display', null);
            })
            .on('mouseout', () => {
                tooltip.transition().duration(300).style('opacity', 0);
            })
            .on('mousemove', mousemove)
            .on('click', onPointClick);
    }

    render() {
        return (
            <div id="chart-container">
                <span>
                    x: {this._x} y: {this._y}
                    Curr Selected: {this._currSelected ? `x: ${this._currSelected.x} y: ${this._currSelected.y}` : 'none'}
                </span>
            </div>
        );
    }
}
