---@type Diesal.GUI
local DiesalGUI = LibStub("DiesalGUI-2.0")
---@type Diesal.Tools
local DiesalTools = LibStub("DiesalTools-2.0")
---@type Diesal.Style
local DiesalStyle = LibStub("DiesalStyle-2.0")
-- ~~| Diesal Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local Colors = DiesalStyle.Colors
local HSL, ShadeColor, TintColor = DiesalTools.HSL, DiesalTools.ShadeColor, DiesalTools.TintColor
-- ~~| Lua Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local tsort = table.sort
local sub, format, lower, upper, gsub = string.sub, string.format, string.lower, string.upper, string.gsub

-- ~~| WoW Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

-- ~~| Dropdown |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local Type = "DiesalDropDown"
local Version = 2
-- ~~| Dropdown Stylesheets |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local Stylesheet = {
    ["frame-background"] = {
        type = "texture",
        layer = "BACKGROUND",
        gradient = { "VERTICAL", Colors.UI_400_GR[1], Colors.UI_400_GR[2] },
    },
    ["frame-outline"] = {
        type = "outline",
        layer = "BORDER",
        color = "000000",
    },
    ["frame-inline"] = {
        type = "outline",
        layer = "BORDER",
        gradient = { "VERTICAL", "FFFFFF", "FFFFFF" },
        alpha = { 0.07, 0.02 },
        position = -1,
    },
    ["frame-hover"] = {
        type = "texture",
        layer = "HIGHLIGHT",
        color = "ffffff",
        alpha = 0.05,
    },
    ["frame-arrow"] = {
        type = "texture",
        layer = "BORDER",
        image = { "DiesalGUIcons", { 2, 1, 16, 256, 128 } },
        alpha = 0.5,
        position = { nil, -5, -7, nil },
        height = 3,
        width = 5,
    },
    ["text-color"] = {
        type = "Font",
        color = Colors.UI_TEXT,
    },
    ["dropdown-background"] = {
        type = "texture",
        layer = "BACKGROUND",
        color = Colors.UI_100,
        alpha = 0.95,
    },
    ["dropdown-inline"] = {
        type = "outline",
        layer = "BORDER",
        color = "ffffff",
        alpha = 0.02,
        position = -1,
    },
    ["dropdown-shadow"] = {
        type = "shadow",
    },
}
-- ~~| Dropdown Locals |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local function compare(a, b)
    return a.value < b.value
