import { Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, ObservableMap } 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 { Histogram } from './components/Histogram';
import { LineChart } from './components/LineChart';
import { PieChart } from './components/PieChart';
import { TableBox } from './components/TableBox';
import './DataVizBox.scss';

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

@observer
export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
    public static LayoutString(fieldStr: string) {
        return FieldView.LayoutString(DataVizBox, fieldStr);
    }

    // all datasets that have been retrieved from the server stored as a map from the dataset url to an array of records
    static dataset = new ObservableMap<string, { [key: string]: string }[]>();
    private _vizRenderer: LineChart | Histogram | PieChart | undefined;

    // all CSV records in the dataset (that aren't an empty row)
    @computed.struct get records() {
        var records = DataVizBox.dataset.get(CsvCast(this.rootDoc[this.fieldKey]).url.href);
        return records?.filter(record => Object.keys(record).some(key => record[key]));
    }

    // currently chosen visualization type: line, pie, histogram, table
    @computed get dataVizView(): DataVizView {
        return StrCast(this.layoutDoc._dataViz, 'table') as DataVizView;
    }

    @computed get dataUrl() {
        return Cast(this.dataDoc[this.fieldKey], CsvField);
    }
    @computed.struct get axes() {
        return StrListCast(this.layoutDoc.dataViz_axes);
    }

    selectAxes = (axes: string[]) => (this.layoutDoc.dataViz_axes = new List<string>(axes));

    @action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
    restoreView = (data: Doc) => {
        const changedView = this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz);
        const changedAxes = this.axes.join('') !== StrListCast(data.config_dataVizAxes).join('') && (this.layoutDoc._dataViz_axes = new List<string>(StrListCast(data.config_dataVizAxes)));
        this.layoutDoc.dataViz_selectedRows = Field.Copy(data.dataViz_selectedRows);
        this.layoutDoc.dataViz_histogram_barColors = Field.Copy(data.dataViz_histogram_barColors);
        this.layoutDoc.dataViz_histogram_defaultColor = data.dataViz_histogram_defaultColor;
        this.layoutDoc.dataViz_pie_sliceColors = Field.Copy(data.dataViz_pie_sliceColors);
        Object.keys(this.layoutDoc).map(key => {
            if (key.startsWith('dataViz_histogram_title') || key.startsWith('dataViz_lineChart_title') || key.startsWith('dataViz_pieChart_title')) {
                this.layoutDoc['_' + key] = data[key];
            }
        });
        const func = () => this._vizRenderer?.restoreView(data);
        if (changedView || changedAxes) {
            setTimeout(func, 100);
            return true;
        }
        return func() ?? false;
    };
    getAnchor = (addAsAnnotation?: boolean, pinProps?: PinProps) => {
        const anchor = !pinProps
            ? this.rootDoc
            : this._vizRenderer?.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.config_dataViz = this.dataVizView;
        anchor.config_dataVizAxes = this.axes.length ? new List<string>(this.axes) : undefined;
        anchor.dataViz_selectedRows = Field.Copy(this.layoutDoc.dataViz_selectedRows);
        anchor.dataViz_histogram_barColors = Field.Copy(this.layoutDoc.dataViz_histogram_barColors);
        anchor.dataViz_histogram_defaultColor = this.layoutDoc.dataViz_histogram_defaultColor;
        anchor.dataViz_pie_sliceColors = Field.Copy(this.layoutDoc.dataViz_pie_sliceColors);
        Object.keys(this.layoutDoc).map(key => {
            if (key.startsWith('dataViz_histogram_title') || key.startsWith('dataViz_lineChart_title') || key.startsWith('dataViz_pieChart_title')) {
                anchor[key] = this.layoutDoc[key];
            }
        });
        this.addDocument(anchor);
        return anchor;
    };

    componentDidMount() {
        this.props.setContentView?.(this);
        if (!DataVizBox.dataset.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) this.fetchData();
    }

    fetchData = () => {
        DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests
        fetch('/csvData?uri=' + this.dataUrl?.url.href) //
            .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res))));
    };

    // toggles for user to decide which chart type to view the data in
    renderVizView = () => {
        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: 75, left: 45 };
        if (this.records) {
            switch (this.dataVizView) {
                case DataVizView.TABLE:
                    return <TableBox layoutDoc={this.layoutDoc} records={this.records} 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._vizRenderer = r ?? undefined)}
                            height={height}
                            width={width}
                            fieldKey={this.fieldKey}
                            margin={margin}
                            rootDoc={this.rootDoc}
                            axes={this.axes}
                            records={this.records}
                            dataDoc={this.dataDoc}
                        />
                    );
                case DataVizView.HISTOGRAM:
                    return (
                        <Histogram
                            layoutDoc={this.layoutDoc}
                            ref={r => (this._vizRenderer = r ?? undefined)}
                            height={height}
                            width={width}
                            fieldKey={this.fieldKey}
                            margin={margin}
                            rootDoc={this.rootDoc}
                            axes={this.axes}
                            records={this.records}
                            dataDoc={this.dataDoc}
                        />
                    );
                case DataVizView.PIECHART:
                    return (
                        <PieChart
                            layoutDoc={this.layoutDoc}
                            ref={r => (this._vizRenderer = r ?? undefined)}
                            height={height}
                            width={width}
                            fieldKey={this.fieldKey}
                            margin={margin}
                            rootDoc={this.rootDoc}
                            axes={this.axes}
                            records={this.records}
                            dataDoc={this.dataDoc}
                        />
                    );
            }
        }
        return 'no data/visualization';
    };

    render() {
        return !this.records?.length ? (
            // displays how to get data into the DataVizBox if its empty
            <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 + p' to bring the data table to your canvas.</div>
        ) : (
            <div
                className="dataViz"
                style={{
                    pointerEvents: this.props.isContentActive() === true ? "all" : "none"
                }}
                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._dataViz = DataVizView.TABLE)} toggleStatus={this.layoutDoc._dataViz == DataVizView.TABLE} />
                    <Toggle text={'LINECHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.LINECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.LINECHART} />
                    <Toggle text={'HISTOGRAM'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz == DataVizView.HISTOGRAM} />
                    <Toggle text={'PIE CHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.PIECHART} />
                </div>
                {this.renderVizView()}
            </div>
        );
    }
}
