setfenv(1, VoiceOver)

---@class Addon : AceAddon, AceAddon-3.0, AceEvent-3.0, AceTimer-3.0
---@field db VoiceOverConfig|AceDBObject-3.0
Addon = LibStub("AceAddon-3.0"):NewAddon("VoiceOver", "AceEvent-3.0", "AceTimer-3.0")

Addon.OnAddonLoad = {}

---@class VoiceOverConfig
local defaults = {
    profile = {
        SoundQueueUI = {
            LockFrame = false,
            FrameScale = 1.1,
            FrameStrata = "HIGH",
            HidePortrait = false,
            HideFrame = false,
            PortraitStyle = 0, -- 0 = Clásico, 1 = Midnight
            PortraitBackgroundTexture = 0, -- 0, 1, 2, 3 (default: 0)
            PortraitFrameAtlas = 1, -- 1, 2, 3 (default: 1)
            PortraitEffectFileID = 6217457, -- FileDataID for the visual effect (default effect)
            PortraitEffectEnabled = false, -- Enable/disable visual effect
            PortraitEffectDetached = false, -- Whether the effect is detached from portrait
            PortraitEffectPosition = nil, -- Saved position when detached
            PortraitEffectFrameSize = 120, -- Size of the effect frame when detached
            PortraitEffectX = 0, -- Model X position (left/right)
            PortraitEffectY = 0, -- Model Y position (up/down)
            PortraitEffectZ = 0, -- Model Z position (zoom)
            PortraitEffectRotation = 0, -- Model rotation in degrees
        },
        Audio = {
            GossipFrequency = Enums.GossipFrequency.OncePerQuestNPC,
            SoundChannel = Enums.SoundChannel.Master,
            AutoToggleDialog = true,
            StopAudioOnDisengage = false,
        },
        MinimapButton = {
            LibDBIcon = {}, -- Table used by LibDBIcon to store position (minimapPos), dragging lock (lock) and hidden state (hide)
            Commands = {
                -- References keys from Options.table.args.SlashCommands.args table
                LeftButton = "Options",
                MiddleButton = "PlayPause",
                RightButton = "Clear",
            }
        },

        DebugEnabled = false,
        ShowQuestFrameButton = true, -- Añade esta línea
        ShowObjectiveTrackerButtons = true,
    },
    char = {
        IsPaused = false,
        hasSeenGossipForNPC = {},
    }
}

local lastGossipOptions
local selectedGossipOption
local currentQuestSoundData
local currentGossipSoundData

-- Sistema de prevención de reproducción duplicada de eventos de quest
-- Rastrea el último tiempo de reproducción por questID+evento para prevenir duplicados
lastQuestEventPlayedTime = {}  -- Formato: ["questID-eventType"] = timestamp
local QUEST_EVENT_COOLDOWN = 5  -- Segundos antes de permitir reproducir el mismo quest+evento de nuevo

-- Sistema para prevenir gossip duplicado después de quest complete
-- Rastrea cuando se reprodujo un quest complete para un NPC para evitar que gossip lo repita
local lastQuestCompleteForNPC = {}  -- Formato: [npcGUID] = { timestamp, questID, rewardText }
local GOSSIP_AFTER_QUEST_COOLDOWN = 3  -- Segundos para bloquear gossip después de quest complete

-- Función auxiliar para verificar si un evento de quest puede reproducirse
function CanPlayQuestEvent(questID, eventType)
    if not questID or questID == 0 then
        return false
    end
    
    local eventKey = tostring(questID) .. "-" .. tostring(eventType)
    local currentTime = GetTime()
    local lastTime = lastQuestEventPlayedTime[eventKey]
    
    if lastTime and (currentTime - lastTime) < QUEST_EVENT_COOLDOWN then
        -- Aún en cooldown, no reproducir
        return false
    end
    
    return true
end

-- Función auxiliar para registrar que un evento de quest se reprodujo
function MarkQuestEventAsPlayed(questID, eventType)
    if not questID or questID == 0 then
        return
    end
    
    local eventKey = tostring(questID) .. "-" .. tostring(eventType)
    lastQuestEventPlayedTime[eventKey] = GetTime()
end

