import {iTenderStatus} from "./iTenderStatus";
import Container from "./database/Container";
import {HCSR04} from "hc-sr04";
import {IContainer} from "./database/IContainer";
import Drink from "./database/Drink";
import {IDrink} from "./database/IDrink";
import debug from "debug";
import {WebSocketHandler} from "./WebSocketHandler";
import {IJob} from "./database/IJob";
import {Utils} from "./Utils";
import {WebSocketPayload} from "./WebSocketPayload";
import {WebSocketEvent} from "./WebSocketEvent";
import {HX711} from "./HX711";
import {SensorType} from "./SensorType";

const log = debug("itender:station");

export class iTender {
    static get containers(): { container: IContainer, sensor: HCSR04 | HX711, pump: null }[] {
        return this._containers;
    }

    static get drinks(): IDrink[] {
        return this._drinks;
    }

    static get currentJob(): IJob | null {
        return this._currentJob;
    }

    private static _status: iTenderStatus = iTenderStatus.STARTING;
    private static _currentJob: IJob | null = null;
    private static _internetConnection: boolean = false;

    static get internetConnection(): boolean {
        return this._internetConnection;
    }

    /**
     * @Deprecated
     * @private
     */
    private static _containers: { container: IContainer, sensor: HCSR04 | HX711, pump: null }[] = [];
    private static _drinks: IDrink[];

    static setStatus(status: iTenderStatus) {
        this._status = status;
        if (WebSocketHandler.ws && WebSocketHandler.ws.readyState == 1)
            WebSocketHandler.sendStatus().then().catch(console.error);
    }

    static get status(): iTenderStatus {
        return this._status;
    }

    static startFill() {
        // todo Fill method


        // End
        this.measureContainers().then();
    }

    static cancelFill() {
        // todo Stop fill method
    }

    static measureContainers(): Promise<void> {
        log("Measuring containers...");

        return new Promise(async resolve => {
            for (let c of (await Container.find({enabled: true}))) {
                try {
                    if( !c.sensorFilledMax || c.sensorFilledMin )
                    {
                        c.enabled = false;
                        await c.save();
                        continue;
                    }

                    let rand = Math.random() * 5;
                    if (c.sensorType == SensorType.ULTRASOUND) {
                        let sensor = new HCSR04(c.sensorPin1, c.sensorPin2);

                        c.rawData = sensor.distance();
                        c.rawData = rand;

                        c.filled = c.rawData * c.sensorFilledMin / 100;

                    } else if (c.sensorType == SensorType.LOADCELL) {
                        let sensor = new HX711(c.sensorPin1, c.sensorPin2);

                        c.rawData = sensor.measure();
                        c.rawData = rand;
                        c.filled = c.rawData * c.sensorFilledMax / 100;
                    }
                } catch (e) {
                    c.filled = -1;
                    c.rawData = 0;
                }
                console.log(c);
                await c.save();
            }
            log("Containers measured!");
            resolve();

            let payload = new WebSocketPayload(WebSocketEvent.CONTAINERS, false, (await Container.find()));
            await WebSocketHandler.send(payload);
        });
    }

    static refreshDrinks(): Promise<void> {
        this.setStatus(iTenderStatus.REFRESHING);
        log("Refreshing drinks...");
        return new Promise(async resolve => {
            this._drinks = [];
            for (let d of (await Drink.find().populate("ingredients.type"))) {

                let drinkAccept = true;
                for (let i of d.ingredients) {
                    let c = await Container.findOne({content: i["type"]});

                    if (!c) {
                        drinkAccept = false;
                        break;
                    }
                }
                if (drinkAccept) {
                    this._drinks.push(d);
                }

            }
            log("Drinks refreshed!");

            resolve();
            this.setStatus(iTenderStatus.READY);
        });
    }

    /**
     * @Deprecated
     */
    static refreshContainers(): Promise<void> {
        log("Refreshing containers...");
        this.setStatus(iTenderStatus.CALCULATING);
        return new Promise(async resolve => {
            let containers = await Container.find();
            for (let c of containers) {
                let i = 0;
                let found = false;
                for (let c2 of this._containers) {
                    if (c2.container._id == c._id) {
                        let sensor;
                        try {
                            //sensor = new HCSR04(c.sensorTrigger, c.sensorEcho);
                        } catch (e) {
                        }
                        this._containers[i] = {
                            container: c,
                            sensor: sensor,
                            pump: null
                        };
                        found = true;
                        break;
                    }
                    i++;
                }
                if (!found) {
                    let sensor;
                    try {
                        //sensor = new HCSR04(c.sensorTrigger, c.sensorEcho);
                    } catch (e) {
                    }
                    this._containers.push({
                        container: c,
                        sensor: sensor,
                        pump: null
                    });
                }

            }

            log("Containers refreshed!");
            this.measureContainers().then().catch(console.error);

            resolve();
            this.setStatus(iTenderStatus.READY);
        });
    }

    public static async autoCheckup() {
        setInterval(async () => {
            if (!this._currentJob)
                return;
            // Check if         startedTime     plus   2 mins   smaller than    now
            if (this._currentJob.startedAt.getTime() + 1000 * 60 * 2 <= Date.now()) {
                // Job can be declared as stuck!
                this._currentJob.successful = false;
                this._currentJob.endAt = new Date();
                await this._currentJob.save();
                this._currentJob = null;
                this.setStatus(iTenderStatus.READY);
            }
        }, 30000);
    }

    public static async checkNetwork() {
        this._internetConnection = await Utils.checkInternet();
    }


    static refreshFromServer(): Promise<void> {
        return new Promise(async (resolve, reject) => {
            let before = iTender._status;
            iTender.setStatus(iTenderStatus.DOWNLOADING)
            // todo

            resolve();
            iTender.setStatus(before);

        });
    }

    private static interval;

    public static toggleTare(state: boolean) {
        clearInterval(iTender.interval);
        if ( state )
            this.interval = setInterval(async () => {
                await this.measureContainers();
            }, 500);
    }
}