local AddOnName, XIVBar = ...;
local _G = _G;
local xb = XIVBar;
local L = XIVBar.L;

local C_ClassTalents = C_ClassTalents;
local C_Traits = C_Traits;
local TalentModule = xb:NewModule("TalentModule", 'AceEvent-3.0')
local GetSpecializationInfo = GetSpecializationInfo;
local GetSpecialization = GetSpecialization;

function TalentModule:GetName()
    return TALENTS;
end

function TalentModule:dump(o)
    if type(o) == 'table' then
        local s = '{ '
        for k, v in pairs(o) do
            if type(k) ~= 'number' then
                k = '"' .. k .. '"'
            end
            s = s .. '[' .. k .. '] = ' .. self:dump(v) .. ','
        end
        return s .. '} '
    else
        return tostring(o)
    end
end

-- Skin Support for ElvUI/TukUI
-- Make sure to disable "Tooltip" in the Skins section of ElvUI together with
-- unchecking "Use ElvUI for tooltips" in XIV options to not have ElvUI fuck with tooltips
function TalentModule:SkinFrame(frame, name)
    if self.useElvUI then
        if frame.StripTextures then
            frame:StripTextures()
        end
        if frame.SetTemplate then
            frame:SetTemplate("Transparent")
        end

        local close = _G[name .. "CloseButton"] or frame.CloseButton
        if close and close.SetAlpha then
            if ElvUI then
                ElvUI[1]:GetModule('Skins'):HandleCloseButton(close)
            end

            if Tukui and Tukui[1] and Tukui[1].SkinCloseButton then
                Tukui[1].SkinCloseButton(close)
            end
            close:SetAlpha(1)
        end
    end
end

function TalentModule:OnInitialize()
    self.LTip = LibStub('LibQTip-1.0')
    self.currentSpecID = 0
    self.currentLootSpecID = 0
    self.loadoutName = ''
    self.specCoords = {
        [1] = {0.00, 0.25, 0, 1},
        [2] = {0.25, 0.50, 0, 1},
        [3] = {0.50, 0.75, 0, 1},
        [4] = {0.75, 1.00, 0, 1}
    }
    self.extraPadding = (xb.constants.popupPadding * 3)
    self.optionTextExtra = 4
    self.loadoutButtons = {}
    self.specButtons = {}
    self.lootSpecButtons = {}
    self.classIcon = xb.constants.mediaPath .. 'spec\\' .. xb.constants.playerClass
    self.useElvUI = xb.db.profile.general.useElvUI and (IsAddOnLoaded('ElvUI') or IsAddOnLoaded('Tukui'))
end

function TalentModule:OnEnable()
    if not xb.db.profile.modules.talent.enabled then
        self:Disable()
        return
    end
    self.currentSpecID = GetSpecialization() -- returns 5 for newly created characters in shadowlands
    if self.currentSpecID == 5 then
        self:Disable()
        return
    end
    self.currentLootSpecID = GetLootSpecialization()
    if self.talentFrame == nil then
        self.talentFrame = CreateFrame("FRAME", "talentFrame", xb:GetFrame('bar'))
        xb:RegisterFrame('talentFrame', self.talentFrame)
    end
    self.talentFrame:Show()

    if (xb.db.profile.modules.talent.loadoutSwitcherEnabled) then
        self.loadoutName = self:GetCurrentLoadoutName()
        self:CreateLoadoutFrames()
    end
    self:CreateTalentFrames()
    self:RegisterFrameEvents()
    self:Refresh()
end

function TalentModule:OnDisable()
    if self.talentFrame and self.talentFrame:IsVisible() then
        self.talentFrame:Hide()
    end
    self:UnregisterEvent('TRADE_SKILL_UPDATE')
    self:UnregisterEvent('SPELLS_CHANGED')
    self:UnregisterEvent('UNIT_SPELLCAST_STOP')
    self:UnregisterEvent('PLAYER_SPECIALIZATION_CHANGED')
    self:UnregisterEvent('ACTIVE_TALENT_GROUP_CHANGED')
    self:UnregisterEvent('PLAYER_LOOT_SPEC_UPDATED')
end

function TalentModule:EnableTalentLoadout()
    self:CreateLoadoutFrames()
    self:Refresh()
    self.loadoutFrame:Show()
end

function TalentModule:DisableTalentLoadout()
    if self.loadoutFrame and self.loadoutFrame:IsVisible() then
        self.loadoutFrame:Hide()
    end
