--[[
    The KeyboardAssignableActionBar shows and lets you manage what your current pending skill assignments will be inside the Skills UI.
    It's distinct from the "real" action bar in that only handles skills, it doesn't necessarily care about the state of each ability, and it can change
    without changing the "real" action bar.
]]--

ZO_KeyboardAssignableActionBar = ZO_InitializingObject:Subclass()

function ZO_KeyboardAssignableActionBar:Initialize(control)
    self.control = control
    self.areHotbarEditsEnabled = true

    self.hotbarSwap = ZO_KeyboardAssignableActionBarHotbarSwap:New(self.control:GetNamedChild("HotbarSwap"))
    self.buttons =
    {
       ZO_KeyboardAssignableActionBarButton:New(self, self.control:GetNamedChild("Button1"), ACTION_BAR_FIRST_NORMAL_SLOT_INDEX + 1),
       ZO_KeyboardAssignableActionBarButton:New(self, self.control:GetNamedChild("Button2"), ACTION_BAR_FIRST_NORMAL_SLOT_INDEX + 2),
       ZO_KeyboardAssignableActionBarButton:New(self, self.control:GetNamedChild("Button3"), ACTION_BAR_FIRST_NORMAL_SLOT_INDEX + 3),
       ZO_KeyboardAssignableActionBarButton:New(self, self.control:GetNamedChild("Button4"), ACTION_BAR_FIRST_NORMAL_SLOT_INDEX + 4),
       ZO_KeyboardAssignableActionBarButton:New(self, self.control:GetNamedChild("Button5"), ACTION_BAR_FIRST_NORMAL_SLOT_INDEX + 5),

       ZO_KeyboardAssignableActionBarButton:New(self, self.control:GetNamedChild("Button6"), ACTION_BAR_ULTIMATE_SLOT_INDEX + 1),
    }
    self:SetupHotbarSwapAnimationComplete()

    local function OnSlotUpdated(hotbarCategory, slotId)
        if hotbarCategory == ACTION_BAR_ASSIGNMENT_MANAGER:GetCurrentHotbarCategory() then
            local button = self.buttons[slotId - ACTION_BAR_FIRST_NORMAL_SLOT_INDEX]
            if button then
                button:Refresh()
            end
        end
    end
    ACTION_BAR_ASSIGNMENT_MANAGER:RegisterCallback("SlotUpdated", OnSlotUpdated)
    ACTION_BAR_ASSIGNMENT_MANAGER:RegisterCallback("SlotNewStatusChanged", OnSlotUpdated)

    local function OnCurrentHotbarUpdated(hotbarCategory, oldHotbarCategory)
        if not ACTION_BAR_ASSIGNMENT_MANAGER:IsHotbarSwapAnimationPlaying() then
            if not self.control:IsHidden() and oldHotbarCategory ~= hotbarCategory then
                self:PlayHotbarSwapAnimation()
            else
                self:RefreshAllButtons()
            end
        end
    end
    ACTION_BAR_ASSIGNMENT_MANAGER:RegisterCallback("CurrentHotbarUpdated", OnCurrentHotbarUpdated)

    local function OnCursorPickup(eventCode, cursorType, ...)
        if cursorType == MOUSE_CONTENT_ACTION then
            local actionType, _, actionValue = ...
            if ZO_ABILITY_DROP_CALLOUT_VALIDITY_FUNCTION_BY_ACTION_TYPE[actionType] and actionValue ~= 0 then
                self:ShowAppropriateAbilityActionButtonDropCallouts(actionType, actionValue)
                PlaySound(SOUNDS.ABILITY_PICKED_UP)
            end
        end
    end
    EVENT_MANAGER:RegisterForEvent("KeyboardAssignableActionBar", EVENT_CURSOR_PICKUP, OnCursorPickup)

    local function OnCursorDropped(eventCode, cursorType)
        if cursorType == MOUSE_CONTENT_ACTION then
            self:HideDropCallouts()
        end
    end
    EVENT_MANAGER:RegisterForEvent("KeyboardAssignableActionBar", EVENT_CURSOR_DROPPED, OnCursorDropped)

    local function OnPlayerActivated(eventCode)
        self:HideDropCallouts()
    end
    EVENT_MANAGER:RegisterForEvent("KeyboardAssignableActionBar", EVENT_PLAYER_ACTIVATED, OnPlayerActivated)
