# --- [Command-Line Parameter Support] ---
param (
    [Parameter()]
    [string]$COMPORT = "6"           # Default COM port for testing
)

### --- [Step 1] Elevate: Ensure script is running with Administrator privileges ---
#if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)) {
#    $argsLine = $MyInvocation.UnboundArguments -join " "
#    Start-Process -FilePath "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -File `"$PSCommandPath`" $argsLine" -Verb RunAs
#    exit
#}

# Force working directory to script location after elevation
Set-Location -Path $PSScriptRoot

# Validate COM port parameter
if (-not $COMPORT) {
    Write-Host "Missing COM port argument. Please call this script with a COM port, e.g. powershell.exe -File script.ps1 6"
    exit 1
}

Write-Host "Selected COM Port: $COMPORT" -ForegroundColor Yellow

### --- [Step 1] Initialize variables, log directory paths, and file names ---

# Customized serial numbers and Bus Prefix
# Expected FTDI serial number
$FTDI_TargetSerial = "FT90HOHLA"
# Expected FTDI VID/PID
$FTDI_BusPrefix = "VID_0403+PID_6001"

$MFGTool_IF91X_Download_Path = (Get-Location).Path
# Write-Output "MFGTool Path: $MFGTool_IF91X_Download_Path"

# Obtain system time (format YYYYMMDDHHMMSS)
$timestamp = Get-Date -Format "yyyyMMddHHmmss"
# Write-Output "Timestamp: $timestamp"

# Read the content of UserScanInput.txt (two levels above the script)
# Write-Output "Folder File Content: $content"
$userInputPath = Join-Path -Path $PSScriptRoot -ChildPath "..\\..\\UserScanInput.txt"
if (-not (Test-Path $userInputPath)) {
    Write-Host "UserScanInput.txt not found: $userInputPath" -ForegroundColor Red
    exit 3
}
$content = Get-Content -Path $userInputPath -Raw

# Start recording the entire script output to a log file
# Get the path of the parent directory two levels up from the current working directory
$MiddlelogDir = Split-Path -Path $MFGTool_IF91X_Download_Path -Parent

# Combine the paths to get the final Logs directory path
$MiddlelogDir = Join-Path -Path $MiddlelogDir -ChildPath "Logs"
$logDir = Join-Path -Path $MiddlelogDir -ChildPath $content

# Replace the $(system time) part of the file name with $content
$Output_Txtfile = "${content}_btfw_download_process-if91x_results_${timestamp}.txt"
$StandardOutput_Txtfile = "btfw_download_process-if91x_results.txt"

# Check if the $content folder exists, and create it if not
$folderPath = Join-Path -Path $MiddlelogDir -ChildPath $content
if (-Not (Test-Path -Path $folderPath)) {
    Write-Host "Creating folder: $folderPath"
    New-Item -Path $folderPath -ItemType Directory | Out-Null
}

# Full path to the output file
$OutputFilePath = Join-Path -Path $logDir -ChildPath $Output_Txtfile

Write-Host "Resolved OutputFilePath: $OutputFilePath"
Write-Host "Resolved LogDir: $logDir"

# Check if the script has permissions to write to the file
if (-not (Test-Path -Path $OutputFilePath)) {
    try {
        New-Item -ItemType File -Path $OutputFilePath -Force | Out-Null
    } catch {
        Write-Host "Failed to create log file: $OutputFilePath" -ForegroundColor Red
        exit 4
    }
}
<#
# Define a function to encapsulate Write-Log and log it.
function Write-Log {
    param (
        [string]$message
    )
    Write-Host $Message
    $Message | Out-File -FilePath $OutputFilePath -Append
}
#>
### ---------- GLOBAL LOG BUFFER (FAST) ----------
$Global:LogBuilder = New-Object System.Text.StringBuilder

function Write-Log {
    param([string]$Message)

    # Show on console
    Write-Host $Message

    # Append to in-memory buffer (FAST)
    [void]$Global:LogBuilder.AppendLine($Message)
}

function Save-Log {
    param([string]$Path)

    # Write final complete log at once (SUPER FAST, NO ENCODING ISSUES)
    $utf8NoBom = New-Object System.Text.UTF8Encoding($false)
    [System.IO.File]::WriteAllText($Path, $Global:LogBuilder.ToString(), $utf8NoBom)
}
Write-Log .