-- Función para registrar que se completó una quest para un NPC
local function MarkQuestCompleteForNPC(npcGUID, questID, rewardText)
    if npcGUID then
        lastQuestCompleteForNPC[npcGUID] = {
            timestamp = GetTime(),
            questID = questID,
            rewardText = rewardText
        }
    end
end

-- Función para verificar si gossip debe bloquearse por quest complete reciente
local function ShouldBlockGossipAfterQuestComplete(npcGUID, gossipText)
    if not npcGUID then
        return false
    end
    
    local lastComplete = lastQuestCompleteForNPC[npcGUID]
    if not lastComplete then
        return false
    end
    
    local timeSinceComplete = GetTime() - lastComplete.timestamp
    if timeSinceComplete > GOSSIP_AFTER_QUEST_COOLDOWN then
        -- Ya pasó el cooldown, limpiar y permitir
        lastQuestCompleteForNPC[npcGUID] = nil
        return false
    end
    
    -- Dentro del cooldown, verificar si el texto es similar
    -- Normalmente el gossip después de completar tiene el mismo texto o muy similar
    print("|cffffff00[VoiceOver]|r Bloqueando gossip - Quest completada hace", string.format("%.1f", timeSinceComplete), "segundos")
    return true
end

function Addon:OnInitialize()
    self.db = LibStub("AceDB-3.0"):New("VoiceOverDB", defaults)
    self.db.RegisterCallback(self, "OnProfileChanged", "RefreshConfig")
    self.db.RegisterCallback(self, "OnProfileReset", "RefreshConfig")

    -- Detectar la configuración regional del cliente
    local locale = GetLocale()

    -- Verificar si es la primera vez que se carga el addon
    if not self.db.profile.isFirstRun then
        if locale == "esMX" or locale == "esES" then
            -- Configuraciones predeterminadas para español
            self.db.profile.QuestProgressEnabled = true
            self.db.profile.QuestProgressDebugEnabled = false
            self.db.profile.ItemReaderEnabled = true
            self.db.profile.BonusQuestDetectorEnabled = true
            self.db.profile.RareAnnounceEnabled = true
            self.db.profile.GameObjectReaderEnabled = true
            self.db.profile.NPCLiveIAEnabled = true
        else
            -- Configuraciones predeterminadas para cualquier otra región
            self.db.profile.QuestProgressEnabled = true
            self.db.profile.ItemReaderEnabled = false
            self.db.profile.BonusQuestDetectorEnabled = false
            self.db.profile.RareAnnounceEnabled = false
            self.db.profile.GameObjectReaderEnabled = false
            self.db.profile.NPCLiveIAEnabled = false
        end

        -- Marcar que ya no es la primera vez que se ejecuta
        self.db.profile.isFirstRun = true
    end

    -- Inicializar QuestFrameAudioButton
    if QuestFrameAudioButton and QuestFrameAudioButton.Initialize then
        QuestFrameAudioButton:Initialize()
    end

    -- Inicializar módulos
    if self.db.profile.QuestProgressEnabled and VoiceOver.MissionProgress and VoiceOver.MissionProgress.Activate then
        VoiceOver.MissionProgress.Activate()
    elseif VoiceOver.MissionProgress and VoiceOver.MissionProgress.Deactivate then
        VoiceOver.MissionProgress.Deactivate()
    end

    -- Activación/Desactivación del modo de depuración para MissionProgress
    if self.db.profile.QuestProgressDebugEnabled and VoiceOver.MissionProgress and VoiceOver.MissionProgress.ActivateDebug then
        VoiceOver.MissionProgress.ActivateDebug()
    elseif VoiceOver.MissionProgress and VoiceOver.MissionProgress.DeactivateDebug then
        VoiceOver.MissionProgress.DeactivateDebug()
    end

    -- ItemReader OBSOLETO - Reemplazado por ItemAdapter.lua
    -- BonusQuestDetector NO NECESARIO - Sistema obsoleto para Midnight

    if self.db.profile.RareAnnounceEnabled and VoiceOver.RareAnnounce and VoiceOver.RareAnnounce.Activate then
        VoiceOver.RareAnnounce.Activate()
    elseif VoiceOver.RareAnnounce and VoiceOver.RareAnnounce.Deactivate then
        VoiceOver.RareAnnounce.Deactivate()
    end

    -- Inicializar módulos
    if self.db.profile.GameObjectReaderEnabled and VoiceOver.GameObjectAdapter and VoiceOver.GameObjectAdapter.Activate then
        VoiceOver.GameObjectAdapter.Activate()
    elseif VoiceOver.GameObjectAdapter and VoiceOver.GameObjectAdapter.Deactivate then
        VoiceOver.GameObjectAdapter.Deactivate()
    end

    -- GameObjectOnlyFrame OBSOLETO - Frame de debug reemplazado por GameObjectAdapter

    -- Activar ItemAdapter
    if VoiceOver.ItemAdapter and VoiceOver.ItemAdapter.Activate then
        VoiceOver.ItemAdapter.Activate()
    end

    -- NPCLiveIA OBSOLETO - Reemplazado por DialogueAdapter.lua

    -- Activación/Desactivación del modo de depuración para SoundControl
    if self.db.profile.SoundControlDebugEnabled and VoiceOver.SoundControlUI and VoiceOver.SoundControlUI.ActivateDebug then
        VoiceOver.SoundControlUI.ActivateDebug()
    elseif VoiceOver.SoundControlUI and VoiceOver.SoundControlUI.DeactivateDebug then
        VoiceOver.SoundControlUI.DeactivateDebug()
    end

    StaticPopupDialogs["VOICEOVER_ERROR"] =
    {
        text = "VoiceOver|n|n%s",
        button1 = OKAY,
        timeout = 0,
        whileDead = 1,
    }

    SoundQueueUI:Initialize()
    DataModules:EnumerateAddons()
    Options:Initialize()

    self:RegisterEvent("ADDON_LOADED")
    self:RegisterEvent("QUEST_DETAIL")
    -- self:RegisterEvent("QUEST_PROGRESS")
    self:RegisterEvent("QUEST_COMPLETE")
    self:RegisterEvent("QUEST_GREETING")
    self:RegisterEvent("QUEST_FINISHED")
    self:RegisterEvent("GOSSIP_SHOW")
    self:RegisterEvent("GOSSIP_CLOSED")

    if select(5, C_AddOns.GetAddOnInfo("VoiceOver")) ~= "MISSING" then
        C_AddOns.DisableAddOn("VoiceOver")
        if not self.db.profile.SeenDuplicateDialog then
            StaticPopupDialogs["VOICEOVER_DUPLICATE_ADDON"] =
            {
                text =
                [[VoiceOver|n|nTo fix the quest autoaccept bugs we had to rename the addon folder. If you're seeing this popup, it means the old one wasn't automatically removed.|n|nYou can safely delete "VoiceOver" from your Addons folder. "AI_VoiceOver" is the new folder.]],
                button1 = OKAY,
                timeout = 0,
                whileDead = 1,
                OnAccept = function()
                    self.db.profile.SeenDuplicateDialog = true
                end,
            }
            StaticPopup_Show("VOICEOVER_DUPLICATE_ADDON")
        end
    end

    if select(5, C_AddOns.GetAddOnInfo("AI_VoiceOver_112")) ~= "MISSING" then
        C_AddOns.DisableAddOn("AI_VoiceOver_112")
        if not self.db.profile.SeenDuplicateDialog112 then
            StaticPopupDialogs["VOICEOVER_DUPLICATE_ADDON_112"] =
            {
                text =
                [[VoiceOver|n|nVoiceOver port for 1.12 has been merged together with other versions and is no longer distributed as a separate addon.|n|nYou can safely delete "AI_VoiceOver_112" from your Addons folder. "AI_VoiceOver" is the new folder.]],
                button1 = OKAY,
                timeout = 0,
                whileDead = 1,
                OnAccept = function()
                    self.db.profile.SeenDuplicateDialog112 = true
                end,
            }
            StaticPopup_Show("VOICEOVER_DUPLICATE_ADDON_112")
        end
    end

    if not DataModules:HasRegisteredModules() then
        StaticPopupDialogs["VOICEOVER_NO_REGISTERED_DATA_MODULES"] =
        {
            text =
            [[VoiceOver|n|nNo sound packs were found.|n|nUse the "/vo options" command, (or Interface Options in newer clients) and go to the DataModules tab for information on where to download sound packs.]],
            button1 = OKAY,
            timeout = 0,
            whileDead = 1,
        }
        StaticPopup_Show("VOICEOVER_NO_REGISTERED_DATA_MODULES")
    end

    local function MakeAbandonQuestHook(field, getFieldData)
        return function()
            local data = getFieldData()
            local soundsToRemove = {}
            for _, soundData in pairs(SoundQueue.sounds) do
                if Enums.SoundEvent:IsQuestEvent(soundData.event) and soundData[field] == data then
                    table.insert(soundsToRemove, soundData)
                end
            end

            for _, soundData in pairs(soundsToRemove) do
                SoundQueue:RemoveSoundFromQueue(soundData)
            end
        end
    end
    if C_QuestLog and C_QuestLog.AbandonQuest then
        hooksecurefunc(C_QuestLog, "AbandonQuest",
            MakeAbandonQuestHook("questID", function() return C_QuestLog.GetAbandonQuest() end))
    elseif AbandonQuest then
        hooksecurefunc("AbandonQuest", MakeAbandonQuestHook("questName", function() return GetAbandonQuestName() end))
    end

    if QuestLog_Update then
        hooksecurefunc("QuestLog_Update", function()
            QuestOverlayUI:Update()
        end)
    end

    if C_GossipInfo and C_GossipInfo.SelectOption then
        hooksecurefunc(C_GossipInfo, "SelectOption", function(optionID)
            if lastGossipOptions then
                for _, info in ipairs(lastGossipOptions) do
                    if info.gossipOptionID == optionID then
                        selectedGossipOption = info.name
                        break
                    end
                end
                lastGossipOptions = nil
            end
        end)
    elseif SelectGossipOption then
        hooksecurefunc("SelectGossipOption", function(index)
            if lastGossipOptions then
                selectedGossipOption = lastGossipOptions[1 + (index - 1) * 2]
                lastGossipOptions = nil
            end
        end)
    end
    -- Gestión de versiones de avisos
    local currentNoticeVersion = 1.0 -- Versión del aviso actual
    if not self.db.char.noticeVersion or self.db.char.noticeVersion < currentNoticeVersion then
        -- Forzar que se muestre el nuevo aviso
        self.db.char.dontShowWelcome = false
        self.db.char.noticeVersion = currentNoticeVersion -- Actualiza la versión del aviso en la base de datos
    end
