import Anime4k = require('@ibaraki-douji/anime4k')
import { Options } from '@ibaraki-douji/anime4k/lib/types';
import path = require("path");
import { tmpdir } from "os";
import { mkdirSync, writeFileSync } from 'fs';
import { spawn, spawnSync } from 'child_process';

export = class Waifu2XGIF {

    public readonly inputPath: string;
    public readonly outputPath: string;
    public readonly up: any;
    public endBuffer: Buffer;
    public end = false;

    constructor (inp: string | Buffer, out: string, options: Options = {}) {
        if (typeof inp == "string") this.inputPath = path.resolve(inp);
        else {
            this.inputPath = path.resolve(tmpdir(), generate(50) + ".gif");
            writeFileSync(this.inputPath, inp as Buffer);
        }
        if (options.outputAsBuffer) {
            this.outputPath = path.resolve(tmpdir(), generate(50) + ".gif");
        } else {
            this.outputPath = path.resolve(out);
        }

        const vidTmp = path.resolve(tmpdir(), generate(50) + ".mp4");
        const outTmp = path.resolve(tmpdir(), generate(50) + ".mp4");

        spawnSync("ffmpeg", [
            "-y",
            "-i", this.inputPath,
            "-pix_fmt", "yuv420p",
            "-c:v", "h264",
            "-movflags", "+faststart",
            "-filter:v", "crop='floor(in_w/2)*2:floor(in_h/2)*2'",
            vidTmp
        ])

        this.up = Anime4k.upscale(vidTmp, outTmp, options);
        this.up.process.on("close", () => {
            this.endBuffer = this.up.endBuffer;
            spawnSync("ffmpeg", [
                "-y",
                "-i", outTmp,
                "-c:v", "gif",
                "-pix_fmt", "bgra",
                this.outputPath
            ])
            this.end = true;
        })
    }

    public static upscale(inp: string | Buffer, out: string, options?: Options) {
        return new Waifu2XGIF(inp, out, options);
    }

    public static listGPUs() {
        return Anime4k.listGPUs();
    }

    public finishedPromise() {
        return new Promise<void>(res => {
            let x = setInterval(() => {
                if (this.end) {
                    clearInterval(x);
                    res();
                }
            }, 100);
        });
    }

}

const generate=(length)=>{const alphabet="AZERTYUIOPQSDFGHJKLMWXCVBNazertyuiopqsdfghjklmwxcvbn123456789";let end="";for(let i=0;i<length;i++)end+=alphabet[Math.floor(Math.random()*alphabet.length)];return end}