#requires -Version 5.1
<#
.SYNOPSIS
  Recover the pf-vdisplay (punktfunk) virtual-display driver after it WEDGES under rapid ADD/REMOVE
  churn - no reboot. The dev-iteration counterpart to redeploy-pf-vdisplay.ps1.

.DESCRIPTION
  Sustained connect/disconnect churn (e.g. a client reconnect loop x the host's 8 pipeline-build
  retries - ~100 ADD/REMOVE cycles) exhausts the driver's IddCx monitor slots: the per-monitor
  target_ids climb, ghost "Generic Monitor (punktfunk)" device nodes pile up, and eventually
  IOCTL_ADD returns 0x80070490 ERROR_NOT_FOUND ("Element nicht gefunden"). Every session then fails
  to create a virtual output -> the client gets a hard blackscreen. A host-service restart's
  IOCTL_CLEAR_ALL does NOT recover it; the driver instance itself must be reloaded.

  Steps (run ELEVATED):
    1. Stop the host service (it holds the driver's control device).
    2. pnputil /remove-device the GHOST (Status != OK = not-present) punktfunk virtual-monitor nodes
       that accumulated - the root of the slot exhaustion.
    3. Disable + Enable the pf-vdisplay adapter (ROOT\DISPLAY\*, "punktfunk Virtual Display") to
       reload the IddCx driver instance and reset its monitor list. (Restart-PnpDevice does NOT exist
       on this box's PowerShell, so we disable+enable explicitly.)
    4. Restart the host service.
  Avoids a reboot on purpose (this box boots to Proxmox).

.PARAMETER Service     Host service name. Default PunktfunkHost.
.PARAMETER AdapterName FriendlyName substring of the IddCx adapter to cycle. Default "punktfunk
                       Virtual Display" (NOT SudoVDA's "SudoMaker Virtual Display Adapter").
.PARAMETER GhostMatch  FriendlyName substring of the virtual monitors to reap. Default "punktfunk".
.PARAMETER KeepGhosts  Skip the ghost-node cleanup; only cycle the adapter.
.PARAMETER NoHost      Don't stop/start the host service (just reset the driver) - used by
                       redeploy-pf-vdisplay.ps1, which manages the service itself.
.PARAMETER Verify      After recovery, run a punktfunk-probe loopback and report whether ADD works
                       again (best-effort; needs punktfunk-probe.exe on PATH or via -Probe).
.PARAMETER Probe       Path to punktfunk-probe.exe for -Verify.

.EXAMPLE
  powershell -ExecutionPolicy Bypass -File reset-pf-vdisplay.ps1
.EXAMPLE
  powershell -ExecutionPolicy Bypass -File reset-pf-vdisplay.ps1 -Verify -Probe C:\t-goal1\debug\punktfunk-probe.exe
#>
[CmdletBinding()]
param(
    [string]$Service     = 'PunktfunkHost',
    [string]$AdapterName = 'punktfunk Virtual Display',
    [string]$GhostMatch  = 'punktfunk',
    [switch]$KeepGhosts,
    [switch]$NoHost,
    [switch]$Verify,
    [string]$Probe
)
$ErrorActionPreference = 'Continue'

function Get-PfAdapter {
    Get-PnpDevice -Class Display -ErrorAction SilentlyContinue |
        Where-Object { $_.FriendlyName -match $AdapterName } | Select-Object -First 1
}

# 1) Stop the host so it isn't mid-IOCTL during the reset (it holds the control device).
$svc = Get-Service $Service -ErrorAction SilentlyContinue
$hostWasRunning = $svc -and $svc.Status -eq 'Running'
if (-not $NoHost -and $hostWasRunning) {
    Write-Host "==> stopping $Service"
    Stop-Service $Service -Force
    Start-Sleep -Seconds 2
}

# 2) Reap the ghost (not-present) punktfunk virtual-monitor device nodes.
if (-not $KeepGhosts) {
    $ghosts = Get-PnpDevice -Class Monitor -ErrorAction SilentlyContinue |
        Where-Object { $_.Status -ne 'OK' -and $_.FriendlyName -match $GhostMatch }
    Write-Host "==> removing $($ghosts.Count) ghost virtual-monitor node(s)"
    $removed = 0
    foreach ($g in $ghosts) {
        pnputil /remove-device $g.InstanceId *> $null
        if ($LASTEXITCODE -eq 0) { $removed++ }
    }
    Write-Host "    removed $removed"
}

# 3) Reload the IddCx adapter instance (disable + enable) to clear its monitor list.
$ad = Get-PfAdapter
if (-not $ad) {
    Write-Warning "pf-vdisplay adapter '$AdapterName' not found (Class Display) - is the driver installed?"
}
else {
    Write-Host "==> cycling adapter $($ad.InstanceId)"
    Disable-PnpDevice -InstanceId $ad.InstanceId -Confirm:$false -ErrorAction Continue
    Start-Sleep -Seconds 3
    Enable-PnpDevice -InstanceId $ad.InstanceId -Confirm:$false -ErrorAction Continue
    Start-Sleep -Seconds 3
    $st = (Get-PnpDevice -InstanceId $ad.InstanceId -ErrorAction SilentlyContinue).Status
    if ($st -ne 'OK') {
        # One retry - a disabled root device occasionally needs a second enable to come back OK.
        Enable-PnpDevice -InstanceId $ad.InstanceId -Confirm:$false -ErrorAction Continue
        Start-Sleep -Seconds 2
        $st = (Get-PnpDevice -InstanceId $ad.InstanceId -ErrorAction SilentlyContinue).Status
    }
    Write-Host "    adapter status: $st"
}

# 4) Restart the host.
if (-not $NoHost -and $svc) {
    Write-Host "==> starting $Service"
    Start-Service $Service
    Start-Sleep -Seconds 3
    Write-Host "    $Service status: $((Get-Service $Service -ErrorAction SilentlyContinue).Status)"
}

# 5) Optional: probe to confirm ADD recovers.
if ($Verify) {
    if (-not $Probe) {
        $Probe = (Get-Command punktfunk-probe.exe -ErrorAction SilentlyContinue).Source
    }
    if (-not $Probe -or -not (Test-Path $Probe)) {
        Write-Warning "-Verify: punktfunk-probe.exe not found (pass -Probe <path>); skipping verification."
    }
    else {
        $log = Join-Path $env:ProgramData 'punktfunk\logs\host.log'
        Write-Host "==> verifying with $Probe"
        & $Probe *> $null
        Start-Sleep -Seconds 2
        $last = Get-Content $log -Tail 80 -ErrorAction SilentlyContinue |
            Select-String -Pattern 'pf-vdisplay created|Element nicht|0x80070490' | Select-Object -Last 1
        if ($last -match 'created') { Write-Host "    OK: ADD succeeded after reset." }
        elseif ($last) { Write-Warning "    ADD still failing after reset: $($last.Line.Trim())" }
        else { Write-Warning "    no ADD outcome found in the log; check $log." }
    }
}

Write-Host "pf-vdisplay reset done."