# Veda IF91x: Execute to download the Wi-Fi firmware
Write-Log "Veda IF91x : Execute to download the Bluetooth firmware"

# Function to check error and stop execution with timeout
function Check-Error {
    param([int]$ExitCode, [string]$Step = "")
    if ($ExitCode -ne 0) {
        Write-Log "Error encountered at step: $Step (ExitCode=$ExitCode)"
        Write-Log "Bluetooth Program failure ..."
        Copy-Item -Path $OutputFilePath -Destination (Join-Path $logDir $StandardOutput_Txtfile) -Force
        exit $ExitCode
    }
}


Write-Log "Please be patient for about 10 seconds for program BT firmware..."
Write-Log "...******************************..."
Write-Log "...*** DO NOT PRESS ANY KEY ! ***..."
Write-Log "...******************************..."
Write-Log ".."

### --- [Step 3] Scan and match FTDI device by serial and BusPrefix to obtain InstanceId ---

# --- Auto-correct COM port for Bluetooth program(prefer COMX) ---

function Get-RP2040-COM-Port {
    param (
        [string]$Vid = "3016",
        [string]$UsbPid = "0008",
        [string]$MI  = "01"   # RP2040 HCI UART Interface
    )

    Write-Log "[DEBUG] Querying Win32_SerialPort for RP2040..."

    $pattern = "VID_$Vid&PID_$UsbPid&MI_$MI"

    $port = Get-CimInstance Win32_SerialPort |
        Where-Object { $_.PNPDeviceID -like "*$pattern*" } |
        Select-Object -First 1

    if ($port) {
        return $port.DeviceID
    }

    return $null
}

function Get-FTDI-COM-Port {
    param (
        [string]$Vid = "0403",
        [string]$UsbPid = "6001",
        [string]$Serial = $null # optional filter
    )

    Write-Log "[DEBUG] Querying Win32_SerialPort for FTDI..."

    $basePattern = "VID_$Vid+PID_$UsbPid"

    $list = Get-CimInstance Win32_SerialPort |
        Where-Object { $_.PNPDeviceID -like "*$basePattern*" }

    if ($list.Count -eq 0) {
        return $null
    }

    # Explicit serial provided
    if ($Serial) {
        $match = $list | Where-Object { $_.PNPDeviceID -like "*$Serial*" } |
                 Select-Object -First 1
        if ($match) { return $match.DeviceID }
    }
    
    # If only ONE FTDI exists
    if ($list.Count -eq 1) {
        return $list[0].DeviceID
    }
    
    # Multiple FTDI > return null
    return $null
}

function Get-BLE-COM-Port {
    param(
        [string]$FTDI_Serial = $null  # optional: If still have specified FTDI number need to use
    )
    
    Write-Log "[DEBUG] Entering Get-BT-COM-Port ..."

    # Priority 1 - RP2040
    $rp = Get-RP2040-COM-Port
    if ($rp) {
        Write-Log "Detected RP2040 HCI UART COM port (MI_01): $rp"
        return $rp
    }

    # Priority 2 - FTDI
    $ftdi = Get-FTDI-COM-Port -Serial $FTDI_Serial
    if ($ftdi) {
        Write-Log "Detected FTDI COM port: $ftdi"
        return $ftdi
    }

    Write-Log "ERROR: No RP2040 MI_01 or FTDI COM port detected!"
    return $null
}

function Get-RP2040-UART-COM {
    <#
        Rapid detection of RP2040 HCI UART (MI_01) 
        using the PnP device database in just 0.05 seconds.
    #>
    $parentId = "8&24EF37F7"

    $pattern = "VID_3016&PID_0008&MI_01"

    # Find PNP devices that meet the criteria
    $dev = Get-PnpDevice -Class "Ports" -ErrorAction SilentlyContinue |
        Where-Object {
            $_.InstanceId -like "*$pattern*" -and
            $_.InstanceId -like "*$parentId*"
        } | Select-Object -First 1

    if (-not $dev) {
        return $null
    }

    # Extract COMx
    $dev.Name -match "COM(\d+)" | Out-Null
    return $Matches[0]
}

# Prefer VID/PID/SN detection; fallback: keep user COM if valid -> else Expected
$ExpectedBLECom = "1"