end

function TalentModule:GetCurrentLoadoutName()
    local curSpecID, name, _ = GetSpecializationInfo(self.currentSpecID)

    local configs = C_ClassTalents.GetConfigIDsBySpecID(curSpecID);
    local total = #configs;
    local loadoutName;

    if total == 0 then
        self.loadoutName = TALENT_FRAME_DROP_DOWN_DEFAULT;
    else
        local selectedID = C_ClassTalents.GetLastSelectedSavedConfigID(curSpecID);
        if selectedID then
            local info = C_Traits.GetConfigInfo(selectedID);
            loadoutName = info and info.name;
        end

        if loadoutName then
            self.loadoutName = loadoutName
        end
    end
end

function TalentModule:Refresh()
    if InCombatLockdown() then
        return
    end

    local db = xb.db.profile
    if self.talentFrame == nil then
        return
    end
    if not db.modules.talent.enabled then
        self:Disable()
        return
    end

    self.currentSpecID = GetSpecialization()
    self.currentLootSpecID = GetLootSpecialization()

    local iconSize = db.text.fontSize + db.general.barPadding
    local curSpecID, name, _ = GetSpecializationInfo(self.currentSpecID)
    local textHeight = db.text.fontSize

    if (xb.db.profile.modules.talent.loadoutSwitcherEnabled) then
        self:GetCurrentLoadoutName()

        -- LOADOUT
        self.loadoutText:SetFont(xb:GetFont(textHeight))
        self.loadoutText:SetTextColor(xb:GetColor('normal'))
        self.loadoutText:SetText(self.loadoutName or "")

        self.loadoutText:SetPoint('LEFT')

        self.loadoutText:Show()

        self.loadoutFrame:SetSize(iconSize + self.loadoutText:GetWidth() + 5, xb:GetHeight())
        self.loadoutFrame:SetPoint('LEFT')

        if self.loadoutFrame:GetWidth() < db.modules.talent.minWidth then
            self.loadoutFrame:SetWidth(db.modules.talent.minWidth)
        end

        self.loadoutFrame:SetSize(self.loadoutFrame:GetWidth(), xb:GetHeight())
    end

    -- TALENTS
    self.specIcon:SetTexture(self.classIcon)
    self.specIcon:SetTexCoord(unpack(self.specCoords[self.currentSpecID]))

    self.specIcon:SetSize(iconSize, iconSize)
    self.specIcon:SetPoint('LEFT')
    self.specIcon:SetVertexColor(xb:GetColor('normal'))

    self.specText:SetFont(xb:GetFont(textHeight))
    self.specText:SetTextColor(xb:GetColor('normal'))
    self.specText:SetText(string.upper(name or ""))

    self.specText:SetPoint('LEFT', self.specIcon, 'RIGHT', 5, 0)

    self.lootSpecButtons[0].icon:SetTexture(self.classIcon)
    self.lootSpecButtons[0].icon:SetTexCoord(unpack(self.specCoords[self.currentSpecID]))

    self.specText:Show()

    self.specFrame:SetSize(iconSize + self.specText:GetWidth() + 5, xb:GetHeight())
    if (xb.db.profile.modules.talent.loadoutSwitcherEnabled) then
        self.specFrame:SetPoint('LEFT', self.loadoutFrame, 'RIGHT', 0, 0)
    else
        self.specFrame:SetPoint('LEFT')
    end

    if self.specFrame:GetWidth() < db.modules.talent.minWidth then
        self.specFrame:SetWidth(db.modules.talent.minWidth)
    end

    if (xb.db.profile.modules.talent.loadoutSwitcherEnabled) then
        self.talentFrame:SetSize(self.loadoutFrame:GetWidth() + self.specFrame:GetWidth(), xb:GetHeight())
    else
        self.talentFrame:SetSize(self.specFrame:GetWidth(), xb:GetHeight())
    end

    local relativeAnchorPoint = 'LEFT'
    local xOffset = db.general.moduleSpacing
    local anchorFrame = xb:GetFrame('clockFrame')
    if not anchorFrame:IsVisible() and not db.modules.clock.enabled then
        if xb:GetFrame('tradeskillFrame'):IsVisible() then
            anchorFrame = xb:GetFrame('tradeskillFrame')
        elseif xb:GetFrame('currencyFrame'):IsVisible() then
            anchorFrame = xb:GetFrame('currencyFrame')
        else
            relativeAnchorPoint = 'RIGHT'
            xOffset = 0
        end
    end
    self.talentFrame:SetPoint('RIGHT', anchorFrame, relativeAnchorPoint, -(xOffset), 0)

    self:CreateLoadoutPopup()
    self:CreateSpecPopup()
    self:CreateLootSpecPopup()
