import { events } from "@neutralinojs/lib";
import {
    createEffect,
    createSignal,
    For,
    Match,
    onCleanup,
    onMount,
    Show,
    Switch,
    type Signal,
} from "solid-js";
import {
    generateOutputCommand,
    getAvailableCodecs,
    getLengthMicroseconds,
    playFile,
    videoFileExtensions,
    type CodecInfo,
    type FFmpegParams,
} from "./util/ffmpeg";
import Neutralino from "@neutralinojs/lib";
import H264Options from "./components/H264Options";
import Configure from "./assets/breeze/actions/16/configure.svg";
import PlaybackStart from "./assets/breeze/actions/16/media-playback-start.svg";
import TrashEmpty from "./assets/breeze/actions/16/trash-empty.svg";

const commonCodecs = new Set(["h264", "hevc", "vp8", "vp9", "av1", "dnxhd"]);

interface RunningProcessInfo {
    process: Neutralino.os.SpawnedProcess;
    file: string;
    length: number;
}

function App() {
    const [windowFocused, setWindowFocused] = createSignal(true);
    const [displayedCodecs, setDisplayedCodecs]: Signal<CodecInfo[]> =
        createSignal([] as CodecInfo[]);
    const [fileList, setFileList] = createSignal([
        "/home/satakunu/Videos/litetask_demo.mkv",
    ]);
    const [selectedClip, setSelectedClip] = createSignal("");
    const [outputCommand, setOutputCommand] = createSignal(
        "ffmpeg -i {filename}",
    );
    const [showCommonCodecs, setShowCommonCodecs] = createSignal(true);
    const [selectedCodec, setSelectedCodec] = createSignal<CodecInfo>();
    const [selectedEncoder, setSelectedEncoder] = createSignal("");
    const [runningProcesses, setRunningProcesses] = createSignal<
        RunningProcessInfo[]
    >([]);
    let supportedCodecs: CodecInfo[] = [];
    let ffmpegParams: FFmpegParams = { vcodec: "" };
    let successfulCount = 0;
    let unsuccessfulCount = 0;
    let totalCount = 0;

    function windowIsFocused() {
        setWindowFocused(true);
    }

    function windowUnfocused() {
        setWindowFocused(false);
    }

    function handleSpawnedProcessEvents(evt: CustomEvent) {
        if (evt.detail.action !== "exit") return;

        if (evt.detail.data === 0) {
            successfulCount += 1;
        } else {
            unsuccessfulCount += 1;

            if (evt.detail.data !== 255) {
                Neutralino.os.showNotification(
                    "File Encoding Failed",
                    `Encoding for file "${runningProcesses()?.find((v) => v.process.id == evt.detail.id)?.file}" failed. Exit code ${evt.detail.data}.`,
                );
            }
        }

        if (successfulCount + unsuccessfulCount === totalCount) {
            Neutralino.os.showNotification(
                "File(s) encoded.",
                `${successfulCount} files encoded successfully. ${unsuccessfulCount} failed or cancelled.`,
            );
            successfulCount = 0;
            unsuccessfulCount = 0;
            totalCount = 0;
        }

        console.log(`FFmpeg exited with code: ${evt.detail.data}`);
    }

    onMount(async () => {
        events.on("windowFocus", windowIsFocused);
        events.on("windowBlur", windowUnfocused);
        events.on("spawnedProcess", handleSpawnedProcessEvents);

        supportedCodecs = await getAvailableCodecs();
        filterDisplayedCodecs();

        const firstCodec = displayedCodecs()[0];

        setSelectedCodec(firstCodec);
        setSelectedEncoder(firstCodec.encoders[0]);
    });

    onCleanup(() => {
        events.off("windowFocus", windowIsFocused);
        events.off("windowBlur", windowUnfocused);
        events.off("spawnedProcess", handleSpawnedProcessEvents);
    });

    function removeBtnClicked() {
        if (selectedClip() === "") return;

        const list = fileList();
        const targetClip = selectedClip();
        setFileList(list.filter((v) => v !== targetClip));
        setSelectedClip("");
    }

    function removeAllBtnClicked() {
        setFileList([]);
        setSelectedClip("");
    }

    function playBtnClicked() {
        playFile(selectedClip());
    }

    async function openBtnClicked() {
        const filePaths = await Neutralino.os.showOpenDialog("Select Videos", {
            multiSelections: true,
            filters: [
                {
                    extensions: ["mp4", "mkv", "mov", "webm"],
                    name: "Common Video Files",
                },
                {
                    extensions: ["*"],
                    name: "All Files",
                },
            ],
        });
        setFileList(Array.from(new Set([...fileList(), ...filePaths])));
    }

    function filterDisplayedCodecs() {
        if (showCommonCodecs()) {
            setDisplayedCodecs(
                supportedCodecs.filter((v) => commonCodecs.has(v.shortName)),
            );
            return;
        }

        setDisplayedCodecs(supportedCodecs);
    }

    function showCommonCodecsChanged(e: InputEvent) {
        const newValue = (e.target as HTMLInputElement).checked;
        setShowCommonCodecs(newValue);
        filterDisplayedCodecs();
    }

    function selectedCodecsChanged(e: InputEvent) {
        const newValue = (e.target as HTMLInputElement).value;
        const codecObj = displayedCodecs().find(
            (v) => v.shortName === newValue,
        );

        if (newValue !== "h264" && newValue !== "hevc") {
            ffmpegParams.twopass = false;
        }

        setSelectedCodec(codecObj);
        let encoder = newValue;
        if (codecObj?.encoders.length !== 0) {
            encoder = codecObj?.encoders[0] ?? "";
        }
        setSelectedEncoder(encoder);
    }

    function onParametersChanged(key: string, value: any) {
        // @ts-ignore
        ffmpegParams[key] = value;
        setOutputCommand(generateOutputCommand(ffmpegParams));
    }

    function settingsBtnPressed() {
        Neutralino.window.create(`${window.location.href}settings`, {
            width: 800,
            height: 600,
            x: 120,
            y: 120,
            injectGlobals: true,
        });
    }

    createEffect(() => {
        let encoder: string | undefined = selectedEncoder();

        if (encoder === "") {
            encoder = undefined;
        }

        ffmpegParams = {
            vcodec: selectedCodec()?.shortName ?? "",
            encoder,
            acodec: ffmpegParams.acodec,
            abitrate: ffmpegParams.abitrate,
            crf: ffmpegParams.crf,
            doNotUseAn: ffmpegParams.doNotUseAn,
            faststart: ffmpegParams.faststart,
            hwaccel: ffmpegParams.hwaccel,
            inputFile: undefined,
            preset: ffmpegParams.preset,
            twopass: ffmpegParams.twopass,
            vbitrate: ffmpegParams.vbitrate,
        };

        setOutputCommand(generateOutputCommand(ffmpegParams));
    });

    async function convertClip(
        clip: string,
    ): Promise<RunningProcessInfo | undefined> {
        ffmpegParams.inputFile = clip;

        const fileName = (await Neutralino.filesystem.getPathParts(clip)).stem;

        const fileExt =
            videoFileExtensions[selectedCodec()?.shortName ?? ""] ?? "";

        switch (window.NL_OS) {
            case "Linux":
                ffmpegParams.outputFile = `${await Neutralino.os.getEnv("HOME")}/Vencoder/${fileName}.${fileExt}`;
                break;
            case "Windows":
                ffmpegParams.outputFile = `${await Neutralino.os.getEnv("HOMEPATH")}\\Vencoder\\${fileName}.${fileExt}`;
                break;
        }

        const outputDir = (
            await Neutralino.filesystem.getPathParts(
                ffmpegParams.outputFile ?? "",
            )
        ).parentPath;
        try {
            await Neutralino.filesystem.getStats(outputDir);
        } catch (e) {
            console.log(e);
            await Neutralino.filesystem.createDirectory(outputDir);
        }

        try {
            await Neutralino.filesystem.getStats(ffmpegParams.outputFile ?? "");
            const userAnswer = await Neutralino.os.showMessageBox(
                "File already exists",
                `A file at ${ffmpegParams.outputFile} already exists. Would you like to overwrite it?`,
                Neutralino.os.MessageBoxChoice.YES_NO,
                Neutralino.os.Icon.QUESTION,
            );

            if (userAnswer === "NO") {
                return;
            }
        } catch (e) {}

        const length = await getLengthMicroseconds(clip);

        return {
            process: await Neutralino.os.spawnProcess(
                generateOutputCommand(ffmpegParams),
            ),
            file: clip,
            length,
        };
    }

    async function convertAllClicked() {
        const list = fileList();

        totalCount = list.length;

        const processes = (await Promise.all(list.map(convertClip))).filter(
            (v) => v !== undefined,
        );

        setRunningProcesses(processes);

        await Neutralino.storage.setData(
            "filesBeingProcessed",
            JSON.stringify(
                processes.map((v) => ({
                    id: v.process.id,
                    in: v.file,
                    len: v.length,
                })),
            ),
        );

        await Neutralino.window.create(`${window.location.href}progress`, {
            width: 600,
            height: 400,
            x: 120,
            y: 120,
            injectGlobals: true,
            maximizable: false,
        });
    }

    async function convertSelectedClicked() {
        const result = await convertClip(selectedClip());

        if (result !== undefined) {
            setRunningProcesses([]);
        }
    }

    return (
        <main class="row flex-col">
            <div class="container" style={{ flex: "1" }}>
                <div class="row h-full">
                    <div class="row flex-col h-full">
                        <header
                            class={`k-page-header k-rborder ${windowFocused() ? "" : "window-blur"}`}
                        >
                            <div class="page-title">Vencoder</div>
                        </header>
                        <div
                            class="row flex-col gap2 k-white-sidebar k-rborder h-full"
                            style={{ padding: "8px" }}
                        >
                            <ul class="k-list-view bordered col">
                                <For each={fileList()}>
                                    {(item, _) => (
                                        <li
                                            class={
                                                item == selectedClip()
                                                    ? "selected"
                                                    : ""
                                            }
                                            onclick={() =>
                                                setSelectedClip(item)
                                            }
                                        >
                                            {item}
                                        </li>
                                    )}
                                </For>
                            </ul>
                            <div class="row gap2">
                                <button
                                    onclick={openBtnClicked}
                                    class="k-button"
                                >
                                    Open...
                                </button>
                                <button
                                    onclick={removeAllBtnClicked}
                                    class="k-button"
                                >
                                    Remove All
                                </button>
                                <button
                                    disabled={selectedClip() === ""}
                                    onclick={removeBtnClicked}
                                    class="icon-button k-button"
                                >
                                    <img
                                        src={TrashEmpty}
                                        alt="Remove Selected Video"
                                    />
                                </button>
                                <button
                                    disabled={selectedClip() === ""}
                                    onclick={playBtnClicked}
                                    class="icon-button k-button"
                                >
                                    <img
                                        src={PlaybackStart}
                                        alt="Preview Selected Video"
                                    />
                                </button>
                                <button
                                    class="icon-button k-button"
                                    onclick={settingsBtnPressed}
                                >
                                    <img
                                        src={Configure}
                                        alt="Configure Vencoder"
                                    />
                                </button>
                            </div>
                        </div>
                    </div>
                    <div class="row flex-col h-full" style={{ width: "100%" }}>
                        <header
                            class={`k-page-header ${windowFocused() ? "" : "window-blur"}`}
                        >
                            <div class="page-title">Conversion Settings</div>
                        </header>
                        <div
                            class="col row flex-col"
                            style={{
                                padding:
                                    "var(--k-grid-unit) var(--k-small-spacing)",
                                flex: "1",
                            }}
                        >
                            <div>
                                <form
                                    class="k-form"
                                    onsubmit={(e) => e.preventDefault()}
                                >
                                    <label for="targetCodec">Codec</label>
                                    <select
                                        class="k-dropdown"
                                        id="targetCodec"
                                        oninput={selectedCodecsChanged}
                                    >
                                        <For each={displayedCodecs()}>
                                            {(item, _) => (
                                                <option value={item.shortName}>
                                                    {item.description}
                                                </option>
                                            )}
                                        </For>
                                    </select>
                                    <div></div>
                                    <div class="checkbox-container">
                                        <input
                                            type="checkbox"
                                            name="commonCodecs"
                                            id="commonCodecs"
                                            oninput={showCommonCodecsChanged}
                                            checked
                                        />
                                        <label for="commonCodecs">
                                            Only show common codecs
                                        </label>
                                    </div>
                                    <Show
                                        when={
                                            selectedCodec()?.encoders.length !==
                                            0
                                        }
                                    >
                                        <label>Encoder</label>
                                        <select
                                            name="videoEncoder"
                                            id="videoEncoder"
                                            class="k-dropdown"
                                            value={selectedEncoder()}
                                            oninput={(e) =>
                                                setSelectedEncoder(
                                                    e.target.value,
                                                )
                                            }
                                        >
                                            <For
                                                each={selectedCodec()?.encoders}
                                            >
                                                {(item, _) => (
                                                    <option>{item}</option>
                                                )}
                                            </For>
                                        </select>
                                    </Show>
                                </form>
                                <Switch fallback={<div></div>}>
                                    <Match
                                        when={
                                            selectedCodec()?.shortName ===
                                                "h264" ||
                                            selectedCodec()?.shortName ===
                                                "hevc"
                                        }
                                    >
                                        <H264Options
                                            codec={selectedCodec()}
                                            params={ffmpegParams}
                                            onParamChanged={onParametersChanged}
                                        />
                                    </Match>
                                </Switch>
                            </div>
                            <div class="row flex-col p-medium">
                                <label for="outputCommand">Command</label>
                                <pre
                                    id="outputCommand"
                                    class="k-text-field w-full col"
                                >
                                    {outputCommand()}
                                </pre>
                            </div>
                        </div>
                        <footer class="k-page-footer row gap2">
                            <button
                                class="k-button"
                                onclick={convertAllClicked}
                            >
                                Convert All
                            </button>
                            <button
                                class="k-button"
                                onclick={convertSelectedClicked}
                                disabled={selectedClip() === ""}
                            >
                                Convert Selected
                            </button>
                        </footer>
                    </div>
                </div>
            </div>
        </main>
    );
}

export default App;
