local VERSION_TEXT = "1.8.8 d";
local VERSION_DATE = 1772630000;


local addonName, addon = ...

local L = {};       --Locale
local API = {};     --Custom APIs used by this addon
local DB;
local DB_PC;        --Per Character

PlumberGlobals = {};

addon.L = L;
addon.API = API;
addon.VERSION_TEXT = VERSION_TEXT;


local CallbackRegistry = {};
CallbackRegistry.events = {};
addon.CallbackRegistry = CallbackRegistry;

local tinsert = table.insert;
local tremove = table.remove;
local type = type;
local ipairs = ipairs;

do  --CallbackRegistry
    --[[
        callbackType:
            1. Function func(owner)
            2. Method owner:func()
    --]]

    function CallbackRegistry:Register(event, func, owner)
        if not self.events[event] then
            self.events[event] = {};
        end

        local callbackType;

        if type(func) == "string" then
            callbackType = 2;
        else
            callbackType = 1;
        end

        tinsert(self.events[event], {callbackType, func, owner})
    end
    CallbackRegistry.RegisterCallback = CallbackRegistry.Register;

    function CallbackRegistry:Trigger(event, ...)
        if self.events[event] then
            for _, cb in ipairs(self.events[event]) do
                if cb[1] == 1 then
                    if cb[3] then
                        cb[2](cb[3], ...);
                    else
                        cb[2](...);
                    end
                else
                    cb[3][cb[2]](cb[3], ...);
                end
            end
        end
    end

    function CallbackRegistry:RegisterSettingCallback(dbKey, func, owner)
        self:Register("SettingChanged."..dbKey, func, owner);
    end

    function CallbackRegistry:UnregisterCallback(event, callback, owner)
        if not owner then return end;

        if self.events[event] then
            local callbacks = self.events[event];
            local i = 1;
            local cb = callbacks[i];

            if type(callback) == "string" then
                while cb do
                    if cb[1] == 2 and cb[2] == callback and cb[3] == owner then
                        tremove(callbacks, i);
                    else
                        i = i + 1;
                    end
                    cb = callbacks[i];
                end
            else
                while cb do
                    if cb[1] == 1 and cb[2] == callback and cb[3] == owner then
                        tremove(callbacks, i);
                    else
                        i = i + 1;
                    end
                    cb = callbacks[i];
                end
            end
        end
    end

    function CallbackRegistry:UnregisterCallbackOwner(event, owner)
        if not owner then return end;

        if self.events[event] then
            local callbacks = self.events[event];
            local i = 1;
            local cb = callbacks[i];
            while cb do
                if cb[3] == owner then
                    tremove(callbacks, i);
                else
                    i = i + 1;
                end
                cb = callbacks[i];
            end
        end
    end
end

local function GetDBValue(dbKey)
    return DB[dbKey]
end
addon.GetDBValue = GetDBValue;

local function SetDBValue(dbKey, value, userInput)
    if DB then
        DB[dbKey] = value;
        addon.CallbackRegistry:Trigger("SettingChanged."..dbKey, value, userInput);
    end
end
addon.SetDBValue = SetDBValue;

local function GetDBBool(dbKey)
    return DB[dbKey] == true
end
addon.GetDBBool = GetDBBool;

local function FlipDBBool(dbKey)
    SetDBValue(dbKey, not GetDBBool(dbKey), true);
end
addon.FlipDBBool = FlipDBBool;


local function GetPersonalData(dbKey)
    --From SavedVariablesPerCharacter
    return DB_PC[dbKey]
end
addon.GetPersonalData = GetPersonalData;

local function SetPersonalData(dbKey, value, userInput)
    DB_PC[dbKey] = value;
end
addon.SetPersonalData = SetPersonalData;

local function GetValidOptionChoice(optionsTbl, dbKey)
    local index = GetDBValue(dbKey);
    if not (index and optionsTbl[index]) then
        index = 1;
    end
    return optionsTbl[index]
end
addon.GetValidOptionChoice = GetValidOptionChoice;


