-- selene: allow(shadowing)
local gfx = playdate.graphics

local ScoreFont <const> = FontFullCircle

local MinimapSizeX, MinimapSizeY <const> = Minimap:getSize()
local MinimapPosX, MinimapPosY = C.Screen.W - MinimapSizeX, C.Screen.H - MinimapSizeY
local MinimapBoundX, MinimapBoundY = (MinimapSizeX + MinimapPosX), (MinimapSizeY + MinimapPosY)

local MinimapMultX <const> = 0.75 * MinimapSizeX / C.Screen.W
local MinimapOffsetX <const> = MinimapPosX + 5
local MinimapMultY <const> = 0.70 * MinimapSizeY / C.FieldHeight
local MinimapOffsetY <const> = MinimapPosY - 15

local RunnerSquareWidth = 8
local FielderCircleRadius = 4
local FielderCircleStrokeWidth = 2

function drawMinimap(runners, fielders)
    Minimap:draw(MinimapPosX, MinimapPosY)
    gfx.setColor(gfx.kColorBlack)
    for _, runner in pairs(runners) do
        local x = (MinimapMultX * runner.x) + MinimapOffsetX
        local y = (MinimapMultY * runner.y) + MinimapOffsetY
        gfx.fillRect(x, y, RunnerSquareWidth, RunnerSquareWidth)
    end
    gfx.setLineWidth(FielderCircleStrokeWidth)
    for _, fielder in pairs(fielders) do
        local x = (MinimapMultX * fielder.x) + MinimapOffsetX
        local y = (MinimapMultY * fielder.y) + MinimapOffsetY
        if x > MinimapPosX and x < MinimapBoundX and y > MinimapPosY and y < MinimapBoundY then
            gfx.drawCircleAtPoint(x, y, FielderCircleRadius)
        end
    end
end

local BallStrikeMarginY <const> = 4
local BallStrikeWidth <const> = 60
local BallStrikeHeight <const> = (BallStrikeMarginY * 2) + ScoreFont:getHeight()

local BallStrikeAnimatorIn <const> =
    playdate.graphics.animator.new(500, BallStrikeHeight, 0, playdate.easingFunctions.outBounce)

local BallStrikeAnimatorOut <const> =
    playdate.graphics.animator.new(500, 0, BallStrikeHeight, playdate.easingFunctions.linear)

-- Start out of frame.
local currentBallStrikeAnimator = utils.staticAnimator(20)

function drawBallsAndStrikes(x, y, balls, strikes)
    if balls == 0 and strikes == 0 then
        if currentBallStrikeAnimator == BallStrikeAnimatorIn then
            currentBallStrikeAnimator = BallStrikeAnimatorOut
            currentBallStrikeAnimator:reset()
        end
        if currentBallStrikeAnimator:ended() then
            return
        end
    end
    if balls + strikes == 1 and currentBallStrikeAnimator ~= BallStrikeAnimatorIn then
        -- First pitch - should pop in now.
        currentBallStrikeAnimator = BallStrikeAnimatorIn
        currentBallStrikeAnimator:reset()
    end

    y = y + currentBallStrikeAnimator:currentValue()
    gfx.setColor(gfx.kColorBlack)
    gfx.fillRect(x, y, BallStrikeWidth, BallStrikeHeight)
    local originalDrawMode = gfx.getImageDrawMode()
    gfx.setImageDrawMode(gfx.kDrawModeInverted)

    local text = tostring(balls) .. " - " .. tostring(strikes)
    local textWidth = ScoreFont:getTextWidth(text)
    local widthDiff = BallStrikeWidth - textWidth
    ScoreFont:drawText(text, x + (widthDiff / 2), y + BallStrikeMarginY)
    gfx.setImageDrawMode(originalDrawMode)
end

local OutBubbleRadius <const> = 5
local ScoreboardMarginX <const> = 6
local ScoreboardMarginRight <const> = 4
local ScoreboardHeight <const> = 55
local Indicator = "> "
local IndicatorWidth <const> = ScoreFont:getTextWidth(Indicator)

---@param battingTeam any
---@return string, number, string, number
function getIndicators(battingTeam)
    if battingTeam == "home" then
        return Indicator, 0, "", IndicatorWidth
    end
    return "", IndicatorWidth, Indicator, 0
end

local stats = {
    homeScore = 0,
    awayScore = 0,
    outs = 0,
    inning = 1,
    battingTeam = nil,
}

function drawScoreboardImpl(x, y)
    local homeScore = stats.homeScore
    local awayScore = stats.awayScore

    local homeIndicator, homeOffset, awayIndicator, awayOffset = getIndicators(stats.battingTeam)

    local homeScoreText = homeIndicator .. "HOME " .. (homeScore > 9 and homeScore or "  " .. homeScore)
    local awayScoreText = awayIndicator .. "AWAY " .. (awayScore > 9 and awayScore or "  " .. awayScore)

    local rectWidth = (ScoreboardMarginX * 2)
        + ScoreboardMarginRight
        + ScoreFont:getTextWidth(homeScoreText)
        + homeOffset

    gfx.setLineWidth(1)
    gfx.setColor(gfx.kColorBlack)
    gfx.fillRect(x, y, rectWidth, ScoreboardHeight)

    local originalDrawMode = gfx.getImageDrawMode()
    gfx.setImageDrawMode(gfx.kDrawModeInverted)

    ScoreFont:drawText(homeScoreText, x + ScoreboardMarginX + homeOffset, y + 6)
    ScoreFont:drawText(awayScoreText, x + ScoreboardMarginX + awayOffset, y + 22)
    local inningOffsetX = (x + ScoreboardMarginX + IndicatorWidth) + (4 * 2.5 * OutBubbleRadius)
    ScoreFont:drawText(tostring(stats.inning), inningOffsetX, y + 39)

    gfx.setImageDrawMode(originalDrawMode)

    gfx.setColor(gfx.kColorWhite)

    function circleParams(i)
        local circleOffset = i * 2.5 * OutBubbleRadius
        return (x + ScoreboardMarginX + OutBubbleRadius + IndicatorWidth) + circleOffset, y + 46, OutBubbleRadius
    end

    for i = stats.outs, 2 do
        gfx.drawCircleAtPoint(circleParams(i))
    end
    for i = 0, (stats.outs - 1) do
        gfx.fillCircleAtPoint(circleParams(i))
    end
end

local newStats = stats

function drawScoreboard(x, y, homeScore, awayScore, outs, battingTeam, inning)
    if
        newStats.homeScore ~= homeScore
        or newStats.awayScore ~= awayScore
        or newStats.outs ~= outs
        or newStats.inning ~= inning
        or newStats.battingTeam ~= battingTeam
    then
        newStats = {
            homeScore = homeScore,
            awayScore = awayScore,
            outs = outs,
            inning = inning,
            battingTeam = battingTeam,
        }
        playdate.timer.new(C.ScoreboardDelayMs, function()
            stats = newStats
        end)
    end
    drawScoreboardImpl(x, y)
end
