include("class.lua")

HotKeyManager = class()

HotKeyManager.TYPE_TYPED = 1
HotKeyManager.TYPE_RELEASED = 2
HotKeyManager.TYPE_PRESSED = 3

function HotKeyManager:init()
    self.keyActions = {}
    self.keyActions[HotKeyManager.TYPE_TYPED] = {}
    self.keyActions[HotKeyManager.TYPE_RELEASED] = {}
    self.keyActions[HotKeyManager.TYPE_PRESSED] = {}

    self.pressTimes = {}
end

function HotKeyManager:__checkType(typee)
    if (type(typee) ~= "number") then
        stackErrorf("Type must be of type of \"number\". Got %q!", type(typee))
        return false
    end

    if (self.keyActions[typee] == nil) then
        stackErrorf("Invalid type %d recieved!", type)
        return false
    end

    return true
end

function HotKeyManager:__getActionTable(type, key)
    if (not self:__checkType(type)) then
        return {}
    end

    local keyTable = self.keyActions[type][key:lower()]

    if (keyTable == nil) then
        self.keyActions[type][key:lower()] = {}
        keyTable = self.keyActions[type][key:lower()]
    end

    return keyTable
end

function HotKeyManager:__invokeType(type, key)
    if (not self:__checkType(type)) then
        return
    end

    local actions = self.keyActions[type][key:lower()]

    if (actions ~= nil) then
        for __, v in ipairs(actions) do
            v:invoke()
        end
    end
end

function HotKeyManager:__createAction(type, key, func, ...)
    if (not self:__checkType(type)) then
        return nil
    end

    local keyTable = self:__getActionTable(type, key)

    local action = HotKeyAction(key, func)
    action:setArguments(...)
    table.insert(keyTable, action)

    return action
end

function HotKeyManager:__removeActionFromType(type, action)
    if (not self:__checkType(type)) then
        return
    end

    for i, v in pairs(self.keyActions[type]) do
        local index = 1
        local done = false

        while (not done) do
            for i = index, #v do
                if (v[i] == action) then
                    table.remove(v, i)
                    index = i
                    break
                end
            end

            done = true
        end
    end
end

function HotKeyManager:addKeyTyped(key, func, ...)
    if (not type(key) == "string") then
        stackErrorf("The 'key' argument must be of type %q but is of type %q!", "string", type(key))
        return nil
    elseif (not type(func) == "function") then
        stackErrorf("The 'function' argument must be of type %q but is of type %q!", "function", type(func))
        return nil
    end

    return self:__createAction(HotKeyManager.TYPE_TYPED, key, func, ...)
end

function HotKeyManager:addKeyPressed(key, func, ...)
    if (not type(key) == "string") then
        stackErrorf("The 'key' argument must be of type %q but is of type %q!", "string", type(key))
        return nil
    elseif (not type(func) == "function") then
        stackErrorf("The 'function' argument must be of type %q but is of type %q!", "function", type(func))
        return nil
    end

    return self:__createAction(HotKeyManager.TYPE_PRESSED, key, func, ...)
end

function HotKeyManager:addKeyReleased(key, func, ...)
    if (not type(key) == "string") then
        stackErrorf("The 'key' argument must be of type %q but is of type %q!", "string", type(key))
        return nil
    elseif (not type(func) == "function") then
        stackErrorf("The 'function' argument must be of type %q but is of type %q!", "function", type(func))
        return nil
    end

    return self:__createAction(HotKeyManager.TYPE_RELEASED, key, func, ...)
end

function HotKeyManager:onKeyPressed(key)
    self.pressTimes[key:lower()] = os.clock()

    self:__invokeType(HotKeyManager.TYPE_PRESSED, key)
end

function HotKeyManager:onKeyReleased(key)
    if (self.pressTimes[key:lower()] ~= nil) then
        local time = os.clock() - self.pressTimes[key:lower()]

        if (time < 0.2) then
            self:__invokeType(HotKeyManager.TYPE_TYPED, key)
        end
    end

    self:__invokeType(HotKeyManager.TYPE_RELEASED, key)

    self.pressTimes[key:lower()] = nil
end

function HotKeyManager:removeAction(action, type)
    if (type ~= nil) then
        if (self:__checkType(type)) then
            self:__removeActionFromType(type, action)
        end
    else
        self:__removeActionFromType(HotKeyManager.TYPE_TYPED, action)
        self:__removeActionFromType(HotKeyManager.TYPE_PRESSED, action)
        self:__removeActionFromType(HotKeyManager.TYPE_RELEASED, action)
    end
end

HotKeyAction = class()

function HotKeyAction:init(key, func)
    self.key = key
    self.func = func

    self.gamestate = ba.GameStates["GS_STATE_GAME_PLAY"]
end

function HotKeyAction:setGamestate(gamestate)
    self.gamestate = gamestate
end

function HotKeyAction:setArguments(...)
    self.args = arg
    self.args.n = nil
end

function HotKeyAction:invoke()
    if (self:checkState()) then
        self.func(unpack(self.args))
    end
end

function HotKeyAction:checkState()
    if (self.gamestate) then
        if (ba.getCurrentGameState().Name ~= self.gamestate.Name) then
            return false
        end
    end

    return true
end
