# OCR 公共函数 (Windows.Media.Ocr 包装, 自带 DPI 感知 + 放大)

# === 关键: 在加载 Forms 之前先设为 DPI 感知, 避免 4K 屏 1.5x 缩放下坐标错位 ===
Add-Type -TypeDefinition @"
using System.Runtime.InteropServices;
public class DPI_AW {
    [DllImport("user32.dll")] public static extern bool SetProcessDPIAware();
}
"@ -ErrorAction SilentlyContinue
[void][DPI_AW]::SetProcessDPIAware()

Add-Type -AssemblyName System.Runtime.WindowsRuntime, System.Drawing, System.Windows.Forms -ErrorAction SilentlyContinue

[Windows.Media.Ocr.OcrEngine,Windows.Foundation.UniversalApiContract,ContentType=WindowsRuntime] | Out-Null
[Windows.Graphics.Imaging.BitmapDecoder,Windows.Foundation.UniversalApiContract,ContentType=WindowsRuntime] | Out-Null
[Windows.Graphics.Imaging.SoftwareBitmap,Windows.Foundation.UniversalApiContract,ContentType=WindowsRuntime] | Out-Null
[Windows.Storage.Streams.InMemoryRandomAccessStream,Windows.Foundation.UniversalApiContract,ContentType=WindowsRuntime] | Out-Null
[Windows.Storage.Streams.DataWriter,Windows.Foundation.UniversalApiContract,ContentType=WindowsRuntime] | Out-Null
[Windows.Globalization.Language,Windows.Foundation.UniversalApiContract,ContentType=WindowsRuntime] | Out-Null
[Windows.Media.Ocr.OcrResult,Windows.Foundation.UniversalApiContract,ContentType=WindowsRuntime] | Out-Null

$script:AsTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() |
    Where-Object { $_.Name -eq 'AsTask' -and $_.IsGenericMethod -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } |
    Select-Object -First 1