end

function ZO_KeyboardAssignableActionBar:SetHotbarEditsEnabled(areHotbarEditsEnabled)
    self.areHotbarEditsEnabled = areHotbarEditsEnabled
end

function ZO_KeyboardAssignableActionBar:AreHotbarEditsEnabled()
    return self.areHotbarEditsEnabled
end

function ZO_KeyboardAssignableActionBar:GetHotbarSwap()
    return self.hotbarSwap
end

function ZO_KeyboardAssignableActionBar:RefreshAllButtons()
    for _, button in ipairs(self.buttons) do
        button:Refresh()
    end
end

function ZO_KeyboardAssignableActionBar:HideDropCallouts()
    for _, button in ipairs(self.buttons) do
        button:HideDropCallout()
    end
end

function ZO_KeyboardAssignableActionBar:ShowAppropriateAbilityActionButtonDropCallouts(actionType, actionValue)
    local validityFunction = ZO_ABILITY_DROP_CALLOUT_VALIDITY_FUNCTION_BY_ACTION_TYPE[actionType]
    for _, button in ipairs(self.buttons) do
        local isValid = validityFunction(actionValue, button.slotId)
        button:ShowDropCallout(isValid)
    end
end

function ZO_KeyboardAssignableActionBar:PlayHotbarSwapAnimation()
    ACTION_BAR_ASSIGNMENT_MANAGER:SetIsHotbarSwapAnimationPlaying(true)
    for _, button in ipairs(self.buttons) do
        button.hotbarSwapAnimation:PlayFromStart()
    end
    self.hotbarSwap:Refresh()
end

