base.ps1

$script:FountEncoding = [System.Text.Encoding]::UTF8

$script:FountClient = $null
$script:FountStream = $null
function Set-FountClient($ComputerName = "localhost", $Port = 16698, $timeout = 100) {
    if ($script:FountClient) {
        if ($script:FountClient.Connected) {
            return $script:FountClient
        }
        else {
            Close-FountClient
        }
    }

    # 创建新的 TCP 客户端
    $Client = New-Object System.Net.Sockets.TcpClient
    if ($Client.ConnectAsync($ComputerName, $Port).Wait($timeout)) {
        if ($Client.Connected) {
            Write-Verbose "成功连接到 Fount 服务器。"
            $script:FountClient = $Client
            return
        }
    }
    Write-Error "无法连接到 Fount 服务器" -ErrorAction Stop
}
function Close-FountClient {
    if ($script:FountStream) {
        $script:FountStream.Dispose()
        $script:FountStream = $null
    }
    if ($script:FountClient) {
        if ($script:FountClient.Connected) {
            $script:FountClient.Close()
        }
        $script:FountClient.Dispose()
        $script:FountClient = $null
    }
}

function Invoke-FountIPC(
    [string]$Type,
    [hashtable]$Data,
    [string]$ComputerName = "localhost",
    [int]$Port = 16698
) {
    # 构建要发送的完整 JSON 对象
    $CommandObject = @{
        type = $Type
        data = $Data
    }
    $JsonCommand = $CommandObject | ConvertTo-Json -Compress -Depth 100
    $JsonCommand += "`n"  # 添加换行符作为消息结束符

    try {
        # 获取或建立连接
        if (-not $script:FountClient) {
            Set-FountClient -ComputerName $ComputerName -Port $Port
            if (-not $script:FountClient) {
                Write-Error "无法连接到 Fount 服务器" -ErrorAction Stop
            }
        }
        if (-not $script:FountClient.Connected) {
            $script:FountClient.Connect($ComputerName, $Port)
            if ($script:FountStream) {
                $script:FountStream.Dispose()
            }
            $script:FountStream = $null
        }
        if (-not $script:FountStream) {
            $script:FountStream = $script:FountClient.GetStream()
        }

        # 获取网络流
        $Stream = $script:FountStream
        $Encoding = $script:FountEncoding

        # 发送数据
        $Bytes = $Encoding.GetBytes($JsonCommand)
        $Stream.Write($Bytes, 0, $Bytes.Length)
        $Stream.Flush()

        # 读取响应(支持大响应和分块读取)
        $MemoryStream = New-Object System.IO.MemoryStream
        $Buffer = New-Object byte[] 4096
        $FoundTerminator = $false

        do {
            $Read = $Stream.Read($Buffer, 0, $Buffer.Length)
            if ($Read -gt 0) {
                $MemoryStream.Write($Buffer, 0, $Read)
                # 检查是否包含换行符
                $CurrentData = $Encoding.GetString($MemoryStream.ToArray())
                if ($CurrentData -match "`n") {
                    $FoundTerminator = $true
                    break
                }
            }
        } while ($Read -gt 0 -and -not $FoundTerminator)

        # 提取有效响应
        $ResponseString = $Encoding.GetString($MemoryStream.ToArray())

        # 解析 JSON 响应
        if (-not [string]::IsNullOrEmpty($ResponseString)) {
            $result = ConvertFrom-Json -InputObject $ResponseString
            if ($result.status -ne 'ok') {
                Write-Error "与 Fount 服务器通信失败: $($result.message)" -ErrorAction Stop
            }
            else {
                return $result.data
            }
        }
        else {
            Write-Error "收到空响应" -ErrorAction Stop
        }
    }
    finally {
        if ($MemoryStream) {
            $MemoryStream.Dispose()
        }
    }
}

function Start-FountShell {
    param(
        [Parameter(Mandatory)]
        [string]$UserName,

        [Parameter(Mandatory)]
        [string]$ShellName,

        [Parameter(ValueFromRemainingArguments)]
        [array]$Arguments
    )

    Invoke-FountIPC -Type "runshell" -Data @{
        username  = $UserName
        shellname = $ShellName
        args      = $Arguments
    }
}

function Invoke-FountShell {
    param (
        [Parameter(Mandatory)]
        [string]$UserName,

        [Parameter(Mandatory)]
        [string]$ShellName,

        $Data
    )

    Invoke-FountIPC -Type "invokeshell" -Data @{
        username  = $UserName
        shellname = $ShellName
        data      = $Data
    }
}

function Test-FountRunning {
    try {
        Invoke-FountIPC -Type "ping" -ErrorAction Stop | Out-Null
        $true
    }
    catch {
        $false
    }
}

function Install-Fount {
    $scriptContent = Invoke-RestMethod https://raw.githubusercontent.com/steve02081504/fount/refs/heads/master/src/runner/main.ps1
    Invoke-Expression "function fountInstaller { $scriptContent }"
    fountInstaller @args
}

function Start-Fount(
    [switch] $NoAutoInstall
) {
    if (Get-Command fount -ErrorAction SilentlyContinue) {
        fount @args
    }
    elseif (!$NoAutoInstall) {
        Install-Fount @args
    }
    else {
        Write-Error "Fount 未安装"
    }
}

function Stop-Fount {
    Invoke-FountIPC -Type "shutdown"
    Close-FountClient
}