--[[
Author: Jarth
Filename: CBs_Buttons.lua
]] --

-------------------------------------------------------------------------------------------------
-- VARIABLES --
-------------------------------------------------------------------------------------------------
local base = CollectionBars

-------------------------------------------------------------------------------------------------
-- FUNCTIONS --
-------------------------------------------------------------------------------------------------

function base.SetFrameAndCombineSize(_type)
    local width, height = base.GetBarWidthHeight(_type)
    base.SetFrameSizeIfExists(_type.Frame, width, height)

    if _type.Saved.Combine then
        base.SetFrameSizeIfExists(base.Global.Combine.Frame, width, height)
    end
end

function base.GetFrame(name, virtual)
    local frame = GetControl(name)
    if not frame then
        frame = base.WM:CreateControlFromVirtual(name, GuiRoot, virtual)
    end
    frame:SetDrawLayer(5)
    return frame
end

function base.GetButtonPosition(_type, index)
    local x = ((index - 1) % _type.BarDepth) * base.Saved.ButtonXY
    local y = (math.floor((index - 1) / _type.BarDepth)) * base.Saved.ButtonXY
    if not _type.Saved.Horizontal then
        return y, x
    else
        return x, y
    end
end

function base.SetupButtons(_type)
    local index = 1
    local selected = _type.Saved.Selected
    local frame = _type.Frame
    _type.IsEmpty = true

    if base.Global.IsDebugEnabled then
        d(string.format("SetupButtons: %s Depth: %s Width: %s", tostring(_type.Name), tostring(_type.BarDepth), tostring(_type.BarWidth)))
    end
    local maxIndex = (_type.BarDepth or 0) * (_type.BarWidth or 0)

    for _, _value in ipairs(_type.OrderedCollection) do
        local hideButton = true
        if selected[_value.Id] and IsCollectibleUnlocked(_value.Id) and IsCollectibleValidForPlayer(_value.Id) then
            if base.Buttons[_value.Id] == nil then
                base.Buttons[_value.Id] = CBs_Button:New(_type, frame, _value.Id, base.Saved)
            end

            if not _type.Saved.HideAll and (maxIndex == 0 or index <= maxIndex) then
                hideButton = false

                local left, top = base.GetButtonPosition(_type, index)
                base.Buttons[_value.Id]:SetBindingText(base.Saved.ShowBinding, base.GetBindingNameFromCId(_value.Id))
                base.Buttons[_value.Id]:Setup(base)
                base.Buttons[_value.Id]:UpdateAnchor(frame, left, top)
                base.Buttons[_value.Id]:UpdatePlaySounds(base.Saved.IsAudioEnabled)
                base.Buttons[_value.Id]:UpdateState()
                index = index + 1
            end
            _type.IsEmpty = false
        end

        if base.Buttons[_value.Id] ~= nil then
            base.Buttons[_value.Id].ctrl:SetHidden(hideButton)
            base.Buttons[_value.Id]:SetSize(base.Saved.ButtonXY)
            base.Buttons[_value.Id]:UpdateState()
        end
    end

    if _type.Frame then
        _type.Frame:SetHidden(_type.IsEmpty)
    end

    if _type.FrameLabel then
        _type.FrameLabel:SetHidden(_type.IsEmpty)
    end
end

function base.UpdateButtonsState(_type, forceId, isAttemptingActivation)
    for _, _value in ipairs(_type.OrderedCollection) do
        local button = base.Buttons[_value.Id]
        if button ~= nil then
            button:UpdateState(forceId, isAttemptingActivation)
        end
    end
end

function base.UpdateButtonsCooldown(type, remaining, duration, cooldown)
    for _cId in pairs(type.Saved.Selected) do
        if base.Buttons[_cId] ~= nil and type.Collection[_cId] then
            base.Buttons[_cId]:UpdateCooldown(remaining, duration, cooldown)
        end
    end
end

function base.GetCooldownText(countDown, duration)
    local cooldown = ""
    if type(duration) == "number" and countDown.StartTime ~= nil then
        local secondsRemaining = math.max(countDown.StartTime + duration - GetFrameTimeMilliseconds(), 0) / 1000
        cooldown = ZO_FormatTimeAsDecimalWhenBelowThreshold(secondsRemaining, 60)
    end
    return cooldown
end