function Test-IsAdministrator {
    $id = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object Security.Principal.WindowsPrincipal $id
    return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

function Show-OcrInstallHelp {
    Write-Host ""
    Write-Host "[!] 未检测到简体中文 OCR 组件, 请按下面任一方式安装:" -ForegroundColor Yellow
    Write-Host "    方式 1: 以管理员打开 PowerShell, 执行:" -ForegroundColor Cyan
    Write-Host "      Add-WindowsCapability -Online -Name Language.OCR~~~zh-CN~0.0.1.0" -ForegroundColor White
    Write-Host "    方式 2: Windows 设置 -> 时间和语言 -> 语言和区域 -> 添加 中文(简体, 中国)" -ForegroundColor Cyan
    Write-Host "    安装后重启电脑, 再运行本工具。" -ForegroundColor Cyan
    Write-Host ""
}

function Ensure-ZhCnOcrCapability {
    try {
        $lang = New-Object Windows.Globalization.Language 'zh-Hans-CN'
        if ([Windows.Media.Ocr.OcrEngine]::IsLanguageSupported($lang)) { return $true }
    } catch {}

    Write-Host "[!] 系统缺少简体中文 OCR 组件" -ForegroundColor Yellow
    if (Test-IsAdministrator) {
        Write-Host "    正在尝试自动安装 Language.OCR~~~zh-CN~0.0.1.0 ..." -ForegroundColor Cyan
        try {
            Add-WindowsCapability -Online -Name 'Language.OCR~~~zh-CN~0.0.1.0' -ErrorAction Stop | Out-Null
            Start-Sleep -Seconds 1
            $lang = New-Object Windows.Globalization.Language 'zh-Hans-CN'
            if ([Windows.Media.Ocr.OcrEngine]::IsLanguageSupported($lang)) {
                Write-Host "[√] 简体中文 OCR 组件安装成功" -ForegroundColor Green
                return $true
            }
        } catch {
            Write-Host "[X] 自动安装 OCR 失败: $($_.Exception.Message)" -ForegroundColor Red
        }
    } else {
        Write-Host "    当前不是管理员权限, 无法自动安装 OCR。" -ForegroundColor Yellow
    }

    Show-OcrInstallHelp
    return $false
}

function Invoke-WinRTAwait($op, $type) {
    $t = $script:AsTaskGeneric.MakeGenericMethod($type).Invoke($null, @($op))
    $t.Wait(-1) | Out-Null
    $t.Result
}

$script:OcrEngine = $null
function Get-OcrEngine {
    if ($script:OcrEngine) { return $script:OcrEngine }
    $zhReady = Ensure-ZhCnOcrCapability
    if (-not $zhReady) {
        throw "简体中文 OCR 组件未安装"
    }
    try {
        $lang = New-Object Windows.Globalization.Language 'zh-Hans-CN'
        if ($zhReady) {
            $script:OcrEngine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage($lang)
        }
    } catch {}
    if (-not $script:OcrEngine) {
        $script:OcrEngine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromUserProfileLanguages()
    }
    if (-not $script:OcrEngine) {
        Show-OcrInstallHelp
        throw "无法创建 OCR 引擎: 请先安装简体中文 OCR 组件"
    }
    $script:OcrEngine
}

function Get-ScreenRegionBitmap {
    param([int]$X, [int]$Y, [int]$W, [int]$H)
    $bmp = New-Object System.Drawing.Bitmap $W, $H
    $g = [System.Drawing.Graphics]::FromImage($bmp)
    $g.CopyFromScreen($X, $Y, 0, 0, (New-Object System.Drawing.Size $W, $H))
    $g.Dispose()
    $bmp
}

function Resize-Bitmap($src, [int]$newW, [int]$newH) {
    $dst = New-Object System.Drawing.Bitmap $newW, $newH
    $g = [System.Drawing.Graphics]::FromImage($dst)
    $g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
    $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
    $g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
    $g.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality
    $g.DrawImage($src, 0, 0, $newW, $newH)
    $g.Dispose()
    $dst
}

function Convert-ToSoftwareBitmap($bitmap) {
    $ms = New-Object System.IO.MemoryStream
    $bitmap.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
    $ras = New-Object Windows.Storage.Streams.InMemoryRandomAccessStream
    $writer = New-Object Windows.Storage.Streams.DataWriter $ras.GetOutputStreamAt(0)
    $writer.WriteBytes($ms.ToArray())
    $null = Invoke-WinRTAwait ($writer.StoreAsync()) ([uint32])
    $writer.DetachStream() | Out-Null
    $decoder = Invoke-WinRTAwait ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($ras)) ([Windows.Graphics.Imaging.BitmapDecoder])
    $soft = Invoke-WinRTAwait ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
    $ms.Dispose()
    $soft
}

function Invoke-OcrOnScreen {
    param(
        [int]$X, [int]$Y, [int]$W, [int]$H,
        [int]$MinSide = 600
    )
    $bmp = Get-ScreenRegionBitmap -X $X -Y $Y -W $W -H $H
    $upscaled = $null
    try {
        $shortSide = [Math]::Min($W, $H)
        if ($shortSide -lt $MinSide) {
            $scale = $MinSide / $shortSide
            if ($scale -gt 5) { $scale = 5 }
            $newW = [int]($W * $scale)
            $newH = [int]($H * $scale)
            $upscaled = Resize-Bitmap $bmp $newW $newH
            $target = $upscaled
        } else {
            $target = $bmp
        }
        $soft = Convert-ToSoftwareBitmap $target
        $engine = Get-OcrEngine
        $res = Invoke-WinRTAwait ($engine.RecognizeAsync($soft)) ([Windows.Media.Ocr.OcrResult])
        $text = ($res.Lines | ForEach-Object { $_.Text }) -join "`n"
        $text -replace '\s+', ''
    } finally {
        $bmp.Dispose()
        if ($upscaled) { $upscaled.Dispose() }
    }
}

function Read-OcrRegion {
    param([string]$Path)
    if (-not (Test-Path $Path)) { return $null }
    $line = (Get-Content $Path -Raw).Trim()
    $parts = $line -split ','
    if ($parts.Count -ne 4) { return $null }
    @{ X = [int]$parts[0]; Y = [int]$parts[1]; W = [int]$parts[2]; H = [int]$parts[3] }
}
