<#
  deploy-mw4.ps1 - assemble a runnable copy of BattleTech:FireStorm at c:\VWE\firestorm\MW4

  RULES-BASED deployment, sourced entirely from the working directory (c:\VWE\firestorm).
  No external/image paths are read.

  Sources:
    Runtime = c:\VWE\firestorm\Gameleap\mw4              (dev superset: runtime DLLs, resources, config)
    BinDir  = c:\VWE\firestorm\Gameleap\code\rel.bin  (our freshly built binaries)

  The dev runtime tree is bloated (~6 GB) with source content and dev-only data.
  A clean runnable deployment is ~900 MB. These rules select the runtime subset:

    KEEP (whole):  Resource\, hsh\, Assets\, Stats\        (compiled .mw4 packages etc.)
    KEEP (subset): Content\shellscripts\files\ ONLY (runtime *_music.wav). The loose .script/.h
                   sources are redundant -- they're packed into props.mw4 and the engine loads
                   packed resources before loose disk files (s_DiskFirst defaults to false).
    KEEP (root):   *.dll *.ini *.options *.txt *.rtf *.ico *.m4c *.sem *.016 *.256
                   plus clokspl.exe (redistributable loader). (.lnk/.h dropped; no runtime use.)
    DROP:          Content\* (except shellscripts\files), Movies\, Notes\, fonts\, tool exes,
                   Thumbs.db, dead .lnk shortcuts, source headers, test/variant .ini, editor EULA.
    OPTIMIZE:      hsh\mechs\*.bmp reduced from 24-bit to 256-color (8bpp) in the deployed copy
                   (matches production; ~3x smaller, no runtime quality loss; source untouched).
    OVERLAY:       fresh build binaries from rel.bin (MW4.exe + the DLLs/EXEs we compile)

  Re-runnable: overlay always runs last so deployed binaries are the fresh build.

  RESOURCE PACKING: by default this first repacks resource\*.mw4 from the Content\ source tree
  (via build-resources.ps1) so the deployed packages reflect current source -- including loose
  edits like Content\ShellScripts\*.script that would otherwise be shadowed by stale .mw4 files
  (the runtime loads packed resources before loose disk files; s_DiskFirst defaults to false).
  Pass -SkipResourceBuild to deploy the existing prebuilt packages as-is.
#>

param(
    [string]$Runtime = "c:\VWE\firestorm\Gameleap\mw4",
    [string]$BinDir  = "c:\VWE\firestorm\Gameleap\code\rel.bin",
    [string]$Dest    = "c:\VWE\firestorm\MW4",
    [switch]$SkipResourceBuild,
    [int]   $ResourceBuildTimeoutSec = 1800
)
$ErrorActionPreference = "Stop"
Add-Type -AssemblyName PresentationCore   # WPF imaging, for the 256-color BMP optimization

