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

# Always work at the script folder
Set-Location -Path $PSScriptRoot

# Validate COM port parameter
if (-not $COMPORT) {
    Write-Host "Missing COM port argument. Example: powershell.exe -File script.ps1 6" -ForegroundColor Red
    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"

# Locate MbtP.exe directory path
$btExe = Join-Path -Path $PSScriptRoot -ChildPath "..\Files\MbtP.exe"
if (-not (Test-Path $btExe)) {
    Write-Host "MbtP.exe not found: $btExe" -ForegroundColor Red
    exit 2
}

$MFGTool_IF91X_BT_ReadBDADDR_Path = (Get-Location).Path
# Write-Output "MFGTool Path: $MFGTool_IF91X_BT_ReadBDADDR_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_BT_ReadBDADDR_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}_bt_read_bdaddr-if91x_results_${timestamp}.txt"
$StandardOutput_Txtfile = "bt_read_bdaddr-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
    }
}

# Veda IF91x: Execute to read BDADDR and reset hci device
Write-Host "Log file: $OutputFilePath" -ForegroundColor Cyan
"==== IF91x Bluetooth Read BDADDR and Reset ====" | Out-File -FilePath $OutputFilePath -Append

# Define a function to encapsulate Write a message to both console and log file
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 read BDADDR and reset"

# Execute the external program and return the ExitCode, 
#   and write the standard output/error output to the log simultaneously
function Invoke-Proc {
    param(
        [string]$FilePath,
        [string[]]$ArgumentList
    )
    $psi = New-Object System.Diagnostics.ProcessStartInfo
    $psi.FileName = $FilePath
    $psi.Arguments = [string]::Join(' ', $ArgumentList)
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError  = $true
    $psi.UseShellExecute = $false
    $psi.CreateNoWindow = $true

    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $psi
    [void]$p.Start()
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()
    $p.WaitForExit()

    if ($stdout) { $stdout | Out-File -FilePath $OutputFilePath -Append }
    if ($stderr) { ("[stderr] " + $stderr) | Out-File -FilePath $OutputFilePath -Append }

    return $p.ExitCode
}

Write-Log .

# 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 "Read BDADDR failure ..."
        Copy-Item -Path $OutputFilePath -Destination (Join-Path $logDir $StandardOutput_Txtfile) -Force
        exit $ExitCode
    }
}

Write-Log "Please be patient for about 3 seconds for Read BDADDR..."
Write-Log "...******************************..."
Write-Log "...*** DO NOT PRESS ANY KEY ! ***..."
Write-Log "...******************************..."
Write-Log ".."

# --- Auto-correct COM port for Bluetooth (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-BT-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
$ExpectedBTCom = "1"

#Write-Log ("Auto-detecting BT COM by VID/PID/SN (Prefix={0}, SN={1})..." -f $FTDI_BusPrefix, $FTDI_TargetSerial)
#$detected = Get-BT-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 ("Read 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 $ExpectedBTCom)
        $COMPORT = $ExpectedBLECom
        $UseAutoDetectedCOM = $false
    }
}

Write-Log ("BT will use {0}" -f $COMPORT)

# Wrapper for MbtP.exe commands with logging and error check
function Invoke-BT {
    param(
        [string[]]$Arguments,   # Avoid using the name "Args" to prevent clash with $args
        [string]$Label
    )
    if (-not $Arguments -or $Arguments.Count -eq 0) {
        Write-Log "Invoke-BT called with empty argument list. Label='$Label'"
        Check-Error -ExitCode -1 -Step "Invoke-BT(empty)"
        return
    }
    $btproc = "./MbtP.exe " + ($Arguments -join ' ')
    Write-Log ">> $btproc"
    $code = Invoke-Proc -FilePath $btExe -ArgumentList $Arguments
    Check-Error -ExitCode $code -Step $Label
}

