#!/usr/bin/env -S gjs -m

/*
 * Copyright (C) 2023 James Westman <james@jwestman.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

/* Script to copy the shield definitions, SVGs, and associated code from
OSM Americana (which is CC0). */

import Gio from "gi://Gio";

function deleteOldIcons() {
    const oldIconsPath = Gio.File.new_for_path("data/shields");
    const enumerator = oldIconsPath.enumerate_children(
        "standard::*",
        Gio.FileQueryInfoFlags.NONE,
        null
    );
    for (const fileInfo of enumerator) {
        const fileName = fileInfo.get_name();
        if (fileName.startsWith("shield_")) {
            const file = Gio.File.new_for_path("data/shields/" + fileName);
            file.delete(null);
        }
    }
}

function copyIcons(osmAmericanaPath) {
    const iconsPath = osmAmericanaPath + "/icons";
    // iterate over all files in the icons directory
    let dir = Gio.File.new_for_path(iconsPath);
    let enumerator = dir.enumerate_children(
        "standard::*",
        Gio.FileQueryInfoFlags.NONE,
        null
    );
    let info;

    const shields = [];

    for (const fileInfo of enumerator) {
        const fileName = fileInfo.get_name();
        if (fileName.startsWith("shield_")) {
            shields.push(fileName);
            const sourceFile = Gio.File.new_for_path(
                iconsPath + "/" + fileName
            );
            const destFile = Gio.File.new_for_path("data/shields/" + fileName);
            sourceFile.copy(destFile, Gio.FileCopyFlags.OVERWRITE, null, null);
        }
    }

    shields.sort();

    /* Create shields.gresource.xml */
    const shieldsGresourceXml = Gio.File.new_for_path(
        "data/shields/org.gnome.Maps.shields.gresource.xml"
    );
    let shieldsGresourceXmlContents =
        '<!-- @generated by updateOsmAmericana.js -->\n<?xml version="1.0" encoding="UTF-8"?>\n<gresources>\n  <gresource prefix="/org/gnome/Maps/shields">\n';
    shieldsGresourceXmlContents += `    <file preprocess="json-stripblanks" compressed="true">shields.json</file>\n`;
    shieldsGresourceXmlContents += `    <file preprocess="json-stripblanks" compressed="true">layer.json</file>\n`;
    for (const shield of shields) {
        shieldsGresourceXmlContents += `    <file preprocess="xml-stripblanks" compressed="true">${shield}</file>\n`;
    }
    shieldsGresourceXmlContents += "  </gresource>\n</gresources>\n";
    shieldsGresourceXml.replace_contents(
        shieldsGresourceXmlContents,
        null,
        false,
        Gio.FileCreateFlags.NONE,
        null
    );
}

function copyShieldsJson(osmAmericanaPath, file) {
    const launcher = Gio.SubprocessLauncher.new(0);
    launcher.set_cwd(osmAmericanaPath);
    const subprocess = launcher.spawnv([
        "sh",
        "-c",
        "npm install && npm run build && npm run shields",
    ]);
    subprocess.wait(null);

    /* Copy the shield definitions */
    const sourceFile = Gio.File.new_for_path(
        osmAmericanaPath + "/dist/shields.json"
    );
    const destFile = Gio.File.new_for_path("data/shields/shields.json");
    const [_status, shieldsData] = sourceFile.load_contents(null);
    const shieldsJson = JSON.parse(
        new TextDecoder("utf-8").decode(shieldsData)
    );

    /* Override the fallback shield definition to use the motorway color
       as a background. Keep the colors in sync. Maybe eventually we can
       get them from the style metadata. */
    const defaultColors = {
        "motorway-light": ["#e1c172", "#caad66"],
        "motorway-dark": ["#58422e", "#685442"],
        "trunk-light": ["#e9cf75", "#d1ba69"],
        "trunk-dark": ["#493727", "#5b4b3c"],
        "primary-light": ["#e9cf75", "#d1ba69"],
        "primary-dark": ["#493727", "#5b4b3c"],
        "secondary-light": ["#ebd68a", "#d3c07c"],
        "secondary-dark": ["#453324", "#574739"],
    };
    for (const highwayClass in defaultColors) {
        const [color, casing] = defaultColors[highwayClass];
        shieldsJson.networks[`default-${highwayClass}`] = {
            shapeBlank: {
                drawFunc: "roundedRectangle",
                params: {
                    radius: 3,
                    strokeColor: casing,
                    outlineWidth: 1,
                    fillColor: color,
                },
            },
            textColor: "#4d3a28",
            padding: {
                left: 1,
                right: 1,
                top: 1,
                bottom: 1,
            },
            textLayout: {
                constraintFunc: "roundedRect",
                options: {
                    radius: 3,
                },
            },
        };
    }

    /* There's a slight difference in the text rendering that I haven't been
       able to track down, that's only really noticeable in US Interstate
       shields. Adjust for it by shifting the padding down half a pixel. */
    for (const network of Object.keys(shieldsJson.networks)) {
        if (network === "US:I" || network.startsWith("US:I:")) {
            shieldsJson.networks[network].padding.top += 0.5;
            shieldsJson.networks[network].padding.bottom -= 0.5;
        }
    }

    destFile.replace_contents(
        JSON.stringify(shieldsJson, null, 2),
        null,
        false,
        Gio.FileCreateFlags.NONE,
        null
    );
}

function copyShieldLayer(osmAmericanaPath) {
    Gio.File.new_for_path("data/shields/layer.json").delete(null);
    const launcher = Gio.SubprocessLauncher.new(0);
    launcher.set_stdout_file_path("data/shields/layer.json");
    launcher.set_cwd(osmAmericanaPath);
    const subprocess = launcher.spawnv([
        "sh",
        "-c",
        "echo 'import {shield} from \"./src/layer/highway_shield.js\"; console.log(JSON.stringify(shield, null, 2))' | node --input-type=module",
    ]);
    subprocess.wait(null);

    const layerFile = Gio.File.new_for_path("data/shields/layer.json");
    const [_status, layerData] = layerFile.load_contents(null);
    const layerJson = JSON.parse(new TextDecoder("utf-8").decode(layerData));

    layerJson.layout["symbol-spacing"] = 500;

    function addHighwayClass(expr) {
        if (expr[0] === "image") {
            expr[1].splice(3, 0, ["get", "class"]);
            expr[1].splice(4, 0, "\n");
        } else {
            for (const subExpr of expr) {
                if (Array.isArray(subExpr)) {
                    addHighwayClass(subExpr);
                }
            }
        }
    }

    addHighwayClass(layerJson.layout["text-field"]);

    layerFile.replace_contents(
        JSON.stringify(layerJson, null, 2),
        null,
        false,
        Gio.FileCreateFlags.NONE,
        null
    );
}

function main(args) {
    if (args.length !== 1) {
        print(
            "Usage: ./updateOsmAmericana.js <path to OSM Americana checkout>"
        );
        return;
    }

    const osmAmericanaPath = args[0];

    deleteOldIcons();
    copyIcons(osmAmericanaPath);
    copyShieldsJson(osmAmericanaPath);
    copyShieldLayer(osmAmericanaPath);
}

main(ARGV);
