TimerEvents = {}

function TimerEvents.OpenEventWindow(player)
    if player:getInterfaceUse() ~= 0 or player:getInterfaceType() ~= 0 or player:getState() == 32 or player:getDieRegen() ~= 0 or player:getTeleport() ~= 0
    then
        return
    end

    if TIMER_EVENTS_COUNT <= 0
    then
        return
    end

    local packetIdentification = string.format("%s_%s", TIMER_EVENTS_PACKET_NAME, player:getName())

    CreatePacket(packetIdentification, TIMER_EVENTS_PACKET)

    SetWordPacket(packetIdentification, TIMER_EVENTS_COUNT)

    SetDwordPacket(packetIdentification, GetTick())

    for i = 1, TIMER_EVENTS_COUNT do
        local eventInfo = TIMER_EVENTS_INFO[i]
        timer = TimerEvents.GetNextEventTime(eventInfo)
        SetDwordPacket(packetIdentification, timer)

        local eventMove = TIMER_EVENTS_MOVE[i]
        local vip = player:getVip()
        local canMove = 0
        if eventMove and eventMove[1] and vip >= eventMove[1].VipType then
            canMove = 1
        end
        SetBytePacket(packetIdentification, canMove)
    end

    SendPacket(packetIdentification, player:getIndex())
    ClearPacket(packetIdentification)
end

function TimerEvents.MoveToEvent(player, eventIndex)
    if TIMER_EVENTS_MOVE_SWITCH ~= 1 then
        return
    end

    if eventIndex < 1 or eventIndex > TIMER_EVENTS_COUNT then
        return
    end

    local eventMove = TIMER_EVENTS_MOVE[eventIndex]
    if not eventMove or not eventMove[1] then
        return
    end

    local move = eventMove[1]
    local Language = player:getLanguage()
    local pInv = Inventory.new(player:getIndex())
    local vip = player:getVip()
    local Resets = player:getReset()
    local MResets = player:getMasterReset()

    -- Verificações do jogador (baseado em MoveSystem.CheckMove)
    if player:getInterfaceUse() ~= 0 or player:getInterfaceType() ~= 0 or player:getTeleport() ~= 0 or player:getDieRegen() ~= 0 then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][1]), player:getIndex(), 1)
        return
    end

    if move.MinLevel ~= -1 and player:getLevel() < move.MinLevel then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][2], move.MinLevel), player:getIndex(), 1)
        return
    end

    if move.MaxLevel ~= -1 and player:getLevel() > move.MaxLevel then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][3], move.MaxLevel), player:getIndex(), 1)
        return
    end

    -- Verificação para mapas que requerem Wing ou Dinorant (ex.: Icarus, Map 10)
    if move.Map == 10 and pInv:isItem(7) == 0 then
        if pInv:isItem(8) == 0 or pInv:getIndex(8) ~= GET_ITEM(13, 3) then
            SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][4]), player:getIndex(), 1)
            return
        end
    end

    -- Verificação para mapas que proíbem montarias específicas (ex.: Atlans, Map 7)
    if move.Map == 7 and pInv:isItem(8) ~= 0 then
        if pInv:getIndex(8) == GET_ITEM(13, 2) or pInv:getIndex(8) == GET_ITEM(13, 3) then
            SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][5]), player:getIndex(), 1)
            return
        end
    end

    if vip < move.VipType then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][6]), player:getIndex(), 1)
        return
    end

    if player:getMoney() < move.Price then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][7], move.Price), player:getIndex(), 1)
        return
    end

    if player:getPKLevel() > 4 and move.Pk == 0 then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][8]), player:getIndex(), 1)
        return
    end

    if move.MinReset ~= -1 and Resets < move.MinReset then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][9], move.MinReset), player:getIndex(), 1)
        return
    end

    if move.MaxReset ~= -1 and Resets > move.MaxReset then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][10], move.MaxReset), player:getIndex(), 1)
        return
    end

    if move.MinMReset ~= -1 and MResets < move.MinMReset then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][11], move.MinMReset), player:getIndex(), 1)
        return
    end

    if move.MaxMReset ~= -1 and MResets > move.MaxMReset then
        SendMessage(string.format(MOVE_CUSTOM_MESSAGE[Language][12], move.MaxMReset), player:getIndex(), 1)
        return
    end

    -- Deduzir zen, se aplicável
    if move.Price > 0 then
        local setMoney = player:getMoney() - move.Price
        player:setMoney(setMoney)
        MoneySend(player:getIndex())
    end

    -- Executar teleporte
    Teleport(player:getIndex(), move.Map, move.CoordX, move.CoordY)
end

function TimerEvents.Protocol(aIndex, Packet, PacketName)
    local player = User.new(aIndex)

    if Packet == TIMER_EVENTS_PACKET then
        if string.format("%s_%s", TIMER_EVENTS_PACKET_NAME, player:getName()) == PacketName then
            ClearPacket(PacketName)
            TimerEvents.OpenEventWindow(player)
        end
    elseif Packet == TIMER_EVENTS_PACKET_MOVE then
        if string.format("%s_%s", TIMER_EVENTS_PACKET_NAME_MOVE, player:getName()) == PacketName then
            local eventIndex = GetBytePacket(PacketName, -1)
            ClearPacket(PacketName)
            TimerEvents.MoveToEvent(player, eventIndex)
        end
    end

    player = nil
end

function TimerEvents.GetNextEventTime(eventInfo)
    local timer = 0

    for n = 1, #eventInfo do
        local eventTime = eventInfo[n]

        if eventTime.tickTime >= 0 then
            if timer == 0 then
                timer = eventTime.tickTime
            else
                if timer > eventTime.tickTime then
                    timer = eventTime.tickTime
                end
            end
        end
    end

    return timer
end

function TimerEvents.CreateTimerTick()
    for i = 1, TIMER_EVENTS_COUNT do
        local eventInfo = TIMER_EVENTS_INFO[i]

        for n = 1, #eventInfo do
            local eventTime = eventInfo[n]

            eventTime.tickTime = GetTimeDays(eventTime.dayOfWeek, eventTime.day, eventTime.hour, eventTime.minute, eventTime.second)
        end
    end
end

function TimerEvents.MainProc()
    for i = 1, TIMER_EVENTS_COUNT do
        local eventInfo = TIMER_EVENTS_INFO[i]

        for n = 1, #eventInfo do
            local eventTime = eventInfo[n]

            eventTime.tickTime = eventTime.tickTime - 1

            if (eventTime.tickTime < 0) then
                eventTime.tickTime = GetTimeDays(eventTime.dayOfWeek, eventTime.day, eventTime.hour, eventTime.minute, eventTime.second)
            end
        end
    end
end

function TimerEvents.Init()
    if TIMER_EVENTS_SWITCH == 0 then
        return
    end

    GameServerFunctions.GameServerProtocol(TimerEvents.Protocol)
    TimerEvents.CreateTimerTick()
    Timer.Interval(1, TimerEvents.MainProc)
end

TimerEvents.Init()

return TimerEvents