end

function TalentModule:CreateLoadoutFrames()
    self.loadoutFrame = self.loadoutFrame or CreateFrame('BUTTON', nil, self.talentFrame, 'SecureActionButtonTemplate')
    self.loadoutIcon = self.loadoutIcon or self.loadoutFrame:CreateTexture(nil, 'OVERLAY')
    self.loadoutText = self.loadoutText or self.loadoutFrame:CreateFontString(nil, 'OVERLAY')

    local template = (TooltipBackdropTemplateMixin and "TooltipBackdropTemplate") or
                         (BackdropTemplateMixin and "BackdropTemplate")
    self.loadoutPopup = self.loadoutPopup or CreateFrame('BUTTON', 'loadoutPopup', self.loadoutFrame, template)
    self.loadoutPopup:SetFrameStrata('TOOLTIP')

    if TooltipBackdropTemplateMixin then
        self.loadoutPopup.layoutType = GameTooltip.layoutType
        NineSlicePanelMixin.OnLoad(self.loadoutPopup.NineSlice)

        if GameTooltip.layoutType then
            self.loadoutPopup.NineSlice:SetCenterColor(GameTooltip.NineSlice:GetCenterColor())
            self.loadoutPopup.NineSlice:SetBorderColor(GameTooltip.NineSlice:GetBorderColor())
        end
    else
        local backdrop = GameTooltip:GetBackdrop()
        if backdrop and (not self.useElvUI) then
            self.loadoutPopup:SetBackdrop(backdrop)
            self.loadoutPopup:SetBackdropColor(GameTooltip:GetBackdropColor())
            self.loadoutPopup:SetBackdropBorderColor(GameTooltip:GetBackdropBorderColor())
        end
    end
end

function TalentModule:CreateTalentFrames()
    self.specFrame = self.specFrame or CreateFrame('BUTTON', nil, self.talentFrame, 'SecureActionButtonTemplate')
    self.specIcon = self.specIcon or self.specFrame:CreateTexture(nil, 'OVERLAY')
    self.specText = self.specText or self.specFrame:CreateFontString(nil, 'OVERLAY')

    local template = (TooltipBackdropTemplateMixin and "TooltipBackdropTemplate") or
                         (BackdropTemplateMixin and "BackdropTemplate")
    self.specPopup = self.specPopup or CreateFrame('BUTTON', 'SpecPopup', self.specFrame, template)
    self.specPopup:SetFrameStrata('TOOLTIP')
    self.lootSpecPopup = self.lootSpecPopup or CreateFrame('BUTTON', 'LootPopup', self.specFrame, template)
    self.lootSpecPopup:SetFrameStrata('TOOLTIP')

    if TooltipBackdropTemplateMixin then
        self.specPopup.layoutType = GameTooltip.layoutType
        NineSlicePanelMixin.OnLoad(self.specPopup.NineSlice)

        self.lootSpecPopup.layoutType = GameTooltip.layoutType
        NineSlicePanelMixin.OnLoad(self.lootSpecPopup.NineSlice)

        if GameTooltip.layoutType then
            self.specPopup.NineSlice:SetCenterColor(GameTooltip.NineSlice:GetCenterColor())
            self.specPopup.NineSlice:SetBorderColor(GameTooltip.NineSlice:GetBorderColor())

            self.lootSpecPopup.NineSlice:SetCenterColor(GameTooltip.NineSlice:GetCenterColor())
            self.lootSpecPopup.NineSlice:SetBorderColor(GameTooltip.NineSlice:GetBorderColor())
        end
    else
        local backdrop = GameTooltip:GetBackdrop()
        if backdrop and (not self.useElvUI) then
            self.specPopup:SetBackdrop(backdrop)
            self.specPopup:SetBackdropColor(GameTooltip:GetBackdropColor())
            self.specPopup:SetBackdropBorderColor(GameTooltip:GetBackdropBorderColor())
            self.lootSpecPopup:SetBackdrop(backdrop)
            self.lootSpecPopup:SetBackdropColor(GameTooltip:GetBackdropColor())
            self.lootSpecPopup:SetBackdropBorderColor(GameTooltip:GetBackdropBorderColor())
        end
    end

    self:CreateSpecPopup()
    self:CreateLootSpecPopup()
