//!wrt $BSPEC:{"ver":1.0,"cpr":"Copyright (C) Windows 96 Team 2022.","dsc":"Win96 System Simulator","aut":"Windows 96 Team","icn":"misc/logo","frn":"System Sandbox","ssy":"gui"}

/*
 * SysBox Win96 environment simulator.
 */

const { sysConf: SCM } = w96;
const { Theme, MenuBar, DialogCreator, OpenFileDialog, DropZone } = w96.ui;

/**
 * Default Sysbox RootFS path.
 */
const SBX_RFS_PATH = FSUtil.combinePath(current.modulePath, "sbx_rootfs.zip");

/**
 * Viewport resolution table
 */
const VP_RESOLUTIONS = [
    { width: 640, height: 480 },
    { width: 800, height: 600 },
    { width: 1024, height: 768 },
    { width: 1280, height: 720 },
    { width: 1280, height: 800 },
    { width: 1600, height: 900 },
    { width: 1920, height: 1080 }
];

/**
 * System Sandbox Application.
 */
class SysBoxApplication extends WApplication {
    constructor() {
        super();
        this.rfsPath = SBX_RFS_PATH;
        this.rfsBlobUrl = "";
        this.rfsOfdInitialDir = "C:/user";
        this.m_rfsType = "default";
        this.vmArgs = {
            fake_lstor: "1",
            live: "1",
            disable_idb: "1",
            debug: "0"
        }
    }

    /**
     * Application entry point.
     */
    async main(argv) {
        super.main(argv);

        const mainwnd = this.createWindow({
            title: "Sysbox",
            icon: await Theme.getIconUrl("misc/logo", "16x16"),
            initialHeight: 480,
            initialWidth: 640,
            taskbar: true,
            body: `
            <div class="appbar"></div>
            <div class="af-container" style="width: 100%; height: 100%;">
                <iframe
                    style="height: 100%; width: 100%; border: none; user-select: none;"
                    sandbox="allow-scripts allow-same-origin allow-pointer-lock allow-presentation allow-top-navigation">
                </iframe>
            </div>
            `
        }, true);

        const body = mainwnd.getBodyContainer();
        const appFrame = body.querySelector("iframe");

        // For getters
        const _this = this;
        
        // Setup menu bar
        const mbar = new MenuBar();

        mbar.addRoot("File", [
            {
                type: "normal",
                label: "Exit",
                onclick: ()=>this.terminate()
            }
        ]);

        mbar.addRoot("System", [
            {
                type: "submenu",
                label: "Boot Arguments",
                items: [
                    {
                        type: "normal",
                        label: "Debug Console",
                        get checked() { return _this.vmArgs.debug == "1" },
                        onclick: ()=>this.toggleBootFlag('debug')
                    }
                ]
            },
            {
                type: "submenu",
                label: "Control",
                items: [
                    {
                        type: "normal",
                        label: "Evaluate...",
                        onclick: ()=>DialogCreator.prompt("Enter code to evaluate", {
                            title: "JS Evaluator"
                        }, (v) => {
                            if((!v) || (v.trim() == ""))
                                return;

                            this.appFrame.contentWindow.eval(v);
                        })
                    },
                    {
                        type: "normal",
                        label: "Invoke Debugger",
                        onclick: ()=>this.appFrame.contentWindow.eval("debugger")
                    },
                    {
                        type: "separator" 
                    },
                    {
                        type: "normal",
                        label: "Restart",
                        onclick: ()=>this.restartSys()
                    }
                ]
            },
            {
                type: "separator"
            },
            {
                type: "submenu",
                label: "Root FS",
                items: [
                    {
                        type: "normal",
                        get checked() {
                            return _this.m_rfsType == "default";
                        },
                        label: "Use Default",
                        onclick: ()=>{
                            this.rfsPath = SBX_RFS_PATH;
                            this.m_rfsType = "default";
                        }
                    },
                    {
                        type: "separator"
                    },
                    {
                        type: "normal",
                        get checked() {
                            return _this.m_rfsType == "file";
                        },
                        label: "Load from FS...",
                        onclick: ()=>new OpenFileDialog(this.rfsOfdInitialDir, [".zip"], (v) => {
                            if((!v) || (v.trim() == ""))
                                return;

                            this.rfsOfdInitialDir = FSUtil.getParentPath(v);
                            this.rfsPath = v;
                            this.m_rfsType = "file";
                        }).show()
                    },
                    {
                        type: "normal",
                        get checked() {
                            return _this.m_rfsType == "dir";
                        },
                        label: "Use directory...",
                        onclick: ()=>DialogCreator.prompt("Enter directory path", {
                            title: "RootFS Dir Entry"
                        }, (v) => {
                            if((!v) || (v.trim() == ""))
                                return;

                            this.rfsPath = v;
                            this.m_rfsType = "dir";
                        })
                    }
                ]
            }
        ], false);

        mbar.addRoot("View", [
            {
                type: "submenu",
                label: "Resolution",
                items: [...VP_RESOLUTIONS.map(x => ({
                    type: "normal",
                    label: `${x.width}x${x.height}`,
                    onclick: ()=>this.setViewport(x.width, x.height)
                }))]
            },
            {
                type: "separator"
            },
            {
                type: "normal",
                label: "Enter Fullscreen",
                onclick: ()=>appFrame.requestFullscreen()
            }
        ], false);

        mbar.addRoot("Help", [
            {
                type: "normal",
                label: "About",
                onclick: ()=>DialogCreator.create({
                    body: "SysBox 1.0",
                    icon: "info",
                    title: "About SysBox"
                })
            }
        ]);

        // Replace menu placeholder
        body.querySelector(".appbar").replaceWith(mbar.getMenuDiv());

        this.mainwnd = mainwnd;

        /** @type {HTMLIFrameElement} */
        this.appFrame = appFrame;

        // Init sys
        await this.restartSys();
        
        mainwnd.show();
    }

