-- UTF-8 Display Library for ComputerCraft
-- 文件名: utf8display.lua

local utf8display = {}

-- 配置参数
utf8display.config = {
    fontUrl = "https://git.liulikeji.cn/xingluo/ComputerCraft-Utf8/raw/branch/main/fonts/fusion-pixel-8px-proportional-zh_hans.lua",
    fontPath = nil,
    cacheFont = true,
    autoScroll = true
}

-- 内部状态
local state = {
    font = nil,
    fontHeight = 8,
    loadedFonts = {}
}

-- 字体管理模块
local fontManager = {}

function fontManager.loadRemoteFont(url)
    if state.loadedFonts[url] then
        return state.loadedFonts[url]
    end

    local response = http.get(url)
    if not response then
        return nil, "无法连接到字体服务器: " .. url
    end

    if response.getResponseCode() ~= 200 then
        return nil, "字体服务器返回错误: " .. response.getResponseCode()
    end

    local content = response.readAll()
    response.close()

    local sandbox = {}
    local chunk, err = load(content, "=remoteFont", "t", sandbox)
    if not chunk then
        return nil, "加载字体失败: " .. err
    end

    local success, result = pcall(chunk)
    if not success then
        return nil, "执行字体脚本失败: " .. result
    end

    local fontData = sandbox.font or sandbox[1] or result
    if utf8display.config.cacheFont then
        state.loadedFonts[url] = fontData
    end

    return fontData
end

function fontManager.loadLocalFont(path)
    if state.loadedFonts[path] then
        return state.loadedFonts[path]
    end

    if not fs.exists(path) then
        return nil, "字体文件不存在: " .. path
    end

    local file = fs.open(path, "r")
    local content = file.readAll()
    file.close()

    local sandbox = {}
    local chunk, err = load(content, "=localFont", "t", sandbox)
    if not chunk then
        return nil, "加载本地字体失败: " .. err
    end

    local success, result = pcall(chunk)
    if not success then
        return nil, "执行本地字体脚本失败: " .. result
    end

    local fontData = sandbox.font or sandbox[1] or result
    if utf8display.config.cacheFont then
        state.loadedFonts[path] = fontData
    end

    return fontData
end

function fontManager.getFont()
    if not state.font then
        local success, err = utf8display.loadFont()
        if not success then
            error("字体加载失败: " .. err)
        end
    end
    return state.font
end

function fontManager.getFontHeight()
    local font = fontManager.getFont()
    if font and font[32] then
        return #font[32]
    end
    return state.fontHeight
end

-- 渲染引擎模块
local renderer = {}

function renderer.displayChar(charMap, x, y, textColor, backgroundColor)
    if not charMap or #charMap == 0 then
        return
    end

    -- 保存当前终端颜色和光标位置
    local originalTextColor = term.getTextColor()
    local originalBackgroundColor = term.getBackgroundColor()
    local originalCursorX, originalCursorY = term.getCursorPos()

    -- 如果没有提供自定义颜色，则使用当前终端颜色
    local useTextColor = textColor or originalTextColor
    local useBackgroundColor = backgroundColor or originalBackgroundColor

    -- 渲染字符（仅在可视区域内）
    local width, height = term.getSize()
    for row = 1, #charMap do
        local drawY = y + row - 1
        if drawY > height then break end -- 超出底部，不再绘制
        term.setCursorPos(x, drawY)
        local line = charMap[row]

        for col = 1, #line do
            local byte = string.byte(line, col)
            if byte < 128 then
                term.setTextColor(useBackgroundColor)
                term.setBackgroundColor(useTextColor)
                term.write(string.char(byte + 128))
            else
                term.setTextColor(useTextColor)
                term.setBackgroundColor(useBackgroundColor)
                term.write(string.char(byte))
            end
        end
    end

    -- 恢复原始终端颜色和光标位置
    term.setTextColor(originalTextColor)
    term.setBackgroundColor(originalBackgroundColor)
    term.setCursorPos(originalCursorX, originalCursorY)
end

function renderer.utf8Decode(str)
    local i = 1
    return function()
        if i > #str then return end

        local b1 = string.byte(str, i)
        i = i + 1

        if b1 < 0x80 then
            return b1
        elseif b1 >= 0xC0 and b1 < 0xE0 then
            local b2 = string.byte(str, i) or 0
            i = i + 1
            return (b1 - 0xC0) * 64 + (b2 - 0x80)
        elseif b1 >= 0xE0 and b1 < 0xF0 then
            local b2 = string.byte(str, i) or 0
            i = i + 1
            local b3 = string.byte(str, i) or 0
            i = i + 1
            return (b1 - 0xE0) * 4096 + (b2 - 0x80) * 64 + (b3 - 0x80)
        else
            return 32
        end
    end