end

function TalentModule:RegisterFrameEvents()
    self:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED', 'Refresh')
    self:RegisterEvent('ACTIVE_TALENT_GROUP_CHANGED', 'Refresh')
    self:RegisterEvent('PLAYER_LOOT_SPEC_UPDATED', 'Refresh')
    self:RegisterEvent('TRAIT_CONFIG_UPDATED', 'Refresh');
    self:RegisterEvent('TRAIT_CONFIG_LIST_UPDATED', 'Refresh');
    self:RegisterEvent('TRAIT_CONFIG_DELETED', 'Refresh');
    self:RegisterEvent('TRAIT_CONFIG_CREATED', 'Refresh');
    self:RegisterEvent('CONFIG_COMMIT_FAILED', 'Refresh');
    self:RegisterEvent('ACTIVE_COMBAT_CONFIG_CHANGED', 'Refresh');

    self.specFrame:EnableMouse(true)
    self.specFrame:RegisterForClicks('AnyUp')

    if (xb.db.profile.modules.talent.loadoutSwitcherEnabled) then
        self.loadoutFrame:EnableMouse(true)
        self.loadoutFrame:RegisterForClicks('AnyUp')

        -- LOADOUTS
        self.loadoutFrame:SetScript('OnEnter', function()
            if InCombatLockdown() then
                return
            end
            self.loadoutText:SetTextColor(unpack(xb:HoverColors()))
            if xb.db.profile.modules.talent.showTooltip then
                if (not self.loadoutPopup:IsVisible()) then
                    self:ShowTooltip()
                end
            end
        end)

        self.loadoutFrame:SetScript('OnLeave', function()
            if InCombatLockdown() then
                return
            end
            local db = xb.db.profile
            self.loadoutText:SetTextColor(xb:GetColor('normal'))
            if xb.db.profile.modules.talent.showTooltip then
                if self.LTip:IsAcquired("TalentTooltip") then
                    self.LTip:Release(self.LTip:Acquire("TalentTooltip"))
                end
            end
        end)

        self.loadoutFrame:SetScript('OnClick', function(_, button)
            if self.LTip:IsAcquired("TalentTooltip") then
                self.LTip:Release(self.LTip:Acquire("TalentTooltip"))
            end

            if InCombatLockdown() then
                return
            end

            if button == 'LeftButton' then
                if not self.loadoutPopup:IsVisible() then
                    self:CreateLoadoutPopup()
                    self.loadoutPopup:Show()
                    self.specPopup:Hide()
                    self.lootSpecPopup:Hide()
                else
                    self.loadoutPopup:Hide()
                    if xb.db.profile.modules.talent.showTooltip then
                        self:ShowTooltip()
                    end
                end
            end
        end)
    end

    -- TALENTS
    self.specFrame:SetScript('OnEnter', function()
        if InCombatLockdown() then
            return
        end
        self.specText:SetTextColor(unpack(xb:HoverColors()))
        if xb.db.profile.modules.talent.showTooltip then
            if (not self.specPopup:IsVisible()) or (not self.lootSpecPopup:IsVisible()) then
                self:ShowTooltip()
            end
        end
    end)

    self.specFrame:SetScript('OnLeave', function()
        if InCombatLockdown() then
            return
        end
        local db = xb.db.profile
        self.specText:SetTextColor(xb:GetColor('normal'))
        if xb.db.profile.modules.talent.showTooltip then
            if self.LTip:IsAcquired("TalentTooltip") then
                self.LTip:Release(self.LTip:Acquire("TalentTooltip"))
            end
        end
    end)

    self.specFrame:SetScript('OnClick', function(_, button)
        if self.LTip:IsAcquired("TalentTooltip") then
            self.LTip:Release(self.LTip:Acquire("TalentTooltip"))
        end

        if InCombatLockdown() then
            return
        end

        if button == 'LeftButton' then
            if not self.specPopup:IsVisible() then
                self.lootSpecPopup:Hide()
                self:CreateSpecPopup()
                self.specPopup:Show()
                if (xb.db.profile.modules.talent.loadoutSwitcherEnabled) then
                    self.loadoutPopup:Hide()
                end
            else
                self.specPopup:Hide()
                if xb.db.profile.modules.talent.showTooltip then
                    self:ShowTooltip()
                end
            end
        elseif button == 'RightButton' then
            if not self.lootSpecPopup:IsVisible() then
                self.specPopup:Hide()
                if (xb.db.profile.modules.talent.loadoutSwitcherEnabled) then
                    self.loadoutPopup:Hide()
                end
                self:CreateLootSpecPopup()
                self.lootSpecPopup:Show()
            else
                self.lootSpecPopup:Hide()
                if xb.db.profile.modules.talent.showTooltip then
                    self:ShowTooltip()
                end
            end
        end
    end)

    self:RegisterMessage('XIVBar_FrameHide', function(_, name)
        if name == 'clockFrame' then
            self:Refresh()
        end
    end)

    self:RegisterMessage('XIVBar_FrameShow', function(_, name)
        if name == 'clockFrame' then
            self:Refresh()
        end
    end)
