import { action, computed, IReactionDisposer, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Field, NumListCast, StrListCast } from '../../../../../fields/Doc';
import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
import { Cast, DocCast } from '../../../../../fields/Types';
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils';
import { DragManager } from '../../../../util/DragManager';
import { LinkManager } from '../../../../util/LinkManager';
import { DocumentView } from '../../DocumentView';
import { DataVizView } from '../DataVizBox';
import './Chart.scss';

interface TableBoxProps {
    rootDoc: Doc;
    layoutDoc: Doc;
    records: { [key: string]: any }[];
    selectAxes: (axes: string[]) => void;
    axes: string[];
    width: number;
    height: number;
    margin: {
        top: number;
        right: number;
        bottom: number;
        left: number;
    };
    docView?: () => DocumentView | undefined;
}

@observer
export class TableBox extends React.Component<TableBoxProps> {
    _inputChangedDisposer?: IReactionDisposer;
    componentDidMount() {
        // if the tableData changes (ie., when records are selected by the parent (input) visulization),
        // then we need to remove any selected rows that are no longer part of the visualized dataset.
        this._inputChangedDisposer = reaction(() => this._tableData.slice(), this.filterSelectedRowsDown, { fireImmediately: true });
    }
    componentWillUnmount() {
        this._inputChangedDisposer?.();
    }
    @computed get _tableDataIds() {
        return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
    }
    // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
    @computed get _tableData() {
        return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
    }

    @computed get parentViz() {
        return DocCast(this.props.rootDoc.dataViz_parentViz);
        // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
        //     .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
        //     .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
    }

    @computed get columns() {
        return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header != '' && header != undefined) : [];
    }

    // updates the 'selected' field to no longer include rows that aren't in the table
    filterSelectedRowsDown = () => {
        const selected = NumListCast(this.props.layoutDoc.dataViz_selectedRows);
        this.props.layoutDoc.dataViz_selectedRows = new List<number>(selected.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data
    };

    render() {
        if (this._tableData.length > 0) {
            return (
                <div
                    className="table-container"
                    style={{ height: this.props.height }}
                    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 }
                        )
                    }>
                    <table className="table">
                        <thead>
                            <tr className="table-row">
                                {this.columns.map(col => (
                                    <th
                                        key={this.columns.indexOf(col)}
                                        style={{
                                            color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined,
                                            background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
                                            fontWeight: 'bolder',
                                            border: '3px solid black',
                                        }}
                                        onPointerDown={e => {
                                            const downX = e.clientX;
                                            const downY = e.clientY;
                                            setupMoveUpEvents(
                                                {},
                                                e,
                                                e => {
                                                    // dragging off a column to create a brushed DataVizBox
                                                    const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
                                                    const targetCreator = (annotationOn: Doc | undefined) => {
                                                        const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
                                                        embedding._dataViz = DataVizView.TABLE;
                                                        embedding._dataViz_axes = new List<string>([col, col]);
                                                        embedding._dataViz_parentViz = this.props.rootDoc;
                                                        embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
                                                        embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors);
                                                        embedding.defaultHistogramColor = this.props.layoutDoc.defaultHistogramColor;
                                                        embedding.pieSliceColors = Field.Copy(this.props.layoutDoc.pieSliceColors);
                                                        return embedding;
                                                    };
                                                    if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
                                                        DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
                                                            dragComplete: e => {
                                                                if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
                                                                    e.linkDocument.link_displayLine = true;
                                                                    e.linkDocument.link_matchEmbeddings = true;
                                                                    // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
                                                                    // e.annoDragData.linkSourceDoc.followLinkZoom = false;
                                                                }
                                                            },
                                                        });
                                                        return true;
                                                    }
                                                    return false;
                                                },
                                                emptyFunction,
                                                action(e => {
                                                    const newAxes = this.props.axes;
                                                    if (newAxes.includes(col)) newAxes.splice(newAxes.indexOf(col), 1);
                                                    else if (newAxes.length > 1) newAxes[1] = col;
                                                    else newAxes.push(col);
                                                    this.props.selectAxes(newAxes);
                                                })
                                            );
                                        }}>
                                        {col}
                                    </th>
                                ))}
                            </tr>
                        </thead>
                        <tbody>
                            {this._tableDataIds
                                ?.map(rowId => ({ record: this.props.records[rowId], rowId }))
                                .map(({ record, rowId }) => (
                                    <tr
                                        key={rowId}
                                        className="table-row"
                                        onClick={action(e => {
                                            // selecting a row
                                            const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('number'), null);
                                            if (selected?.includes(rowId)) selected.splice(selected.indexOf(rowId), 1);
                                            else selected?.push(rowId);
                                            e.stopPropagation();
                                        })}
                                        style={{ background: NumListCast(this.props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '', width: '110%' }}>
                                        {this.columns.map(col => {
                                            // each cell
                                            const colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false;
                                            return (
                                                <td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}>
                                                    {record[col]}
                                                </td>
                                            );
                                        })}
                                    </tr>
                                ))}
                        </tbody>
                    </table>
                </div>
            );
        } else
            return (
                // when it is a brushed table and the incoming table doesn't have any rows selected
                <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
            );
    }
}