#Write-Log ("Auto-detecting BLE COM by VID/PID/SN (Prefix={0}, SN={1})..." -f $FTDI_BusPrefix, $FTDI_TargetSerial)
#$detected = Get-BLE-COM-Port -FTDI_Serial $FTDI_TargetSerial

Write-Log "Auto-detecting RP2040 HCI UART COM (MI_01)..."

$rpCOM = Get-RP2040-UART-COM

if (-not $rpCOM) {
    Write-Log "ERROR: No RP2040 HCI UART (MI_01) COM port detected!"
    exit 10
}

$COMPORT = $rpCOM
$detected = $COMPORT
Write-Log ("Detected RP2040 HCI UART COM port -> {0}" -f $COMPORT)

    if ($detected) {
    # --- Normalize COM Port Format ---
    # RP2040 returns "COM29", FTDI may return "29"
    # We must unify both into numeric form ("29") for download.bat which prefixes COM
    if ($detected -match '^COM(\d+)$') {
        # $detected = $Matches[1]       # extract only the digits
        $detected = "$detected"
    }
    elseif ($detected -match '^\d+$') {
        # already numeric, do nothing
    }
    else {
        Write-Log "ERROR: Invalid COM port format detected: $detected"
        exit 10
    }

        $COMPORT = $detected
    $UseAutoDetectedCOM = $true

    Write-Log ("Detected FTDI/RP2040 COM port -> {0}" -f $COMPORT)
    Write-Log ("Program will use {0}" -f $COMPORT)
}
else {
    # Check if the COM given by the user exists; if it exists, respect it and do not overwrite it
    $userOk = $false
    try {
        $userOk = (Get-CimInstance Win32_SerialPort | Where-Object { $_.DeviceID -eq ("{0}" -f $COMPORT) }) -ne $null
    } catch { $userOk = $false }

    if ($userOk) {
        Write-Log ("VID/PID detection failed; keeping user-specified {0}" -f $COMPORT)
    } else {
        Write-Log ("VID/PID detection failed; falling back to {0}" -f $ExpectedWLCom)
        $COMPORT = $ExpectedBLECom
        $UseAutoDetectedCOM = $false
    }
}

