local Functions = mUI:NewModule("mUI.Functions")

-- Pixel Perfect Scaling System (ElvUI approach)
-- Must be at file scope so it's available before OnInitialize
do
    local physicalWidth, physicalHeight = GetPhysicalScreenSize()
    mUI.physicalWidth = physicalWidth
    mUI.physicalHeight = physicalHeight
    mUI.perfect = 768 / physicalHeight
    mUI.mult = mUI.perfect / UIParent:GetScale()

    function mUI:Scale(x)
        local m = self.mult
        if m == 1 or x == 0 then
            return x
        else
            local y = m > 1 and m or -m
            return x - x % (x < 0 and y or -y)
        end
    end

    function mUI:PixelScaleChanged(event)
        if event == "UI_SCALE_CHANGED" then
            self.physicalWidth, self.physicalHeight = GetPhysicalScreenSize()
            self.perfect = 768 / self.physicalHeight
        end
        self.mult = self.perfect / UIParent:GetScale()
    end

    -- Disable WoW's built-in pixel snapping on a frame and its textures
    -- This prevents WoW from doing inconsistent sub-pixel rounding on borders
    function mUI:DisablePixelSnap(frame)
        if frame and not frame:IsForbidden() and not frame.PixelSnapDisabled then
            if frame.SetSnapToPixelGrid then
                frame:SetSnapToPixelGrid(false)
                frame:SetTexelSnappingBias(0)
            elseif frame.GetStatusBarTexture then
                local texture = frame:GetStatusBarTexture()
                if type(texture) == "table" and texture.SetSnapToPixelGrid then
                    texture:SetSnapToPixelGrid(false)
                    texture:SetTexelSnappingBias(0)
                end
            end
            frame.PixelSnapDisabled = true
        end
    end

    -- Create a pixel-perfect backdrop on a frame (ElvUI SetTemplate approach)
    function mUI:SetPixelBorders(frame, bgColor, borderColor)
        if not frame.SetBackdrop then
            Mixin(frame, BackdropTemplateMixin)
        end

        local edgeSize = mUI:Scale(1)

        frame:SetBackdrop({
            bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
            edgeFile = "Interface\\ChatFrame\\ChatFrameBackground",
            edgeSize = edgeSize
        })

        -- Disable pixel snapping on backdrop textures
        for _, region in next, {frame:GetRegions()} do
            if region and region.IsObjectType then
                mUI:DisablePixelSnap(region)
            end
        end

        if bgColor then
            frame:SetBackdropColor(unpack(bgColor))
        end

        if borderColor then
            frame:SetBackdropBorderColor(unpack(borderColor))
        end
    end
end