end

function TalentModule:CreateLoadoutPopup()
    if not self.loadoutPopup then
        return;
    end

    local curSpecID, name, _ = GetSpecializationInfo(self.currentSpecID)

    local configs = C_ClassTalents.GetConfigIDsBySpecID(curSpecID);
    local total = #configs;
    local loadoutName;

    local db = xb.db.profile
    local iconSize = db.text.fontSize + db.general.barPadding
    self.loadoutOptionString = self.loadoutOptionString or self.loadoutPopup:CreateFontString(nil, 'OVERLAY')
    self.loadoutOptionString:SetFont(xb:GetFont(db.text.fontSize + self.optionTextExtra))
    local r, g, b, _ = unpack(xb:HoverColors())
    self.loadoutOptionString:SetTextColor(r, g, b, 1)
    self.loadoutOptionString:SetText(L['Set Loadout'])
    self.loadoutOptionString:SetPoint('TOP', 0, -(xb.constants.popupPadding))
    self.loadoutOptionString:SetPoint('CENTER')

    local popupWidth = self.loadoutPopup:GetWidth()
    local popupHeight = xb.constants.popupPadding + db.text.fontSize + self.optionTextExtra
    local changedWidth = false
    for i = 1, total do
        if self.loadoutButtons[i] == nil then
            local configInfo = C_Traits.GetConfigInfo(configs[i])
            local loadoutID = configInfo.ID
            local loadoutName = configInfo.name

            local button = CreateFrame('BUTTON', nil, self.loadoutPopup)
            local buttonText = button:CreateFontString(nil, 'OVERLAY')

            buttonText:SetFont(xb:GetFont(db.text.fontSize))
            buttonText:SetTextColor(xb:GetColor('normal'))
            buttonText:SetText(loadoutName)
            buttonText:SetPoint('LEFT')
            local textWidth = iconSize + 5 + buttonText:GetStringWidth()

            button:SetID(loadoutID)
            button:SetSize(textWidth, iconSize)
            button.isSettable = true

            button:EnableMouse(true)
            button:RegisterForClicks('AnyUp')

            button:SetScript('OnEnter', function()
                buttonText:SetTextColor(r, g, b, 1)
            end)

            button:SetScript('OnLeave', function()
                buttonText:SetTextColor(xb:GetColor('normal'))
            end)

            button:SetScript('OnClick', function(self, button)
                if InCombatLockdown() then
                    return;
                end
                if button == 'LeftButton' then
                    local autoApply = true;
                    local result = C_ClassTalents.LoadConfig(self:GetID(), autoApply)
                    if result ~= 0 then
                        C_ClassTalents.UpdateLastSelectedSavedConfigID(curSpecID, self:GetID());
                    end
                end
                TalentModule.loadoutPopup:Hide()
            end)

            self.loadoutButtons[i] = button

            if textWidth > popupWidth then
                popupWidth = textWidth
                changedWidth = true
            end
        end -- if nil
    end -- for ipairs portOptions
    for portId, button in pairs(self.loadoutButtons) do
        if button.isSettable then
            button:SetPoint('LEFT', xb.constants.popupPadding, 0)
            button:SetPoint('TOP', 0, -(popupHeight + xb.constants.popupPadding))
            button:SetPoint('RIGHT')
            popupHeight = popupHeight + xb.constants.popupPadding + db.text.fontSize
        else
            button:Hide()
        end
    end -- for id/button in portButtons
    if changedWidth then
        popupWidth = popupWidth + self.extraPadding
    end

    if popupWidth < self.loadoutFrame:GetWidth() then
        popupWidth = self.loadoutFrame:GetWidth()
    end

    if popupWidth < (self.loadoutOptionString:GetStringWidth() + self.extraPadding) then
        popupWidth = (self.loadoutOptionString:GetStringWidth() + self.extraPadding)
    end
    self.loadoutPopup:SetSize(popupWidth, popupHeight + xb.constants.popupPadding)

    local popupPadding = xb.constants.popupPadding
    if db.general.barPosition == 'TOP' then
        popupPadding = -(popupPadding)
    end

    self.loadoutPopup:ClearAllPoints()
    self.loadoutPopup:SetPoint(db.general.barPosition, self.loadoutFrame, xb.miniTextPosition, 0, 0)
    self:SkinFrame(self.loadoutPopup, "SpecToolTip")
    self.loadoutPopup:Hide()
