# decode-smoke-tests.ps1
#
# Hardware-independent H.264 decoder baseline smoke test:
# decodes raw Annex-B H.264 from stdin to BGRA rawvideo on stdout. This proves
# the required parser, decoder and raw-output components are present without
# making the reproducible build depend on the build machine's GPU. Production
# Display decoding explicitly requires D3D11VA and is qualified separately by
# hardware/test-live-decoder.ps1.
#
# The display command's -fflags nobuffer discards every packet consumed
# during input analysis (proven empirically against both the Gyan binary and
# FFmpeg 8.1.1 during Milestone 0 -- see baseline/BASELINE.md "Decode
# acquisition behaviour"). So this file runs two tests, exactly like
# baseline/capture-gyan-baseline.ps1 did:
#   A. decode-exact  -- the display args with only 'nobuffer' removed, fed a
#                       finite file, asserting an exact byte count
#   B. decode-live   -- a realtime-paced (-re) encoder piped into the
#                       UNMODIFIED display command, asserting whole BGRA
#                       frames emerge (production stream-acquisition shape)
#
# Not intended to be run standalone; called by test.ps1.

[CmdletBinding()]
param(
    [Parameter(Mandatory)][string]$FfmpegPath,
    [Parameter(Mandatory)][string]$OutDir,
    [Parameter(Mandatory)][ValidateSet('h264_nvenc', 'h264_amf', 'h264_qsv')][string]$Encoder
)

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
Import-Module (Join-Path $PSScriptRoot '..\scripts\BuildCommon.psm1') -Force

$w = 320; $h = 180; $frameBytes = $w * $h * 4

# --- A. decode-exact ---
$frames = 2
$h264File = Join-Path $OutDir 'encode-smoke.h264'
$rawFile  = Join-Path $OutDir 'decode-smoke-exact.bgra'

if (-not (Test-Path $h264File) -or (Get-Item $h264File).Length -eq 0) {
    throw "decode-smoke-tests: encode-smoke-tests did not provide $h264File."
}

$decExactArgs = "-f h264 -probesize 32 -analyzeduration 0 -fflags flush_packets -flags low_delay " +
                "-i pipe:0 -f rawvideo -pix_fmt bgra -fps_mode passthrough -loglevel warning pipe:1"
$dec = Invoke-FfmpegProcess -FfmpegPath $FfmpegPath -ArgumentLine $decExactArgs -StdoutFile $rawFile -StdinFile $h264File -TimeoutSec 30
$rawSize = if (Test-Path $rawFile) { (Get-Item $rawFile).Length } else { 0 }
$expectedSize = $frameBytes * $frames
$exactPassed = ($dec.exitCode -eq 0 -and $rawSize -eq $expectedSize)
Write-Host ("decode-exact exit={0} bytes={1} (expected {2})  {3}" -f $dec.exitCode, $rawSize, $expectedSize, $(if ($exactPassed) { 'PASS' } else { 'FAIL' }))
if (-not $exactPassed) { Write-Host ("  stderr: {0}" -f $dec.stderrTail) -ForegroundColor Red }

# --- B. decode-live ---
$liveRawFile = Join-Path $OutDir 'decode-smoke-live.bgra'
$liveFrames = 90; $liveFps = 30
$liveInputArgs = "-re -f lavfi -i color=black:size=${w}x${h}:rate=$liveFps"
$liveFilterArgs = ''
$liveEncoderArgs = switch ($Encoder) {
    'h264_nvenc' { '-preset p1 -tune ll -rc vbr -b:v 2M -maxrate 4M -rc-lookahead 0 -profile:v high -level 4.0 -forced-idr 1' }
    'h264_amf'   { '-usage ultralowlatency -quality speed -rc vbr_peak -b:v 2M -maxrate 4M -bufsize 1M -profile:v high -level 4.0 -pix_fmt yuv420p' }
    'h264_qsv'   {
        $liveInputArgs = "-f gdigrab -framerate $liveFps -video_size ${w}x${h} -offset_x 0 -offset_y 0 -i desktop"
        $liveFilterArgs = "-vf drawbox=x=0:y=0:w=${w}:h=${h}:color=black:t=fill"
        '-preset veryfast -b:v 2M -profile:v high -level 4.0 -pix_fmt yuv420p'
    }
}
$liveEncArgs = ("{0} -vframes {1} {2} -c:v {3} {4} " +
                '-bsf:v "dump_extra=freq=keyframe,filter_units=remove_types=6|12" ' +
                "-g $liveFps -bf 0 -an -flush_packets 1 -f h264 pipe:1") -f
                $liveInputArgs, $liveFrames, $liveFilterArgs, $Encoder, $liveEncoderArgs