function Functions:OnInitialize()
    -- Load Database
    Functions.db = mUI.db.profile

    -- Get Modules
    local General = mUI:GetModule("mUI.Modules.General")

    -- Get Player Class, Colors and Theme
    local _, class = UnitClass("player")
    local classColor = RAID_CLASS_COLORS[class]
    local customColor = Functions.db.general.color or {0, 0, 0, 1}
    local themes = {
        Disabled = {1, 1, 1},
        Dark = {0.3, 0.3, 0.3},
        Class = {classColor.r, classColor.g, classColor.b},
        Custom = {customColor[1], customColor[2], customColor[3], customColor[4]}
    }
    local theme = themes[Functions.db.general.theme]

    -- Debug Print Function
    function mUI:Debug(msg)
        print("|cff009cffm|r|cffffd100UI|r:", msg)
    end

    -- Update on UI scale change
    mUI:RegisterEvent("UI_SCALE_CHANGED", function()
        mUI:PixelScaleChanged("UI_SCALE_CHANGED")
    end)

    function mUI:GameVersion()
        return {
            ["Vanilla"] = WOW_PROJECT_CLASSIC == WOW_PROJECT_ID,
            ["TBC"] = WOW_PROJECT_BURNING_CRUSADE_CLASSIC == WOW_PROJECT_ID,
            ["Mists"] = WOW_PROJECT_MISTS_CLASSIC == WOW_PROJECT_ID,
            ["Mainline"] = WOW_PROJECT_MAINLINE == WOW_PROJECT_ID
        }
    end

    -- Update both bars in the OnEvent handler
    function mUI:abbrNum(num)
        if num >= 1e6 then
            return string.format("%.1fM", num / 1e6)
        elseif num >= 1e3 then
            return string.format("%.1fK", num / 1e3)
        else
            return tostring(num)
        end
    end

    -- Version check
    local currentVersion = C_AddOns.GetAddOnMetadata("mUI", "version")

    local function GetDefaultCommChannel()
        if IsInRaid() then
            return IsInRaid(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "RAID"
        elseif IsInGroup() then
            return IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "PARTY"
        elseif IsInGuild() then
            return "GUILD"
        else
            return "YELL"
        end
    end

    function mUI:ReceiveVersion(_, version, _, sender)
        if not Functions.db.new_version then
            if (version > currentVersion) then
                mUI:Debug("A newer version is available. If you experience any errors or bugs, updating is highly recommended.")
                Functions.db.new_version = version
            end
        elseif (Functions.db.new_version == currentVersion) or (Functions.db.new_version <= currentVersion) then
            Functions.db.new_version = false
        end
    end

    function mUI:SendVersion(channel)
        self:SendCommMessage("mUIVersion", currentVersion, channel or GetDefaultCommChannel())
    end

    mUI:RegisterComm("mUIVersion", "ReceiveVersion")
    mUI:RegisterEvent("ZONE_CHANGED_NEW_AREA", function()
        mUI:SendVersion()
        if IsInGuild() then
            mUI:SendVersion("GUILD")
        end
    end)
    C_Timer.After(30, function()
        mUI:SendVersion()
        if IsInGuild() then
            mUI:SendVersion("GUILD")
        end
        mUI:SendVersion("YELL")
    end)

    -- Link Popup
    function mUI:Link(url)
        if not url then
            return
        end
        StaticPopupDialogs["mUIPopup"] = nil
        StaticPopupDialogs["mUIPopup"] = {
            text = "|cff009cffmuleyo|r|cffffd100UI|r\n\n|cffffcc00Copy the link below ( CTRL + C )|r",
            button1 = CLOSE,
            whileDead = true,
            hideOnEscape = true,
            hasEditBox = true,
            editBoxWidth = 200,
            EditBoxOnEscapePressed = function(self)
                self:GetParent():Hide()
            end,
            OnShow = function(self, data)
                self.EditBox:SetText(data.url)
                self.EditBox:HighlightText()
                self.EditBox:SetScript("OnKeyDown", function(_, key)
                    if key == "C" and IsControlKeyDown() then
                        C_Timer.After(0.3, function()
                            self.EditBox:GetParent():Hide()
                            UIErrorsFrame:AddMessage("Link copied to clipboard")
                        end)
                    end
                end)
            end
        }
        StaticPopup_Show("mUIPopup", "", "", {
            url = url
        })
    end

    function mUI:Reload(module)
        StaticPopupDialogs["mUIReloadPopup"] = nil -- Clear Popup before creating a new one
        StaticPopupDialogs["mUIReloadPopup"] = {
            text = "|cff009cffmuleyo|r|cffffd100UI|r\n\n|cffffcc00Your UI requires a reload.\n\nReason: " .. module .. "|r",
            button1 = "Reload UI",
            whileDead = true,
            hideOnEscape = false,
            StaticPopup1Button1:HookScript("OnClick", function()
                ReloadUI()
            end)
        }
        StaticPopup_Show("mUIReloadPopup", "", "")
    end

    function mUI:ApplyFont(font, fontSize, flag)
        local fontName = font:GetFont()
        font:SetFont(fontName, fontSize, flag)
    end

    function mUI:AddMixin(frame)
        if not frame.Backdrop then
            Mixin(frame, BackdropTemplateMixin)
        end
    end

    -- Theme Functions
    function mUI:Color(sub, alpha)
        -- Update Custom Color & Theme
        customColor = mUI.db.profile.general.color or {0, 0, 0, 1}
        themes["Custom"] = {customColor[1], customColor[2], customColor[3], customColor[4]}
        if not General:IsEnabled() then
            theme = themes["Disabled"]
        else
            theme = themes[mUI.db.profile.general.theme]
        end

        if theme then
            if theme[4] then
                alpha = alpha or theme[4]
            end
            if not alpha then
                alpha = 1
            end
            local color = {0, 0, 0, alpha}
            if sub then
                color[1] = theme[1] - sub
                color[2] = theme[2] - sub
                color[3] = theme[3] - sub
            end
            return color
        end
    end

    function mUI:Skin(frame, isTable, isGui)
        -- Update Custom Color & Theme
        customColor = mUI.db.profile.general.color or {0, 0, 0, 1}
        themes["Custom"] = {customColor[1], customColor[2], customColor[3], customColor[4]}
        if not General:IsEnabled() then
            theme = themes["Disabled"]
        else
            theme = themes[mUI.db.profile.general.theme]
        end

        -- Get blacklisted frames
        local blacklist = mUI:GetModule("mUI.Modules.General.Theme").blacklist
        if not blacklist then
            return
        end

        if frame then
            if isGui then
                for _, v in pairs({frame:GetRegions()}) do
                    if v:GetObjectType() == "Texture" then
                        if v.SetDesaturated then
                            v:SetDesaturated(true)
                        end
                        v:SetVertexColor(0.15, 0.15, 0.15, 1)
                    end
                end
            elseif not isTable then
                for _, v in pairs({frame:GetRegions()}) do
                    if (not blacklist[v:GetName()]) and (not blacklist[v]) then
                        if v:GetObjectType() == "Texture" then
                            if themes["Disabled"] == theme then
                                if v.SetDesaturated then
                                    v:SetDesaturated(false)
                                end
                            else
                                if v.SetDesaturated then
                                    v:SetDesaturated(true)
                                end
                            end
                            v:SetVertexColor(unpack(mUI:Color(0.15)))
                        end
                    end
                end
            else
                for _, v in pairs(frame) do
                    if v then
                        if themes["Disabled"] == theme then
                            if v.SetDesaturated then
                                v:SetDesaturated(false)
                            end
                        else
                            if v.SetDesaturated then
                                v:SetDesaturated(true)
                            end
                        end
                        v:SetVertexColor(unpack(mUI:Color(0.15)))
                    end
                end
            end
        end
    end
end
