import { blenderPath, blenderScriptPath, templatePath, finsishedIconPath, dataPath } from "./paths";
import {spawn} from "child_process";
import logger from "./logger";
import { setBlenderLoading, setBlenderStatus } from "./ui/menu";
import { setLogNumber, setStatus, addTerminalLine } from "./ui/renderingPage";
import {imageLoading, imageLoaded} from "./ui/settingsPage";
import { getInOutSettings, getActiveProfile } from "./settings";
import { pageSetRendering, setProgress, openPage, Page } from "../renderer";
import { setLog, setRenderProgress, startProgress, stopProgress } from "./progressController";
import { ipcRenderer } from "electron";
import path from 'path';
import fs from "fs";
// import { getDoNotDisturb } from "electron-notification-state";

const blenderStartString = [
    templatePath,
    "--background",
    "--python",
    blenderScriptPath
]

let blenderConsole = spawn(blenderPath, blenderStartString).on('error', function(err) {
    logger.errorMSG("Could not start blender: " + err.toString());
});
let readyToAcceptCommand = false;
let renderingPicture = false;
let renderingVideo = false;
let waitingForRender = false;

function getOutPath(log:string) {
    let fullOutPath = path.join(getInOutSettings().output, log.substring(log.lastIndexOf("\\")).replace(".csv", "."+getActiveProfile().videoFormat));
    
    if(fs.existsSync(fullOutPath)) {
        let i = 1;
        while(fs.existsSync(fullOutPath.replace("."+getActiveProfile().videoFormat, " ("+i+")."+getActiveProfile().videoFormat))) {
            i++;
        }
        fullOutPath = fullOutPath.replace("."+getActiveProfile().videoFormat, " ("+i+")."+getActiveProfile().videoFormat);
    }
    
    return fullOutPath;
}

let outputArgs:string[] = [];

function blenderArgs() {
    const outputList:string[] = [];
    getInOutSettings().logs.forEach(log => {
        outputList.push(getOutPath(log));
    });
    
    outputArgs = outputList;
    
    return JSON.stringify({
        stickMode2:getActiveProfile().stickMode2,
        width:getActiveProfile().width,
        stickDistance:getActiveProfile().stickDistance,
        fps:getActiveProfile().fps,
        videoFormat:getActiveProfile().videoFormat,
        logs:getInOutSettings().logs,
        output:outputList,
        dataPath:dataPath
    });
}

function startBlender() {
    let frames = "0";
    let lastFrame = "0";
    let log = "0";
    
    blenderConsole.stdout.on('data', function(data) {
        const dataStr = data.toString();
        
        logger.info("Blender: " + dataStr);
        
        if(renderingVideo) {
            addTerminalLine(dataStr);
        }
        
        if (dataStr.includes("Blender started successfully")) {
            renderingPicture = false;
            renderingVideo = false;
            setBlenderStatus("Started");
        }
        if (dataStr.includes("Blender quit")) {
            if(renderingPicture) {
                logger.errorMSG("Rendering preview Failed!");
            } else if(renderingVideo) {
                logger.errorMSG("Rendering video Failed!");
            }
            
            readyToAcceptCommand = false;
            renderingPicture = false;
            renderingVideo = false;
            setBlenderStatus("Restarting");
            setBlenderLoading(true);
            setProgress();
            restartBlender();
        }
        
        if(dataStr.includes("Frames:")) {
            frames = dataStr.split(":")[1];
            renderingVideo = true;
            readyToAcceptCommand = false;
            setRenderProgress(parseInt(log), true, 0, 0);
        }
        if(dataStr.includes("Fra:") && renderingVideo) {
            lastFrame = dataStr.split("Fra:")[1].split(" ")[0];
            setStatus("Rendering Frame " + lastFrame + "/" + frames);
            setRenderProgress(parseInt(log), false, parseInt(frames), parseInt(lastFrame));
        }
        if(dataStr.includes("Finished") && renderingVideo) {
            pageSetRendering(false);
            stopProgress();
            if(lastFrame == frames) {
                openPage(Page.RenderFinish);
                ipcRenderer.send("renderFinished");
                // TODO: only show notification if not in do not disturb mode, currently not working. Details: https://github.com/felixrieseberg/macos-notification-state/issues/30
                new Notification("Render Finished", {
                    body: "Rendering finished successfully!",
                    icon: finsishedIconPath
                }).onclick = function() {
                    ipcRenderer.send("openApp");
                }
            } else {
                logger.errorMSG("Render Failed!");
            }
        }
        if(dataStr.includes("Init:") && renderingVideo) {
            setStatus("Initialize Frame " + dataStr.split(":")[1] + "/" + frames);
            setRenderProgress(parseInt(log), true, parseInt(frames), parseInt(dataStr.split(":")[1]));
        }
        if(dataStr.includes("Lognr:") && renderingVideo) {
            log = dataStr.split(":")[1];
            if(log !== "1") {
                setLog(parseInt(log));
            }
            setLogNumber(log);
        }
        
        if(dataStr.includes("Waiting for command")) {
            pageSetRendering(false);
            setProgress();
            
            if(renderingPicture) {
                imageLoaded();
            }
            
            if(!waitingForRender) {
                readyToAcceptCommand = true;
                renderingPicture = false;
                renderingVideo = false;
                setBlenderStatus("Ready");
                setBlenderLoading(false);
            } else {
                waitingForRender = false;
                renderingPicture = true;
                blenderConsole.stdin.write("getRender -- "+blenderArgs()+"\n");
                setBlenderStatus("Rendering");
                setBlenderLoading(true);
                imageLoading();
            }
        }
    });
    
    blenderConsole.stderr.on('data', function(data:string) {
        logger.errorMSG("Blender: " + data);
    });
}

function restartBlender() {
    pageSetRendering(false);
    blenderConsole.kill();
    blenderConsole = spawn(blenderPath, blenderStartString);
    startBlender();
}

enum blenderCmd {
    getRender,
    startRendering,
    stopRendering,
}

function blender(command:blenderCmd) {
    if(command === blenderCmd.getRender) {
        if(readyToAcceptCommand) {
            readyToAcceptCommand = false;
            renderingPicture = true;
            imageLoading();
            setBlenderStatus("Rendering");
            setBlenderLoading(true);
            blenderConsole.stdin.write("getRender -- "+blenderArgs()+"\n");
        } else {
            waitingForRender = true;
        }
    } else if(command === blenderCmd.startRendering) {
        if(readyToAcceptCommand) {
            if(getInOutSettings().logs.length === 0) {
                logger.warningMSG("No log selected!");
            } else {
                readyToAcceptCommand = false;
                renderingVideo = true;
                pageSetRendering(true);
                setBlenderStatus("Rendering");
                setBlenderLoading(true);
                blenderConsole.stdin.write("startRendering -- "+blenderArgs()+"\n");
                
                startProgress();
            }
        }
    } else if(command === blenderCmd.stopRendering) {
        readyToAcceptCommand = false;
        renderingPicture = false;
        renderingVideo = false;
        restartBlender();
    }
}

ipcRenderer.on("isRenderActiveClose", () => {
    if(renderingVideo) {
        ipcRenderer.send("renderActiveClose");
    } else {
        ipcRenderer.send("renderInactiveClose");
    }
});

export {
    blender,
    blenderCmd,
    startBlender,
    renderingPicture,
    outputArgs
}