end

function Addon:RefreshConfig()
    SoundQueueUI:RefreshConfig()
end

function Addon:ADDON_LOADED(event, addon)
    addon = addon or arg1 -- Thanks, Ace3v...
    local hook = self.OnAddonLoad[addon]
    if hook then
        hook()
    end
end

local function GossipSoundDataAdded(soundData)
    Utils:CreateNPCModelFrame(soundData)

    -- Save current gossip sound data for dialog/frame sync option
    currentGossipSoundData = soundData
end

local function QuestSoundDataAdded(soundData)
    Utils:CreateNPCModelFrame(soundData)

    -- Save current quest sound data for dialog/frame sync option
    currentQuestSoundData = soundData
end

local GetTitleText =
    GetTitleText -- Store original function before EQL3 (Extended Quest Log 3) overrides it and starts prepending quest level
function Addon:QUEST_DETAIL()
    if self.db.profile.ManualMode then
        return
    end

    local questID = GetQuestID()
    local questTitle = GetTitleText()
    local questText = GetQuestText()
    local guid = Utils:GetNPCGUID()
    local targetName = Utils:GetNPCName()

    if not questID or questID == 0 then
        return
    end

    -- Verificar si este evento ya se reprodujo recientemente
    if not CanPlayQuestEvent(questID, Enums.SoundEvent.QuestAccept) then
        return
    end

    -- Can happen if the player interacted with an NPC while having main menu or options opened
    if not guid and not targetName then
        return
    end

    if Addon.db.char.RecentQuestTitleToID and questID ~= 0 then
        Addon.db.char.RecentQuestTitleToID[questTitle] = questID
    end

    local type = guid and Utils:GetGUIDType(guid)
    if type == Enums.GUID.Item then
        -- Allow quests started from items to have VO, book icon will be displayed for them
    elseif not type or not Enums.GUID:CanHaveID(type) then
        -- If the quest is started by something that we cannot extract the ID of (e.g. Player, when sharing a quest) - try to fallback to a questgiver from a module's database
        local id
        type, id = DataModules:GetQuestLogQuestGiverTypeAndID(questID)
        guid = id and Enums.GUID:CanHaveID(type) and Utils:MakeGUID(type, id) or guid
        targetName = id and DataModules:GetObjectName(type, id) or targetName or "Unknown Name"
    end

    -- print("QUEST_DETAIL", questID, questTitle);
    ---@type SoundData
    local soundData = {
        event = Enums.SoundEvent.QuestAccept,
        questID = questID,
        name = targetName,
        title = questTitle,
        text = questText,
        unitGUID = guid,
        unitIsObjectOrItem = Utils:IsNPCObjectOrItem(),
        addedCallback = QuestSoundDataAdded,
    }

    if not self.db.profile.ManualMode then
        -- Marcar como reproducido antes de añadir a la cola
        MarkQuestEventAsPlayed(questID, Enums.SoundEvent.QuestAccept)
        SoundQueue:AddSoundToQueue(soundData)
    end
