--- @alias Device "mouse" | "key" | "pad"

--- @param device Device
--- @param key string
local function control(device, key)
    --- @type {type: Device, key: string}
    local t = { type = device, key = key }
    return t
end

local controls = {}
controls.keymap = {
    cameraMoveUp = control("key", "w"),
    cameraMoveLeft = control("key", "a"),
    cameraMoveRight = control("key", "d"),
    cameraMoveDown = control("key", "s"),
    cameraMoveScroll = control("mouse", "3"),
    cameraAnimateTo = control('key', 't'),
    fullMana = control("key", "m"),
    select = control("mouse", "1"),
    endTurnTest = control("key", "e"),
    toggleTurns = control("key", "r"),
}

local currentKeys = {}
local cachedKeys = {}

--- @type number
controls.mouseWheelY = 0
love.wheelmoved = function(x, y)
    controls.mouseWheelY = y
end

--- polling controls in O(n)
--- should be called at the beginning of every frame
function controls:poll()
    for k, v in pairs(self.keymap) do
        local type = v.type
        local idx = v.key
        if type == "key" then
            currentKeys[k] = love.keyboard.isDown(idx)
        end

        if type == "mouse" then
            if not tonumber(idx) then return false end
            currentKeys[k] = love.mouse.isDown(tonumber(idx) --[[@as number]])
        end
    end
end

--- store active controls
--- should be called at the end of every frame
function controls:cache()
    for k, v in pairs(currentKeys) do
        cachedKeys[k] = v
    end
    controls.mouseWheelY = 0
end

--- marks a control consumed for the current frame
---
--- the control will no longer be considered active
--- @param key string
function controls:consume(key)
    cachedKeys[key] = true
    currentKeys[key] = nil
end

--- check if a control is active
--- @param key string
function controls:isDown(key)
    return not not currentKeys[key]
end

--- check if a control was activated during current logical frame
--- @param key string
function controls:isJustPressed(key)
    return currentKeys[key] and not cachedKeys[key]
end

return controls