end

function TalentModule:CreateSpecPopup()
    if not self.specPopup then
        return;
    end

    local db = xb.db.profile
    local iconSize = db.text.fontSize + db.general.barPadding
    self.specOptionString = self.specOptionString or self.specPopup:CreateFontString(nil, 'OVERLAY')
    self.specOptionString:SetFont(xb:GetFont(db.text.fontSize + self.optionTextExtra))
    local r, g, b, _ = unpack(xb:HoverColors())
    self.specOptionString:SetTextColor(r, g, b, 1)
    self.specOptionString:SetText(L['Set Specialization'])
    self.specOptionString:SetPoint('TOP', 0, -(xb.constants.popupPadding))
    self.specOptionString:SetPoint('CENTER')

    local popupWidth = self.specPopup:GetWidth()
    local popupHeight = xb.constants.popupPadding + db.text.fontSize + self.optionTextExtra
    local changedWidth = false
    for i = 1, GetNumSpecializations() do
        if self.specButtons[i] == nil then

            local _, name, _ = GetSpecializationInfo(i)
            local button = CreateFrame('BUTTON', nil, self.specPopup)
            local buttonText = button:CreateFontString(nil, 'OVERLAY')
            local buttonIcon = button:CreateTexture(nil, 'OVERLAY')

            buttonIcon:SetTexture(self.classIcon)
            buttonIcon:SetTexCoord(unpack(self.specCoords[i]))
            buttonIcon:SetSize(iconSize, iconSize)
            buttonIcon:SetPoint('LEFT')
            buttonIcon:SetVertexColor(xb:GetColor('normal'))

            buttonText:SetFont(xb:GetFont(db.text.fontSize))
            buttonText:SetTextColor(xb:GetColor('normal'))
            buttonText:SetText(name)
            buttonText:SetPoint('LEFT', buttonIcon, 'RIGHT', 5, 0)
            local textWidth = iconSize + 5 + buttonText:GetStringWidth()

            button:SetID(i)
            button:SetSize(textWidth, iconSize)
            button.isSettable = true

            button:EnableMouse(true)
            button:RegisterForClicks('AnyUp')

            button:SetScript('OnEnter', function()
                buttonText:SetTextColor(r, g, b, 1)
            end)

            button:SetScript('OnLeave', function()
                buttonText:SetTextColor(xb:GetColor('normal'))
            end)

            button:SetScript('OnClick', function(self, button)
                if InCombatLockdown() then
                    return;
                end
                if button == 'LeftButton' then
                    SetSpecialization(self:GetID())
                end
                TalentModule.specPopup:Hide()
            end)

            self.specButtons[i] = button

            if textWidth > popupWidth then
                popupWidth = textWidth
                changedWidth = true
            end
        end -- if nil
    end -- for ipairs portOptions
    for portId, button in pairs(self.specButtons) do
        if button.isSettable then
            button:SetPoint('LEFT', xb.constants.popupPadding, 0)
            button:SetPoint('TOP', 0, -(popupHeight + xb.constants.popupPadding))
            button:SetPoint('RIGHT')
            popupHeight = popupHeight + xb.constants.popupPadding + db.text.fontSize
        else
            button:Hide()
        end
    end -- for id/button in portButtons
    if changedWidth then
        popupWidth = popupWidth + self.extraPadding
    end

    if popupWidth < self.specFrame:GetWidth() then
        popupWidth = self.specFrame:GetWidth()
    end

    if popupWidth < (self.specOptionString:GetStringWidth() + self.extraPadding) then
        popupWidth = (self.specOptionString:GetStringWidth() + self.extraPadding)
    end
    self.specPopup:SetSize(popupWidth, popupHeight + xb.constants.popupPadding)

    local popupPadding = xb.constants.popupPadding
    if db.general.barPosition == 'TOP' then
        popupPadding = -(popupPadding)
    end

    self.specPopup:ClearAllPoints()
    self.specPopup:SetPoint(db.general.barPosition, self.specFrame, xb.miniTextPosition, 0, 0)
    self:SkinFrame(self.specPopup, "SpecToolTip")
    self.specPopup:Hide()
