import { action, computed, observable } from "mobx";

import { ColumnModel } from "./ColumnModel";
import { RowModel } from "./RowModel";
import { GridType, Option, SelectOption } from "../types";

export enum CellEvent {
    ValueChanged,
    TypeChanged,
    EditStart,
    EditEnd,
    ReadOnlyChanged,
}

export class CellModel {

    @observable
    data : any;

    @observable
    temp : any;

    @observable
    editing : boolean;

    column : ColumnModel;

    columnProperty : string;

    row : RowModel;

    dirty : boolean;

    custom : any;

    @observable
    filter : string;

    @observable
    typeOverride : GridType;

    constructor(cell : any, column : ColumnModel | string, row? : RowModel) {
        this.column = column instanceof ColumnModel ? column : null;
        this.row = row ? row : null;

        this.dirty = false;
        this.editing = false;
        this.filter = "";

        if (typeof cell === "object") {
            if (cell) {
                this.data = cell.value;
                this.temp = cell.value;
                this.typeOverride = cell.type;
            }
            else {
                this.data = null;
                this.temp = null;
                this.typeOverride = null;
            }
        }
        else {
            this.data = cell;
            this.temp = cell;
        }

        this.columnProperty = column ? (column as ColumnModel).property : (column as string);
    }

    @computed
    get color() {
        const type = typeof this.data;

        if (type === "string") {
            return this.data;
        }
        else if (type === "object") {
            return `rgba(${this.data.r}, ${this.data.g}, ${this.data.b}, ${this.data.a})`;
        }
    }

    @computed
    get isReadOnly() : boolean {
        if (this.typeOverride && this.typeOverride.readOnly !== undefined) {
            return this.typeOverride.readOnly;
        }

        return (this.column ? this.column.readOnly : false) || (this.row ? this.row.readOnly : false);
    }

    @computed
    get type() {
        if (!this.column) {
            return this.typeOverride;
        }

        return {
            ...this.column.type,
            ...this.typeOverride
        };
    }

    parseValue(temp) {
        const cellType : any = this.type;

        if (cellType.kind.toLowerCase() === "number") {
            let data = parseFloat(temp);

            if (data !== 0 && !data) {
                data = this.data;
            }
            else {
                let max = (cellType.max === undefined || cellType.max === null) ? Infinity : cellType.max;
                let min = (cellType.min === undefined || cellType.min === null) ? -Infinity : cellType.min;

                if (data < min) {
                    data = min;
                }
                else if (data > max) {
                    data = max;
                }
            }

            temp = data;
        }
        return temp;
    }

    setValue(data : any) {
        data = this.parseValue(data);

        this.data = data;
        this.temp = data;

        this.row.onCellChanged(this, CellEvent.ValueChanged, {
            value: data
        });
    }

    getValue() : any {
        const type : GridType = this.type;

        switch(type.kind.toLowerCase()) {
            case "toggle":
                return this.data ? type.options.on : type.options.off;
            case "button":
                return type.label;
            case "enum":
                if (this.data || this.data == 0) {
                    for (const opt of type.options as Option[]) {
                        const valueType = typeof opt;

                        if (valueType === "string" && this.data == opt) {
                            return opt;
                        }
                        else if (valueType === "object" && (opt as SelectOption).value === this.data) {
                            return (opt as SelectOption).description;
                        }
                    }
                }
                return this.data;
            default:
                return this.data;
        }
    }

    @action
    setTypeOverride(type : any, noEvent? : boolean) {
        if (typeof type === "string") {
            this.typeOverride = {
                kind: type
            };
        }
        else {
            this.typeOverride = type;
        }

        if (!noEvent) {

        }
    }

    stopEditing(force : boolean = false) {
        this.dirty = false;

        if (force || this.type.kind.toLowerCase() !== "color") {
            this.editing = false;
        }
    }

    startEditing() {
        this.temp = this.data;
        this.editing = true;
    }

    contains(filter : string) : boolean {
        const value = this.getValue();
        const compare = ((value === undefined) || value == null) ? "" : value.toString()
                                                                             .toLowerCase();

        //console.log(`${this.column.property} filter: ${filter} ? data: ${compare}, [${compare.includes(filter)}] type: ${this.type.kind}`);

        const matches = compare.includes(filter);

        if (matches) {
            this.filter = filter;
        }
        return matches;
    }

    serialize() {
        return {
            data: this.data || null,
            property: this.columnProperty,
            typeOverride: this.typeOverride
        };
    }

    checkAndSet(data) {
        let kind;

        if (!this.column && this.typeOverride) {
            kind = this.typeOverride.kind;
        }
        else if (this.typeOverride){
            kind = this.typeOverride.kind;
        }
        else if (this.column){
            kind = this.column.type.kind;
        }

        if (kind === "enum") {
            const options : any = this.type.options;

            let valid = false;
            for (const opt of options) {
                if (typeof opt === "string" && opt === data) {
                    valid = true;
                }
                else if (opt.value && opt.value === data) {
                    valid = true;
                }
            }

            if (!valid) {
                console.warn(`Invalid value of '${data}' received for column ${this.columnProperty}[${this.row.index}]`);
                return;
            }
        }

        this.data = data;
    }

    handleEvent(event : CellEvent, args : any) {
        switch (event) {
            case CellEvent.ValueChanged:
                this.checkAndSet(args[0]);
                break;
            case CellEvent.TypeChanged:
                this.setTypeOverride(args[0]);
                break;
            case CellEvent.ReadOnlyChanged:
                if (this.typeOverride) {
                    this.typeOverride.readOnly = args[0];
                }
                else {
                    this.typeOverride = {
                        readOnly: args[0]
                    } as GridType;
                }
                break;
            default:
                break;
        }
    }
}
