#!/usr/bin/env -S docker run --rm -v ${PWD}/scripts:/scripts -v ${PWD}/.github/workflows:/.github/workflows dlanguage/dmd dmd -run /scripts/generate_badges.d
// To run this script:
// cd /path/to/cpu_features
// ./scripts/generate_badges.d
import std.algorithm : each, map, cartesianProduct, filter, joiner, sort, uniq;
import std.array;
import std.base64 : Base64;
import std.conv : to;
import std.file : exists;
import std.format;
import std.range : chain, only;
import std.stdio;
import std.string : representation;
import std.traits : EnumMembers;
immutable string bazel_svg = ``;
const string bazel_svg_base64 = Base64.encode(representation(bazel_svg));
immutable string zig_svg = ``;
const string zig_svg_base64 = Base64.encode(representation(zig_svg));
enum BuildSystem
{
    CMake,
    Bazel,
    Zig
}
enum Cpu
{
    amd64,
    AArch64,
    ARM,
    MIPS,
    POWER,
    RISCV,
    LOONGARCH,
    s390x,
}
enum Os
{
    Linux,
    FreeBSD,
    MacOS,
    Windows,
}
struct Badge
{
const:
    Cpu cpu;
    Os os;
    BuildSystem build_system;
private:
    string id()
    {
        return format("%d%c%d", cast(uint)(os) + 1, cast(char)('a' + cpu),
            cast(uint)(build_system));
    }
    string link_ref()
    {
        return format("[l%s]", id());
    }
    string image_ref()
    {
        return format("[i%s]", id());
    }
    string filename()
    {
        import std.uni : toLower;
        return toLower(format("%s_%s_%s.yml", cpu, os, build_system));
    }
    bool enabled()
    {
        return exists("../.github/workflows/" ~ filename());
    }
    string append_logo(string url)
    {
        final switch (build_system)
        {
        case BuildSystem.CMake:
            return url ~ "&logo=cmake";
        case BuildSystem.Bazel:
            return url ~ "&logo=data:image/svg%2bxml;base64," ~ bazel_svg_base64;
        case BuildSystem.Zig:
            return url ~ "&logo=data:image/svg%2bxml;base64," ~ zig_svg_base64;
        }
    }
public:
    string disabled_image_ref()
    {
        return format("[d%d]", cast(uint)(build_system));
    }
    string text()
    {
        if (enabled())
            return format("[![%s]%s]%s", build_system, image_ref, link_ref);
        return format("![%s]%s", build_system, disabled_image_ref);
    }
    string disabled_image_link()
    {
        return append_logo(format("%s: https://img.shields.io/badge/n%%2Fa-lightgrey?",
                disabled_image_ref));
    }
    string link_decl()
    {
        return format("%s: https://github.com/google/cpu_features/actions/workflows/%s",
            link_ref, filename());
    }
    string image_decl()
    {
        return append_logo(format("%s: https://img.shields.io/github/actions/workflow/status/google/cpu_features/%s?branch=main&event=push&label=",
                image_ref, filename()));
    }
}
auto tableHeader(in Os[] oses)
{
    return chain(only(""), oses.map!(to!string)).array;
}
auto tableAlignment(in Os[] oses)
{
    return chain(only(":--"), oses.map!(v => "--:")).array;
}
auto tableCell(Range)(in Os os, in Cpu cpu, Range badges)
{
    return badges.filter!(b => b.cpu == cpu && b.os == os)
        .map!(b => b.text())
        .joiner("
").to!string;
}
auto tableRow(Range)(in Cpu cpu, in Os[] oses, Range badges)
{
    return chain(only(cpu.to!string), oses.map!(os => tableCell(os, cpu, badges))).array;
}
auto tableRows(Range)(in Os[] oses, in Cpu[] cpus, Range badges)
{
    return cpus.map!(cpu => tableRow(cpu, oses, badges)).array;
}
auto table(Range)(in Os[] oses, in Cpu[] cpus, Range badges)
{
    return chain(only(tableHeader(oses)), only(tableAlignment(oses)),
        tableRows(oses, cpus, badges));
}
void main()
{
    immutable allCpus = [EnumMembers!Cpu];
    immutable allOses = [EnumMembers!Os];
    immutable allBuildSystems = [EnumMembers!BuildSystem];
    auto badges = cartesianProduct(allCpus, allOses, allBuildSystems).map!(
        t => Badge(t[0], t[1], t[2]));
    writefln("%(|%-( %s |%) |\n%) |", table(allOses, allCpus, badges));
    writeln();
    badges.filter!(b => !b.enabled)
        .map!(b => b.disabled_image_link())
        .array
        .sort
        .uniq
        .each!writeln;
    badges.filter!(b => b.enabled)
        .map!(b => [b.link_decl(), b.image_decl()])
        .joiner().array.sort.uniq.each!writeln;
}