# --- [Command-Line Parameter Support] ---
param (
    [Parameter(Mandatory=$true, Position=0)]
    [ValidateNotNullOrEmpty()]
    [string]$Country,                # e.g. ALL / US / Others

    [Parameter(Mandatory=$true, Position=1)]
    [ValidateRange(36,165)]
    [int]$Channel,                   # e.g. 36..165

    [Parameter(Mandatory=$true, Position=2)]
    [ValidateNotNullOrEmpty()]
    [string]$RateMbps,               # MCS"0".."15"
    
    [Parameter(Mandatory=$true, Position=3)]
    [ValidateNotNullOrEmpty()]
    [string]$TargetPower,            # Sepcification Power
                                     # e.g. 16, 17.5

    [Parameter(Mandatory=$true, Position=4)]
    [int]$BandwidthMHz,              # 11an support 20MHz and 40MHz

    [Parameter()]
    [string]$COMPORT = "6"           # Default COM port for testing
)

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

# Locate wl_rc1.exe directory path
$WLExe = Join-Path $PSScriptRoot "..\Files\wl_rc1.exe"

if (-not (Test-Path $WLExe)) {
    Write-Host "[ERROR] wl_rc1.exe not found at: $WLExe" -ForegroundColor Red
    exit 101
}

# 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 = "FT8EBM6XA"
# Expected FTDI VID/PID
$FTDI_BusPrefix = "VID_0403+PID_6001"

$MFGTool_IF91X_WLAN_Tx_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_WLAN_Tx_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}_wlan_11an_tx_process-if91x_results_${timestamp}.txt"
$StandardOutput_Txtfile = "wlan_11an_tx_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
    }
}

# Veda IF91x: Execute to do WLAN 5GHz transmit Tx performance testing
Write-Host "Log file: $OutputFilePath" -ForegroundColor Cyan
"==== IF91x 5GHz TX Test (11an) ====" | 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 transmit 5GHz Tx 11an performance"

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

Write-Log "Please be patient for about 5 seconds for Transmit Tx..."
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-WL-COM-Port {
    param(
        [string]$FTDI_Serial = $null  # optional: If still have specified FTDI number need to use
    )
    
    Write-Log "[DEBUG] Entering Get-WL-COM-Port ..."

    # Priority 1 - RP2040
    $rp = Get-RP2040-COM-Port
    if ($rp) {
        Write-Log "Detected RP2040 HCI UART COM port (MI_03): $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_03 or FTDI COM port detected!"
    return $null
}

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

    $pattern = "VID_3016&PID_0008&MI_03"

    # 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
$ExpectedWLCom = "3"

#Write-Log ("Auto-detecting WL COM by VID/PID/SN (Prefix={0}, SN={1})..." -f $FTDI_BusPrefix, $FTDI_TargetSerial)
#$detected = Resolve-WLComPort -VidPidPrefix $FTDI_BusPrefix -TargetSerial $FTDI_TargetSerial

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

$rpCOM = Get-RP2040-UART-COM

if (-not $rpCOM) {
    Write-Log "ERROR: No RP2040 HCI UART (MI_03) 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 $ExpectedWLCom)
        $COMPORT = $ExpectedWLCom
	$UseAutoDetectedCOM = $false
    }
}
Write-Log ("WL will use {0}" -f $COMPORT)

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 3A] Resolve RF for 5GHz 11an (from 4 args) ---
function Get-PhyByRate {
    param([string]$rate)
    $r = $rate.Trim()
    if (@("0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15") -contains $r)
        { return "11an" }
    else
        { return $null }
}

$PHY = Get-PhyByRate $RateMbps
if (-not $PHY) {
    Write-Log ("[RF][ERR] Unsupported rate for 11an: {0}" -f $RateMbps)
    exit 30
}

# Protocol rule
if ($PHY -eq "11an" -and $Channel -eq 166) {
    Write-Log ("[RF][ERR] 11a is not allowed on CH166. Use CH36..165 or others.")
    exit 31
}

# 5GHz fixed band=a；legacy datarate use 5g_rate -h HT-<MCS> -b 20
$BandArg  = @("a")
$RateArgs = @("5g_rate","-h",$RateMbps,"-b","20")

