///|
/// A target that zig-cross builds for. `Host` is the machine itself: zig only compiles for it,
/// and the wrappers do not strip anything.
pub(all) enum Target {
  LinuxX64
  LinuxArm64
  DarwinArm64
  DarwinX64
  WindowsX64
  Host
} derive(Eq, Debug)

///|
/// The target names, in the order that the usage text shows them.
fn all_target_names() -> Array[String] {
  [
    "linux-x64", "linux-arm64", "darwin-arm64", "darwin-x64", "windows-x64", "host",
  ]
}

///|
pub fn Target::parse(text : String) -> Target? {
  match text {
    "linux-x64" => Some(LinuxX64)
    "linux-arm64" => Some(LinuxArm64)
    "darwin-arm64" => Some(DarwinArm64)
    "darwin-x64" => Some(DarwinX64)
    "windows-x64" => Some(WindowsX64)
    "host" => Some(Host)
    _ => None
  }
}

///|
pub fn Target::name(self : Target) -> String {
  match self {
    LinuxX64 => "linux-x64"
    LinuxArm64 => "linux-arm64"
    DarwinArm64 => "darwin-arm64"
    DarwinX64 => "darwin-x64"
    WindowsX64 => "windows-x64"
    Host => "host"
  }
}

///|
/// The zig target triple. `Host` has none: zig then compiles for the machine it runs on.
pub fn zig_triple(target : Target) -> String? {
  match target {
    LinuxX64 => Some("x86_64-linux-musl")
    LinuxArm64 => Some("aarch64-linux-musl")
    DarwinArm64 => Some("aarch64-macos")
    DarwinX64 => Some("x86_64-macos")
    WindowsX64 => Some("x86_64-windows-gnu")
    Host => None
  }
}

///|
/// The `Host` wrapper has one more rule. On Linux, a debug build compiles `backtrace.c`, which calls
/// `_Unwind_Backtrace`. gcc links its unwinder (libgcc) by itself, but `zig cc` does not, so a link
/// step needs zig's bundled `-lunwind`. macOS has the unwinder in libSystem and needs nothing. The
/// tool can run on wasm, so the wrapper decides at run time with `uname -s`. A compile step (`-c`)
/// does not link, so it gets no library.
///
/// The `cc` wrapper for `MOON_CC`: a /bin/sh script that runs `zig cc`.
///
/// A cross build needs two workarounds for upstream bugs in moon and in the MoonBit runtime:
///
/// 1. The runtime file `env.c` includes  before it defines `_CRT_RAND_S`. On Windows
///    (mingw), `rand_s` is then not declared and the compile fails. A `-D_CRT_RAND_S` on the
///    command line defines it before the first include. The flag does no harm on other targets,
///    so every wrapper has it.
/// 2. moon passes `-DMOONBIT_USE_SIMDUTF` and links the prebuilt objects `moonbit_simdutf.o`,
///    `simdutf.o` and `libbacktrace.a`. These objects are built for the machine that runs moon,
///    not for `MOON_CC`'s target, so the link fails or the binary gets the wrong architecture.
///    A cross wrapper removes the flag and the three files. The `Host` wrapper removes nothing.
pub fn cc_wrapper(target : Target) -> String {
  match zig_triple(target) {
    None =>
      "#!/bin/sh\n" +
      "case \"$(uname -s)\" in Linux) unwind=-lunwind;; *) unwind=;; esac\n" +
      "for a in \"$@\"; do case \"$a\" in -c) unwind=;; esac; done\n" +
      "exec zig cc -D_CRT_RAND_S \"$@\" $unwind\n"
    Some(triple) =>
      "#!/bin/sh\n" +
      "for a in \"$@\"; do shift; case \"$a\" in -DMOONBIT_USE_SIMDUTF|*/moonbit_simdutf.o|*/simdutf.o|*/libbacktrace.a) ;; *) set -- \"$@\" \"$a\";; esac; done\n" +
      "exec zig cc -target \{triple} -D_CRT_RAND_S \"$@\"\n"
  }
}

///|
/// The `ar` wrapper for `MOON_AR`. moon puts the prebuilt simdutf objects into the runtime
/// archive; a cross build must leave them out (see `cc_wrapper`).
pub fn ar_wrapper(target : Target) -> String {
  match zig_triple(target) {
    None => "#!/bin/sh\nexec zig ar \"$@\"\n"
    Some(_) =>
      "#!/bin/sh\n" +
      "for a in \"$@\"; do shift; case \"$a\" in */moonbit_simdutf.o|*/simdutf.o|moonbit_simdutf.o|simdutf.o) ;; *) set -- \"$@\" \"$a\";; esac; done\n" +
      "exec zig ar \"$@\"\n"
  }
}