import { action, computed, ObservableMap, ObservableSet } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Field, StrListCast } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { Cast, CsvCast, StrCast } from '../../../../fields/Types';
import { CsvField } from '../../../../fields/URLField';
import { Docs } from '../../../documents/Documents';
import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
import { PinProps } from '../trails';
import { LineChart } from './components/LineChart';
import { TableBox } from './components/TableBox';
import './DataVizBox.scss';
import { Histogram } from './components/Histogram';
import { PieChart } from './components/PieChart';
import { Toggle, ToggleType, Type } from 'browndash-components';

export enum DataVizView {
    TABLE = 'table',
    LINECHART = 'lineChart',
    HISTOGRAM = 'histogram',
    PIECHART = 'pieChart'
}

@observer
export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {

    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(DataVizBox, fieldKey);
    }
    // says we have an object and any string
    // 2 ways of doing it
    // @observable private pairs: { [key: string]: number | string | undefined }[] = [];
    // @observable private pairs: { [key: string]: FieldResult }[] = [];
    static pairSet = new ObservableMap<string, { [key: string]: string }[]>();
    @computed.struct get pairs() {
        return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href);
    }
    private _chartRenderer: LineChart | Histogram | PieChart | undefined;
    // // another way would be store a schema that defines the type of data we are expecting from an imported doc

    // method1() {
    //     this.pairs[0].x = 3;
    // }

    // method() {
    //     // this.pairs[0].x = 3;
    //     // go through the pairs
    //     const x = this.pairs[0].x;
    //     if (typeof x == 'number') {
    //         let x1 = Number(x);
    //         // let x1 = NumCast(x);
    //     }
    // }

    // could use field result
    // [key: string]: FieldResult;
    // instead of numeric x,y in there,

    // TODO: nda - use onmousedown and onmouseup when dragging and changing height and width to update the height and width props only when dragging stops

    @computed get dataVizView(): DataVizView {
        return StrCast(this.layoutDoc._dataVizView, 'table') as DataVizView;
    }

    @action
    restoreView = (data: Doc) => {
        const changedView = this.dataVizView !== data.presDataVizView && (this.layoutDoc._dataVizView = data.presDataVizView);
        const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._data_vizAxes = new List<string>(StrListCast(data.presDataVizAxes)));
        this.layoutDoc.selected = Field.Copy(data.selected);
        this.layoutDoc.histogramBarColors = Field.Copy(data.histogramBarColors);
        this.layoutDoc.defaultHistogramColor = data.defaultHistogramColor;
        this.layoutDoc.pieSliceColors = Field.Copy(data.pieSliceColors);
        Object.keys(this.layoutDoc).map(key => {
            if (key.startsWith('histogram-title') || key.startsWith('lineChart-title') || key.startsWith('pieChart-title')){ this.layoutDoc['_'+key] = data[key]; }
        })

        const func = () => this._chartRenderer?.restoreView(data);
        if (changedView || changedAxes) {
            setTimeout(func, 100);
            return true;
        }
        return func() ?? false;
    };

    getAnchor = (addAsAnnotation?: boolean, pinProps?: PinProps) => {
        const anchor = !pinProps
            ? this.rootDoc
            : this._chartRenderer?.getAnchor(pinProps) ??
              Docs.Create.ConfigDocument({
                  // when we clear selection -> we should have it so chartBox getAnchor returns undefined
                  // this is for when we want the whole doc (so when the chartBox getAnchor returns without a marker)
                  /*put in some options*/
              });

        anchor.presDataVizView = this.dataVizView;
        anchor.presDataVizAxes = this.axes.length ? new List<string>(this.axes) : undefined;
        anchor.selected = Field.Copy(this.layoutDoc.selected);
        anchor.histogramBarColors = Field.Copy(this.layoutDoc.histogramBarColors);
        anchor.defaultHistogramColor = this.layoutDoc.defaultHistogramColor;
        anchor.pieSliceColors = Field.Copy(this.layoutDoc.pieSliceColors);
        Object.keys(this.layoutDoc).map(key => {
            if (key.startsWith('histogram-title') || key.startsWith('lineChart-title') || key.startsWith('pieChart-title')){ anchor[key] = this.layoutDoc[key]; }
        })

        this.addDocument(anchor);
        return anchor;
    };

    @computed.struct get axes() {
        return StrListCast(this.layoutDoc.data_vizAxes);
    }
    selectAxes = (axes: string[]) => (this.layoutDoc.data_vizAxes = new List<string>(axes));

    @computed get selectView() {
        const width = this.props.PanelWidth() * 0.9;
        const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9;
        const margin = { top: 10, right: 25, bottom: 50, left: 25 };
        if (!this.pairs) return 'no data';
        switch (this.dataVizView) {
            case DataVizView.TABLE:     return <TableBox layoutDoc={this.layoutDoc} pairs={this.pairs} axes={this.axes} height={height} width={width} margin={margin} rootDoc={this.rootDoc} docView={this.props.DocumentView} selectAxes={this.selectAxes}/>;
            case DataVizView.LINECHART: return <LineChart layoutDoc={this.layoutDoc} ref={r => (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />;
            case DataVizView.HISTOGRAM: return <Histogram layoutDoc={this.layoutDoc} ref={r => (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />;
            case DataVizView.PIECHART: return <PieChart layoutDoc={this.layoutDoc} ref={r => (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />;
        }
    }
    @computed get dataUrl() {
        return Cast(this.dataDoc[this.fieldKey], CsvField);
    }

    componentDidMount() {
        this.props.setContentView?.(this);
        this.fetchData();
    }

    fetchData() {
        if (DataVizBox.pairSet.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) return;
        DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []);
        fetch('/csvData?uri=' + this.dataUrl?.url.href) //
            .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res))));
    }

    // handle changing the view using a button
    @action changeViewHandler(e: React.MouseEvent<HTMLButtonElement>) {
        e.preventDefault();
        e.stopPropagation();
        this.layoutDoc._dataVizView = this.dataVizView === DataVizView.TABLE ? DataVizView.LINECHART : DataVizView.TABLE;
    }

    render() {
        if (!this.layoutDoc._dataVizView) this.layoutDoc._dataVizView = this.dataVizView;
        return !this.pairs?.length ? (
            <div className="start-message">
                To create a DataViz box, either import / drag a CSV file into your canvas
                or copy a data table and use the command 'ctrl + t' to bring the data table
                to your canvas.
            </div>
        ) : (
            <div
                className="dataViz"
                onWheel={e => e.stopPropagation()}
                ref={r =>
                    r?.addEventListener(
                        'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
                        (e: WheelEvent) => {
                            if (!r.scrollTop && e.deltaY <= 0) e.preventDefault();
                            e.stopPropagation();
                        },
                        { passive: false }
                    )
                }>
                <div className={'datatype-button'}>
                    <Toggle text={"TABLE"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
                        onClick={e => this.layoutDoc._dataVizView = DataVizView.TABLE} 
                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.TABLE} 
                    />
                    <Toggle text={"LINECHART"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
                        onClick={e => this.layoutDoc._dataVizView = DataVizView.LINECHART} 
                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.LINECHART} 
                    />
                    <Toggle text={"HISTOGRAM"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
                        onClick={e => this.layoutDoc._dataVizView = DataVizView.HISTOGRAM} 
                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.HISTOGRAM} 
                    />
                    <Toggle text={"PIE CHART"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
                        onClick={e => this.layoutDoc._dataVizView = DataVizView.PIECHART} 
                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.PIECHART} 
                    />
                </div>
                {this.selectView}
            </div>
        );
    }
}
