@echo off
setlocal
set "MONITOR_LAUNCH_SCRIPT=%~f0"
set "MONITOR_LAUNCH_MODE=%~1"

rem First run: right-click and run as administrator to authorize later launches.
powershell.exe -NoProfile -NonInteractive -Command "$parts = [regex]::Split([IO.File]::ReadAllText($env:MONITOR_LAUNCH_SCRIPT), '(?m)^# POWERSHELL_BOOTSTRAP\r?$', 2); & ([scriptblock]::Create($parts[1]))"
if "%errorlevel%"=="2" exit /b 0
if errorlevel 1 (
    echo ERROR: First run must use Run as administrator to register the launcher task.
    exit /b 1
)

cd /d "%~dp0"
echo Current directory is %CD%

if not exist monitor.exe (
    echo ERROR: monitor.exe was not found.
    exit /b 1
)
taskkill /f /im monitor_running.exe
powershell.exe -NoProfile -NonInteractive -Command "Start-Sleep -Seconds 5"
copy /y monitor.exe monitor_running.exe
if errorlevel 1 exit /b 1
powershell.exe -NoProfile -NonInteractive -Command "Start-Sleep -Seconds 5"
monitor_running.exe --app=monitor --dbIp=119.29.142.51 --dbPort=1292 --centerIp=119.29.142.51 --centerPort=1291
exit /b %errorlevel%

# POWERSHELL_BOOTSTRAP
$ErrorActionPreference = 'Stop'
try {
    $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = [Security.Principal.WindowsPrincipal]::new($identity)
    $isAdmin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    if ($env:MONITOR_LAUNCH_MODE -eq '--monitor-task') {
        if (-not $isAdmin) {
            throw 'The registered monitor task did not grant administrator privileges.'
        }
        exit 0
    }
    $scriptPath = [IO.Path]::GetFullPath($env:MONITOR_LAUNCH_SCRIPT)
    $sid = $identity.User.Value
    $sha = [Security.Cryptography.SHA256]::Create()
    try {
        $bytes = [Text.Encoding]::UTF8.GetBytes($scriptPath.ToLowerInvariant() + '|' + $sid)
        $key = [BitConverter]::ToString($sha.ComputeHash($bytes)).Replace('-', '').Substring(0, 20)
    } finally {
        $sha.Dispose()
    }
    $taskName = 'MonitorLauncher-' + $key
    $scheduler = New-Object -ComObject 'Schedule.Service'
    $scheduler.Connect()
    $folder = $scheduler.GetFolder('\')
    if (-not $isAdmin) {
        # Demand-run a task authorized during the initial administrator launch.
        $folder.GetTask($taskName).Run($null) | Out-Null
        exit 2
    }
    if ($isAdmin) {
        $task = $scheduler.NewTask(0)
        $task.RegistrationInfo.Description = 'Launch monitor as its original administrator user on demand.'
        $task.Principal.UserId = $sid
        $task.Principal.LogonType = 3 # TASK_LOGON_INTERACTIVE_TOKEN; no password stored.
        $task.Principal.RunLevel = 1 # TASK_RUNLEVEL_HIGHEST.
        $task.Settings.Enabled = $true
        $task.Settings.AllowDemandStart = $true
        $task.Settings.ExecutionTimeLimit = 'PT0S'
        $task.Settings.DisallowStartIfOnBatteries = $false
        $task.Settings.StopIfGoingOnBatteries = $false
        # A restart may overlap the old task while its monitor is being stopped.
        $task.Settings.MultipleInstances = 0 # TASK_INSTANCES_PARALLEL.
        $task.Settings.Hidden = $true
        $action = $task.Actions.Create(0)
        $action.Path = Join-Path $env:SystemRoot 'System32/cmd.exe'
        $action.Arguments = '/d /s /c ""' + $scriptPath + '" --monitor-task"'
        $action.WorkingDirectory = [IO.Path]::GetDirectoryName($scriptPath)
        # Allow this account to read/run; task modifications require elevation.
        $acl = 'D:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;GRGX;;;' + $sid + ')'
        $folder.RegisterTaskDefinition($taskName, $task, 6, $sid, $null, 3, $acl) | Out-Null
        Write-Output ('Administrator launcher registered: ' + $taskName)
        # Install the CPU temperature driver once; monitor still runs without it.
        try {
            if (-not (Get-CimInstance -ClassName Win32_SystemDriver -Filter "Name='PawnIO'")) {
                $installer = Join-Path ([IO.Path]::GetDirectoryName($scriptPath)) 'vendor/pawnio/PawnIO_setup.exe'
                if (-not (Test-Path -LiteralPath $installer)) {
                    throw ('installer not found: ' + $installer)
                }
                $expectedHash = 'A3A46226C5E2824F4CDD42BE0EECBABFC672C86F7889710F5AB1E6AD385B47A0'
                if ((Get-FileHash -LiteralPath $installer -Algorithm SHA256).Hash -ne $expectedHash) {
                    throw 'installer checksum mismatch.'
                }
                $signature = Get-AuthenticodeSignature -LiteralPath $installer
                if ($signature.Status -ne 'Valid') {
                    throw ('installer signature is not valid: ' + $signature.Status)
                }
                $process = Start-Process -FilePath $installer -ArgumentList @('-install', '-silent') -WindowStyle Hidden -Wait -PassThru
                if ($process.ExitCode -notin @(0, 3010)) {
                    throw ('installation failed, exit code: ' + $process.ExitCode)
                }
                Write-Output 'PawnIO driver installed.'
            }
        } catch {
            Write-Output ('WARNING: PawnIO driver unavailable, CPU temperature will not be read: ' + $_.Exception.Message)
        }
    }
    if ($env:MONITOR_LAUNCH_MODE -eq '--monitor-restart') {
        # Start in a separate scheduler instance so stopping the old task cannot
        # also terminate the replacement process through its Windows job.
        $folder.GetTask($taskName).Run($null) | Out-Null
        exit 2
    }
    exit 0
} catch {
    Write-Error -ErrorAction Continue $_
    exit 1
}
