import {IJob} from "./database/IJob";
import {iTenderStatus} from "./iTenderStatus";
import {MyGPIO} from "./MyGPIO";
import GPIO from "rpi-gpio";
import {SensorType} from "./SensorType";
import {clearInterval} from "timers";
import {IContainer} from "./database/IContainer";
import {iTender} from "./iTender";
import debug from "debug";
import {ArduinoProxyPayload} from "./ArduinoProxyPayload";
import {ArduinoProxyPayloadType} from "./ArduinoProxyPayloadType";
import {ArduinoProxy} from "./ArduinoProxy";
import {ContainerHelper} from "./ContainerHelper";

const isPI = require("detect-rpi");

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

export class Mixer {
    static get currentJob(): IJob {
        return this._currentJob;
    }

    /**
     * Timers for the job, for the pumps etc.
     * @private
     */
    private static _jobTimers: NodeJS.Timeout[] = [];

    /**
     * The current itender job
     * @private
     */
    private static _currentJob: IJob;

    /**
     * Checks if the job has finished every 500ms
     * @private
     */
    private static _jobEndCheckInterval: NodeJS.Timer;


    /**
     * Start the internal fill method, a sub-method of the onReceiveFill method
     * This method only gets executed if REALLY all is okay, it is the internal function
     * @param job
     */
    static async startFill(job: IJob) {
        this._jobTimers = [];

        job.startedAt = new Date();
        await job.populate([{path: "amounts.ingredient"}, {path: "amounts.container"}, {path: "drink"}]);
        log("New fill job " + job.drink.name + " will take " + job.estimatedTime + "s");

        this._currentJob = job;
        iTender.setStatus(iTenderStatus.FILLING);


        for (let x of job.amounts) {

            // Start pump here
            try {
                if (x.container.useProxy) {
                    console.log("Using proxy as filler");
                    let payload = new ArduinoProxyPayload(ArduinoProxyPayloadType.SET_PIN, {
                        pin: x.container.pumpPin,
                        mode: "DIGITAL",
                        value: 255
                    });
                    await payload.send();
                } else {
                    console.log("Using normal gpio rpi");
                    await MyGPIO.setup(x.container.pumpPin, GPIO.DIR_OUT)
                    await MyGPIO.write(x.container.pumpPin, true);
                }

            } catch (e) {
                console.debug(e);
                if (isPI()) {
                    log("[ERROR] GPIO I/O Error " + e);

                    // Todo error handling to user
                    await this.cancelFill();
                    return;
                } else {
                    log("[WARNING] GPIO I/O Error, but it's normal cause you are not on raspberry");
                }
            }


            let waitTime = (iTender.secondsPer100ml as number) / 100 * x.amount * 1000;
            log(`Starting output of pump ${x.container.pumpPin}`);
            //mixLog(x.ingredient + " takes " + (waitTime / 1000) + "s for " + x.amount + "ml");
            let timer = setTimeout(async () => {
                // Remove from list of timers

                log(`Stopping output of pump ${x.container.pumpPin}`);
                // Stop pump here
                try {
                    if (x.container.useProxy) {
                        let payload = new ArduinoProxyPayload(ArduinoProxyPayloadType.SET_PIN, {
                            pin: x.container.pumpPin,
                            mode: "DIGITAL",
                            "value": 0
                        });
                        await payload.send();
                    } else {
                        await MyGPIO.write(x.container.pumpPin, false);
                    }

                } catch (e) {
                    if (isPI()) {
                        log("[ERROR] GPIO I/O Error " + e);
                        await this.cancelFill();
                        return;
                    } else {
                        log("[WARNING] GPIO I/O Error, but it's normal cause you are not on raspberry");
                    }
                }

                if (x.container.sensorType == SensorType.NONE) {
                    // V2: Manual measuring
                    x.container.filled = x.container.filled - x.amount;
                    await x.container.save();
                }

               this._jobTimers.splice(this._jobTimers.indexOf(timer),1);

            }, waitTime);
            this._jobTimers.push(timer);
        }



        this._jobEndCheckInterval = setInterval(async () => {
            if (this._jobTimers.length != 0)
                return;


            clearInterval(this._jobEndCheckInterval);
            job.endAt = new Date();
            job.successful = true;
            log("Job successful");
            await job.save();
            setTimeout(() => iTender.setStatus(iTenderStatus.READY), 2500);

            await ContainerHelper.measureContainers();

        }, 500);
    }


    /**
     * Cancel the fill instantly
     */
    static async cancelFill() {
        if (!this._currentJob || iTender.status != iTenderStatus.FILLING)
            return;

        clearInterval(this._jobEndCheckInterval);
        this._currentJob.successful = false;
        this._currentJob.endAt = new Date();
        await this._currentJob.save();

        for (let timer of this._jobTimers) {
            // Clears all the ongoing stop timers
            clearTimeout(timer);
        }

        for (let jobIngredient of this._currentJob.amounts) {
            // stop pump pin
            try {
                if (jobIngredient.container.useProxy) {
                    let payload = new ArduinoProxyPayload(ArduinoProxyPayloadType.SET_PIN, {
                        pin: jobIngredient.container.pumpPin,
                        mode: "DIGITAL",
                        "value": 0
                    });
                    await payload.send();
                } else {
                    await MyGPIO.write(jobIngredient.container.pumpPin, false);
                }
            } catch (e) {

            }

            // ToDo v2 calc
            let container: IContainer = jobIngredient.container;
            let deltaStartStop = (this._currentJob.endAt.getTime() - this._currentJob.startedAt.getTime()) / 1000;

            // füllmenge - ( ( (stopp-start) / 1000 ) * ( sekunden100ml / 100 ) )
            container.filled = container.filled - (deltaStartStop * (iTender.secondsPer100ml / 100)) // V2: Near the current fill value based on time values from delta start stop

            container.save().then();
        }

        iTender.setStatus(iTenderStatus.READY);
    }
}