#!/usr/bin/env pwsh

$OUT_DIR  = "out";
$DEPS_DIR = "thirdparty";
$EXE_NAME = "p2601";

$C_SOURCE      = "source/main.c";
$C_INCLUDES    = "-Isrc -I$DEPS_DIR";
$C_FLAGS       = "-std=c23 -fblocks -g $C_INCLUDES";
$RELEASE_FLAGS = "-Ofast -Wno-everything -ffast-math";

$WINDOWS_FLAGS = "-ld3d11 -lgdi32";
$MACOS_FLAGS   = "-x objective-c -lobjc -framework Foundation -framework Cocoa -framework CoreFoundation -framework CoreGraphics -framework QuartzCore -framework Metal -framework MetalKit";
$LINUX_FLAGS   = "-lgl";
$WASM_FLAGS    = "-c -fno-sanitize=address -fno-sanitize=undefined";


# ..............

$ErrorActionPreference = "Stop";

$HOST_OS = &{
   if ($IsWindows) {
      "windows"
   } elseif ($IsMacOS) {
      "macos";
   } elseif ($IsLinux) {
      "linux";
   } else {
      Write-Error "Unsupported host operating system";
   }
};

$HOST_ARCH = &{
   switch ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) {
      "X86"   { "x86" }
      "X64"   { "x86_64" }
      "Arm"   { "arm" }
      "Arm64" { "aarch64" }
      default { Write-Error "Unsupported host architecture"; }
   }
};

function Get-Ext($platform) {
   if ($platform -eq "windows") { return ".exe"; }
   if ($platform -eq "emscripten") { return ".o"; }
   return "";
}

$ZIG_VERSION = "0.15.2";
$ZIG_SLUG    = "zig-${HOST_ARCH}-${HOST_OS}-${ZIG_VERSION}";
$ZIG_DIR     = Join-Path $DEPS_DIR $ZIG_SLUG
$ZIG         = Join-Path $ZIG_DIR "zig$(Get-Ext $HOST_OS)"

$BLOCKS_RT_DIR     = Join-Path $DEPS_DIR "blocksruntime";
$BLOCKS_RT_FLAGS   = "-I$BLOCKS_RT_DIR -I$(Join-Path $BLOCKS_RT_DIR "BlocksRuntime")";
$BLOCKS_RT_RUNTIME = (Join-Path $BLOCKS_RT_DIR "BlocksRuntime" "runtime.c");
$BLOCKS_RT_DATA    = (Join-Path $BLOCKS_RT_DIR "BlocksRuntime" "data.c");
$BLOCKS_RT_SOURCE  = "$BLOCKS_RT_RUNTIME $BLOCKS_RT_DATA";

$EMSDK_REPO =  "https://github.com/emscripten-core/emsdk.git";
$EMSDK_DIR  =  Join-Path $DEPS_DIR "emsdk";
$WASM_FLAGS += " -isystem" + (Join-Path $EMSDK_DIR "upstream" "emscripten" "cache" "sysroot" "include");

# try vendoring zig
if (-not (Test-Path $ZIG)) {
   $ext = "";
   if ($IsWindows) {
      $ext = ".zip";
   } else {
      $ext = ".tar.xz";
   }

   $zig_url = "https://ziglang.org/download/$ZIG_VERSION/$ZIG_SLUG$ext";
   Write-Host ":: Downloading Zig $ZIG_VERSION ($HOST_OS/$HOST_ARCH)";
   Write-Host $zig_url;

   if ($IsWindows) {
      $tmp = Join-Path $DEPS_DIR "zig-compiler$ext";
      Invoke-WebRequest -Uri $zig_url -OutFile $tmp -UseBasicParsing
      Expand-Archive -Path $tmp -DestinationPath $DEPS_DIR -Force
      Remove-Item $tmp
   } else {
      curl -sSfL $zig_url | tar -xJ -C $DEPS_DIR
   }
}

# try vendoring emsdk
if (-not (Test-Path $EMSDK_DIR)) {
   Write-Host ":: Cloning emsdk";
   git clone $EMSDK_REPO $EMSDK_DIR

   Push-Location $EMSDK_DIR

   if ($IsWindows) {
      &"./emsdk.bat" install latest
      &"./emsdk.bat" activate latest
   } else {
      &"./emsdk" install latest
      &"./emsdk" activate latest
   }

   Pop-Location
}

# ensure the out directory exists
New-Item -ItemType Directory -Force -Path $OUT_DIR | Out-Null;


# ...............

function GetFlags($os, $release) {
   $flags = $C_FLAGS;
   switch ($os) {
      "windows" {
         $flags += " $WINDOWS_FLAGS $BLOCKS_RT_FLAGS -DPLATFORM_WINDOWS";
      }
      "macos" {
         $flags += " $MACOS_FLAGS -DPLATFORM_MACOS";
      }
      "linux" {
         $flags += " $LINUX_FLAGS $BLOCKS_RT_FLAGS -DPLATFORM_LINUX";
      }
      "emscripten" {
         $flags += " $WASM_FLAGS $BLOCKS_RT_FLAGS -DPLATFORM_WASM -DARCH_32"
      }
      default {
         Write-Error "Unsupported operating system '$os'";
      }
   }

   if ($release) {
      $flags += " $RELEASE_FLAGS";
   }

   return $flags;
}

