<#
.SYNOPSIS
    STM32F407 펌웨어 플래싱 (sht30_fw) — ST-LINK 경유.

.DESCRIPTION
    빌드된 .bin/.hex 또는 .elf 를 STM32F407VGT6 에 기록한다. 사용 가능한
    프로그래머를 자동 탐지하여 사용한다(우선순위):
      1) STM32_Programmer_CLI  (STM32CubeProgrammer)
      2) st-flash              (stlink-tools)
      3) openocd               (st-link 인터페이스 + stm32f4x 타깃)

    폐쇄망/현장 배포: 이 스크립트는 네트워크에 접속하지 않는다(로컬 프로그래머만 사용).

.PARAMETER Programmer
    강제 프로그래머 선택: auto | cubecli | stflash | openocd. 기본 auto.

.PARAMETER Address
    플래시 시작 주소(.bin 기록 시). 기본 0x08000000.

.PARAMETER BuildDir
    산출물 디렉터리. 기본 firmware/build.

.PARAMETER NoVerify
    기록 후 검증 단계를 생략한다(빠른 반복용).

.EXAMPLE
    pwsh firmware/scripts/flash.ps1
    pwsh firmware/scripts/flash.ps1 -Programmer openocd
#>
[CmdletBinding()]
param(
    [ValidateSet('auto', 'cubecli', 'stflash', 'openocd')]
    [string]$Programmer = 'auto',

    [string]$Address = '0x08000000',

    [string]$BuildDir,

    [switch]$NoVerify
)

$ErrorActionPreference = 'Stop'

$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$FwRoot    = Split-Path -Parent $ScriptDir
if (-not $BuildDir) { $BuildDir = Join-Path $FwRoot 'build' }

# SHT30 단일 보드 타깃.
$target = 'sht30_fw'

# ── 산출물 탐색 (.elf / .hex / .bin) ─────────────────────────────────────────
function Find-Artifact([string]$ext) {
    $hits = Get-ChildItem -Path $BuildDir -Recurse -Filter "$target.$ext" -ErrorAction SilentlyContinue
    if ($hits) { return $hits[0].FullName }
    return $null
}
$elf = Find-Artifact 'elf'
$hex = Find-Artifact 'hex'
$bin = Find-Artifact 'bin'

if (-not ($elf -or $hex -or $bin)) {
    throw "$target 산출물을 찾을 수 없습니다($BuildDir). 먼저 빌드하세요: build.ps1"
}

Write-Host "==== 플래싱: $target ====" -ForegroundColor Cyan
Write-Host "  build dir : $BuildDir"
Write-Host "  elf/hex/bin: $elf / $hex / $bin"
Write-Host "  address   : $Address (.bin 기록 시)"

function Test-Tool([string]$name) {
    return [bool](Get-Command $name -ErrorAction SilentlyContinue)
}

# ── 프로그래머 선택 ──────────────────────────────────────────────────────────
if ($Programmer -eq 'auto') {
    if     (Test-Tool 'STM32_Programmer_CLI') { $Programmer = 'cubecli' }
    elseif (Test-Tool 'st-flash')             { $Programmer = 'stflash' }
    elseif (Test-Tool 'openocd')              { $Programmer = 'openocd' }
    else {
        throw @"
플래싱 도구를 찾을 수 없습니다. 다음 중 하나를 PATH 에 두세요:
  - STM32_Programmer_CLI (STM32CubeProgrammer)
  - st-flash (stlink-tools)
  - openocd
"@
    }
}
Write-Host "  programmer: $Programmer" -ForegroundColor Green

# ── 실행 헬퍼 ────────────────────────────────────────────────────────────────
function Invoke-Flash([string]$exe, [string[]]$flashArgs) {
    Write-Host "  > $exe $($flashArgs -join ' ')" -ForegroundColor DarkGray
    & $exe @flashArgs
    if ($LASTEXITCODE -ne 0) { throw "$exe 실패 (exit $LASTEXITCODE)" }
}

switch ($Programmer) {

    # ── STM32CubeProgrammer CLI ──────────────────────────────────────────────
    'cubecli' {
        if (-not (Test-Tool 'STM32_Programmer_CLI')) { throw "STM32_Programmer_CLI 가 PATH 에 없습니다." }
        # .hex 우선(주소 내장), 없으면 .elf, 그다음 .bin(+주소).
        $img = if ($hex) { $hex } elseif ($elf) { $elf } else { $bin }
        $args = @('-c', 'port=SWD', 'mode=UR', '-w', $img)
        if ($img -eq $bin) { $args += $Address }
        if (-not $NoVerify) { $args += '-v' }
        $args += @('-rst')   # 기록 후 리셋하여 실행
        Invoke-Flash 'STM32_Programmer_CLI' $args
    }

    # ── st-flash (stlink-tools) — .bin + 주소 ────────────────────────────────
    'stflash' {
        if (-not (Test-Tool 'st-flash')) { throw "st-flash 가 PATH 에 없습니다." }
        if (-not $bin) { throw "st-flash 는 .bin 이 필요합니다(빌드 산출물 없음): $target.bin" }
        # st-flash 는 자체 검증 후 기록. --reset 로 기록 후 리셋.
        $args = @('--reset', 'write', $bin, $Address)
        Invoke-Flash 'st-flash' $args
        Write-Host "  (st-flash 는 기록 중 자동 검증함)" -ForegroundColor DarkGray
    }

    # ── OpenOCD (ST-LINK + stm32f4x) ─────────────────────────────────────────
    'openocd' {
        if (-not (Test-Tool 'openocd')) { throw "openocd 가 PATH 에 없습니다." }
        # .elf 우선(주소 불필요), 없으면 .hex.
        $img = if ($elf) { $elf } elseif ($hex) { $hex } else { $bin }
        if ($img -eq $bin) {
            throw "OpenOCD 경로는 .elf 또는 .hex 를 권장합니다(.bin 은 주소 지정 필요 — TODO(hw)로 별도 처리)."
        }
        # program <img> verify reset exit : 기록→검증→리셋→종료.
        $verifyTok = if ($NoVerify) { '' } else { ' verify' }
        $cmd = "program `"$img`"$verifyTok reset exit"
        $args = @(
            '-f', 'interface/stlink.cfg'
            '-f', 'target/stm32f4x.cfg'
            '-c', $cmd
        )
        Invoke-Flash 'openocd' $args
    }
}

Write-Host "`n==== 플래싱 완료: $target ====" -ForegroundColor Cyan
Write-Host "  보드가 리셋되어 펌웨어가 실행됩니다. USART3(PD8/PD9, 115200)로 로그 확인." -ForegroundColor Green
