---@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")
-- ~~| Lua Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local sub, format, match, lower = string.sub, string.format, string.match, string.lower
local tsort = table.sort
local tostring = tostring
-- ~~| WoW Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

-- ~~| TableExplorerBranch |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local Type = "DiesalBranch"
local Version = 1
-- ~~| Stylesheets |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local Stylesheet = {
    ["button-highlight"] = {
        type = "texture",
        layer = "HIGHLIGHT",
        color = "ffffff",
        alpha = 0.1,
    },
    ["button-selected"] = {
        type = "texture",
        layer = "BORDER",
        color = "0059b3",
        alpha = 0,
    },
}
local wireFrameSheet = {
    ["frame-white"] = {
        type = "outline",
        layer = "OVERLAY",
        color = "ffffff",
    },
    ["button-purple"] = {
        type = "outline",
        layer = "OVERLAY",
        color = "aa00ff",
    },
    ["content-yellow"] = {
        type = "outline",
        layer = "OVERLAY",
        color = "fffc00",
    },
}
-- ~~| Methods |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---@class Diesal.GUI.Branch.Methods
---@field OnAcquire fun(self: Diesal.GUI.Object.Branch)
---@field OnRelease fun(self: Diesal.GUI.Object.Branch)
---@field ApplySettings fun(self: Diesal.GUI.Object.Branch): boolean | nil
---@field Collapse fun(self: Diesal.GUI.Object.Branch)
---@field Expand fun(self: Diesal.GUI.Object.Branch)
---@field UpdateHeight fun(self: Diesal.GUI.Object.Branch)
---@field SetLabel fun(self: Diesal.GUI.Object.Branch, text: string)
---@field SetIconTexture fun(self: Diesal.GUI.Object.Branch, texture: string)
---@field SetIconCoords fun(self: Diesal.GUI.Object.Branch, left: number, right: number, top: number, bottom: number)
---@field SetIconExpanded fun(self: Diesal.GUI.Object.Branch)
---@field SetIconCollapsed fun(self: Diesal.GUI.Object.Branch)
---@field SetSelected fun(self: Diesal.GUI.Object.Branch, state: boolean)
---@field UpdateLines fun(self: Diesal.GUI.Object.Branch)
local methods = {
    ["OnAcquire"] = function(self)
        self:ApplySettings()
        self:SetStylesheet(Stylesheet)
        self:Show()
    end,
    ["OnRelease"] = function(self) end,
    ["ApplySettings"] = function(self)
        if not self.settings.position then
            return false
        end

        local button = self.button
        local content = self.content

        button:SetHeight(self.settings.buttonHeight)

        if not self.settings.leaf then
            self[self.settings.expanded and "Expand" or "Collapse"](self)
        else
            self:UpdateLines()
            self:UpdateHeight()
        end

        self:ClearAllPoints()
        if self.settings.position == 1 then
            self:SetPoint("TOPLEFT", self.settings.indent, 0)
            self:SetPoint("RIGHT")
        else
            local anchor = self.settings.parentObject.children[self.settings.position - 1].frame
            self:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, 0)
            self:SetPoint("RIGHT")
        end
    end,
    ["Collapse"] = function(self)
        if self.settings.leaf then
            return
        end
        self:FireEvent("OnStateChange", false)
        self.settings.expanded = false
        self:SetIconCollapsed()
        self:UpdateLines()
        self.content:Hide()
        self:UpdateHeight()
    end,
    ["Expand"] = function(self)
        if self.settings.leaf then
            return
        end
        self:FireEvent("OnStateChange", true)
        self.settings.expanded = true
        self:SetIconExpanded()
        self:UpdateLines()
        self.content:Show()
        self:UpdateHeight()
    end,
    ["UpdateHeight"] = function(self)
        local contentHeight = 0
        local settings = self.settings
        local children = self.children

        if settings.expanded then
            for i = 1, #children do
                contentHeight = contentHeight + children[i].frame:GetHeight()
            end
        end
        self.content:SetHeight(contentHeight)
        self:SetHeight(settings.buttonHeight + contentHeight)
        settings.parentObject:UpdateHeight()
    end,
    ["SetLabel"] = function(self, text)
        self.text:SetText(text)
    end,
    ["SetIconTexture"] = function(self, texture)
        self.icon:SetTexture(texture)
    end,
    ["SetIconCoords"] = function(self, left, right, top, bottom)
        self.icon:SetTexCoord(left, right, top, bottom)
    end,
    ["SetIconExpanded"] = function(self)
        self.icon:SetTexCoord(DiesalTools.GetIconCoords(2, 8, 16, 256, 128))
    end,
    ["SetIconCollapsed"] = function(self)
        self.icon:SetTexCoord(DiesalTools.GetIconCoords(1, 8, 16, 256, 128))
    end,
    ["SetSelected"] = function(self, state)
        self:UpdateStyle("button-selected", {
            type = "texture",
            alpha = state and 1 or 0,
        })
    end,
    ["UpdateLines"] = function(self)
        if not self.settings.branches then
            return
        end
        local foldColor = "353535"
        local expandColor = "5a5a5a"
        -- Frame Fold Line
        if not self.settings.last and not self.settings.leaf then
            self:UpdateStyle("frame-foldLine", {
                type = "texture",
                layer = "OVERLAY",
                color = foldColor,
                position = { 6, nil, -11, 2 },
                width = 1,
            })
        end
        -- expandable Fold Lines
        if not self.settings.leaf then
            self:UpdateStyle("button-square", {
                type = "outline",
                layer = "BORDER",
                color = foldColor,
                position = { 10, nil, -2, nil },
                width = 9,
                height = 9,
            })
            self:UpdateStyle("button-expandH", {
                type = "texture",
                layer = "BORDER",
                color = expandColor,
                position = { 8, nil, -6, nil },
                width = 5,
                height = 1,
            })
            self:UpdateStyle("button-expandV", {
                type = "texture",
                layer = "BORDER",
                color = expandColor,
                position = { 6, nil, -4, nil },
                width = 1,
                height = 5,
                alpha = 1,
            })
            if self.settings.expanded then
                self:UpdateStyle("button-expandV", {
                    type = "texture",
                    alpha = 0,
                })
            end
        else -- Leaf nodes
            self:UpdateStyle("button-lineV", {
                type = "texture",
                layer = "BORDER",
                color = foldColor,
                position = { 6, nil, -6, nil },
                height = 1,
                width = 6,
            })
            self:UpdateStyle("button-lineH", {
                type = "texture",
                layer = "BORDER",
                color = foldColor,
                position = { 6, nil, 0, 0 },
                width = 1,
            })
            if self.settings.last then
                self:UpdateStyle("button-lineH", {
                    type = "texture",
                    position = { 6, nil, 0, -7 },
                })
            end
        end
    end,
}