if (-not $UseAutoDetectedCOM) {
    ### --- [Step 3-1] Scan and match FTDI device by serial and BusPrefix to obtain InstanceId ---
    # Get Hardware VIDs/PIDs of connected FTDI bus via devcon (optional verification)
    $devcon = "devcon64.exe"
    if (-not (Get-Command $devcon -ErrorAction SilentlyContinue)) {
        Write-Log "WARNING: devcon64.exe not found in PATH. FTDI verification skipped."
    } else {
        $devconOutput = & $devcon hwids *FTDI* 2>$null
        $instanceId = $devconOutput | Where-Object { $_ -like "*$FTDI_BusPrefix+$FTDI_TargetSerial\0000" } | Select-Object -First 1

        if (-not $instanceId) {
            Write-Log ("FTDI device not found by devcon (Serial={0}, Prefix={1})." -f $FTDI_TargetSerial, $FTDI_BusPrefix)
            # We do not hard-fail here since COM was already resolved; uncomment to enforce:
            # Copy-Item -Path $OutputFilePath -Destination (Join-Path $logDir $StandardOutput_Txtfile) -Force
            # exit 5
        } else {
            Write-Log "Found FTDI device Instance ID:"
            Write-Log ("    {0}" -f $instanceId)
        }
    }
    ### --- [Step 4] Modify Registry to set LatencyTimer = 1 and restart FTDI device ---
    # Setting the Registry Path and Latency Values to 1
    $registryPath = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS\$FTDI_BusPrefix+$FTDI_TargetSerial\0000\Device Parameters"
    Set-ItemProperty -Path $registryPath -Name "LatencyTimer" -Value 1

    ### --- [Step 3] Run firmware program tool (MbtP.exe), with logging and error check ---
    # Check if valid FTDI bus VID/PID were obtained
    if (-not $instanceId) {
        Write-Log "Failed to obtain FTDI bus VID/PID."
        exit 1
    }

    Write-Log "..."
    Write-Log ".. Bluetooth Firmware programming .."
    $MbtPBat = Join-Path -Path $PSScriptRoot -ChildPath "..\\Files\\MbtP.exe"
    #$HCDFile = Join-Path -Path $PSScriptRoot -ChildPath "..\\Files\\CYW55900A0_001.001.071.0147.0000_Generic_UART_37_4MHz_wlbga_iPA_sLNA_ANT0.hcd"
    #$HCDFile = Join-Path -Path $PSScriptRoot -ChildPath "..\\Files\\CYW55900A0_001.001.071.0154.0000_250115_TEST_ONLY.hcd"
    #$HCDFile = Join-Path -Path $PSScriptRoot -ChildPath "..\\Files\\CYW55900A0_001.001.071.0199.0000_251128_TEST_ONLY.hcd"
    $HCDFile = Join-Path -Path $PSScriptRoot -ChildPath "..\\Files\\CYW55900A0_001.001.071.0199.0000_251218_TEST_ONLY.hcd"
    $quotedHcd = "`"$HCDFile`""
    $MbtPDir = Split-Path $MbtPBat -Parent

    # Program via MbtP.exe tool with properly argument
    Start-Process -FilePath $MbtPBat -ArgumentList @("download", "COM$COMPORT", $quotedHcd) -Wait -NoNewWindow -WorkingDirectory $MbtPDir -RedirectStandardOutput "$logDir\$Output_Txtfile"
    $output_Program | Out-File -FilePath $OutputFilePath -Append
    Check-Error
}
else {
    Write-Log "Auto-detect successful -> skipping FTDI fallback logic."
    
    Write-Log "..."
    
    
    Write-Log ".. Bluetooth Firmware programming .."
    Write-Log ".. Push [RECOVERY > RESET] button on chip to autobaud state then press ENTER key! .."
    Write-Log "..."
    Write-Log ".. ** Step.1 ** "
    Write-Log "..           Push-hold [RECOVERY] > Press [RESET] > Release [RESET] > Release [RECOVERY]"
    Write-Log ".. ** Step.2 ** "
    Write-Log "..           Press keyboard [ENTER] key"
    Write-Log "."

    # Define flasher path and firmware path
    $flasherExe = Join-Path -Path $PSScriptRoot -ChildPath "..\\Files\\ifx_flasher_cli.exe"
    #$firmwareHex = Join-Path -Path $PSScriptRoot -ChildPath "..\\Files\\wifi-unified-tester_download.hex"
    #$HCDFile = Join-Path -Path $PSScriptRoot -ChildPath "..\\Files\\CYW55900A0_001.001.071.0154.0000_250115_TEST_ONLY.hcd"
    $HCDFile = Join-Path -Path $PSScriptRoot -ChildPath "..\\Files\\CYW55900A0_001.001.071.0199.0000_251128_TEST_ONLY.hcd"
    $quotedHcd = "`"$HCDFile`""
    $flasherDir = Split-Path $flasherExe -Parent

    # Build argument list
    $arguments = @(
        "-b", "if91x",
        "-c", $COMPORT,    # <-- COM29 (auto-detected)
        "-f", "`"$HCDFile`"",
        "-ce"
    )
    
    # Create a temporary pipe file for capturing stdout in ANSI/UTF8
    $tempLog = Join-Path $logDir "tmp_ifx_stdout.txt"
    
    # Start flasher (still interactive)
    Start-Process -FilePath $flasherExe `
        -ArgumentList $arguments `
        -WorkingDirectory $flasherDir `
        -RedirectStandardOutput $tempLog `
        -NoNewWindow `
        -Wait

    # Read captured text in correct encoding (UTF8 prevents full-width/NUL)
    $captured = Get-Content -Path $tempLog -Raw -Encoding UTF8

    # Append to log
    $captured | Out-File -FilePath $OutputFilePath -Append -Encoding UTF8

    # Remove temp log
    Remove-Item $tempLog -Force -ErrorAction SilentlyContinue
    Check-Error
}

# Final success message
Write-Log "....."
Write-Log "Device programmed successfully ..."
Write-Log "Done."

# ---- Write all logs to file ONCE (FAST + No BOM + No NUL) ----
Save-Log -Path $OutputFilePath

# Copy as standard output filename
Copy-Item -Path $OutputFilePath -Destination (Join-Path $logDir $StandardOutput_Txtfile) -Force