end

function Addon:QUEST_COMPLETE()
    if self.db.profile.ManualMode then
        return
    end

    local questID = GetQuestID()
    local questTitle = GetTitleText()
    local questText = GetRewardText()
    local guid = Utils:GetNPCGUID()
    local targetName = Utils:GetNPCName()

    if not questID or questID == 0 then
        return
    end

    -- Verificar si este evento ya se reprodujo recientemente
    if not CanPlayQuestEvent(questID, Enums.SoundEvent.QuestComplete) then
        return
    end

    -- Can happen if the player interacted with an NPC while having main menu or options opened
    if not guid and not targetName then
        return
    end

    if Addon.db.char.RecentQuestTitleToID and questID ~= 0 then
        Addon.db.char.RecentQuestTitleToID[questTitle] = questID
    end

    -- print("QUEST_COMPLETE", questID, questTitle);
    ---@type SoundData
    local soundData = {
        event = Enums.SoundEvent.QuestComplete,
        questID = questID,
        name = targetName,
        title = questTitle,
        text = questText,
        unitGUID = guid,
        unitIsObjectOrItem = Utils:IsNPCObjectOrItem(),
        addedCallback = QuestSoundDataAdded,
    }

    if not self.db.profile.ManualMode then
        -- Marcar como reproducido antes de añadir a la cola
        MarkQuestEventAsPlayed(questID, Enums.SoundEvent.QuestComplete)
        -- Registrar este quest complete para el NPC para evitar duplicados en gossip
        MarkQuestCompleteForNPC(guid, questID, questText)
        SoundQueue:AddSoundToQueue(soundData)
    end