# Run MbtP.exe and verify stdout by regex (content-first gating)
function Invoke-BTWithCheck {
    param(
        [string[]]$Arguments,
        [string]$Label,
        [regex]$SuccessRegex,           # e.g. (?im)^\s*0000\s*<\s*04\s+0E\s+04\s+01\s+03\s+0C\s+00\s*>\s*$|(?i)^\s*Success\s*$
        [int]$OnFailExitCode = 40,
        [bool]$IgnoreExitCodeIfSuccess = $true
    )
    if (-not $Arguments -or $Arguments.Count -eq 0) {
        Write-Log "Invoke-BTWithCheck called with empty argument list. Label='$Label'"
        Check-Error -ExitCode -1 -Step "Invoke-BTWithCheck(empty)"
        return
    }

    #Write-Log ("[DEBUG] Using MbtP.exe Path: {0}" -f $btExe)
    #Write-Log ("[DEBUG] Exists? {0}" -f (Test-Path $btExe))

    $btproc = "./MbtP.exe " + ($Arguments -join ' ')
    Write-Log ">> $btproc"

    # Execute process (clone of Invoke-Proc but inline to capture immediately)
    $psi = New-Object System.Diagnostics.ProcessStartInfo
    $psi.FileName = $btExe
    $psi.Arguments = [string]::Join(' ', $Arguments)
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError  = $true
    $psi.UseShellExecute = $false
    $psi.CreateNoWindow = $true

    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $psi
    [void]$p.Start()
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()
    $p.WaitForExit()

    if ($stdout) { $stdout | Out-File -FilePath $OutputFilePath -Append }
    if ($stderr) { ("[stderr] " + $stderr) | Out-File -FilePath $OutputFilePath -Append }

    # ---- CONTENT-FIRST GATING ----
    $contentOK = $false
    if ($SuccessRegex) {
        #$contentOK = ($stdout -match $SuccessRegex)
	$contentOK = $SuccessRegex.IsMatch($stdout)
    }

    if ($contentOK) {
        if ($p.ExitCode -ne 0 -and $IgnoreExitCodeIfSuccess) {
            Write-Log ("[HCI] Success pattern matched; ignoring non-zero exit code ({0})." -f $p.ExitCode)
        }
        return  # treat as success
    }

    # Not matched by content
    if ($p.ExitCode -ne 0) {
        # Exit because process reported error
        Check-Error -ExitCode $p.ExitCode -Step $Label
        return
    }

    # ExitCode==0 but no successful pattern is matched -> considered failed based on the content
    $lastHex = ($stdout -split "`r?`n") | Where-Object { $_ -match '([0-9A-Fa-f]{2}\s+){2,}[0-9A-Fa-f]{2}' } | Select-Object -Last 1
    if (-not $lastHex) { 
        $lastHex = "(no hex pattern found in stdout)"
    }
    Write-Log "[HCI][ERR] Expected success pattern not found in stdout."
    Write-Log ("[HCI][ERR] Pattern: {0}" -f $SuccessRegex.ToString())
    Write-Log ("[HCI][ERR] Last hex line: {0}" -f $lastHex)
    Copy-Item -Path $OutputFilePath -Destination (Join-Path $logDir $StandardOutput_Txtfile) -Force
    exit $OnFailExitCode
}

if (-not $UseAutoDetectedCOM) {
    ### --- [Step 2] 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 3] Run MbtP tool, with logging and error check ---  
Write-Log "..."
Write-Log (".. Bluetooth Read BDADDR / Reset ..")
# Sequence of commands for read BDADDR and hci device reset
$portArg = "{0}" -f $COMPORT
#Invoke-BT -Arguments @($portArg,"help")                         -Label "help"
#Invoke-BT -Arguments @("reset",$portArg)                        -Label "reset"

# HCI Reset must return "Command Complete" for opcode 0x0C03 with status=0x00.
# Accept either explicit hex sequence or tool's "Success" line.
$ResetOk = [regex]'(?i)04\s+0E\s+04\s+01\s+03\s+0C\s+00|Success'

Invoke-BTWithCheck -Arguments @("reset",$portArg) `
                   -Label "reset" `
                   -SuccessRegex $ResetOk `
                   -OnFailExitCode 41

Invoke-BT -Arguments @("read_bdaddr",$portArg)                  -Label "read_bdaddr"
#Invoke-BTWithCheck -Arguments @("read_bdaddr",$portArg) `
#                   -Label "read_bdaddr" `
#                   -SuccessRegex ([regex]".*")
# Command Complete for LE Transmitter Test: 04 0E 0A 01 09 10 00 93
# Accept either explicit hex sequence or tool's "BD_AdDDR" line.
$ReadAddrOk = [regex]'(?i)04\s+0E\s+0A\s+01\s+09\s+10\s+00\s+93|BD_AdDDR'

#Invoke-BTWithCheck -Arguments @("read_bdaddr",$portArg) `
#                   -Label ("read_bdaddr {0}") `
#                   -SuccessRegex $ReadAddrOk `
#                   -OnFailExitCode 42

# Final success message and log copy
Write-Log "....."
Write-Log "Bluetooth read BDADDR and Reset successfully ..."
Write-Log "Done."

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