Transitions = {
    ---@type Scene | nil
    nextScene = nil,
    ---@type Scene | nil
    previousScene = nil,
}

local gfx = playdate.graphics

local previousSceneImage
local previousSceneMask
local nextSceneImage

local batImageTable = {}

local batOffset = 80
local degStep = 3

function loadBatImageTable()
    for deg = 90 - (degStep * 3), 270 + (degStep * 3), degStep do
        local img = gfx.image.new(C.Screen.W, C.Screen.H)
        gfx.pushContext(img)
        BigBat:drawRotated(C.Center.x, C.Screen.H + batOffset, 90 + deg)
        gfx.popContext()
        batImageTable[deg] = img
    end
end
loadBatImageTable()

local function update()
    local lastAngle
    local seamAngle = math.rad(270)
    while seamAngle > math.rad(90) do
        local deltaSeconds = playdate.getElapsedTime()
        playdate.resetElapsedTime()
        seamAngle = seamAngle - (deltaSeconds * 3)
        local seamAngleDeg = math.floor(math.deg(seamAngle))
        seamAngleDeg = seamAngleDeg - (seamAngleDeg % degStep)

        -- Skip re-drawing if no change
        if lastAngle ~= seamAngleDeg then
            lastAngle = seamAngleDeg
            nextSceneImage:draw(0, 0)

            gfx.pushContext(previousSceneMask)
            gfx.setImageDrawMode(gfx.kDrawModeFillBlack)
            batImageTable[seamAngleDeg]:draw(0, 0)
            gfx.popContext()

            previousSceneImage:setMaskImage(previousSceneMask)
            previousSceneImage:draw(0, 0)
            batImageTable[seamAngleDeg]:draw(0, 0)
        end

        coroutine.yield()
    end
    playdate.update = function()
        Transitions.nextScene:update()
    end
end

---@param nextScene fun() The next playdate.update function
function transitionTo(nextScene)
    if not Transitions.nextScene then
        error("Expected Transitions to already have nextScene defined! E.g. by calling transitionBetween")
    end
    local previousScene = Transitions.nextScene
    transitionBetween(previousScene, nextScene)
end

---@param previousScene Scene Has the current playdate.update function
---@param nextScene Scene Has the next playdate.update function
function transitionBetween(previousScene, nextScene)
    playdate.wait(2) -- TODO: There's some sort of timing wack here.
    playdate.update = update

    previousSceneImage = gfx.image.new(C.Screen.W, C.Screen.H)
    gfx.pushContext(previousSceneImage)
    previousScene:update()
    gfx.popContext()

    nextSceneImage = gfx.image.new(C.Screen.W, C.Screen.H)
    gfx.pushContext(nextSceneImage)
    nextScene:update()
    gfx.popContext()

    previousSceneMask = gfx.image.new(C.Screen.W, C.Screen.H, gfx.kColorWhite)
    previousSceneImage:setMaskImage(previousSceneMask)

    Transitions.nextScene = nextScene
    Transitions.previousScene = previousScene
end