end

-- 公共API函数
function utf8display.setConfig(key, value)
    if utf8display.config[key] ~= nil then
        utf8display.config[key] = value
        return true
    end
    return false, "配置项不存在"
end

function utf8display.getConfig(key)
    if utf8display.config[key] ~= nil then
        return utf8display.config[key]
    end
    return nil, "配置项不存在"
end

function utf8display.loadFont()
    if utf8display.config.fontPath then
        state.font, err = fontManager.loadLocalFont(utf8display.config.fontPath)
        if not state.font then
            return false, err
        end
    else
        state.font, err = fontManager.loadRemoteFont(utf8display.config.fontUrl)
        if not state.font then
            return false, err
        end
    end
    state.fontHeight = fontManager.getFontHeight()
    return true
end

-- 主动初始化字体函数
function utf8display.initFont()
    return utf8display.loadFont()
end

local function clampCursorPos(x, y, width, height)
    return math.min(math.max(x, 1), width), math.min(math.max(y, 1), height)
end

function utf8display.write(raw_str)
    str = tostring(raw_str)
    -- 将换行符替换为空格
    str = string.gsub(str, "\n", " ")
    
    local font = fontManager.getFont()
    local fontHeight = fontManager.getFontHeight()
    local textColor = term.getTextColor()
    local backgroundColor = term.getBackgroundColor()

    local startX, startY = term.getCursorPos()
    local width, height = term.getSize()
    local cursorX, cursorY = startX, startY
    local charCount = 0
    local contentFitsHorizontally = true
    local contentFitsVertically = true

    for code in renderer.utf8Decode(str) do
        if code == 10 then -- 换行符 (理论上不会再有，因为我们已经替换了)
            cursorX = 1
            cursorY = cursorY + fontHeight
        else
            local charMap = font[code] or font[32]
            local charWidth = #charMap[1]

            -- 不自动换行：即使超出横向宽度也继续写（但不渲染超出部分）
            if cursorX + charWidth - 1 > width then
                contentFitsHorizontally = false
            end

            -- 判断是否还能在竖直方向绘制（至少第一行能画）
            if cursorY <= height then
                renderer.displayChar(charMap, cursorX, cursorY, textColor, backgroundColor)
                charCount = charCount + 1
            else
                contentFitsVertically = false
                -- 不 break，继续推进光标逻辑（用于正确设置最终光标位置）
            end

            cursorX = cursorX + charWidth
        end
    end

    -- 确定最终光标位置
    local finalX, finalY
    if not contentFitsHorizontally or not contentFitsVertically then
        -- 若任一方向溢出，光标回到起始位置
        finalX, finalY = startX, startY
    else
        -- 否则放在正常结束位置
        finalX, finalY = cursorX, cursorY
    end

    -- 安全设置光标（避免超出屏幕导致卡住）
    finalX, finalY = clampCursorPos(finalX, finalY, width, height)
    term.setCursorPos(finalX, finalY)

    return true, {
        textColor = textColor,
        backgroundColor = backgroundColor,
        startX = startX,
        startY = startY,
        endX = finalX,
        endY = finalY,
        charCount = charCount,
        fontHeight = fontHeight,
        overflowX = not contentFitsHorizontally,
        overflowY = not contentFitsVertically
    }
end

