import functionPlot from 'function-plot';
import { action, computed, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
import { documentSchema } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
import { Cast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { DocFocusOptions, DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { PinProps, PresBox } from './trails';

const EquationSchema = createSchema({});

type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSchema]>;
const EquationDocument = makeInterface(EquationSchema, documentSchema);

@observer
export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(FunctionPlotBox, fieldKey);
    }
    public static GraphCount = 0;
    _plot: any;
    _plotId = '';
    _plotEle: any;
    constructor(props: any) {
        super(props);
        this._plotId = 'graph' + FunctionPlotBox.GraphCount++;
    }
    componentDidMount() {
        this.props.setContentView?.(this);
        reaction(
            () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.rootDoc.xRange, this.rootDoc.yRange],
            () => this.createGraph()
        );
    }
    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
        const anchor = Docs.Create.ConfigDocument({
            //
            annotationOn: this.rootDoc,
        });
        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.rootDoc);
        anchor.presXRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
        anchor.presYRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
        if (addAsAnnotation) this.addDocument(anchor);
        return anchor;
    };
    createGraph = (ele?: HTMLDivElement) => {
        this._plotEle = ele || this._plotEle;
        const width = this.props.PanelWidth();
        const height = this.props.PanelHeight();
        const fns = DocListCast(this.dataDoc.data).map(doc => StrCast(doc.text, 'x^2').replace(/\\frac\{(.*)\}\{(.*)\}/, '($1/$2)'));
        try {
            this._plotEle.children.length && this._plotEle.removeChild(this._plotEle.children[0]);
            this._plot = functionPlot({
                target: '#' + this._plotEle.id,
                width,
                height,
                xAxis: { domain: Cast(this.rootDoc.xRange, listSpec('number'), [-10, 10]) },
                yAxis: { domain: Cast(this.rootDoc.yRange, listSpec('number'), [-1, 9]) },
                grid: true,
                data: fns.map(fn => ({
                    fn,
                    // derivative: { fn: "2 * x", updateOnMouseMove: true }
                })),
            });
        } catch (e) {
            console.log(e);
        }
    };

    @undoBatch
    drop = (e: Event, de: DragManager.DropEvent) => {
        if (de.complete.docDragData?.droppedDocuments.length) {
            const added = de.complete.docDragData.droppedDocuments.reduce((res, doc) => res && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, doc), true);
            !added && e.preventDefault();
            e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
            return added;
        }
        return false;
    };

    _dropDisposer: any;
    protected createDropTarget = (ele: HTMLDivElement) => {
        this._dropDisposer?.();
        if (ele) {
            this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc);
        }
        // if (this.layout_autoHeight) this.tryUpdateScrollHeight();
    };
    @computed get theGraph() {
        return <div id={`${this._plotId}`} ref={r => r && this.createGraph(r)} style={{ position: 'absolute', width: '100%', height: '100%' }} onPointerDown={e => e.stopPropagation()} />;
    }
    render() {
        TraceMobx();
        return (
            <div
                ref={this.createDropTarget}
                style={{
                    pointerEvents: !this.props.isContentActive() ? 'all' : undefined,
                    width: this.props.PanelWidth(),
                    height: this.props.PanelHeight(),
                }}>
                {this.theGraph}
                <div
                    style={{
                        display: this.props.isSelected() ? 'none' : undefined,
                        position: 'absolute',
                        width: '100%',
                        height: '100%',
                        pointerEvents: 'all',
                    }}
                />
            </div>
        );
    }
}