end

function TalentModule:CreateLootSpecPopup()
    if not self.lootSpecPopup then
        return;
    end

    local db = xb.db.profile
    local iconSize = db.text.fontSize + db.general.barPadding
    self.lootSpecOptionString = self.lootSpecOptionString or self.lootSpecPopup:CreateFontString(nil, 'OVERLAY')
    self.lootSpecOptionString:SetFont(xb:GetFont(db.text.fontSize + self.optionTextExtra))
    local r, g, b, _ = unpack(xb:HoverColors())
    self.lootSpecOptionString:SetTextColor(r, g, b, 1)
    self.lootSpecOptionString:SetText(L['Set Loot Specialization'])
    self.lootSpecOptionString:SetPoint('TOP', 0, -(xb.constants.popupPadding))
    self.lootSpecOptionString:SetPoint('CENTER')

    local popupWidth = self.lootSpecPopup:GetWidth()
    local popupHeight = xb.constants.popupPadding + db.text.fontSize + self.optionTextExtra
    local changedWidth = false
    for i = 0, GetNumSpecializations() do
        if self.lootSpecButtons[i] == nil then
            local specId = i
            local name = ''
            if i == 0 then
                name = L['Current Specialization'];
                specId = self.currentSpecID
            else
                _, name, _ = GetSpecializationInfo(i)
            end
            local button = CreateFrame('BUTTON', nil, self.lootSpecPopup)
            local buttonText = button:CreateFontString(nil, 'OVERLAY')
            local buttonIcon = button:CreateTexture(nil, 'OVERLAY')

            buttonIcon:SetTexture(self.classIcon)
            buttonIcon:SetTexCoord(unpack(self.specCoords[specId]))
            buttonIcon:SetSize(iconSize, iconSize)
            buttonIcon:SetPoint('LEFT')
            buttonIcon:SetVertexColor(xb:GetColor('normal'))

            buttonText:SetFont(xb:GetFont(db.text.fontSize))
            buttonText:SetTextColor(xb:GetColor('normal'))
            buttonText:SetText(name)
            buttonText:SetPoint('LEFT', buttonIcon, 'RIGHT', 5, 0)
            local textWidth = iconSize + 5 + buttonText:GetStringWidth()

            button:SetID(i)
            button:SetSize(textWidth, iconSize)
            button.isSettable = true
            button.text = buttonText
            button.icon = buttonIcon

            button:EnableMouse(true)
            button:RegisterForClicks('AnyUp')

            button:SetScript('OnEnter', function()
                buttonText:SetTextColor(r, g, b, 1)
            end)

            button:SetScript('OnLeave', function()
                buttonText:SetTextColor(xb:GetColor('normal'))
            end)

            button:SetScript('OnClick', function(self, button)
                if InCombatLockdown() then
                    return;
                end
                if button == 'LeftButton' then
                    local id = 0
                    local name = ''
                    if self:GetID() ~= 0 then
                        id, name = GetSpecializationInfo(self:GetID())
                    else
                        name = GetSpecializationInfo(GetSpecialization())
                    end
                    SetLootSpecialization(id)
                end
                TalentModule.lootSpecPopup:Hide()
            end)

            self.lootSpecButtons[i] = button

            if textWidth > popupWidth then
                popupWidth = textWidth
                changedWidth = true
            end
        end -- if nil
    end -- for ipairs portOptions
    for portId, button in pairs(self.lootSpecButtons) do
        if button.isSettable then
            button:SetPoint('LEFT', xb.constants.popupPadding, 0)
            button:SetPoint('TOP', 0, -(popupHeight + xb.constants.popupPadding))
            button:SetPoint('RIGHT')
            popupHeight = popupHeight + xb.constants.popupPadding + db.text.fontSize
        else
            button:Hide()
        end
    end -- for id/button in portButtons
    if changedWidth then
        popupWidth = popupWidth + self.extraPadding
    end

    if popupWidth < self.specFrame:GetWidth() then
        popupWidth = self.specFrame:GetWidth()
    end

    if popupWidth < (self.lootSpecOptionString:GetStringWidth() + self.extraPadding) then
        popupWidth = (self.lootSpecOptionString:GetStringWidth() + self.extraPadding)
    end
    self.lootSpecPopup:SetSize(popupWidth, popupHeight + xb.constants.popupPadding)

    local popupPadding = xb.constants.popupPadding
    if db.general.barPosition == 'TOP' then
        popupPadding = -(popupPadding)
    end

    self.lootSpecPopup:ClearAllPoints()
    self.lootSpecPopup:SetPoint(db.general.barPosition, self.specFrame, xb.miniTextPosition, 0, 0)
    self:SkinFrame(self.lootSpecPopup, "LootSpecToolTip")
    self.lootSpecPopup:Hide()