Write-Log ("[RF] Country={0}  PHY={1}  CH={2}  BW={3}MHz  Rate=HT-MCS{4}" -f `
            $Country,$PHY,$Channel,$BandwidthMHz,$RateMbps)

### --- [Step 3] Run WL tool, with logging and error check ---
# Wrapper for wl_rc1.exe commands with logging and error check
function Invoke-WL {
    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-WL called with empty argument list. Label='$Label'"
        Check-Error -ExitCode -1 -Step "Invoke-WL(empty)"
        return
    }
    $wlproc = "./wl_rc1.exe " + ($Arguments -join ' ')
    Write-Log ">> $wlproc"
    $code = Invoke-Proc -FilePath $WLExe -ArgumentList $Arguments
    Check-Error -ExitCode $code -Step $Label
}

###  Persistent WL Session Engine 
# Global object for persistent session
$Global:WLSession = $null
$Global:WLSessionOut = $null

function Start-WLSession {
    param(
        [string]$ExePath,
        [string]$ComPort
    )

    Write-Log "[WL] Starting persistent WL session on COM$ComPort ..."

    $psi = New-Object System.Diagnostics.ProcessStartInfo
    $psi.FileName = $ExePath
    $psi.Arguments = "--serial $ComPort --interactive"
    $psi.RedirectStandardInput = $true
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError  = $true
    $psi.UseShellExecute = $false
    $psi.CreateNoWindow = $true

    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $psi
    [void]$p.Start()

    $Global:WLSession = $p
    $Global:WLSessionOut = $p.StandardOutput

    # Do not wait prompt symbol ">" 
    Start-Sleep -Milliseconds 300

    Write-Log "[WL] Session assumed active (RP2040 mode)."
}

function Send-WL {
    param(
        [string]$Command,
        [int]$TimeoutMs = 3000
    )

    if ($null -eq $Global:WLSession) {
        throw "[WL][ERROR] WL session not started."
    }

    Write-Log (">> $Command")

    # Send command
    $Global:WLSession.StandardInput.WriteLine($Command)
    $Global:WLSession.StandardInput.Flush()

    # Prepare for response
    $response = ""
    $sw = [System.Diagnostics.Stopwatch]::StartNew()

    $stream = $Global:WLSession.StandardOutput.BaseStream
    $buffer = New-Object byte[] 4096

    while ($sw.ElapsedMilliseconds -lt $TimeoutMs) {

        # Non-blocking read (returns 0 if no data)
        $bytesRead = $stream.Read($buffer, 0, $buffer.Length)

        if ($bytesRead -gt 0) {

            $text = [System.Text.Encoding]::UTF8.GetString($buffer, 0, $bytesRead)
            $response += $text

            # Check for prompt
            if ($response.Trim().EndsWith(">")) {
                break
            }
        }
        else {
            # wl commands often have no output -> early exit
            if ($response.Length -eq 0 -and $sw.ElapsedMilliseconds -gt 350) {
                break
            }
            Start-Sleep -Milliseconds 20
        }
    }

    if ($response.Trim().Length -gt 0) {
        Write-Log ($response.Trim())
    }

    return $response
}

function Stop-WLSession {
    if ($null -eq $Global:WLSession) {
        return
    }

    Write-Log "[WL] Closing persistent session..."

    try {
        $Global:WLSession.StandardInput.WriteLine("exit")
        $Global:WLSession.StandardInput.Flush()
        Start-Sleep -Milliseconds 200
    } catch {}

    try {
        $Global:WLSession.Kill()
    } catch {}

    $Global:WLSession = $null
    $Global:WLSessionOut = $null
}

Write-Log "..."
Write-Log (".. Wi-Fi Transmit Tx {0} (5GHz) .." -f $PHY)
# Sequence of commands for WLAN 5GHz 11a TX test
<#
Invoke-WL -Arguments @("--serial",$COMPORT,"ver")                                 -Label "ver"
Invoke-WL -Arguments @("--serial",$COMPORT,"cur_etheraddr")                       -Label "cur_etheraddr"
Invoke-WL -Arguments @("--serial",$COMPORT,"pkteng_stop","tx")                    -Label "pkteng_stop tx"
Invoke-WL -Arguments @("--serial",$COMPORT,"down")                                -Label "down"
Invoke-WL -Arguments @("--serial",$COMPORT,"spect","0")                           -Label "spect 0"
Invoke-WL -Arguments @("--serial",$COMPORT,"mpc","0")                             -Label "mpc 0"
Invoke-WL -Arguments @("--serial",$COMPORT,"phy_watchdog","0")                    -Label "phy_watchdog 0"
#Invoke-WL -Arguments @("--serial",$COMPORT,"country","ALL")                       -Label "country ALL"
#Invoke-WL -Arguments @("--serial",$COMPORT,"band","b")                            -Label "band b"
#Invoke-WL -Arguments @("--serial",$COMPORT,"channel","1")                         -Label "channel 1"
Invoke-WL -Arguments @("--serial",$COMPORT,"country",$Country)                    -Label ("country {0}" -f $Country)
Invoke-WL -Arguments @("--serial",$COMPORT,"band",$BandArg)                       -Label ("band {0}" -f $BandArg)
#Invoke-WL -Arguments @("--serial",$COMPORT,"channel",$Channel)                    -Label ("channel {0}" -f $Channel)
Invoke-WL -Arguments @("--serial", $COMPORT, "chanspec", ("{0}/{1}" -f $Channel, $BandwidthMHz)) -Label ("chanspec {0}/{1}" -f $Channel, $Bandwidth)
Invoke-WL -Arguments @("--serial",$COMPORT,"mimo_txbw","-1")                      -Label "mimo_txbw -1"
Invoke-WL -Arguments @("--serial",$COMPORT,"up")                                  -Label "up"
Invoke-WL -Arguments @("--serial",$COMPORT,"phy_forcecal","1")                    -Label "phy_forcecal 1"
Invoke-WL -Arguments @("--serial",$COMPORT,"scansuppress","1")                    -Label "scansuppress 1"
Invoke-WL -Arguments @("--serial",$COMPORT,"ldpc_cap","1")                        -Label "ldpc_cap 1"
Invoke-WL -Arguments @("--serial",$COMPORT,"phy_set_ant","0x10")                  -Label "phy_set_ant 0x10"
Invoke-WL -Arguments @("--serial",$COMPORT,"txcore","-s","1","-c","1")            -Label "txcore -s 1 -c 1"
$ArgsRate = @("--serial",$COMPORT) + $RateArgs
Invoke-WL -Arguments $ArgsRate                                                    -Label ("{0}" -f ($RateArgs -join ' '))
#Invoke-WL -Arguments @("--serial",$COMPORT,"phy_txpwrctrl","1")                   -Label "phy_txpwrctrl 1"
#Invoke-WL -Arguments @("--serial",$COMPORT,"txpwr1","-1")                         -Label "txpwr1 -1"
Invoke-WL -Arguments @("--serial",$COMPORT,"txpwr1","-o","-q",$TargetPower)  -Label ("wl txpwr1 -o -q {0}" -f $TargetPower)
Invoke-WL -Arguments @("--serial",$COMPORT,"pkteng_start","00:11:22:33:44:55","tx","1000","1024","0") -Label "pkteng_start"
#>

# ================================================================
# Start WL Session
# ================================================================

Start-WLSession -ExePath $WLExe -ComPort $COMPORT

Write-Log "."
Write-Log (".. Wi-Fi Transmit Tx 11ax (5GHz) ..")

# ================================================================
# Execute WL Commands (Persistent Mode)
# ================================================================
Send-WL "ver"
Send-WL "cur_etheraddr"
Send-WL "pkteng_stop tx"
Send-WL "down"
Send-WL "spect 0"
Send-WL "mpc 0"
Send-WL "phy_watchdog 0"
Send-WL "country $Country"
Send-WL "band $BandArg"
Send-WL "chanspec $Channel/$BandwidthMHz"
Send-WL "mimo_txbw -1"
Send-WL "up"
Send-WL "phy_forcecal 1"
Send-WL "scansuppress 1"
Send-WL "ldpc_cap 1"
Send-WL "phy_set_ant 0x10"
Send-WL "txcore -s 1 -c 1"
#Send-WL "2g_rate -r $RateMbps -b $BandwidthMHz"
Send-WL ($RateArgs -join " ")
Send-WL "phy_txpwrctrl 1"
Send-WL "txpwr1 -o -q $TargetPower"
Send-WL "pkteng_start 00:11:22:33:44:55 tx 1000 1024 0"

# ================================================================
# Close WL Session
# ================================================================
Stop-WLSession

# Final success message and log copy
Write-Log "....."
Write-Log "WLAN 5GHz Transmit Tx successfully ..."
Write-Log "Done."

# Copy Process Log with MAC as Standard naming format file
Copy-Item -Path $OutputFilePath -Destination (Join-Path $logDir $StandardOutput_Txtfile) -Force