function utf8display.print(raw_str)
    str = tostring(raw_str)
    local font = fontManager.getFont()
    local fontHeight = fontManager.getFontHeight()
    local textColor = term.getTextColor()
    local backgroundColor = term.getBackgroundColor()

    local startX, startY = term.getCursorPos()
    local width, height = term.getSize()

    -- 预计算所需总高度（用于滚动）
    local tempCursorX, tempCursorY = startX, startY
    for code in renderer.utf8Decode(str) do
        if code == 10 then
            tempCursorX = 1
            tempCursorY = tempCursorY + fontHeight
        else
            local charMap = font[code] or font[32]
            local charWidth = #charMap[1]
            if tempCursorX + charWidth - 1 > width then
                tempCursorX = 1
                tempCursorY = tempCursorY + fontHeight
            end
            tempCursorX = tempCursorX + charWidth
        end
    end

    local totalContentHeight = tempCursorY + fontHeight - startY
    local availableHeight = height - startY + 1
    local scrollHeight = math.max(0, totalContentHeight - availableHeight)

    if scrollHeight > 0 and utf8display.config.autoScroll then
        term.scroll(scrollHeight)
        startY = startY - scrollHeight
    end

    -- 实际渲染
    local cursorX, cursorY = startX, startY
    local charCount = 0

    for code in renderer.utf8Decode(str) do
        if code == 10 then
            cursorX = 1
            cursorY = cursorY + fontHeight
        else
            local charMap = font[code] or font[32]
            local charWidth = #charMap[1]
            if cursorX + charWidth - 1 > width then
                cursorX = 1
                cursorY = cursorY + fontHeight
            end
            if cursorY <= height then
                renderer.displayChar(charMap, cursorX, cursorY, textColor, backgroundColor)
                charCount = charCount + 1
            end
            cursorX = cursorX + charWidth
        end
    end

    -- print 总是把光标放到下一行开头（符合常规行为）
    local finalY = cursorY + fontHeight
    local finalX = 1
    finalX, finalY = clampCursorPos(finalX, finalY, width, height)
    term.setCursorPos(finalX, finalY)

    return true, {
        textColor = textColor,
        backgroundColor = backgroundColor,
        startX = startX,
        startY = startY,
        endX = finalX,
        endY = finalY,
        charCount = charCount,
        fontHeight = fontHeight
    }
end

function utf8display.blit(raw_str, textColorStr, backgroundColorStr)
    text = tostring(raw_str)
    -- 将换行符替换为空格
    text = string.gsub(text, "\n", " ")
    
    local font = fontManager.getFont()
    local fontHeight = fontManager.getFontHeight()

    local startX, startY = term.getCursorPos()
    local width, height = term.getSize()
    local cursorX, cursorY = startX, startY
    local charCount = 0
    local contentFitsHorizontally = true
    local contentFitsVertically = true

    -- 解析颜色字符串
    local textColors = {}
    local backgroundColors = {}
    for i = 1, #textColorStr do
        textColors[i] = colors.fromBlit(string.sub(textColorStr, i, i))
    end
    for i = 1, #backgroundColorStr do
        backgroundColors[i] = colors.fromBlit(string.sub(backgroundColorStr, i, i))
    end

    local index = 1
    for code in renderer.utf8Decode(text) do
        if code == 10 then -- 换行符 (理论上不会再有，因为我们已经替换了)
            cursorX = 1
            cursorY = cursorY + fontHeight
        else
            local charMap = font[code] or font[32]
            local charWidth = #charMap[1]

            if cursorX + charWidth - 1 > width then
                contentFitsHorizontally = false
            end

            if cursorY <= height then
                local useTextColor = textColors[index] or term.getTextColor()
                local useBackgroundColor = backgroundColors[index] or term.getBackgroundColor()
                renderer.displayChar(charMap, cursorX, cursorY, useTextColor, useBackgroundColor)
                charCount = charCount + 1
            else
                contentFitsVertically = false
            end

            cursorX = cursorX + charWidth
            index = index + 1
        end
    end

    local finalX, finalY
    if not contentFitsHorizontally or not contentFitsVertically then
        finalX, finalY = startX, startY
    else
        finalX, finalY = cursorX, cursorY
    end

    finalX, finalY = clampCursorPos(finalX, finalY, width, height)
    term.setCursorPos(finalX, finalY)

    return true, {
        startX = startX,
        startY = startY,
        endX = finalX,
        endY = finalY,
        charCount = charCount,
        fontHeight = fontHeight,
        overflowX = not contentFitsHorizontally,
        overflowY = not contentFitsVertically
    }
end

-- 工具函数
function utf8display.getFontInfo()
    local font = fontManager.getFont()
    local fontHeight = fontManager.getFontHeight()

    return {
        height = fontHeight,
        loaded = font ~= nil,
        source = utf8display.config.fontPath or utf8display.config.fontUrl,
        cached = state.loadedFonts[utf8display.config.fontPath or utf8display.config.fontUrl] ~= nil
    }
end

function utf8display.isInitialized()
    return state.font ~= nil
end

-- 自动初始化（第一次使用时）
setmetatable(utf8display, {
    __index = function(self, key)
        if key == "write" or key == "print" or key == "blit" then
            if not utf8display.isInitialized() then
                local success, err = utf8display.loadFont()
                if not success then
                    error("自动初始化失败: " .. err)
                end
            end
            return rawget(self, key)
        end
        return rawget(self, key)
    end
})

return utf8display