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

local AnnouncementFont <const> = playdate.graphics.font.new("fonts/Roobert-20-Medium.pft")
local AnnouncementTransitionMs <const> = 300
local AnnouncerMarginX <const> = 26

local AnnouncerAnimatorInY <const> =
    playdate.graphics.animator.new(AnnouncementTransitionMs, -70, 0, playdate.easingFunctions.outBounce)
local AnnouncerAnimatorOutY <const> =
    playdate.graphics.animator.new(AnnouncementTransitionMs, 0, -70, playdate.easingFunctions.outQuint)

-- selene: allow(unscoped_variables)
---@class Announcer
---@field textQueue string[]
---@field animatorY pd_animator
Announcer = {}

function Announcer.new()
    return setmetatable({
        textQueue = {},
        animatorY = AnnouncerAnimatorInY,
    }, { __index = Announcer })
end

local DurationMs <const> = 2000

function Announcer:popIn()
    self.animatorY = AnnouncerAnimatorInY
    self.animatorY:reset()

    playdate.timer.new(DurationMs, function()
        self.animatorY = AnnouncerAnimatorOutY
        self.animatorY:reset()
        -- If this popIn() call was inside a timer, successive messages would be
        -- allowed to transition out. However, the Out animation, shortly followed by
        -- a new message popping in, is actually *more* jarring than the interrupt.
        if #self.textQueue ~= 1 then
            self:popIn()
            table.remove(self.textQueue, 1)
        else
            playdate.timer.new(AnnouncementTransitionMs, function()
                table.remove(self.textQueue, 1)
            end)
        end
    end)
end

function Announcer:say(text)
    self.textQueue[#self.textQueue + 1] = text
    if #self.textQueue == 1 then
        self:popIn()
    end
end

function Announcer:draw(x, y)
    if #self.textQueue == 0 then
        return
    end
    x = x - 5 -- Infield center is slightly offset from screen center

    local originalDrawMode = gfx.getImageDrawMode()
    local width = math.max(150, (AnnouncerMarginX * 2) + AnnouncementFont:getTextWidth(self.textQueue[1]))
    local animY = self.animatorY:currentValue()

    gfx.setColor(gfx.kColorBlack)
    gfx.fillRect(x - (width / 2), y + animY, width, 50)
    gfx.setImageDrawMode(gfx.kDrawModeInverted)
    AnnouncementFont:drawTextAligned(self.textQueue[1], x, y + 10 + animY, kTextAlignment.center)
    gfx.setImageDrawMode(originalDrawMode)
end