$liveDecArgs = "-f h264 -probesize 32 -analyzeduration 0 -fflags nobuffer+flush_packets -flags low_delay " +
               "-i pipe:0 -f rawvideo -pix_fmt bgra -fps_mode passthrough -loglevel warning pipe:1"

$decPsi = New-Object System.Diagnostics.ProcessStartInfo
$decPsi.FileName = $FfmpegPath; $decPsi.Arguments = $liveDecArgs
$decPsi.UseShellExecute = $false; $decPsi.CreateNoWindow = $true
$decPsi.RedirectStandardInput = $true; $decPsi.RedirectStandardOutput = $true; $decPsi.RedirectStandardError = $true

$encPsi = New-Object System.Diagnostics.ProcessStartInfo
$encPsi.FileName = $FfmpegPath; $encPsi.Arguments = $liveEncArgs
$encPsi.UseShellExecute = $false; $encPsi.CreateNoWindow = $true
$encPsi.RedirectStandardOutput = $true; $encPsi.RedirectStandardError = $true

$decP = [System.Diagnostics.Process]::Start($decPsi)
$encP = [System.Diagnostics.Process]::Start($encPsi)

$liveOutStream = [System.IO.File]::Create($liveRawFile)
$decOutTask = $decP.StandardOutput.BaseStream.CopyToAsync($liveOutStream)
$decErrTask = $decP.StandardError.ReadToEndAsync()
$encErrTask = $encP.StandardError.ReadToEndAsync()

try {
    $encP.StandardOutput.BaseStream.CopyTo($decP.StandardInput.BaseStream)
    $decP.StandardInput.BaseStream.Flush()
} catch {}
try { $decP.StandardInput.Close() } catch {}

$liveTimedOut = $false
if (-not $encP.WaitForExit(30000)) { $liveTimedOut = $true; try { $encP.Kill() } catch {}; $encP.WaitForExit() }
if (-not $decP.WaitForExit(30000)) { $liveTimedOut = $true; try { $decP.Kill() } catch {}; $decP.WaitForExit() }
try { $decOutTask.Wait(5000) | Out-Null } catch {}
$liveOutStream.Close()
$liveDecErr = ''; try { if ($decErrTask.Wait(5000)) { $liveDecErr = $decErrTask.Result } } catch {}
$liveEncExit = $encP.ExitCode; $liveDecExit = $decP.ExitCode
$encP.Dispose(); $decP.Dispose()

$liveBytes = (Get-Item $liveRawFile).Length
$liveOutFrames = [int][math]::Floor($liveBytes / $frameBytes)
$liveWhole = (($liveBytes % $frameBytes) -eq 0)
# Analysis may legitimately consume 1-2 keyframe intervals before lock-on;
# require at least one full second of whole decoded frames (the macOS/OB1
# reference runs in Milestone 0 both produced 60/90).
$livePassed = (-not $liveTimedOut) -and $liveEncExit -eq 0 -and $liveDecExit -eq 0 -and $liveWhole -and $liveOutFrames -ge $liveFps

Write-Host ("decode-live  encExit={0} decExit={1} frames={2}/{3} wholeFrames={4}  {5}" -f `
    $liveEncExit, $liveDecExit, $liveOutFrames, $liveFrames, $liveWhole, $(if ($livePassed) { 'PASS' } else { 'FAIL' }))
if (-not $livePassed) {
    $tail = (($liveDecErr -split "`r?`n" | Where-Object { $_ -ne '' }) | Select-Object -Last 3) -join ' | '
    Write-Host ("  stderr: {0}" -f $tail) -ForegroundColor Red
}

[ordered]@{
    name   = 'decode-smoke-tests'
    passed = ($exactPassed -and $livePassed)
    decodeExact = [ordered]@{
        passed = $exactPassed; args = $decExactArgs; exitCode = $dec.exitCode
        outBytes = $rawSize; expectedBytes = $expectedSize; stderrTail = $dec.stderrTail
    }
    decodeLive = [ordered]@{
        passed = $livePassed; encoderArgs = $liveEncArgs; decoderArgs = $liveDecArgs
        framesIn = $liveFrames; outFrames = $liveOutFrames; wholeFramesOnly = $liveWhole
        timedOut = $liveTimedOut; encExitCode = $liveEncExit; decExitCode = $liveDecExit
    }
}