---@class DiesalBranch : Diesal.GUI.Object.Branch

---@class Diesal.GUI.Object.Branch.Settings.Default
---@field fontObject Font
---@field branches boolean
---@field expanded boolean
---@field buttonHeight number
---@field indent number
---@field expIconTex string
---@field colIconTex string
---@field expIconCoords {[1]: number, [2]: number, [3]: number, [4]: number }
---@field colIconCoords {[1]: number, [2]: number, [3]: number, [4]: number }

---@class Diesal.GUI.Object.Branch.Settings : Diesal.GUI.Object.Branch.Settings.Default
---@field parentObject Diesal.GUI.Object.Accordian.Section
---@field last boolean
---@field position number
---@field leaf boolean

-- ~~| TableExplorerBranch Constructor |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local function Constructor()
    ---@class Diesal.GUI.Object.Branch : Diesal.GUI.ObjectBase, Diesal.GUI.Branch.Methods
    ---@field button Button
    ---@field icon Texture
    ---@field text Diesal.GUI.Region.FontString
    ---@field content Frame
    ---@field defaults Diesal.GUI.Object.Branch.Settings.Default
    ---@field settings Diesal.GUI.Object.Branch.Settings
    local self = DiesalGUI:Create(Type, true)

    self.isContainer = true

    local frame = CreateFrame("Frame", nil, UIParent)
    self.frame = frame
    -- ~~ Default Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    self.defaults = {
        fontObject = DiesalFontNormal,
        branches = true,
        expanded = true,
        buttonHeight = 14,
        indent = 13,
        expIconTex = "DiesalGUIcons",
        colIconTex = "DiesalGUIcons",
        expIconCoords = { DiesalTools.GetIconCoords(2, 8, 16, 256, 128) },
        colIconCoords = { DiesalTools.GetIconCoords(1, 8, 16, 256, 128) },
    }
    -- ~~ Events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -- OnAcquire, OnRelease, OnHeightSet, OnWidthSet
    -- ~~ Construct ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    local button = self:CreateRegion("Button", "button", frame)
    button:SetPoint("TOPRIGHT")
    button:SetPoint("TOPLEFT")
    button:RegisterForClicks("RightButtonUp", "LeftButtonUp")
    button:RegisterForDrag("LeftButton")
    ---@param this Button
    ---@param button MouseButton
    button:SetScript("OnClick", function(this, button)
        DiesalGUI:OnMouse(this, button)
        if button == "LeftButton" then
            self[not self.settings.expanded and "Expand" or "Collapse"](self)
        end
        self:FireEvent("OnClick", button)
    end)
    ---@param this Button
    button:SetScript("OnDoubleClick", function(this, ...)
        self:FireEvent("OnDoubleClick", ...)
    end)
    ---@param this Button
    button:SetScript("OnDragStart", function(this, ...)
        self:FireEvent("OnDragStart", ...)
    end)
    ---@param this Button
    button:SetScript("OnDragStop", function(this, ...)
        self:FireEvent("OnDragStop", ...)
    end)
    ---@param this Button
    button:SetScript("OnEnter", function(this, ...)
        self:FireEvent("OnEnter", ...)
    end)
    ---@param this Button
    button:SetScript("OnLeave", function(this, ...)
        self:FireEvent("OnLeave", ...)
    end)

    local icon = self:CreateRegion("Texture", "icon", button)
    DiesalStyle:StyleTexture(icon, {
        type = "texture",
        position = { 0, nil, 2, nil },
        height = 16,
        width = 16,
        image = { "DiesalGUIcons" },
    })

    local text = self:CreateRegion("FontString", "text", button)
    text:SetPoint("TOPLEFT", 14, -1)
    text:SetPoint("BOTTOMRIGHT", -4, 0)
    text:SetHeight(0)
    text:SetJustifyH("TOP")
    text:SetJustifyH("LEFT")
    text:SetWordWrap(false)

    local content = self:CreateRegion("Frame", "content", button)
    content:SetPoint("TOPLEFT", button, "BOTTOMLEFT", 0, 0)
    content:SetPoint("TOPRIGHT")
    content:SetHeight(0)
    -- ~~ Methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    self:SetMethods(methods)
    --[[ for method, func in pairs(methods) do
        self[method] = func
    end ]]
    -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    return self
end
DiesalGUI:RegisterObjectConstructor(Type, Constructor, Version)
