local gfx = playdate.graphics

local HeaderFont <const> = Roobert11Medium
local DetailFont <const> = FontFullCircle

---@alias TextObject { text: string, font: pd_font }

---@param texts TextObject[]
local function drawTexts(texts)
    local xOffset = 10
    local initialOffset <const> = -(HeaderFont:getHeight()) / 2
    local yOffset = initialOffset

    --- The text height plus a margin scaled to that height
    function getOffsetOffset(textObject)
        return (-4 + math.floor(textObject.font:getHeight() * 1.6)) / 2
    end

    -- Inverted buffer around text to separate it from the background
    for _, textObject in ipairs(texts) do
        local offsetOffset = getOffsetOffset(textObject)
        yOffset = yOffset + offsetOffset
        gfx.setImageDrawMode(gfx.kDrawModeInverted)
        for x = xOffset - 6, xOffset + 6 do
            for y = yOffset - 6, yOffset + 6 do
                textObject.font:drawText(textObject.text, x, y)
            end
        end
        yOffset = yOffset + offsetOffset
    end

    -- Drawing the actual text afterward (instead of inline) keeps the inverted buffer from drawing over it.
    yOffset = initialOffset
    gfx.setImageDrawMode(gfx.kDrawModeCopy)
    for _, textObject in ipairs(texts) do
        local offsetOffset = getOffsetOffset(textObject)
        yOffset = yOffset + offsetOffset
        textObject.font:drawText(textObject.text, xOffset, yOffset)
        yOffset = yOffset + offsetOffset
    end
end

---@param text string
---@return TextObject
local function header(text)
    return { text = text, font = HeaderFont }
end

---@param text string
---@return TextObject
local function detail(text)
    return { text = text, font = DetailFont }
end

---@class ControlScreen
---@field sceneToReturnTo Scene
---@field private renderedImage pd_image Static image doesn't need to be constantly re-rendered.
ControlScreen = {}

---@return pd_image
local function draw()
    local image = gfx.image.new(C.Screen.W, C.Screen.H)
    gfx.pushContext(image)
    BallBackground:draw(0, 0)
    drawTexts({
        header("Batting:"),
        detail("Swing the crank to swing your bat"),
        detail("But watch out! Some pitches are tricky!"),

        header("Pitching:"),
        detail("Swing the crank to pitch the ball"),
        detail("But be careful! Throw too hard and it might go wild!"),
        detail("(shh: try holding A or B while you pitch)"),

        header("Fielding:"),
        detail("To throw, hold a direction button and crank!"),
        detail("Right throws to 1st, Up goes to 2nd, etc."),
    })
    gfx.popContext()
    return image
end

---@param sceneToReturnTo Scene
---@return ControlScreen
function ControlScreen.new(sceneToReturnTo)
    return setmetatable({
        sceneToReturnTo = sceneToReturnTo,
        renderedImage = draw(),
    }, { __index = ControlScreen })
end

function ControlScreen:update()
    gfx.animation.blinker.updateAll()
    gfx.clear()
    self.renderedImage:draw(0, 0)
    drawButton("B", 370, 210)
    if playdate.buttonJustPressed(playdate.kButtonA) or playdate.buttonJustPressed(playdate.kButtonB) then
        transitionBetween(self, self.sceneToReturnTo)
    end
end
