import * as React from "react";
import { CSSProperties } from "react";
import { observer } from "mobx-react";
import detectPassiveEvents from "detect-passive-events";

import { GridModel } from "./models/GridModel";
import { RowModel } from "./models/RowModel";
import { ColumnModel } from "./models/ColumnModel";
import { getColumnsFromChildren } from "./util/util";

import { GirdHeaders } from "./headers/GridHeaders";
import { GridGroups } from "./groups/GridGroups";
import { ContextMenu } from "@dewesoft-web/grid2/contextmenu/ContextMenu"

import * as theme from "./flexGrid.scss";
import { CellModel } from "./models/CellModel";
import { BaseProps, BooleanString, CommonProps } from "../ui";

const PAGE_UP = 33;
const PAGE_DOWN = 34;
const HOME = 36;
const END = 35;
const ARROW_UP = 38;
const ARROW_DOWN = 40;
const ARROW_RIGHT = 39;
const ARROW_LEFT = 37;

let counter = 0;

export interface GridProps extends BaseProps {
    Model? : GridModel;
    Name : string;
    RowHeight? : string;
    Virtualize? : BooleanString;

    [prop : string] : any;
}

@observer
export class FlexGrid extends React.Component<GridProps, any> {

    model : GridModel;

    element : HTMLDivElement;
    private tabIndex : number;

    constructor(props : GridProps) {
        super(props);

        if (this.props.Model) {
            this.model = this.props.Model;
        }
        else {
            this.model = new GridModel(this.props.Name, this.props, [], getColumnsFromChildren(this.props.children));
        }

        this.tabIndex = counter++;
        this.model.onInitialized();
    }

    onHeadersResize = (e : Event) => {
        if (this.element) {
            const groupsEl = this.element.querySelector(".groups") as HTMLElement;
            groupsEl.style.width = (e.target as HTMLElement).clientWidth + "px";
        }
    };

    componentDidMount() {
        if (this.element) {
            this.model.scrollHeight = this.element.scrollHeight;
            this.model.scrollWidth = this.element.scrollWidth;

            const headersEl = this.element.querySelector(".headers");
            if (headersEl) {
                const groupsEl = this.element.querySelector(".groups") as HTMLElement;
                groupsEl.style.width = headersEl.clientWidth + "px";

                headersEl.addEventListener("onresize", this.onHeadersResize);
            }
        }
    }

    componentWillUnmount() {
        if (this.model.element) {

            if (this.model.virtualize) {
                (this.model.element.removeEventListener as any)("scroll");
                this.model.element.removeEventListener("onresize", this.onScroll);
            }

            const headersEl = this.element.querySelector(".headers");
            if (headersEl) {
                headersEl.removeEventListener("onresize", this.onHeadersResize);
            }
        }
    }

    shouldComponentUpdate() {
        return !this.model.getIsBatch();
    }

    onKeyDown = (e) => {
        if (e.target instanceof HTMLInputElement) {
            return;
        }

        switch (e.keyCode) {
            case ARROW_UP:
                this.model.selectPreviousRow();
                break;
            case ARROW_DOWN:
                this.model.selectNextRow();
                break;
            case PAGE_UP:
            case HOME:
                this.model.selectFirstRow();
                break;
            case PAGE_DOWN:
            case END:
                this.model.selectLastRow();
                break;
            case ARROW_RIGHT:
                this.model.selectNextColumn();
                break;
            case ARROW_LEFT:
                this.model.selectPreviousColumn();
                break;
            default:
                this.model.onKeyPressed(e);
                break;
        }

        e.preventDefault();
    };

    onScroll = (e : Event) => {
        const target = e.target as HTMLDivElement;
        const height = this.model.getElementHeight(target, e.type === "onresize");


        this.model.scrollHeight = target.scrollHeight;

        if (!this.model.getIsBatch()) {
            this.model.setScrollPosition(target.scrollTop, height);
        }
    };

    setElement = (el) => {
        this.element = el;

        if (el && this.model.virtualize) {
            this.element.addEventListener("onresize", this.onScroll);
            this.element.addEventListener("scroll", this.onScroll, (detectPassiveEvents.hasSupport ? { passive: true } : false) as any);
            this.model.setElement(el);
        }
    };

    onRowSelect = (row : RowModel, col : ColumnModel, selectToRow? : boolean) => {
        // console.log("START FlexGrid: OnRowSelect");

        this.model.onRowSelect(row, col, selectToRow);

        // console.log("END FlexGrid: OnRowSelect");
    };

    onStartSelect = (row : RowModel, col : ColumnModel) => {
        // console.log("START FlexGrid: OnStartSelect");

        this.model.startSelectRow(row, col);

        // console.log("END FlexGrid: OnStartSelect");
    };

    onTrySelect = (row : RowModel) => {
        // console.log("START FlexGrid: OnTrySelect");

        this.model.onTrySelect(row);

        // console.log("END FlexGrid: OnTrySelect");
    };

    onEditCell = (row : RowModel, col : ColumnModel) => {
        // console.log("START FlexGrid: OnEditCell");

        this.model.onEditCell(row, col);

        // console.log("END FlexGrid: OnEditCell");
    };

    onStartEdit = (cell : CellModel) => {
        // console.log("START FlexGrid: OnStartEdit");

        this.model.onStartEdit(cell);

        // console.log("END FlexGrid: OnStartEdit");
    };

    onSelectColumn = (col : ColumnModel) => {
        this.model.selectAllRows(col);
    };

    mouseUp = () => {
        this.model.stopSelecting();

        // console.log("END SELECT");
    };

    onValueChanged = (value) => {
        this.model.setValueForSelected(value);
    };

    onGroupByChanged = (order) => {
        this.model.setGroupBy(order);
    };

    render() {
        const style = {
            ...(this.model.props as any).style
        };

        if (!style.height) {
            style.height = this.model.clientHeight;
        }

        if (!this.model.virtualize) {
            style.height = this.model.unvirtualizedHeight;
        }

        const wrapStyle : CSSProperties = {
            height: "100%"
        };

        return (
            <div style={wrapStyle}>
                <div className={theme.flexGridWrapper}
                    style={style}
                    onMouseUp={this.mouseUp}
                    onKeyDown={this.onKeyDown}
                    tabIndex={this.tabIndex}
                    ref={this.setElement}
                >
                    <div className={theme.flexGrid} style={{ height: this.model.scrollHeight }}>
                        <GirdHeaders top={this.model.scrollTop - 24}
                                    columns={this.model.orderedColumns}
                                    onClick={this.onSelectColumn}
                        />
                        <GridGroups top={this.model.scrollTop}
                                    groups={this.model.groups}
                                    onRowSelect={this.onRowSelect}
                                    onStartSelect={this.onStartSelect}
                                    onTrySelect={this.onTrySelect}
                                    onEditCell={this.onEditCell}
                                    onValueChanged={this.onValueChanged}
                                    onStartEdit={this.onStartEdit}
                                    rowHeight={this.model.rowHeight}
                        />
                    </div>
                </div>
                <ContextMenu modelGrid={this.model} name={"context_menu"}/>
            </div>
        );
    }
}