local DefaultValues = {
    AutoJoinEvents = true,
    BackpackItemTracker = true,
        HideZeroCountItem = true,
        ConciseTokenTooltip = true,
        TrackItemUpgradeCurrency = true,
        TrackHolidayItem = true,
        TrackerBarInsideSeparateBag = false,
    GossipFrameMedal = true,
    EmeraldBountySeedList = true,       --Show a list of Dreamseed when appoaching Emarad Bounty Soil
    WorldMapPinSeedPlanting = true,     --Aditional Map Pin: Dreamseed
    AlternativePlayerChoiceUI = true,   --Revamp PlayerChoiceFrame for Dreamseed Nurturing
    HandyLockpick = true,               --Right-click to lockpick inventory items (Rogue/Mechagnome)
    Technoscryers = true,               --Show Technoscryers on QuickSlot (Azerothian Archives World Quest)
    PlayerChoiceFrameToken = true,      --Add owned token count to PlayerChoiceFrame
    ExpansionLandingPage = true,        --Display extra info on the ExpansionLandingPage
    Delves_Dashboard = true,            --Show Great Vault Progress on DelvesDashboardFrame
    Delves_SeasonProgress = true,       --Display Seaonal Journey changes on a progress bar
    Delves_Automation = true,           --Auto select enhancement (PlayerChoiceFrame)
    WoWAnniversary = true,              --QuickSlot for Mount Maniac Event
        VotingResultsExpanded = true,
    BlizzFixFishingArtifact = true,     --Fix Fishing Artifact Traits Not Showing bug
    QuestItemDestroyAlert = true,       --Show related quest info when destroying a quest-starting item
    SpellcastingInfo = false,           --Show the spell info when hovering over target/focus cast bars. Logging target spells and displayed it on UnitPopupMenu
    ChatOptions = true,                 --Add Leave button to Channel Context Menu
    NameplateWidget = true,             --Show required items on nameplate widget set
    PartyInviterInfo = false,           --Show the inviter's level and class
        PartyInviter_Race = false,
        PartyInviter_Faction = false,
    PlayerTitleUI = false,              --Add search box and filter to TitleManagerPane
    Plunderstore = true,
        Plunderstore_HideCollected = true,
    BlizzardSuperTrack = false,         --Add timer to the SuperTrackedFrame when tracking a POI with time format
    ProfessionsBook = true,             --Show unspent points on ProfessionsBookFrame
    EditModeShowPlumberUI = true,
    SoftTargetName = false,             --Show object's name on SoftTargetFrame
        SoftTarget_TextOutline = false,
        SoftTarget_FontSize = 2,
        SoftTarget_IconSize = 2,
        SoftTarget_CastBar = true,
        SoftTarget_Objectives = false,
        SoftTarget_House_HideIcon = false,
        SoftTarget_House_HideName = false,
    AppearanceTab = false,              --Adjust Appearance Tab models to reduce GPU usage spike
        AppearanceTab_ModelCount = 1,
    ItemUpgradeUI = true,
    HolidayDungeon = true,              --Auto select holiday dungeons once
    StaticPopup_Confirm = true,         --Add a brief delay to purchase non-refundable items / Reduce item conversion confirm delay
    QueueStatus = false,                --Add a progress bar to LFG Eye
        QueueStatus_ShowTime = false,   --Relative Queue Time
        QueueStatus_TextPosition = 1,   --0:Center, 1-4:Clockwise
    InstanceDifficulty = false,         --Instance Difficulty Selector
    TransmogChatCommand = false,        --Adjust /outfit command behavior
    CraftSearchExtended = false,        --Show more search result, custom keywords
    SourceAchievementLink = true,       --Make Achievement name in MountJournal, DecorCatalog interactable
    TransmogOutfitSelect = true,        --Show Minimized Transmog Outfit Collection


    --Tooltip
    TooltipChestKeys = true,            --Show keys that unlocked the current chest or door
    TooltipRepTokens = true,            --Show faction info for items that grant rep
    TooltipSnapdragonTreats = true,     --Show info on Snapdragon Treats (An item that changes this mount's color)
    TooltipItemReagents = false,        --For items with "use to combine": show the reagent count
    TooltipProfessionKnowledge = true,  --Show unspent points on GameTooltip
    TooltipDelvesItem = true,           --Show weekly Coffer Key cap on chest tooltip
    TooltipItemQuest = true,            --Show the quest of quest starting items in bags
    TooltipTransmogEnsemble = true,     --A Raid Ensemble now unlocks outfits (tints) from 4 difficulties, but the default UI only gives one


    --Reduction
    BossBanner_MasterSwitch = false,
        BossBanner_HideLootWhenSolo = true,
        BossBanner_ValuableItemOnly = true,


    --New Expansion Landing Page
    NewExpansionLandingPage = true,
        LandingPage_Activity_HideCompleted = true,
        LandingPage_Raid_CollapsedAchievement = false,
        LandingPage_AdvancedTooltip = true,

        LandingButton_ShowButton = true,
        LandingButton_Unaffected = false,
        LandingButton_PrimaryUI = 1,
        LandingButton_SmartExpansion = false,
        LandingButton_ReduceSize = false,
        LandingButton_DarkColor = false,
        LandingButton_HideWhenIdle = false,
        --LandingButton_UseLibDBIcon = nil,     --Addon-dependant. Init on first load
        LandingButton_UseLibDBIcon_NoBorder = true, --Remove the golden button border if supported

        --LandingButton_Pos_X, LandingButton_Pos_Y


    --Custom Loot Window
    LootUI = false,
        LootUI_FontSize = 12,
        LootUI_FadeDelayPerItem = 0.25,
        LootUI_ItemsPerPage = 6,
        LootUI_BackgroundAlpha = 0.5,
        LootUI_ShowItemCount = false,
        LootUI_NewTransmogIcon = true,
        LootUI_UseCustomColor = false,
        LootUI_GrowUpwards = false,
        LootUI_WindowHide = false,
        LootUI_CombineItems = false,
        LootUI_LowFrameStrata = false,
        LootUI_HideTitle = false,
        LootUI_ShowReputation = false,
        LootUI_ShowAllMoneyChange = false,
        LootUI_ShowAllCurrencyChange = false,
        LootUI_ReplaceDefaultAlert = false,
        LootUI_ForceAutoLoot = true,
        LootUI_LootUnderMouse = false,
        LootUI_UseHotkey = true,
        LootUI_HotkeyName = "E",
        LootUI_UseStockUI = false,


    --Unified Map Pin System
    WorldMapPin_TWW = true,             --Master Switch for TWW Map Pins
        WorldMapPin_Size = 1,           --1: Default
        WorldMapPin_TWW_Delve = true,   --Show Bountiful Delves on continent map
        WorldMapPin_TWW_Quest = true,   --Show Special Assignment on continent map
        WorldMapPin_PlayerPing = true,


    --Modify default interface behavior:
    BlizzFixEventToast = true,          --Make Toast non-interactable
    MerchantPrice = false,              --Merchant Price (Alt Currency) Overview, gray insufficient items


    --In-game Navigation: Use waypoint (Super Tracking) to navigate players. Generally default to false, since it will mute WoW's own SuperTrackedFrame
    Navigator_MasterSwitch = true,      --Decide if using our SuperTrackedFrame or the default one
        Navigator_Dreamseed = false,


    --Talking Head Revamp
    TalkingHead_MasterSwitch = false,
        TalkingHead_FontSize = 100,         --% Multiply default QuestFont Height
        TalkingHead_InstantText = false,
        TalkingHead_TextOutline = false,
        TalkingHead_HideInInstance = false,
        TalkingHead_HideWorldQuest = false,
        TalkingHead_BelowWorldMap = false,


    --QuickSlot
        QuickSlotHighContrastMode = false,


    --SpellFlyout DrawerMacro
        SpellFlyout_CloseAfterClick = true,
        SpellFlyout_SingleRow = false,
        SpellFlyout_HideUnusable = false,
        SpellFlyout_UpdateFrequently = false,


    --LegionRemix
    LegionRemix = true,
        LegionRemix_TraitSubIconStyle = 3,  --1:OFF, 2:Mini Icon, 3:Replace Icon
        LegionRemix_AutoUpgrade = true,
        LegionRemix_PaperDollTraitDetail = false,
    LegionRemix_HideWorldTier = true,
    LegionRemix_LFGSpam = true,


    EnableNewByDefault = false,             --Always enable newly added features
    SettingsPanel_AutoShowChangelog = false,
    SettingsPanel_ChangelogFontSize = 1,


    --Housing
    DecorModelScaleRef = true,
        DecorModelScaleRef_ShowBanana = false,
    Housing_Macro = true,
    Housing_DecorHover = true,
        Housing_DecorHover_EnableDupe = true,
        Housing_DecorHover_DuplicateKey = 2,    --1:Ctrl, 2:Alt
    Housing_CustomizeMode = true,
    Housing_Clock = true,
        Housing_Clock_AnalogClock = true,
    TooltipDyeDeez = true,                  --Show dyes on pigment tooltip
    Housing_CatalogSearch = false,
    Housing_ItemAcquiredAlert = true,       --Click AlertFrame to view decor model


    --Namaplte: Quest Indicator
    NameplateQuest = false,
        NameplateQuest_IconSize = 2,
        NameplateQuest_ShowPartyQuest = false,
        NameplateQuest_ShowTargetProgress = false,
        NameplateQuest_ShowProgressOnHover = false,
        NameplateQuest_ShowProgressOnKeyPress = false,
            NameplateQuest_ShowProgressModifierKey = "ALT",
        NameplateQuest_WidgetOffsetX = 0,
        NameplateQuest_WidgetOffsetY = 0,
        NameplateQuest_ProgressFormat = 1,
        NameplateQuest_ProgressShowIcon = false,
        NameplateQuest_TextOutline = true,
        --NameplateQuest_Side = "RIGHT",    --Initial value dedfined by detecting addon


    --Break Time Reminder
    BreakTime = false,
        BreakTime_Cycle = 30,
        BreakTime_Rest = 5,
        BreakTime_Delay = 5,
        BreakTime_FlashTaskbar = false,
        BreakTime_DNDCombat = true,
        BreakTime_DNDInstances = false,


    --Declared elsewhere:
        --DreamseedChestABTesting = math.random(100) >= 50


    --Deprecated:
    --DruidModelFix = true,                 --Fixed by Blizzard in 10.2.0
    --BlizzFixWardrobeTrackingTip = true,   --Hide Wardrobe tip that cannot be disabled   --Tip removed by Blizzard
    --MinimapMouseover = false,             --Ridden with compatibility issue
    --LandingPageSwitch = true,             --Right click on ExpansionLandingPageMinimapButton to open a menu to access mission report  --Merged into NewExpansionLandingPage
};


local NeverEnableByDefault = {
    AppearanceTab = true,
    NameplateQuest = true,
    BreakTime = true,
};


local function LoadDatabase()
    PlumberDB = PlumberDB or {};
    PlumberStorage = PlumberStorage or {};
    PlumberDB_PC = PlumberDB_PC or {};

    DB = PlumberDB;
    DB_PC = PlumberDB_PC;

    local alwaysEnableNew = DB.EnableNewByDefault or false;
    local newDBKeys = {};

    for dbKey, value in pairs(DefaultValues) do
        if DB[dbKey] == nil then
            DB[dbKey] = value;
            if alwaysEnableNew and type(value) == "boolean" and not NeverEnableByDefault[dbKey] then
                --Not all Booleans are the master switch of individual module
                --Send these new ones to ControlCenter
                --Test: /run PlumberDB = {EnableNewByDefault = true}
                newDBKeys[dbKey] = true;
            end
        end
    end

    for dbKey, value in pairs(DB) do
        CallbackRegistry:Trigger("SettingChanged."..dbKey, value);
    end

    if not DB.installTime or type(DB.installTime) ~= "number" then
        DB.installTime = VERSION_DATE;
    end

    if DB.lastVersionTime then
        if (VERSION_DATE - 1 > DB.lastVersionTime) and DB.SettingsPanel_AutoShowChangelog then
            CallbackRegistry:Trigger("ShowChangelog");
        end
    end
    DB.lastVersionTime = VERSION_DATE;

    DefaultValues = nil;

    CallbackRegistry:Trigger("NewDBKeysAdded", newDBKeys);
    CallbackRegistry:Trigger("DBLoaded", DB);


    PlumberStorage.CreatureSpells = nil;    --Store SpellcastingInfo, retired in  Midnight
end


local EL = CreateFrame("Frame");
EL:RegisterEvent("ADDON_LOADED");
EL:RegisterEvent("PLAYER_ENTERING_WORLD");
EL:RegisterEvent("PLAYER_LOGOUT");

EL:SetScript("OnEvent", function(self, event, ...)
    if event == "ADDON_LOADED" then
        local name = ...
        if name == addonName then
            self:UnregisterEvent(event);
            LoadDatabase();
        end
    elseif event == "PLAYER_ENTERING_WORLD" then
        self:UnregisterEvent(event);
        local seasonID = API.GetTimerunningSeason();
        if seasonID and seasonID > 0 then
            CallbackRegistry:Trigger("TimerunningSeason", seasonID);
        end
        addon.ControlCenter:InitializeModules();


        local isInitialLogin = ...

        local lastLoginTime = time();

        if type(DB.lastLogoutTime) ~= "number" then
            DB.lastLogoutTime = nil;
        end

        if type(DB.lastLoginTime) ~= "number" then
            DB.lastLoginTime = lastLoginTime;
        end

        if (not DB.lastLogoutTime) or (lastLoginTime > DB.lastLogoutTime + 600) then
            --If the player didn't log in for 10 min, consider it as a new game session
            DB.lastLoginTime = lastLoginTime;
            CallbackRegistry:Trigger("NewGameSessionBegin");
        end

    elseif event == "PLAYER_LOGOUT" then
        DB.lastLogoutTime = time();
    end
end);


do
    local tocVersion = select(4, GetBuildInfo());
    tocVersion = tonumber(tocVersion or 0);

    local function IsToCVersionEqualOrNewerThan(targetVersion)
        return tocVersion >= targetVersion
    end
    addon.IsToCVersionEqualOrNewerThan = IsToCVersionEqualOrNewerThan;

    addon.IS_MIDNIGHT = IsToCVersionEqualOrNewerThan(120000);

    addon.IS_CLASSIC = C_AddOns.GetAddOnMetadata(addonName, "X-Flavor") ~= "retail";

    addon.IS_MOP = C_AddOns.GetAddOnMetadata(addonName, "X-Expansion") == "MOP";


    function addon.GetLastLoginTime()
        return DB.lastLoginTime or time()
    end
end