function base.IsCollectibleUsable(button)
    local isCollectibleUsable = button ~= nil and button.type.Cooldown.StartTime == nil and IsCollectibleUsable(button.cId)
    if base.Global.IsDebugEnabled then
        d(string.format("IsCollectibleUsable: %s;", tostring(isCollectibleUsable)))
    end
    if not isCollectibleUsable and button.cId then
        local _, duration = GetCollectibleCooldownAndDuration(button.cId)
        isCollectibleUsable = button.type.Cooldown.StartTime + duration < GetFrameTimeMilliseconds()
        if base.Global.IsDebugEnabled then
            d(string.format("IsCollectibleUsable: %s; StartTime: %s; Time:~%s", tostring(isCollectibleUsable), tostring(button.type.Cooldown.StartTime + duration), tostring(GetFrameTimeMilliseconds())))
        end
    end
    return isCollectibleUsable
end

function base.Activate(button)
    if base.IsCollectibleUsable(button) then
        if base.Global.IsDebugEnabled then
            d(string.format("Name: %s; IsActiveActivationEnabled: %s; ", base.Addon.Abbreviation .. button.type.Name, tostring(base.Saved.IsActiveActivationEnabled)))
        end
        if (base.Saved.IsActiveActivationEnabled) then
            if base.Global.IsDebugEnabled then
                d(string.format("Name: %s; RegisterForEvent: %s; Collectible: %s; EventTS: %s; Unregister..", base.Addon.Abbreviation .. button.type.Name, EVENT_COLLECTIBLE_USE_RESULT, button.cId, tostring(button.type.EventTS)))
            end
            EVENT_MANAGER:UnregisterForEvent(base.Addon.Abbreviation .. button.type.Name .. tostring(button.type.EventTS), EVENT_COLLECTIBLE_USE_RESULT)
            button.type.EventTS = GetTimeStamp()
            EVENT_MANAGER:RegisterForEvent(
                base.Addon.Abbreviation .. button.type.Name .. tostring(button.type.EventTS),
                EVENT_COLLECTIBLE_USE_RESULT,
                function(_, result, isAttemptingActivation)
                    local countDown = button.type.Cooldown
                    local success = false

                    if result == COLLECTIBLE_USAGE_BLOCK_REASON_NOT_BLOCKED and button.button then
                        success = true
                        countDown.CollectibleId = button.cId
                        base.UpdateButtonsState(button.type, button.cId, isAttemptingActivation)
                    end

                    local successActivate = isAttemptingActivation and success
                    countDown.StartTime = GetFrameTimeMilliseconds()

                    if base.Global.IsDebugEnabled then
                        d(string.format("Event: %s; Result: %s; isAttemptingActivation: %s; successActivate: %s; EvnetTS: %s;", EVENT_COLLECTIBLE_USE_RESULT, result, tostring(isAttemptingActivation), tostring(successActivate), tostring(button.type.EventTS)))
                    end

                    EVENT_MANAGER:UnregisterForEvent(base.Addon.Abbreviation .. button.type.Name .. tostring(button.type.EventTS), EVENT_COLLECTIBLE_USE_RESULT)

                    if success then
                        if base.Global.IsDebugEnabled then
                            d(string.format("RegisterForUpdate: %s", (base.Addon.Abbreviation .. button.type.Name .. countDown.Event)))
                        end
                        EVENT_MANAGER:UnregisterForUpdate(base.Addon.Abbreviation .. button.type.Name .. countDown.Event)
                        EVENT_MANAGER:RegisterForUpdate(
                            base.Addon.Abbreviation .. button.type.Name .. countDown.Event,
                            countDown.Tick,
                            function()
                                local remaining, duration = GetCollectibleCooldownAndDuration(countDown.CollectibleId)
                                local cooldown = base.GetCooldownText(countDown, duration)

                                base.UpdateButtonsCooldown(button.type, remaining, duration, cooldown)

                                if base.Global.IsDebugEnabled then
                                    d(string.format("TICK: Event: %s; Duration: %s;", (base.Addon.Abbreviation .. button.type.Name .. countDown.Event), duration))
                                end
                                if duration == 0 then
                                    countDown.StartTime = nil
                                    local isActive = successActivate or IsCollectibleActive(button.cId)
                                    base.UpdateButtonsState(button.type, button.cId, isActive)
                                    EVENT_MANAGER:UnregisterForUpdate(base.Addon.Abbreviation .. button.type.Name .. countDown.Event)

                                    if base.Global.IsDebugEnabled then
                                        d(string.format("TICK: Event: %s; Duration: %s; isActive: %s;", (base.Addon.Abbreviation .. button.type.Name .. countDown.Event), duration, tostring(isActive)))
                                    end
                                end
                            end
                        )
                    end
                end
            )
        end
        button:UpdateUsable()
        button:OnClicked()
    end
end