function GetSource($os) {
   $source = $C_SOURCE;
   switch ($os) {
      "macos" {
         # Do nothing here since macos already supports the blocks runtime
         # and our local copy conflicts with the one shipped.
      }
      "windows" {
         $source += " $BLOCKS_RT_SOURCE";
      }
      "linux" {
         $source += " $BLOCKS_RT_SOURCE";
      }
      "emscripten" {
         $source += " $BLOCKS_RT_SOURCE"
      }
      default {
         Write-Error "Unsupported operating system '$os'";
      }
   }
   return $source;
}

function CompileCommand($target_os, $release) {
   return "$ZIG cc $(GetFlags $target_os $release) $(GetSource $target_os)";
}

# ..............

$command = $args[0];
$rest    = $args[1..($args.Length-1)];
switch ($command) {
   {($_ -eq "build") -or ($_ -eq "b")} {
      $release = $rest[0] -eq "release" -or $rest[0] -eq "fast";
      $cmd     = "$(CompileCommand $HOST_OS $release) -o $(Join-Path $OUT_DIR $($EXE_NAME + (Get-Ext $HOST_OS)))";

      Write-Host (":: Compiling natively" + $(if ($release) { " (release mode)" } else { "" }));
      Write-Host $cmd;

      Invoke-Expression $cmd | Write-Host;
   }
   {($_ -eq "build-cross") -or ($_ -eq "bc")} {
      $target  = $rest[0];
      $rest    = $rest[1..($rest.Length-1)];
      $release = $rest[0] -eq "release" -or $rest[0] -eq "fast";

      $target_os = ($target -split "-")[1];
      if ($null -eq $target_os) {
         Write-Error "Unsupported target '$target' - run list-targets to see possible targets";
      }

      $out = Join-Path $OUT_DIR ($EXE_NAME + (Get-Ext $target_os));
      $cmd = "$(CompileCommand $target_os $release) --target=$target -o $out";

      Write-Host (":: Cross-compiling for $target" + $(if ($release) { " (release mode)" } else { "" }));
      Write-Host $cmd;

      Invoke-Expression $cmd | Write-Host;

      if ($target_os -eq "emscripten") {
         if (-not $IsWindows) {
            Set-Alias "python" "python3";
         }

         [Environment]::SetEnvironmentVariable("EMSDK_QUIET", 1);
         & (Join-Path $EMSDK_DIR "emsdk_env.ps1");

         $runtime_o = (Join-Path $OUT_DIR "p2601-blocks-runtime.o");
         $cmd = "$ZIG cc $WASM_FLAGS $BLOCKS_RT_FLAGS --target=$target -c $BLOCKS_RT_RUNTIME -o $runtime_o";
         Write-Host $cmd;

         Invoke-Expression $cmd | Write-Host;

         $data_o = (Join-Path $OUT_DIR "p2601-blocks-data.o");
         $cmd = "$ZIG cc $WASM_FLAGS $BLOCKS_RT_FLAGS --target=$target -c $BLOCKS_RT_DATA -o $data_O";
         Write-Host $cmd;

         Invoke-Expression $cmd | Write-Host;

         $binary = Join-Path $OUT_DIR ($EXE_NAME + (Get-Ext $HOST_OS));
         $cmd = "emcc $out $runtime_o $data_o -sUSE_WEBGL2=1 -sFULL_ES3=1 -sALLOW_MEMORY_GROWTH -o " + (Join-Path $OUT_DIR ($EXE_NAME + ".html"));
         Write-Host $cmd;

         Invoke-Expression $cmd | Write-Host;
      }
   }
   {($_ -eq "run") -or ($_ -eq "r")} {
      $binary = Join-Path $OUT_DIR ($EXE_NAME + (Get-Ext $HOST_OS));

      $release = $rest[0] -eq "release" -or $rest[0] -eq "fast";
      &"./do.ps1" build if ($release) { "release" } else { "" };

      if ($LASTEXITCODE -eq 0) {
         Invoke-Expression $binary | Write-Host;
      }
   }
   {($_ -eq "run-wasm") -or ($_ -eq "rw")} {
      &"./do.ps1" build-cross wasm32-emscripten;
      
      $server_in  = (Join-Path $DEPS_DIR "tinyhttp.c");
      $server_out = (Join-Path $OUT_DIR ($EXE_NAME + "-server" + (Get-Ext $HOST_OS)));
      
      if (-not (Test-Path $server_out)) {
         $cmd = "$ZIG cc $server_in -o $server_out";
         if ($IsWindows) {
            $cmd += " -lws2_32";
         }
         
         Write-Host $cmd;
         Invoke-Expression $cmd | Write-Host;
      }
      
      Invoke-Expression $server_out | Write-Host;
   }
   {($_ -eq "clean") -or ($_ -eq "c")} {
      Write-Host ":: Cleaning up build artifacts...";
      Remove-Item -Path (Join-Path $OUT_DIR ($EXE_NAME + "*")) -Force;
   }
   {($_ -eq "list") -or ($_ -eq "l")} {
      # zig target doesn't actually output json...
      Write-Host ":: Available cross-compilation targets";
      Write-Host "   x86_64-windows-gnu";
      Write-Host "   x86_64-linux-gnu";
      Write-Host "   wasm32-emscripten";
   }
   default {
      Write-Host ":: Available things to do";
      Write-Host "   build (b)                 compiles the program natively";
      Write-Host "   build-cross (bc) <target> cross-compiles the program for the target triple (ex. x86_64-windows-gnu)";
      Write-Host "   run (r)                   compiles and runs the native program";
      Write-Host "   run-wasm (rw)             compiles and runs the wasm program";
      Write-Host "   clean (c)                 cleans up all build artifacts";
      Write-Host "   list-targets (lt)         lists all cross-compliation targets";
      return;
   }
}