    /**
     * Sets the viewport width and height.
     */
    setViewport(width, height) {
        this.mainwnd.setSize(width, height + 40);
    }

    /**
     * (Re)starts the system
     */
    async restartSys() {
        await this.reinitRfs();

        let argString = "";
        const argKeys = Object.keys(this.vmArgs);

        for(let k of argKeys)
            argString += `&${k}=${encodeURIComponent(this.vmArgs[k])}`;

        this.appFrame.src = `/?${argString.substring(1)}&rootfs=${this.rfsBlobUrl}&host_t=${Date.now()}`;
    }

    /**
     * Reinits the rootfs.
     */
    async reinitRfs() {
        // Revoke Blob
        if(this.rfsBlobUrl != "")
            URL.revokeObjectURL(this.rfsBlobUrl);

        // Check if rootFS exists
        if(!await FS.exists(this.rfsPath)) {
            DialogCreator.alert(`${this.rfsPath}: No such file or directory`, {
                icon: "error",
                title: "Sysbox"
            });

            return;
        }

        if(!await FS.isFile(this.rfsPath))
            await this.prepRootfsDir();
        else
            this.rfsBlobUrl = URL.createObjectURL(await FS.toBlob(this.rfsPath));
    }

    /**
     * Toggles the specified boot flag.
     * @param {String} name The name of the boot flag to toggle.
     */
    toggleBootFlag(name) {
        this.vmArgs[name] = (this.vmArgs[name] == "1") ? "0" : "1";
    }

    /**
     * Pack dir structure in zip file.
     */
    async folder2Zip(path) {
        const zip = new JSZip();
        const strct = (await FS.walk(path)).map(x=>x.substring(3, x.length));

        for(let i of strct) {
            const p = "c:/" + i;

            if(!await FS.isFile(p))
                continue;

            try {
                await zip.file(i.substring(path.length - 2), await FS.readbin(p));
            } catch(e) {
                console.log("Error zipping contents.");
            }
        }

        const blob = await zip.generateAsync({ type: "blob" });
        return URL.createObjectURL(blob);
    }

    /**
     * Prepares a RootFS dir.
     */
    async prepRootfsDir() {
        const pDlg = w96.ui.DialogCreator.progress(`Packing ${this.rfsPath}...`, {"icon": "info"});
        pDlg.wnd.setControlBoxStyle("WS_CBX_NONE");

        // Pack dir
        this.rfsBlobUrl = await this.folder2Zip(this.rfsPath);
        pDlg.close();
    }

    /**
     * On app termination.
     */
    async ontermination() {
        super.ontermination();

        this.mainwnd = null;

        // End any group that might be created from the instance
        console.groupEnd();
    }
}

return await WApplication.execAsync(new SysBoxApplication(), this.boxedEnv.args, this);