end
local function sortList(list, orderedKeys)
    if orderedKeys then
        return orderedKeys
    end
    local sortedList = {}
    local orderedKeys = {}
    for key, value in pairs(list) do
        sortedList[#sortedList + 1] = { key = key, value = value }
    end
    tsort(sortedList, compare)
    for i, value in ipairs(sortedList) do
        orderedKeys[#orderedKeys + 1] = value.key
    end

    return orderedKeys
end
-- ~~| Dropdown Methods |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---@class Diesal.GUI.DropDown.Methods
local methods = {
    ---@param self Diesal.GUI.Object.DropDown
    OnAcquire = function(self)
        self:ApplySettings()
        self:SetStylesheet(Stylesheet)
        -- self:SetStylesheet(wireFrameSheet)
        self:Show()
        self:SetFrameStrata("TOOLTIP")
    end,

    ---@param self Diesal.GUI.Object.DropDown
    OnRelease = function(self) end,

    ---@param self Diesal.GUI.Object.DropDown
    ApplySettings = function(self)
        local settings = self.settings
        local content = self.content
        local frame = self.frame
        local children = self.children
        local dropdown = self.dropdown

        frame:SetWidth(settings.width)
        frame:SetHeight(settings.height)

        content:SetPoint("TOPLEFT", dropdown, settings.dropdownPadLeft, -settings.dropdownPadTop)
        content:SetPoint("BOTTOMRIGHT", dropdown, -settings.dropdownPadRight, settings.dropdownPadBottom)

        local menuHeight = 0
        for i = 1, #children do
            menuHeight = menuHeight + children[i].frame:GetHeight()
        end
        dropdown:SetHeight(settings.dropdownPadTop + menuHeight + settings.dropdownPadBottom)
    end,

    ---@param self Diesal.GUI.Object.DropDown
    ---@param list table
    SetList = function(self, list)
        self:ReleaseChildren()
        self:SetText("")
        local settings = self.settings
        settings.list = list
        local foldColor = "353535"
        local expandColor = "5a5a5a"
        self.dropdown:SetFrameStrata("TOOLTIP")
        for position, item in ipairs(list) do
            local dropdownItem = DiesalGUI:Create("DiesalDropDownItem")
            DiesalGUI:OnMouse(dropdownItem.frame, "LeftButton")
            self:AddChild(dropdownItem)
            dropdownItem:SetParentObject(self)
            dropdownItem:SetSettings({
                key = item.key,
                value = item.value,
                position = position,
                clickable = item.clickable,
                indent = item.indent or 0,
            }, true)
            dropdownItem.text:SetPoint("TOPLEFT", 12 + (item.indent * 6), -2)
            if item.indent and item.indent > 0 then
                local leftOffset = -6 + (-6 * item.indent)
                dropdownItem:UpdateStyle("frame-lineV", {
                    type = "texture",
                    layer = "BORDER",
                    color = foldColor,
                    position = { leftOffset, nil, -8, nil },
                    height = 1,
                    width = 6,
                })
                dropdownItem:UpdateStyle("frame-lineH", {
                    type = "texture",
                    layer = "BORDER",
                    color = foldColor,
                    position = { leftOffset, nil, 0, 0 },
                    width = 1,
                })
                if item.last then
                    dropdownItem:UpdateStyle("frame-lineH", {
                        type = "texture",
                        position = { leftOffset, nil, 0, -7 },
                    })
                end
            end
        end
        self:ApplySettings()
    end,

    ---@param self Diesal.GUI.Object.DropDown
    SetValue = function(self, key)
        local selectionTable = {}
        local selectedKey, dropdownText, selectedValue
        local selectedCount = 0

        if key ~= "CLEAR" then
            if self.settings.multiSelect then
                for i = 1, #self.children do
                    if self.children[i].settings.key == key then
                        selectedKey = key
                        self.children[i]:SetSelected(true)
                    end
                    if self.children[i].settings.selected then
                        selectedCount = selectedCount + 1
                        if dropdownText then
                            dropdownText = format("%s, %s", dropdownText, self.children[i].settings.value)
                        else
                            dropdownText = self.children[i].settings.value
                        end
                        selectionTable[#selectionTable + 1] = self.children[i].settings.key
                    end
                end
            else
                for i = 1, #self.children do
                    local child = self.children[i]
                    child:SetSelected(false)
                    if child.settings.key == key then
                        child:SetSelected(true)
                        dropdownText = child.settings.value
                        selectionTable = { key }
                        selectedKey = key
                        selectedValue = self.value
                    end
                end
            end
        else
            self:ClearSelection()
        end

        if self.settings.multiSelect then
            dropdownText = string.format("+%d", selectedCount)
        end

        if selectedKey then
            self:SetText(dropdownText)
            self:FireEvent("OnValueChanged", selectedKey, selectedValue, selectionTable)
        else
            self:SetText("")
            self:FireEvent("OnValueChanged", selectedKey, selectedValue, selectionTable)
        end
    end,

    ---@param self Diesal.GUI.Object.DropDown
    ClearSelection = function(self)
        for i = 1, #self.children do
            self.children[i]:SetSelected(false)
        end
        self:SetText("")
    end,

    ---@param self Diesal.GUI.Object.DropDown
    SetValueTable = function(self, keyTable)
        if not self.settings.multiSelect or type(keyTable) ~= "table" then
            return
        end
        local dropdownItems = self.children
        local selectionTable = {}
        local selectedKey
        local dropdownText
        local selectedCount = 0

        for i = 1, #dropdownItems do
            local dropdownItem = dropdownItems[i]
            dropdownItem:SetSelected(false)
            for _, key in ipairs(keyTable) do
                if dropdownItem.settings.key == key then
                    dropdownItem:SetSelected(true)
                    selectedCount = selectedCount + 1
                    if not self.settings.multiSelect then
                        dropdownText = dropdownText and format("%s, %s", dropdownText, dropdownItem.settings.value) or dropdownItem.settings.value
                    end
                    selectionTable[#selectionTable + 1] = dropdownItem.settings.key
                end
            end
        end
        self:FireEvent("OnValueChanged", nil, nil, selectionTable)
        if self.settings.multiSelect then
            dropdownText = string.format("+%d", selectedCount)
        end
        self:SetText(dropdownText)
    end,

    ---@param self Diesal.GUI.Object.DropDown
    SetMultiSelect = function(self, state)
        self.settings.multiSelect = state
    end,

    ---@param self Diesal.GUI.Object.DropDown
    SetText = function(self, text)
        self.text:SetText(text)
        for i = 1, #self.children do
            local child = self.children[i]
            if child.settings.value == text then
                self.value = child.settings.key
            end
        end
    end,

    ---@param self Diesal.GUI.Object.DropDown
    SetFocus = function(self)
        DiesalGUI:SetFocus(self)
    end,

    ---@param self Diesal.GUI.Object.DropDown
    ClearFocus = function(self)
        self.dropdownShown = false
        self.dropdown:Hide()
    end,

    ---@param self Diesal.GUI.Object.DropDown
    EnableMouse = function(self, state)
        self.frame:EnableMouse(state)
    end,

    ---@param self Diesal.GUI.Object.DropDown
    RegisterForClicks = function(self, ...)
        self.frame:RegisterForClicks(...)
    end,

    ---@param self Diesal.GUI.Object.DropDown
    SetJustifyV = function(self, justify)
        self.text:SetJustifyV(justify)
    end,

    ---@param self Diesal.GUI.Object.DropDown
    SetJustifyH = function(self, justify)
        self.text:SetJustifyH(justify)
    end,
    ---@param self Diesal.GUI.Object.DropDown
    ShowDropDown = function(self)
        self.dropdown:SetFrameStrata("TOOLTIP")
        self.dropdown:SetParent(self.frame)
        self.dropdown:Show()
        self.dropdownShown = true
    end,
    ---@param self Diesal.GUI.Object.DropDown
    HideDropDown = function(self)
        self.dropdown:Hide()
        self.dropdownShown = false
    end,
    ---@param self Diesal.GUI.Object.DropDown
    ToggleDropDown = function(self)
        if self.dropdownShown then
            self:HideDropDown()
        else
            self:ShowDropDown()
        end
    end,
}

---@class Diesal.GUI.Object.DropDown.Settings
---@field dropdownPadLeft number
---@field dropdownPadRight number
---@field dropdownPadTop number
---@field dropdownPadBottom number
---@field itemHeight number
---@field width number
---@field height number
---@field list table
---@field multiSelect boolean

---@class DiesalDropDown : Diesal.GUI.Object.DropDown

local f = CreateFrame("Button", nil, UIParent)

-- ~~| Dropdown Constructor |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local function Constructor()
    ---@class Diesal.GUI.Object.DropDown : Diesal.GUI.ObjectBase, Diesal.GUI.DropDown.Methods
    ---@field settings Diesal.GUI.Object.DropDown.Settings
    ---@field children Diesal.GUI.Object.DropDown.Item[]
    ---@field text FontString
    ---@field dropdown Frame
    ---@field content Frame
    ---@field value string
    local self = DiesalGUI:Create(Type, true)
    self:SetMethods(methods)
    self.dropdownShown = true

    local frame = CreateFrame("Button", nil, UIParent)
    frame:SetFrameStrata("TOOLTIP")
    self.frame = frame

    self.isContainer = true
    self.defaults = {
        dropdownPadLeft = 4,
        dropdownPadRight = 4,
        dropdownPadTop = 4,
        dropdownPadBottom = 4,
        itemHeight = 16,
        width = 100,
        height = 16,
    }
    -- ~~ Registered Events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -- OnValueChanged(event,selectedKey,selectedValue,selectionTable)
    -- OnValueSelected(event,selectedKey,selectedValue,selectionTable)
    -- OnEnter, OnLeave
    -- ~~ Construct ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    frame:SetScript("OnClick", function(this, button, down)
        if button == "LeftButton" then
            if self.dropdown:IsVisible() then
                self.dropdown:Hide()
            else
                DiesalGUI:OnMouse(this, button)
                self.dropdown:Show()
            end
        end
        self:FireEvent("OnClick", button, down)
    end)
    frame:SetScript("OnEnter", function(this, ...)
        self:FireEvent("OnEnter", ...)
    end)
    frame:SetScript("OnLeave", function(this, ...)
        self:FireEvent("OnLeave", ...)
    end)

    local text = self:CreateRegion("FontString", "text", frame)
    text:ClearAllPoints()
    text:SetPoint("TOPLEFT", 4, -1)
    text:SetPoint("BOTTOMRIGHT", -14, 1)
    text:SetJustifyV("MIDDLE")
    text:SetJustifyH("LEFT")

    local dropdown = self:CreateRegion("Frame", "dropdown", frame)
    dropdown:SetFrameStrata("TOOLTIP")
    dropdown:SetFrameLevel(998)
    dropdown:SetToplevel(true)
    dropdown:SetPoint("TOPRIGHT", frame, "BOTTOMRIGHT", 0, -2)
    dropdown:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 0, -2)
    dropdown:SetScript("OnShow", function(this)
        self:SetFocus()
    end)
    dropdown:SetScript("OnHide", function(this)
        self.dropdownShown = false
    end)
    dropdown:Hide()
    self.dropdown = dropdown

    self:CreateRegion("Frame", "content", dropdown)

    -- ~~ Methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    --self:SetMethods(methods)
    --[[ for method, func in pairs(methods) do
        self[method] = func
    end ]]
    -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    return self
end
DiesalGUI:RegisterObjectConstructor(Type, Constructor, Version)
