/**
 *
 * Run `yarn global add canvas` first
 */

const { createCanvas } = require("canvas");
const fs = require("fs");
const path = require("path");

const outputFolder = path.join(__dirname, "..", "wires", "sets");

const dimensions = 192;
const lineSize = 14;
const lowerLineSize = 32;

const variants = {
    first: "#61ef6f",
    second: "#5fb2f1",
    conflict: "#f74c4c",
};

function hexToRGB(h) {
    let r = 0,
        g = 0,
        b = 0;

    // 3 digits
    if (h.length == 4) {
        r = "0x" + h[1] + h[1];
        g = "0x" + h[2] + h[2];
        b = "0x" + h[3] + h[3];

        // 6 digits
    } else if (h.length == 7) {
        r = "0x" + h[1] + h[2];
        g = "0x" + h[3] + h[4];
        b = "0x" + h[5] + h[6];
    }

    return [+r, +g, +b];
}

function RGBToHSL(r, g, b) {
    // Make r, g, and b fractions of 1
    r /= 255;
    g /= 255;
    b /= 255;

    // Find greatest and smallest channel values
    let cmin = Math.min(r, g, b),
        cmax = Math.max(r, g, b),
        delta = cmax - cmin,
        h = 0,
        s = 0,
        l = 0;
    // Calculate hue
    // No difference
    if (delta == 0) h = 0;
    // Red is max
    else if (cmax == r) h = ((g - b) / delta) % 6;
    // Green is max
    else if (cmax == g) h = (b - r) / delta + 2;
    // Blue is max
    else h = (r - g) / delta + 4;

    h = Math.round(h * 60);

    // Make negative hues positive behind 360°
    if (h < 0) h += 360;

    // Calculate lightness
    l = (cmax + cmin) / 2;

    // Calculate saturation
    s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));

    // Multiply l and s by 100
    s = +(s * 100).toFixed(1);
    l = +(l * 100).toFixed(1);

    return [h, s, l];
}

function HSLToRGB(h, s, l) {
    // Must be fractions of 1
    s /= 100;
    l /= 100;

    let c = (1 - Math.abs(2 * l - 1)) * s,
        x = c * (1 - Math.abs(((h / 60) % 2) - 1)),
        m = l - c / 2,
        r = 0,
        g = 0,
        b = 0;

    if (0 <= h && h < 60) {
        r = c;
        g = x;
        b = 0;
    } else if (60 <= h && h < 120) {
        r = x;
        g = c;
        b = 0;
    } else if (120 <= h && h < 180) {
        r = 0;
        g = c;
        b = x;
    } else if (180 <= h && h < 240) {
        r = 0;
        g = x;
        b = c;
    } else if (240 <= h && h < 300) {
        r = x;
        g = 0;
        b = c;
    } else if (300 <= h && h < 360) {
        r = c;
        g = 0;
        b = x;
    }
    r = Math.round((r + m) * 255);
    g = Math.round((g + m) * 255);
    b = Math.round((b + m) * 255);

    return [r, g, b];
}

async function run() {
    console.log("Running");

    const promises = [];

    for (const variantId in variants) {
        const variantColor = variants[variantId];
        const variantHSL = RGBToHSL(...hexToRGB(variantColor));
        const darkenedColor = HSLToRGB(variantHSL[0], variantHSL[1] - 15, variantHSL[2] - 20);
        const hexDarkenedColor = "rgb(" + darkenedColor.join(",") + ")";

        console.log(variantColor, "->", hexToRGB(variantColor), variantHSL, "->", darkenedColor);

        const parts = {
            forward: [[0.5, 0, 0.5, 1]],
            turn: [
                [0.5, 0.5, 0.5, 1],
                [0.5, 0.5, 1, 0.5],
            ],
            split: [
                [0.5, 0.5, 0.5, 1],
                [0, 0.5, 1, 0.5],
            ],
            cross: [
                [0, 0.5, 1, 0.5],
                [0.5, 0, 0.5, 1],
            ],
        };

        for (const partId in parts) {
            const partLines = parts[partId];

            const canvas = createCanvas(dimensions, dimensions);
            const context = canvas.getContext("2d");
            context.quality = "best";
            context.clearRect(0, 0, dimensions, dimensions);

            const lineCanvas = createCanvas(dimensions, dimensions);
            const lineContext = lineCanvas.getContext("2d");
            lineContext.quality = "best";
            lineContext.clearRect(0, 0, dimensions, dimensions);
            lineContext.strokeStyle = hexDarkenedColor;
            lineContext.lineWidth = lowerLineSize;
            lineContext.lineCap = "square";
            lineContext.imageSmoothingEnabled = false;

            // Draw lower lines
            partLines.forEach(([x1, y1, x2, y2]) => {
                lineContext.beginPath();
                lineContext.moveTo(x1 * dimensions, y1 * dimensions);
                lineContext.lineTo(x2 * dimensions, y2 * dimensions);
                lineContext.stroke();
            });

            context.globalAlpha = 0.4;
            context.drawImage(lineCanvas, 0, 0, dimensions, dimensions);

            context.globalAlpha = 1;
            context.imageSmoothingEnabled = false;
            context.lineCap = "square";
            context.strokeStyle = variantColor;
            context.lineWidth = lineSize;

            // Draw upper lines
            partLines.forEach(([x1, y1, x2, y2]) => {
                context.beginPath();
                context.moveTo(x1 * dimensions, y1 * dimensions);
                context.lineTo(x2 * dimensions, y2 * dimensions);
                context.stroke();
            });

            const out = fs.createWriteStream(path.join(outputFolder, variantId + "_" + partId + ".png"));
            const stream = canvas.createPNGStream();
            stream.pipe(out);
            promises.push(new Promise(resolve => stream.on("end", resolve)));
        }
    }

    console.log("Waiting for completion");
    await Promise.all(promises);

    console.log("Done!");
}

run();