end

function Addon:ShouldPlayGossip(guid, text)
    local npcKey = guid or "unknown"

    local gossipSeenForNPC = self.db.char.hasSeenGossipForNPC[npcKey]

    if self.db.profile.Audio.GossipFrequency == Enums.GossipFrequency.OncePerQuestNPC then
        local numActiveQuests = C_GossipInfo.GetNumActiveQuests()
        local numAvailableQuests = C_GossipInfo.GetNumAvailableQuests()
        local npcHasQuests = (numActiveQuests > 0 or numAvailableQuests > 0)
        if npcHasQuests and gossipSeenForNPC then
            return
        end
    elseif self.db.profile.Audio.GossipFrequency == Enums.GossipFrequency.OncePerNPC then
        if gossipSeenForNPC then
            return
        end
    elseif self.db.profile.Audio.GossipFrequency == Enums.GossipFrequency.Never then
        return
    end

    return true, npcKey
end

function Addon:QUEST_GREETING()
    local guid = Utils:GetNPCGUID()
    local targetName = Utils:GetNPCName()
    local greetingText = GetGreetingText()

    -- Can happen if the player interacted with an NPC while having main menu or options opened
    if not guid and not targetName then
        return
    end

    local play, npcKey = self:ShouldPlayGossip(guid, greetingText)
    if not play then
        return
    end

    -- Play the gossip sound
    ---@type SoundData
    local soundData = {
        event = Enums.SoundEvent.QuestGreeting,
        name = targetName,
        text = greetingText,
        unitGUID = guid,
        unitIsObjectOrItem = Utils:IsNPCObjectOrItem(),
        addedCallback = GossipSoundDataAdded,
        startCallback = function()
            self.db.char.hasSeenGossipForNPC[npcKey] = true
        end
    }
    SoundQueue:AddSoundToQueue(soundData)
end