function Robo($src,$dst,$extra) {
    # /XF Thumbs.db: never copy Windows thumbnail caches that litter the dev tree.
    $a = @("`"$src`"","`"$dst`"","/E","/R:1","/W:1","/NFL","/NDL","/NP","/NJH","/XF","Thumbs.db") + $extra
    $p = Start-Process robocopy -ArgumentList $a -Wait -PassThru -NoNewWindow
    if ($p.ExitCode -ge 8) { throw "robocopy '$src' failed (code $($p.ExitCode))" }
}

# Reduce a 24-bit BMP to 256-color (8bpp indexed) in place, matching production's hsh\mechs art.
# Returns bytes saved, or -1 if skipped (already 8bpp) / failed. ~3x size reduction, no visible
# loss on these small mech thumbnails. Uses WPF FormatConvertedBitmap with an optimal palette.
function Convert-BmpTo8bit($path) {
    try {
        $fs = [System.IO.File]::OpenRead($path)
        $dec = New-Object System.Windows.Media.Imaging.BmpBitmapDecoder($fs,
            [System.Windows.Media.Imaging.BitmapCreateOptions]::PreservePixelFormat,
            [System.Windows.Media.Imaging.BitmapCacheOption]::OnLoad)
        $src = $dec.Frames[0]; $fs.Close()            # OnLoad caches in memory; file now free to overwrite
        if ($src.Format -eq [System.Windows.Media.PixelFormats]::Indexed8) { return -1 }   # already optimized
        $before = (Get-Item $path).Length
        $pal  = New-Object System.Windows.Media.Imaging.BitmapPalette($src,256)
        $conv = New-Object System.Windows.Media.Imaging.FormatConvertedBitmap
        $conv.BeginInit(); $conv.Source=$src
        $conv.DestinationFormat=[System.Windows.Media.PixelFormats]::Indexed8
        $conv.DestinationPalette=$pal; $conv.EndInit()
        $enc = New-Object System.Windows.Media.Imaging.BmpBitmapEncoder
        $enc.Frames.Add([System.Windows.Media.Imaging.BitmapFrame]::Create($conv))
        $os = [System.IO.File]::Create($path); $enc.Save($os); $os.Close()
        return ($before - (Get-Item $path).Length)
    } catch { Write-Warning "      BMP convert failed: $path ($($_.Exception.Message))"; return -1 }
}

# Return the canonical set of game resource packages (lowercased "resource\...\X.mw4" keys) by
# parsing the build manifest tree (Content\MechWarrior4.build -> child .build files -> each one's
# [resource\X.mw4] page). The shared dev resource\ also holds EDITOR-only extras (extra maps,
# _backup sets); this lets the game deploy ship only the packages the game actually references.
function Get-GamePackages($contentDir) {
    $set = New-Object 'System.Collections.Generic.HashSet[string]'
    $master = Join-Path $contentDir "MechWarrior4.build"
    if (-not (Test-Path $master)) { return $set }
    $refs = [regex]::Matches((Get-Content $master -Raw), '([^\s={}]+\.build)') |
            ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique
    foreach ($ref in $refs) {
        $bf = Join-Path $contentDir $ref
        if (-not (Test-Path $bf)) { continue }
        foreach ($ln in (Get-Content $bf)) {
            if ($ln -match '\[\s*(resource\\[^\]]+\.mw4)\s*\]') {   # -match is case-insensitive
                [void]$set.Add(($Matches[1] -replace '/','\').ToLower()); break
            }
        }
    }
    return $set
}

Write-Host "== MW4 rules-based deployment ==" -ForegroundColor Cyan
Write-Host "  runtime: $Runtime"
Write-Host "  build  : $BinDir"
Write-Host "  dest   : $Dest`n"
if (-not (Test-Path $Runtime)) { throw "Runtime source not found: $Runtime" }
if (-not (Test-Path $BinDir))  { throw "Build output not found: $BinDir (build MW4Application first)" }
if (Test-Path $Dest) { Write-Host "  (dest exists, refreshing in place)`n" }
New-Item -ItemType Directory -Force -Path $Dest | Out-Null

# 0) Pack resources from source BEFORE copying, so the deployed resource\*.mw4 are current.
if ($SkipResourceBuild) {
    Write-Host "[0/5] Resource packing SKIPPED (-SkipResourceBuild); deploying prebuilt packages.`n" -ForegroundColor DarkYellow
} else {
    Write-Host "[0/5] Packing resources from source (resource\*.mw4) ..." -ForegroundColor Yellow
    & (Join-Path $PSScriptRoot "build-resources.ps1") -Runtime $Runtime -BinDir $BinDir -TimeoutSec $ResourceBuildTimeoutSec
    Write-Host ""
}

# 1) Runtime dirs. Resource is copied SELECTIVELY -- only the canonical game packages from the
#    build manifest (+ each one's .dep, + non-package metadata .nfo/.tga/.txt). The shared dev
#    resource\ also holds editor-only extras (extra maps, _backup sets) that must NOT ship.
#    hsh/Assets/Stats copy whole (Thumbs.db excluded by Robo).
Write-Host "[1/5] Resource (game packages only), hsh, Assets, Stats ..." -ForegroundColor Yellow
$gamePkgs = Get-GamePackages (Join-Path $Runtime "Content")
if ($gamePkgs.Count -eq 0) { throw "Manifest parse returned 0 game packages -- check Content\MechWarrior4.build" }
$srcRes = Join-Path $Runtime "resource"; $dstRes = Join-Path $Dest "Resource"
$rc = 0; $rs = 0
if (Test-Path $srcRes) {
    Get-ChildItem $srcRes -Recurse -File | ForEach-Object {
        $rel = $_.FullName.Substring($srcRes.Length).TrimStart('\')
        if ($_.Name -ieq "Thumbs.db") { $rs++; return }
        if ($rel -imatch '^Pilots\\') { return }   # player profiles -> copied verbatim below (not package-filtered)
        $ext = $_.Extension.ToLower()
        if ($ext -eq ".mw4" -or $ext -eq ".dep") {
            $key = ("resource\" + ($rel -replace '\.dep$','.mw4')).ToLower()
            if (-not $gamePkgs.Contains($key)) { $rs++; return }     # editor-only package -> skip
        }
        $dst = Join-Path $dstRes $rel
        $dd = Split-Path $dst -Parent
        if (-not (Test-Path $dd)) { New-Item -ItemType Directory -Force $dd | Out-Null }
        Copy-Item $_.FullName $dst -Force; $rc++
    }
}
Write-Host ("      Resource: {0} files copied ({1} game packages + metadata), {2} skipped (editor-only/cruft)" -f $rc,$gamePkgs.Count,$rs)
# Pilot profiles (Resource\Pilots\*) are player data, NOT content packages -- copy verbatim
# (each pilot's options.mw4 has a .mw4 extension but isn't in the manifest, so the package
# filter above would drop it). Robo preserves the per-pilot Games\ subtree too.
$srcPilots = Join-Path $srcRes "Pilots"
if (Test-Path $srcPilots) {
    Robo $srcPilots (Join-Path $dstRes "Pilots") @()
    Write-Host "      Resource\Pilots copied verbatim (player profiles)"
}
foreach ($d in "hsh","Assets","Stats") {
    if (Test-Path (Join-Path $Runtime $d)) { Robo (Join-Path $Runtime $d) (Join-Path $Dest $d) @() }
}

# 2) Content subset: ONLY shellscripts\files (runtime audio: *_music.wav). The loose
#    .script/.h/.hpp sources are redundant -- they're packed into props.mw4 and the engine reads
#    packed resources before loose disk files (s_DiskFirst=false), so they'd never be loaded.
#    Production ships only files\ as well.
Write-Host "[2/5] Content\shellscripts\files (runtime audio only) ..." -ForegroundColor Yellow
$ssf = Join-Path $Runtime "Content\shellscripts\files"
if (Test-Path $ssf) { Robo $ssf (Join-Path $Dest "Content\shellscripts\files") @() }

# 3) Root runtime files by extension whitelist (.lnk/.h excluded -- shortcuts & source headers
#    have no runtime use). Plus a name-based skip list of dev-only files absent from a production
#    install: editor EULA, alternate icon, empty dev logs, and test/variant config .ini files.
Write-Host "[3/5] Root runtime files (dlls, config, localization) ..." -ForegroundColor Yellow
$keepExt = ".dll",".ini",".options",".txt",".rtf",".ico",".m4c",".sem",".016",".256"
# Dev-only DLLs that must NOT be deployed: dbghelp.dll/imagehlp.dll are SYSTEM dlls
# whose 2003 copies shadow Windows' and break DirectDraw init (DDERR_NODIRECTDRAWHW);
# the rest are tool/optional dlls the runtime game (per known-good) does not use.
# ddraw.dll is DDrawCompat -- the editor installs it into the shared data tree for its windowed
# viewport, but the fullscreen game must keep native DirectDraw, so never ship it here.
$skipDll = "dbghelp.dll","imagehlp.dll","ijl10.dll","ctcl.dll","Language.oeg.dll","Resources.dll","ddraw.dll"
$skipName = "edeula.rtf","mech4.ico","debuglog.txt","erfviewer.txt","servercyclex.txt","tctd.ini",
            "options-ext.ini","options-cam-var.ini","options-game-var.ini","options-mr-var.ini",
            "ctcl-camera-test.ini","ctcl-game-test.ini","ctcl-mr-test.ini"
Get-ChildItem $Runtime -File |
    Where-Object { ($keepExt -contains $_.Extension.ToLower()) -and ($skipDll -notcontains $_.Name) -and ($skipName -notcontains $_.Name) } |
    ForEach-Object { Copy-Item $_.FullName (Join-Path $Dest $_.Name) -Force }
# clokspl.exe is a runtime loader (the only non-built exe the clean deploy keeps)
if (Test-Path (Join-Path $Runtime "clokspl.exe")) { Copy-Item (Join-Path $Runtime "clokspl.exe") $Dest -Force }

# 4) Overlay freshly built binaries
Write-Host "[4/5] Overlaying fresh build artifacts from rel.bin ..." -ForegroundColor Yellow
$artifacts = "MW4.exe","MW4pro.exe","Launcher.exe","autoconfig.exe","mw4print.exe","MissionLang.dll","ScriptStrings.dll","ctcls.dll"
foreach ($a in $artifacts) {
    $src = Join-Path $BinDir $a
    if (Test-Path $src) { Copy-Item $src (Join-Path $Dest $a) -Force; Write-Host ("      + {0,-18} {1:N0} bytes" -f $a,(Get-Item $src).Length) }
    else { Write-Warning "      build artifact missing: $a" }
}

# 4b) Apply the DirectDraw 16-bit compatibility shim (REQUIRED on Win10/11).
# MW4 runs at bitdepth=16; without DWM8And16BitMitigation it fails graphics init
# with DDERR_NODIRECTDRAWHW. Per-user (HKCU) needs no admin. The matching all-users
# (HKLM) entry is in build-env\mw4-compat-hklm.reg (import elevated for full parity).
Write-Host "[4b ] Applying DirectDraw compat shim (HKCU) for $Dest\MW4.exe ..." -ForegroundColor Yellow
$layers = "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
if (-not (Test-Path $layers)) { New-Item -Path $layers -Force | Out-Null }
Set-ItemProperty -Path $layers -Name "$Dest\MW4.exe" -Value "DWM8And16BitMitigation HIGHDPIAWARE" -Type String
Write-Host "      set: $Dest\MW4.exe = DWM8And16BitMitigation HIGHDPIAWARE"

# 4c) Optimize hsh\mechs preview BMPs to 256-color (8bpp). The dev tree stores them as 24-bit
#     (~600 KB each, ~23 MB total); production ships 256-color (~200 KB). Converts the DEPLOYED
#     copies only -- source 24-bit originals are untouched. Idempotent (skips already-8bpp).
Write-Host "[4c ] Optimizing hsh\mechs BMPs to 256-color ..." -ForegroundColor Yellow
$mechDir = Join-Path $Dest "hsh\mechs"
$saved = 0L; $cnt = 0
if (Test-Path $mechDir) {
    Get-ChildItem $mechDir -Filter *.bmp | ForEach-Object {
        $d = Convert-BmpTo8bit $_.FullName
        if ($d -ge 0) { $saved += $d; $cnt++ }
    }
    Write-Host ("      converted {0} BMP(s), saved {1:N1} MB" -f $cnt,($saved/1MB))
} else { Write-Host "      (no hsh\mechs dir)" }

# 5) Report
Write-Host "[5/5] Deployment summary:" -ForegroundColor Yellow
$exe = Join-Path $Dest "MW4.exe"
if (-not (Test-Path $exe)) { throw "MW4.exe missing from $Dest" }
$files = Get-ChildItem $Dest -Recurse -File
$mb = ($files | Measure-Object Length -Sum).Sum/1MB
Write-Host ("      {0} : {1:N0} files, {2:N0} MB" -f $Dest, $files.Count, $mb) -ForegroundColor Green
Write-Host ("      MW4.exe {0:N0} bytes ({1})" -f (Get-Item $exe).Length,(Get-Item $exe).LastWriteTime) -ForegroundColor Green
"      per top-level:"
Get-ChildItem $Dest -Directory | ForEach-Object {
    $m=(Get-ChildItem $_.FullName -Recurse -File -EA SilentlyContinue|Measure-Object Length -Sum).Sum/1MB
    "        {0,8:N1} MB  {1}" -f $m,$_.Name }
Write-Host "`nDone. Runnable game deployed to $Dest" -ForegroundColor Cyan
