import { observable, action, computed } from "mobx";

import { CellModel } from "./CellModel";
import { ColumnModel } from "./ColumnModel";
import { EventEmitter } from "./EventEmitter";
import { CellEvent } from "./CellModel";
import { EventArguments } from "@dewesoft-web/ui/events";
import { Row, SortDirecton } from "../types";

export enum RowEvent {
    IndexChanged,
    VisibleChanged,
    ReadOnlyChanged,
    SelectedChanged,
    CellValueChanged,
    RowCellChanged,
    CellChanged,
}

export interface RowCells {
    [col : string] : CellModel;
}

export class RowModel extends EventEmitter<RowEvent> {

    @observable
    cells : RowCells;

    @observable
    selected : boolean;

    @observable
    first : boolean;

    @observable
    last : boolean;

    @observable
    readOnly : boolean;

    @observable
    scrollVisible : boolean;

    @observable
    searchVisible : boolean;

    @observable
    visible : boolean;

    index : number;

    @observable
    displayIndex : number;

    columns : ColumnModel[];

    virtualize : boolean;

    constructor(index : number, displayIndex : number, rowInfo : Row, columns : ColumnModel[], virtualize : boolean) {
        super();

        this.virtualize = virtualize;
        this.index = index;
        this.displayIndex = displayIndex;

        this.cells = {};

        for (const col of columns) {
            this.cells[col.property] = new CellModel(rowInfo.data[col.property], col, this);
        }

        for (const prop of Object.keys(rowInfo.data)) {
            if (!this.cells[prop]) {
                this.cells[prop] = new CellModel(rowInfo.data[prop], prop, this);
            }
        }

        this.columns = columns;
        this.readOnly = rowInfo.readOnly === true;

        this.selected = false;
        this.first = false;
        this.last = false;

        this.scrollVisible = !virtualize;
        this.searchVisible = true;
        this.visible = rowInfo.visible !== false;

        this.select = this.select.bind(this);
    }

    onInitialized() {

    }

    @computed
    get isVisible() {
        return this.scrollVisible && this.searchVisible && this.visible;
    }

    @action
    selectCustom(first : boolean, last : boolean) {
        // this.selected = true;
        this.setSelected(true);

        this.first = first;
        this.last = last;
    }

    @action
    select(noEvent? : boolean) {
        // this.selected = true;
        this.setSelected(true, noEvent);

        this.first = true;
        this.last = true;
    }

    @action
    deselect(noEvent? : boolean) {
        // this.selected = false;
        this.setSelected(false, noEvent);

        this.first = false;
        this.last = false;
    }

    @action
    setSelected(select : boolean, noEvent? : boolean) {
        if (noEvent !== true && this.selected !== select) {
            this.triggerEvent(RowEvent.SelectedChanged, {
                selected: select
            });
        }

        this.selected = select;
    }

    triggerSelected() {
        this.triggerEvent(RowEvent.SelectedChanged, {
            selected: this.selected
        });
    }

    edit(col : ColumnModel) : CellModel {

        // const cell = this.cells.find((cell) => cell.column === col);
        const cell = this.cells[col.property];

        if (cell) {
            cell.startEditing();
        }

        return cell;
    }

    setValue(value : any, col : ColumnModel) {
        // const cell = this.cells.find((cell) => cell.column === col);
        const cell = this.cells[col.property];

        if (cell) {
            cell.setValue(value);
        }
    }

    resetFilter() {
        this.searchVisible = true;

        for (const col of this.columns) {
            this.cells[col.property].filter = "";
        }
    }

    @action
    contains(filter : string) : boolean {
        if (filter === "") {
            this.resetFilter();
            return true;
        }

        this.searchVisible = false;

        // console.group(`Row ${this.index}`);

        for (const col of this.columns) {
            if (this.cells[col.property].contains(filter)) {
                this.searchVisible = true;
            }
        }

        // console.groupEnd();

        return this.searchVisible;
    }

    compare(ascending : boolean, column : string, other : RowModel) {
        if (this.cells[column].data < other.cells[column].data) {
            return ascending ? -1 : 1;
        }
        else if (this.cells[column].data > other.cells[column].data) {
            return ascending ? 1 : -1;
        }

        return 0;
    }

    serialize() {
        const keys = Object.keys(this.cells);
        const numKeys = keys.length;

        const cells = new Array(numKeys);
        for (let i = 0; i < numKeys; i++) {
            cells[i] = this.cells[keys[i]].serialize();
        }

        return {
            cells: cells,
            selected: this.selected,
            readOnly: this.readOnly,
            visible: this.isVisible,
            index: this.index
        };
    }

    onCellChanged(cell : CellModel, event : CellEvent, args : EventArguments) {
        // console.log(`RowData[${this.index}]{${cell.column.property}} ${CellEvent[event]} => ${JSON.stringify(args)}`);

        this.triggerEvent(RowEvent.CellChanged, {
            column: cell.column.property,
            cellEvent: event,
            args: args
        });
    }

    addColumnData() {
        for (const col of this.columns) {
            if (!this.cells[col.property]) {
                this.cells[col.property] = new CellModel(null, col, this);
            }
        }
    }

    removeColumnData(columnProp : string) {
        this.cells[columnProp] = undefined;
    }

    private cppCellChanged(col : string, cellEvent : CellEvent, args) {
        if (this.cells[col]) {
            this.cells[col].handleEvent(cellEvent, args);
        }
        else {
            console.warn("No such row cell: " + col);
        }
    }

    handleEvent(event : RowEvent, args : any[]) {

        switch (event) {
            case RowEvent.IndexChanged:
                break;
            case RowEvent.VisibleChanged:
                this.visible = args[0];
                break;
            case RowEvent.SelectedChanged:
                if (args[0]) {
                    this.select(true);
                }
                else {
                    this.deselect(false);
                }
                break;
            case RowEvent.CellValueChanged:
                break;
            case RowEvent.RowCellChanged:
                break;
            case RowEvent.CellChanged: {
                const [col, cellEvent, cellArgs] = args;
                this.cppCellChanged(col, cellEvent, cellArgs);
            }
                break;
            case RowEvent.ReadOnlyChanged:
                this.readOnly = args[0];
                break;
            default:
                break;
        }
    }
}