function Addon:GOSSIP_SHOW()
    local guid = Utils:GetNPCGUID()
    local targetName = Utils:GetNPCName()
    local gossipText = C_GossipInfo.GetText()

    -- Can happen if the player interacted with an NPC while having main menu or options opened
    if not guid and not targetName then
        return
    end

    -- Verificar si debe bloquearse por quest complete reciente con este NPC
    if ShouldBlockGossipAfterQuestComplete(guid, gossipText) then
        return
    end

    local play, npcKey = self:ShouldPlayGossip(guid, gossipText)
    if not play then
        return
    end

    -- Play the gossip sound
    ---@type SoundData
    local soundData = {
        event = Enums.SoundEvent.Gossip,
        name = targetName,
        title = gossipText,
        text = gossipText,
        unitGUID = guid,
        unitIsObjectOrItem = Utils:IsNPCObjectOrItem(),
        addedCallback = GossipSoundDataAdded,
        startCallback = function()
            self.db.char.hasSeenGossipForNPC[npcKey] = true
        end
    }
    SoundQueue:AddSoundToQueue(soundData)

    selectedGossipOption = nil
    lastGossipOptions = nil
    if C_GossipInfo and C_GossipInfo.GetOptions then
        lastGossipOptions = C_GossipInfo.GetOptions()
    elseif GetGossipOptions then
        lastGossipOptions = { GetGossipOptions() }
    end
end

function Addon:QUEST_FINISHED()
    if Addon.db.profile.Audio.StopAudioOnDisengage and currentQuestSoundData then
        SoundQueue:RemoveSoundFromQueue(currentQuestSoundData)
    end
    currentQuestSoundData = nil
end

function Addon:GOSSIP_CLOSED()
    if Addon.db.profile.Audio.StopAudioOnDisengage and currentGossipSoundData then
        SoundQueue:RemoveSoundFromQueue(currentGossipSoundData)
    end
    currentGossipSoundData = nil

    selectedGossipOption = nil
end

--AVISO DE BIENVENIDA (Solo imagen)
local f = CreateFrame("Frame", "VoiceOverWelcomeFrame", UIParent)
f:SetSize(1024, 512) -- Proporción 2:1 (original 4096x2048)
f:SetPoint("CENTER")
f:SetFrameStrata("FULLSCREEN_DIALOG")
f:SetFrameLevel(100)

-- La imagen de bienvenida
local bienvenida = f:CreateTexture(nil, "ARTWORK")
bienvenida:SetTexture("Interface\\AddOns\\AI_VoiceOver\\Textures\\BIENVENIDA.blp")
bienvenida:SetAllPoints(f)

-- Botón de cerrar (X)
local closeButton = CreateFrame("Button", nil, f, "UIPanelCloseButton")
closeButton:SetPoint("TOPRIGHT", f, "TOPRIGHT", -140, -16)
closeButton:SetScript("OnClick", function()
    f:Hide()
    Addon.db.char.dontShowWelcome = true
end)

-- Botón "Cerrar" texto
local closeTextButton = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
closeTextButton:SetSize(100, 25)
closeTextButton:SetText("Cerrar")
closeTextButton:SetPoint("BOTTOM", f, "BOTTOM", 0, 20)
closeTextButton:SetScript("OnClick", function()
    f:Hide()
    Addon.db.char.dontShowWelcome = true
end)

-- Hacer el frame arrastrable
f:EnableMouse(true)
f:SetMovable(true)
f:RegisterForDrag("LeftButton")
f:SetScript("OnDragStart", f.StartMoving)
f:SetScript("OnDragStop", f.StopMovingOrSizing)

-- Cerrar al hacer clic derecho en cualquier parte de la imagen
f:SetScript("OnMouseDown", function(self, button)
    if button == "RightButton" then
        self:Hide()
        Addon.db.char.dontShowWelcome = true
    end
end)

function Addon:OnEnable()
    local currentNoticeVersion = 7.0 -- Nueva versión para forzar mostrar

    if not self.db.char.noticeVersion or self.db.char.noticeVersion < currentNoticeVersion then
        self.db.char.dontShowWelcome = false
        self.db.char.noticeVersion = currentNoticeVersion
    end

    if self.db and self.db.char and not self.db.char.dontShowWelcome then
        f:Show()
    end
end

f:Hide()

