--[[
    Commander 3.0 - TUI Buffer

    A 2D grid of Cells representing the terminal screen.
    Widgets render to this buffer, then it's converted to WoW FontStrings.

    Key operations:
    - SetCell(x, y, char, fg) - Set single cell
    - SetString(x, y, text, fg) - Set string (UTF-8 aware)
    - Fill(rect, char, fg) - Fill rectangular area
    - ToLines() - Convert to array of WoW colorized strings
]]

local ADDON_NAME, C3 = ...

local Cell = C3.TUI.Cell

-------------------------------------------------------------------------------
-- TuiBuffer Class
-------------------------------------------------------------------------------

local TuiBuffer = C3.class()
C3.TUI = C3.TUI or {}
C3.TUI.Buffer = TuiBuffer

---Create a new TUI buffer
---@param width number Number of columns
---@param height number Number of rows
function TuiBuffer:init(width, height)
    self.width = width or 80
    self.height = height or 24
    self.cells = {}  -- Flat array: index = (y-1)*width + x

    -- Pre-allocate cells
    local size = self.width * self.height
    for i = 1, size do
        self.cells[i] = Cell.Acquire()
    end
end

---Get the flat index for (x, y) coordinates
---@param x number 1-indexed column
---@param y number 1-indexed row
---@return number
function TuiBuffer:_index(x, y)
    return (y - 1) * self.width + x
end

---Check if coordinates are within bounds
---@param x number
---@param y number
---@return boolean
function TuiBuffer:Contains(x, y)
    return x >= 1 and x <= self.width and y >= 1 and y <= self.height
end

---Get cell at position
---@param x number
---@param y number
---@return table|nil
function TuiBuffer:GetCell(x, y)
    if not self:Contains(x, y) then return nil end
    return self.cells[self:_index(x, y)]
end

---Set cell at position
---@param x number
---@param y number
---@param char string
---@param fg? string
---@return TuiBuffer self
function TuiBuffer:SetCell(x, y, char, fg)
    if not self:Contains(x, y) then return self end

    local cell = self.cells[self:_index(x, y)]
    cell.char = char or " "
    cell.fg = fg
    return self
end

---Set a string starting at (x, y), clipped to buffer width
---Uses UTF-8 aware iteration for proper Unicode handling
---@param x number Starting column
---@param y number Row
---@param text string
---@param fg? string Foreground color
---@param maxWidth? number Maximum width (default: remaining width)
---@return number endX The ending X position
function TuiBuffer:SetString(x, y, text, fg, maxWidth)
    if not self:Contains(x, y) then return x end
    if not text then return x end

    maxWidth = maxWidth or (self.width - x + 1)
    local col = x
    local count = 0

    -- UTF-8 aware iteration
    -- Pattern matches: ASCII chars OR multi-byte UTF-8 sequences
    for char in text:gmatch("[%z\1-\127\194-\244][\128-\191]*") do
        if col > self.width or count >= maxWidth then break end
        self:SetCell(col, y, char, fg)
        col = col + 1
        count = count + 1
    end

    return col
end

---Fill a rectangular area
---@param rect table Rect with x, y, width, height
---@param char? string Character to fill (default " ")
---@param fg? string
function TuiBuffer:Fill(rect, char, fg)
    char = char or " "
    for row = rect.y, rect.y + rect.height - 1 do
        for col = rect.x, rect.x + rect.width - 1 do
            self:SetCell(col, row, char, fg)
        end
    end
end

---Draw a horizontal line
---@param x number Starting column
---@param y number Row
---@param length number Length of line
---@param char? string Character to use (default "─")
---@param fg? string
function TuiBuffer:HLine(x, y, length, char, fg)
    char = char or "─"
    for i = 0, length - 1 do
        self:SetCell(x + i, y, char, fg)
    end
end

---Draw a vertical line
---@param x number Column
---@param y number Starting row
---@param length number Length of line
---@param char? string Character to use (default "│")
---@param fg? string
function TuiBuffer:VLine(x, y, length, char, fg)
    char = char or "│"
    for i = 0, length - 1 do
        self:SetCell(x, y + i, char, fg)
    end
end

---Clear the entire buffer
---@param char? string Character to clear with (default " ")
function TuiBuffer:Clear(char)
    char = char or " "
    for i = 1, #self.cells do
        local cell = self.cells[i]
        cell.char = char
        cell.fg = nil
    end
end

---Resize the buffer
---@param width number
---@param height number
function TuiBuffer:Resize(width, height)
    local oldWidth = self.width
    local oldHeight = self.height
    local oldCells = self.cells

    self.width = width
    self.height = height
    self.cells = {}

    -- Allocate new cells
    local size = width * height
    for i = 1, size do
        self.cells[i] = Cell.Acquire()
    end

    -- Copy old content that fits
    for y = 1, math.min(oldHeight, height) do
        for x = 1, math.min(oldWidth, width) do
            local oldIdx = (y - 1) * oldWidth + x
            local newIdx = (y - 1) * width + x
            local oldCell = oldCells[oldIdx]
            if oldCell then
                self.cells[newIdx].char = oldCell.char
                self.cells[newIdx].fg = oldCell.fg
            end
        end
    end

    -- Release old cells back to pool
    for _, cell in ipairs(oldCells) do
        Cell.Release(cell)
    end
end

---Convert buffer to array of colorized WoW strings
---@return table Array of strings, one per row
function TuiBuffer:ToLines()
    local lines = {}

    for y = 1, self.height do
        local parts = {}
        local currentFg = nil

        for x = 1, self.width do
            local cell = self.cells[self:_index(x, y)]
            local char = cell.char or " "
            local fg = cell.fg

            -- Color change needed?
            if fg ~= currentFg then
                if currentFg then
                    parts[#parts + 1] = "|r"
                end
                if fg then
                    parts[#parts + 1] = "|cFF" .. fg
                end
                currentFg = fg
            end

            parts[#parts + 1] = char
        end

        -- Close any open color
        if currentFg then
            parts[#parts + 1] = "|r"
        end

        lines[y] = table.concat(parts)
    end

    return lines
end

---Get the Rect representing the full buffer area
---@return table Rect
function TuiBuffer:Area()
    return C3.Rect.Acquire(1, 1, self.width, self.height)
end

---Get buffer size
---@return number width, number height
function TuiBuffer:GetSize()
    return self.width, self.height
end

---Release all cells (for cleanup)
function TuiBuffer:Destroy()
    for _, cell in ipairs(self.cells) do
        Cell.Release(cell)
    end
    self.cells = {}
end

return TuiBuffer