function ZO_KeyboardAssignableActionBar:SetupHotbarSwapAnimationComplete()
    -- A hotbar is made up of N buttons and N button animations, but we only want to trigger this handler once.
    -- Since each animation ends at the same time, we can just register to the first button as a stand-in for all of the buttons.
    self.buttons[1].hotbarSwapAnimation:SetHandler("OnStop", function()
        ACTION_BAR_ASSIGNMENT_MANAGER:SetIsHotbarSwapAnimationPlaying(false)
        -- The bar state may have changed during the animation (so it was deferred), but after the midpoint (so it wouldn't be reflected during that refresh)
        -- Refresh an extra time for good measure
        self:RefreshAllButtons()
        self.hotbarSwap:Refresh()
    end)
end

local ACTION_BUTTON_BORDERS =
{
    normal = "EsoUI/Art/ActionBar/abilityFrame64_up.dds",
    mouseDown = "EsoUI/Art/ActionBar/abilityFrame64_down.dds",
    mouseOver = "EsoUI/Art/ActionBar/actionBar_mouseOver.dds",
}

ZO_KeyboardAssignableActionBarButton = ZO_InitializingObject:Subclass()

function ZO_KeyboardAssignableActionBarButton:Initialize(hotbar, control, slotId)
    self.hotbar = hotbar
    self.slotId = slotId
    self.isMousedOver = false
    self.isSlotNew = false
    self.actionName = nil
    self.actionPriority = nil

    self.icon = control:GetNamedChild("Icon")
    self.lock = control:GetNamedChild("Lock")

    self.newIndicator = control:GetNamedChild("NewIndicator")
    self.newIndicatorIdle = ANIMATION_MANAGER:CreateTimelineFromVirtual("ZO_KeyboardAssignableActionBarNewIndicator_Idle", self.newIndicator)
    self.newIndicatorFadeout = ANIMATION_MANAGER:CreateTimelineFromVirtual("ZO_KeyboardAssignableActionBarNewIndicator_Fadeout", self.newIndicator)

    self.button = control:GetNamedChild("Button")
    self.button.owner = self

    self.dropCallout = control:GetNamedChild("DropCallout")

    self.buttonText = control:GetNamedChild("ButtonText")

    self:SetupHotbarSwapAnimation(control:GetNamedChild("FlipCard"))

    self:Refresh()
end

function ZO_KeyboardAssignableActionBarButton:SetupHotbarSwapAnimation(flipCard)
    local timeline = ANIMATION_MANAGER:CreateTimelineFromVirtual("HotbarSwapAnimation", flipCard)
    local width, height = flipCard:GetDimensions()

    local firstAnimation = timeline:GetFirstAnimation()
    firstAnimation:SetStartAndEndWidth(width, width)
    firstAnimation:SetStartAndEndHeight(height, 0)
    firstAnimation:SetHandler("OnStop", function()
        -- delay refresh until the icon is invisible
        self:Refresh()
    end)

    local lastAnimation = timeline:GetLastAnimation()
    lastAnimation:SetStartAndEndWidth(width, width)
    lastAnimation:SetStartAndEndHeight(0, height)

    self.hotbarSwapAnimation = timeline
end

function ZO_KeyboardAssignableActionBarButton:Refresh()
    local hotbarData = ACTION_BAR_ASSIGNMENT_MANAGER:GetCurrentHotbar()
    local slotData = hotbarData:GetSlotData(self.slotId)
    local iconTexture = slotData:GetIcon()
    local mouseDownFrameTexture = self.hotbar:AreHotbarEditsEnabled() and ACTION_BUTTON_BORDERS.mouseDown or ACTION_BUTTON_BORDERS.normal -- do not indicate pressed if it won't do anything
    local mouseOverFrameTexture = self.hotbar:AreHotbarEditsEnabled() and ACTION_BUTTON_BORDERS.mouseOver or "" -- do not indicate mouse over if we can't actually click it
    if iconTexture then
        ZO_ActionSlot_SetupSlot(self.icon, self.button, iconTexture, ACTION_BUTTON_BORDERS.normal, mouseDownFrameTexture, nil, mouseOverFrameTexture)
    else
        ZO_ActionSlot_ClearSlot(self.icon, self.button, ACTION_BUTTON_BORDERS.normal, mouseDownFrameTexture, nil, mouseOverFrameTexture)
    end

    self.lock:SetHidden(not hotbarData:IsSlotLocked(self.slotId))
    local isNew = hotbarData:IsSlotNew(self.slotId)
    if isNew ~= self.isSlotNew then
        self.isSlotNew = isNew
        if isNew then
            self.newIndicatorFadeout:Stop()
            self.newIndicatorIdle:PlayFromStart()
        else
            self.newIndicatorIdle:Stop()
            self.newIndicatorFadeout:PlayFromStart()
        end
    end

    local DONT_DESATURATE = false
    ZO_ActionSlot_SetUnusable(self.icon, not slotData:IsUsable(), DONT_DESATURATE)

    local KEYBOARD_MODE = false
    local actionName = ACTION_BAR_ASSIGNMENT_MANAGER:GetActionNameForSlot(self.slotId, hotbarData:GetHotbarCategory(), KEYBOARD_MODE)
    local actionPriority = ACTION_BAR_ASSIGNMENT_MANAGER:GetAutomaticCastPriorityForSlot(self.slotId, hotbarData:GetHotbarCategory())
    if actionName ~= self.actionName or actionPriority ~= self.actionPriority then
        ZO_Keybindings_UnregisterLabelForBindingUpdate(self.buttonText)
        if actionName then
            local HIDE_UNBOUND = false
            ZO_Keybindings_RegisterLabelForBindingUpdate(self.buttonText, actionName, HIDE_UNBOUND)
        elseif actionPriority then
            self.buttonText:SetText(tostring(actionPriority))
        else
            self.buttonText:SetText("")
        end
        self.actionName = actionName
        self.actionPriority = actionPriority
    end

    if self.isMousedOver then
        self:ShowTooltip()
    end
end

function ZO_KeyboardAssignableActionBarButton:ClearNewIndicator()
    ACTION_BAR_ASSIGNMENT_MANAGER:GetCurrentHotbar():ClearSlotNew(self.slotId)
end

function ZO_KeyboardAssignableActionBarButton:ShowTooltip()
    self:HideTooltip() -- Hide the old tooltip unconditionally, it may be a different tooltip control

    local hotbarData = ACTION_BAR_ASSIGNMENT_MANAGER:GetCurrentHotbar()
    if hotbarData:IsSlotLocked(self.slotId) then
        InitializeTooltip(InformationTooltip, self.button, BOTTOM, 0, -5, TOP)
        local DEFAULT_FONT = ""
        InformationTooltip:AddLine(GetString(SI_ACTION_BAR_SLOT_LOCKED_HEADER), "ZoFontHeader", ZO_SELECTED_TEXT:UnpackRGBA())
        ZO_Tooltip_AddDivider(InformationTooltip)
        InformationTooltip:AddLine(hotbarData:GetSlotUnlockText(self.slotId), DEFAULT_FONT, ZO_ERROR_COLOR:UnpackRGBA())
        self.lastTooltip = InformationTooltip
    else
        local slotData = hotbarData:GetSlotData(self.slotId)
        local tooltip = slotData:GetKeyboardTooltipControl()
        if tooltip then
            InitializeTooltip(tooltip, self.button, BOTTOM, 0, -5, TOP)
            slotData:SetKeyboardTooltip(tooltip)
            self.lastTooltip = tooltip
        end
    end
end

function ZO_KeyboardAssignableActionBarButton:HideTooltip()
    if self.lastTooltip then
        ClearTooltip(self.lastTooltip)
        self.lastTooltip = nil
    end
end

function ZO_KeyboardAssignableActionBarButton:ShowDropCallout(isValid)
    if not isValid then
        self.dropCallout:SetColor(1, 0, 0, 1)
    else
        self.dropCallout:SetColor(1, 1, 1, 1)
    end

    self.dropCallout:SetHidden(false)
end

function ZO_KeyboardAssignableActionBarButton:HideDropCallout()
    self.dropCallout:SetHidden(true)
end

function ZO_KeyboardAssignableActionBarButton:TryCursorPickup()
    if self.hotbar:AreHotbarEditsEnabled() then
        if GetCursorContentType() == MOUSE_CONTENT_EMPTY then
            local hotbarData = ACTION_BAR_ASSIGNMENT_MANAGER:GetCurrentHotbar()
            local slotData = hotbarData:GetSlotData(self.slotId)
            if slotData then
                slotData:TryCursorPickup()
                hotbarData:ClearSlot(self.slotId)
            end
        end
    end
end

function ZO_KeyboardAssignableActionBarButton:TryCursorPlace()
    if not self.hotbar:AreHotbarEditsEnabled() then
        return
    end

    if GetCursorContentType() == MOUSE_CONTENT_EMPTY then
        return
    end

    local hotbarData = ACTION_BAR_ASSIGNMENT_MANAGER:GetCurrentHotbar()
    local oldSlotData = hotbarData:GetSlotData(self.slotId)
    if oldSlotData == nil then
        -- invalid slot
        return
    end

    local abilityId = GetCursorAbilityId()
    if abilityId == nil then
        return
    end

    if hotbarData:AssignSkillToSlotByAbilityId(self.slotId, abilityId) then
        ClearCursor()
        oldSlotData:TryCursorPickup()
        PlaySound(SOUNDS.ABILITY_SLOTTED)
    end
end

function ZO_KeyboardAssignableActionBarButton:ShowActionMenu()
    if not self.hotbar:AreHotbarEditsEnabled() then
        return
    end 
    local hotbarData = ACTION_BAR_ASSIGNMENT_MANAGER:GetCurrentHotbar()
    local slotData = hotbarData:GetSlotData(self.slotId)

    if slotData and not slotData:IsEmpty() and not IsActionSlotRestricted(self.slotId, hotbarData:GetHotbarCategory()) then
        ClearMenu()
        AddMenuItem(GetString(SI_ABILITY_ACTION_CLEAR_SLOT), function()
            if hotbarData:ClearSlot(self.slotId) then
                PlaySound(SOUNDS.ABILITY_SLOT_CLEARED)
            end
        end)
        ShowMenu(self.button)
    end
end

ZO_KeyboardAssignableActionBarHotbarSwap = ZO_InitializingObject:Subclass()

function ZO_KeyboardAssignableActionBarHotbarSwap:Initialize(control)
    self.control = control
    control.owner = self

    self.hotbarNameLabel = control:GetNamedChild("HotbarName")

    self:RegisterForEvents()

    self:Refresh()
end

function ZO_KeyboardAssignableActionBarHotbarSwap:GetControl()
    return self.control
end

function ZO_KeyboardAssignableActionBarHotbarSwap:GetHotbarNameLabel()
    return self.hotbarNameLabel
end

function ZO_KeyboardAssignableActionBarHotbarSwap:RegisterForEvents()
    local function OnCurrentHotbarUpdated(hotbarCategory)
        self:Refresh()
    end
    ACTION_BAR_ASSIGNMENT_MANAGER:RegisterCallback("CurrentHotbarUpdated", OnCurrentHotbarUpdated)

    local function OnHotbarSwapVisibleStateChanged()
        self:Refresh()
    end
    ACTION_BAR_ASSIGNMENT_MANAGER:RegisterCallback("HotbarSwapVisibleStateChanged", OnHotbarSwapVisibleStateChanged)
end

function ZO_KeyboardAssignableActionBarHotbarSwap:Refresh()
    self.enabled = ACTION_BAR_ASSIGNMENT_MANAGER:CanCycleHotbars()

    local hotbarCategory = ACTION_BAR_ASSIGNMENT_MANAGER:GetCurrentHotbarCategory()
    self.activeWeaponPair = GetWeaponPairFromHotbarCategory(hotbarCategory)

    if not ACTION_BAR_ASSIGNMENT_MANAGER:ShouldShowHotbarSwap() then
        self.control:SetHidden(true)
        return
    end

    self.control:SetHidden(false)
    self.control:SetEnabled(self.enabled)
    if self.activeWeaponPair ~= ACTIVE_WEAPON_PAIR_NONE then
        self.control:SetText(zo_strformat(SI_ACTIVE_WEAPON_PAIR, self.activeWeaponPair))
    else
        self.control:SetText("")
    end

    --We don't show any text for the primary and backup bars
    if self.activeWeaponPair == ACTIVE_WEAPON_PAIR_NONE then
        local hotbarName = ACTION_BAR_ASSIGNMENT_MANAGER:GetCurrentHotbarName()
        self.hotbarNameLabel:SetHidden(false)
        self.hotbarNameLabel:SetText(hotbarName)
    else
        self.hotbarNameLabel:SetHidden(true)
        --Set to empty string for the pointer box tutorial that anchors to this
        self.hotbarNameLabel:SetText("")
    end
end

function ZO_KeyboardAssignableActionBarHotbarSwap:SwapBars()
    ACTION_BAR_ASSIGNMENT_MANAGER:CycleCurrentHotbar()
end

function ZO_KeyboardAssignableActionBarHotbarSwap:ShowTooltip()
    InitializeTooltip(InformationTooltip, self.control, RIGHT, -5, 0)
    InformationTooltip:AddLine(GetString(SI_WEAPON_SWAP_TOOLTIP), "", ZO_TOOLTIP_DEFAULT_COLOR:UnpackRGB())

    if not self.enabled then
        InformationTooltip:AddLine(GetString(SI_WEAPON_SWAP_DISABLED_TOOLTIP), "", ZO_ERROR_COLOR:UnpackRGB())
    end
end

function ZO_KeyboardAssignableActionBarHotbarSwap:HideTooltip()
    ClearTooltip(InformationTooltip)
end

-- Button XML
function ZO_KeyboardAssignableActionBarButton_OnMouseEnter(control)
    control.owner.isMousedOver = true
    control.owner:ShowTooltip()
end

function ZO_KeyboardAssignableActionBarButton_OnMouseExit(control)
    control.owner.isMousedOver = false
    control.owner:HideTooltip()
    control.owner:ClearNewIndicator()
end

function ZO_KeyboardAssignableActionBarButton_OnMouseClicked(control, buttonId)
    if buttonId == MOUSE_BUTTON_INDEX_LEFT then
        control.owner:TryCursorPlace()
    elseif buttonId == MOUSE_BUTTON_INDEX_RIGHT then
        control.owner:ShowActionMenu()
    end
end

function ZO_KeyboardAssignableActionBarButton_OnDragStart(control)
    control.owner:TryCursorPickup()
end

function ZO_KeyboardAssignableActionBarButton_OnReceiveDrag(control)
    control.owner:TryCursorPlace()
end

-- Weapon Swap XML
function ZO_KeyboardAssignableActionBarHotbarSwap_OnMouseEnter(control)
    control.owner:ShowTooltip()
end

function ZO_KeyboardAssignableActionBarHotbarSwap_OnMouseExit(control)
    control.owner:HideTooltip()
end

function ZO_KeyboardAssignableActionBarHotbarSwap_OnClicked(control)
    control.owner:SwapBars()
end