end

function TalentModule:ShowTooltip()
    if self.LTip:IsAcquired("TalentTooltip") then
        self.LTip:Release(self.LTip:Acquire("TalentTooltip"))
    end
    local tooltip = self.LTip:Acquire("TalentTooltip", 2, "LEFT", "RIGHT")
    tooltip:SmartAnchorTo(self.talentFrame)
    local r, g, b, _ = unpack(xb:HoverColors())
    tooltip:AddHeader("|cFFFFFFFF[|r" .. SPECIALIZATION .. "|cFFFFFFFF]|r")
    tooltip:SetCellTextColor(1, 1, r, g, b, 1)
    tooltip:AddLine(" ")

    local name = ''
    if self.currentLootSpecID == 0 then
        _, name, _ = GetSpecializationInfo(self.currentSpecID)
    else
        _, name, _ = GetSpecializationInfoByID(self.currentLootSpecID)
    end
    tooltip:AddLine(L['Current Loot Specialization'], "|cFFFFFFFF" .. name .. "|r")
    tooltip:SetCellTextColor(tooltip:GetLineCount(), 1, r, g, b, 1)

    tooltip:AddLine(" ")
    tooltip:AddLine('<' .. L['Left-Click'] .. '>', "|cFFFFFFFF" .. L['Set Specialization'] .. "|r")
    tooltip:SetCellTextColor(tooltip:GetLineCount(), 1, r, g, b, 1)
    tooltip:AddLine('<' .. L['Right-Click'] .. '>', "|cFFFFFFFF" .. L['Set Loot Specialization'] .. "|r")
    tooltip:SetCellTextColor(tooltip:GetLineCount(), 1, r, g, b, 1)
    self:SkinFrame(tooltip, "TalentTooltip")
    tooltip:Show()
end

function TalentModule:GetDefaultOptions()
    return 'talent', {
        enabled = true,
        enableLoadoutSwitcher = true,
        showTooltip = true,
        minWidth = 50
    }
end

function TalentModule:GetConfig()
    return {
        name = self:GetName(),
        type = "group",
        args = {
            enable = {
                name = ENABLE,
                order = 0,
                type = "toggle",
                get = function()
                    return xb.db.profile.modules.talent.enabled;
                end,
                set = function(_, val)
                    xb.db.profile.modules.talent.enabled = val
                    if val then
                        self:Enable()
                    else
                        self:Disable()
                    end
                end
            },
            enableLoadoutSwitcher = {
                name = L['Enable Loadout Switcher'],
                order = 1,
                type = "toggle",
                get = function()
                    return xb.db.profile.modules.talent.loadoutSwitcherEnabled;
                end,
                set = function(_, val)
                    xb.db.profile.modules.talent.loadoutSwitcherEnabled = val
                    if val then
                        self:EnableTalentLoadout()
                    else
                        self:DisableTalentLoadout()
                    end
                end
            },
            showTooltip = {
                name = L['Show Tooltips'],
                order = 2,
                type = "toggle",
                get = function()
                    return xb.db.profile.modules.talent.showTooltip;
                end,
                set = function(_, val)
                    xb.db.profile.modules.talent.showTooltip = val;
                    self:Refresh();
                end
            },
            minWidth = {
                name = L['Talent Minimum Width'],
                type = 'range',
                order = 3,
                min = 10,
                max = 200,
                step = 10,
                get = function()
                    return xb.db.profile.modules.talent.minWidth;
                end,
                set = function(info, val)
                    xb.db.profile.modules.talent.minWidth = val;
                    self:Refresh();
                end
            }
        }
    }
end
