
---@class detailsframework
local detailsFramework = _G.DetailsFramework
if (not detailsFramework or not DetailsFrameworkCanLoad) then
	return
end

local CreateFrame = CreateFrame
local defaultRed, defaultGreen, defaultBlue = detailsFramework:GetDefaultBackdropColor()
--local defaultColorTable = {defaultRed, defaultGreen, defaultBlue, 1}
local defaultColorTable = {0.98, 0.98, 0.98, 1}
local defaultBorderColorTable = {0.1, 0.1, 0.1, 1}

---@type edgenames[]
local cornerNames = {"TopLeft", "TopRight", "BottomLeft", "BottomRight"}

---@param self df_roundedpanel
---@param textures cornertextures
---@param width number|nil
---@param height number|nil
---@param xOffset number|nil
---@param yOffset number|nil
---@param bIsBorder boolean|nil
local setCornerPoints = function(self, textures, width, height, xOffset, yOffset, bIsBorder)
    for cornerName, thisTexture in pairs(textures) do
        thisTexture:SetSize(width or 16, height or 16)
        thisTexture:SetTexture(self.options.corner_texture)

        xOffset = xOffset or 0
        yOffset = yOffset or 0

        --todo: adjust the other corners setpoint offset
        --todo (done): use mask when the alpha is below 0.98, disable the mask when the alpha is above 0.98

        if (cornerName == "TopLeft") then
            thisTexture:SetTexCoord(0, 0.5, 0, 0.5)
            thisTexture:SetPoint(cornerName, self, cornerName, -xOffset, yOffset)

        elseif (cornerName == "TopRight") then
            thisTexture:SetTexCoord(0.5, 1, 0, 0.5)
            thisTexture:SetPoint(cornerName, self, cornerName, xOffset, yOffset)

        elseif (cornerName == "BottomLeft") then
            thisTexture:SetTexCoord(0, 0.5, 0.5, 1)
            thisTexture:SetPoint(cornerName, self, cornerName, -xOffset, -yOffset)

        elseif (cornerName == "BottomRight") then
            thisTexture:SetTexCoord(0.5, 1, 0.5, 1)
            thisTexture:SetPoint(cornerName, self, cornerName, xOffset, -yOffset)
        end
    end
end

detailsFramework.RoundedCornerPanelMixin = {
    RoundedCornerConstructor = function(self)
        self.CornerTextures = {}
        self.CenterTextures = {}
        self.BorderCornerTextures = {}
        self.BorderEdgeTextures = {}

        self.cornerRoundness = 0

        for i = 1, #cornerNames do
            ---@type texture
            local newCornerTexture = self:CreateTexture(nil, "border", nil, 0)
            self.CornerTextures[cornerNames[i]] = newCornerTexture
            self[cornerNames[i]] = newCornerTexture
        end

        --create the top texture which connects the top corners with a horizontal line
        ---@type texture
        local topHorizontalEdge = self:CreateTexture(nil, "border", nil, 0)
        topHorizontalEdge:SetPoint("topleft", self.CornerTextures["TopLeft"], "topright", 0, 0)
        topHorizontalEdge:SetPoint("bottomleft", self.CornerTextures["TopLeft"], "bottomright", 0, 0)
        topHorizontalEdge:SetPoint("topright", self.CornerTextures["TopRight"], "topleft", 0, 0)
        topHorizontalEdge:SetPoint("bottomright", self.CornerTextures["TopRight"], "bottomleft", 0, 0)
        topHorizontalEdge:SetTexture(unpack(defaultColorTable))

        --create the bottom texture which connects the bottom corners with a horizontal line
        ---@type texture
        local bottomHorizontalEdge = self:CreateTexture(nil, "border", nil, 0)
        bottomHorizontalEdge:SetPoint("topleft", self.CornerTextures["BottomLeft"], "topright", 0, 0)
        bottomHorizontalEdge:SetPoint("bottomleft", self.CornerTextures["BottomLeft"], "bottomright", 0, 0)
        bottomHorizontalEdge:SetPoint("topright", self.CornerTextures["BottomRight"], "topleft", 0, 0)
        bottomHorizontalEdge:SetPoint("bottomright", self.CornerTextures["BottomRight"], "bottomleft", 0, 0)
        bottomHorizontalEdge:SetTexture(unpack(defaultColorTable))

        --create the center block which connects the bottom left of the topleft corner with the top right of the bottom right corner
        ---@type texture
        local centerBlock = self:CreateTexture(nil, "border", nil, 0)
        centerBlock:SetPoint("topleft", self.CornerTextures["TopLeft"], "bottomleft", 0, 0)
        centerBlock:SetPoint("bottomleft", self.CornerTextures["BottomLeft"], "topleft", 0, 0)
        centerBlock:SetPoint("topright", self.CornerTextures["BottomRight"], "topright", 0, 0)
        centerBlock:SetPoint("bottomright", self.CornerTextures["BottomRight"], "topright", 0, 0)
        centerBlock:SetTexture(unpack(defaultColorTable))

        self.CenterTextures[#self.CenterTextures+1] = topHorizontalEdge
        self.CenterTextures[#self.CenterTextures+1] = bottomHorizontalEdge
        self.CenterTextures[#self.CenterTextures+1] = centerBlock

        self.TopHorizontalEdge = topHorizontalEdge
        self.BottomHorizontalEdge = bottomHorizontalEdge
        self.CenterBlock = centerBlock

        ---@type width
        local width = self.options.width
        ---@type height
        local height = self.options.height

        self:SetSize(width, height)

        --fill the corner and edge textures table
        setCornerPoints(self, self.CornerTextures)
    end,

    ---get the highest frame level of the rounded panel and its children
    ---@param self df_roundedpanel
    ---@return framelevel
    GetMaxFrameLevel = function(self)
        ---@type framelevel
        local maxFrameLevel = 0
        local children = {self:GetChildren()}

        for i = 1, #children do
            local thisChild = children[i]
            ---@cast thisChild frame
            if (thisChild:GetFrameLevel() > maxFrameLevel) then
                maxFrameLevel = thisChild:GetFrameLevel()
            end
        end

        return maxFrameLevel
    end,

    ---create a frame placed at the top side of the rounded panel, this frame has a member called 'Text' which is a fontstring for the title
    ---@param self df_roundedpanel
    ---@return df_roundedpanel
    CreateTitleBar = function(self)
        ---@type df_roundedpanel
        local titleBar = detailsFramework:CreateRoundedPanel(self, "$parentTitleBar", {width = self.options.width - 6, height = 16})
        titleBar:SetPoint("top", self, "top", 0, -4)
        titleBar:SetRoundness(5)
        titleBar:SetFrameLevel(9500)
        titleBar.bIsTitleBar = true
        self.TitleBar = titleBar
        self.bHasTitleBar = true

        local textFontString = titleBar:CreateFontString("$parentText", "overlay", "GameFontNormal")
        textFontString:SetPoint("center", titleBar, "center", 0, 0)
        titleBar.Text = textFontString

        local closeButton = detailsFramework:CreateCloseButton(titleBar, "$parentCloseButton")
        closeButton:SetPoint("right", titleBar, "right", -3, 0)
		closeButton:SetSize(10, 10)
		closeButton:SetAlpha(0.3)
        closeButton:SetScript("OnClick", function(self)
            self:GetParent():GetParent():Hide()
        end)
        detailsFramework:SetButtonTexture(closeButton, "common-search-clearbutton")

        return titleBar
    end,

    ---return the width and height of the corner textures
    ---@param self df_roundedpanel
    ---@return number, number
    GetCornerSize = function(self)
        return self.CornerTextures["TopLeft"]:GetSize()
    end,

    ---set how rounded the corners should be
    ---@param self df_roundedpanel
    ---@param roundness number
    SetRoundness = function(self, roundness)
        self.cornerRoundness = roundness
        self:OnSizeChanged()
    end,

    ---adjust the size of the corner textures and the border edge textures
    ---@param self df_roundedpanel
    OnSizeChanged = function(self)
        --if the frame has a titlebar, need to adjust the size of the titlebar
        if (self.bHasTitleBar) then
            self.TitleBar:SetWidth(self:GetWidth() - 14)
        end

        --if the frame height is below 32, need to recalculate the size of the corners
        ---@type height
        local frameHeight = self:GetHeight()

        if (frameHeight < 32) then
            local newCornerSize = frameHeight / 2

            --set the new size of the corners on all corner textures
            for _, thisTexture in pairs(self.CornerTextures) do
                thisTexture:SetSize(newCornerSize - (self.cornerRoundness - 2), newCornerSize)
            end

            --check if the frame has border and set the size of the border corners as well
            if (self.bHasBorder) then
                for _, thisTexture in pairs(self.BorderCornerTextures) do
                    thisTexture:SetSize(newCornerSize-2, newCornerSize+2)
                end

                --hide the left and right edges as the corner textures already is enough to fill the frame
                self.BorderEdgeTextures["Left"]:Hide()
                self.BorderEdgeTextures["Right"]:Hide()

                local horizontalEdgesNewSize = self:CalculateBorderEdgeSize("horizontal")
                self.BorderEdgeTextures["Top"]:SetSize(horizontalEdgesNewSize + (self.options.horizontal_border_size_offset or 0), 1)
                self.BorderEdgeTextures["Bottom"]:SetSize(horizontalEdgesNewSize + (self.options.horizontal_border_size_offset or 0), 1)
            end

            self.CenterBlock:Hide()
        else
            if (self.bHasBorder) then
                self.BorderEdgeTextures["Left"]:Show()
                self.BorderEdgeTextures["Right"]:Show()
            end

            ---@type width, height
            local cornerWidth, cornerHeight = 16, 16

            self.CenterBlock:Show()

            for _, thisTexture in pairs(self.CornerTextures) do
                thisTexture:SetSize(cornerWidth-self.cornerRoundness, cornerHeight-self.cornerRoundness)
            end

            if (self.bHasBorder) then
                for _, thisTexture in pairs(self.BorderCornerTextures) do
                    thisTexture:SetSize(cornerWidth-self.cornerRoundness, cornerHeight-self.cornerRoundness)
                end

                local horizontalEdgesNewSize = self:CalculateBorderEdgeSize("horizontal")
                self.BorderEdgeTextures["Top"]:SetSize(horizontalEdgesNewSize, 1)
                self.BorderEdgeTextures["Bottom"]:SetSize(horizontalEdgesNewSize, 1)

                local verticalEdgesNewSize = self:CalculateBorderEdgeSize("vertical")
                self.BorderEdgeTextures["Left"]:SetSize(1, verticalEdgesNewSize)
                self.BorderEdgeTextures["Right"]:SetSize(1, verticalEdgesNewSize)
            end
        end
    end,

    ---get the size of the edge texture
    ---@param self df_roundedpanel
    ---@param alignment "vertical"|"horizontal"
    ---@return number edgeSize
    CalculateBorderEdgeSize = function(self, alignment)
        ---@type string
        local borderCornerName = next(self.BorderCornerTextures)
        if (not borderCornerName) then
            return 0
        end

        ---@type texture
        local borderTexture = self.BorderCornerTextures[borderCornerName]

        alignment = alignment:lower()

        if (alignment == "vertical") then
            return self:GetHeight() - (borderTexture:GetHeight() * 2) + 2

        elseif (alignment == "horizontal") then
            return self:GetWidth() - (borderTexture:GetHeight() * 2) + 2
        end

        error("df_roundedpanel:CalculateBorderEdgeSize(self, alignment) alignment must be 'vertical' or 'horizontal'")
    end,

    ---@param self df_roundedpanel
    CreateBorder = function(self)
        local r, g, b, a = 0, 0, 0, 0.8

        --create the corner edges
        for i = 1, #cornerNames do
            ---@type texture
            local newBorderTexture = self:CreateTexture(nil, "background", nil, 0)
            self.BorderCornerTextures[cornerNames[i]] = newBorderTexture
            newBorderTexture:SetTexture(unpack(defaultColorTable))
            newBorderTexture:SetVertexColor(r, g, b, a)
            self[cornerNames[i] .. "Border"] = newBorderTexture
        end

        setCornerPoints(self, self.BorderCornerTextures, 16, 16, 1, 1, true)

        --create the top, left, bottom and right edges, the edge has 1pixel width and connects the corners
        ---@type texture
        local topEdge = self:CreateTexture(nil, "background", nil, 0)
        topEdge:SetPoint("bottom", self, "top", 0, 0)
        self.BorderEdgeTextures["Top"] = topEdge

        ---@type texture
        local leftEdge = self:CreateTexture(nil, "background", nil, 0)
        leftEdge:SetPoint("right", self, "left", 0, 0)
        self.BorderEdgeTextures["Left"] = leftEdge

        ---@type texture
        local bottomEdge = self:CreateTexture(nil, "background", nil, 0)
        bottomEdge:SetPoint("top", self, "bottom", 0, 0)
        self.BorderEdgeTextures["Bottom"] = bottomEdge

        ---@type texture
        local rightEdge = self:CreateTexture(nil, "background", nil, 0)
        rightEdge:SetPoint("left", self, "right", 0, 0)
        self.BorderEdgeTextures["Right"] = rightEdge

        ---@type width
        local horizontalEdgeSize = self:CalculateBorderEdgeSize("horizontal")
        ---@type height
        local verticalEdgeSize = self:CalculateBorderEdgeSize("vertical")

        --set the edges size
        topEdge:SetSize(horizontalEdgeSize, 1)
        leftEdge:SetSize(1, verticalEdgeSize)
        bottomEdge:SetSize(horizontalEdgeSize, 1)
        rightEdge:SetSize(1, verticalEdgeSize)

        for edgeName, thisTexture in pairs(self.BorderEdgeTextures) do
            ---@cast thisTexture texture
            thisTexture:SetTexture(unpack(defaultColorTable))
            thisTexture:SetVertexColor(r, g, b, a)
        end

        self.TopEdgeBorder = topEdge
        self.BottomEdgeBorder = bottomEdge
        self.LeftEdgeBorder = leftEdge
        self.RightEdgeBorder = rightEdge

        self.bHasBorder = true
    end,

    ---@param self df_roundedpanel
    ---@param red any
    ---@param green number|nil
    ---@param blue number|nil
    ---@param alpha number|nil
    SetTitleBarColor = function(self, red, green, blue, alpha)
        if (self.bHasTitleBar) then
            red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)
            self.TitleBar:SetColor(red, green, blue, alpha)
        end
    end,

    ---@param self df_roundedpanel
    ---@param red any
    ---@param green number|nil
    ---@param blue number|nil
    ---@param alpha number|nil
    SetBorderCornerColor = function(self, red, green, blue, alpha)
        if (not self.bHasBorder) then
            self:CreateBorder()
        end

        red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)

        for _, thisTexture in pairs(self.BorderCornerTextures) do
            thisTexture:SetVertexColor(red, green, blue, alpha)
        end

        for _, thisTexture in pairs(self.BorderEdgeTextures) do
            thisTexture:SetVertexColor(red, green, blue, alpha)
        end
    end,

    ---@param self df_roundedpanel
    ---@param red any
    ---@param green number|nil
    ---@param blue number|nil
    ---@param alpha number|nil
    SetColor = function(self, red, green, blue, alpha)
        red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)

        for _, thisTexture in pairs(self.CornerTextures) do
            thisTexture:SetVertexColor(red, green, blue, alpha)
        end

        for _, thisTexture in pairs(self.CenterTextures) do
            thisTexture:SetVertexColor(red, green, blue, alpha)
        end
    end,
}

local defaultOptions = {
    width = 200,
    height = 200,
    use_titlebar = false,
    use_scalebar = false,
    title = "",
    scale = 1,
    roundness = 0,
    color = defaultColorTable,
    border_color = defaultColorTable,
    corner_texture = [[Interface\CHARACTERFRAME\TempPortraitAlphaMaskSmall]],
}

local defaultPreset = {
    border_color = {.1, .1, .1, 0.834},
    color = {defaultRed, defaultGreen, defaultBlue},
    roundness = 3,
}

---create a regular panel with rounded corner
---@param parent frame
---@param name string|nil
---@param optionsTable table|nil
---@return df_roundedpanel
function detailsFramework:CreateRoundedPanel(parent, name, optionsTable)
    ---@type df_roundedpanel
    local newRoundedPanel = CreateFrame("frame", name, parent, "BackdropTemplate")
    newRoundedPanel:EnableMouse(true)
    newRoundedPanel.__dftype = "df_roundedpanel"
    newRoundedPanel.__rcorners = true

    detailsFramework:Mixin(newRoundedPanel, detailsFramework.RoundedCornerPanelMixin)
    detailsFramework:Mixin(newRoundedPanel, detailsFramework.OptionsFunctions)
    newRoundedPanel:BuildOptionsTable(defaultOptions, optionsTable or {})
    newRoundedPanel:RoundedCornerConstructor()
    newRoundedPanel:SetScript("OnSizeChanged", newRoundedPanel.OnSizeChanged)

    if (newRoundedPanel.options.use_titlebar) then
        ---@type df_roundedpanel
        local titleBar = detailsFramework:CreateRoundedPanel(newRoundedPanel, "$parentTitleBar", {height = 26})
        titleBar:SetPoint("top", newRoundedPanel, "top", 0, -7)
        newRoundedPanel.TitleBar = titleBar
        titleBar:SetRoundness(5)
        newRoundedPanel.bHasTitleBar = true
    end

    if (newRoundedPanel.options.use_scalebar) then
        detailsFramework:CreateScaleBar(newRoundedPanel.TitleBar or newRoundedPanel, newRoundedPanel.options)
        newRoundedPanel:SetScale(newRoundedPanel.options.scale)
    end

    newRoundedPanel:SetRoundness(newRoundedPanel.options.roundness)
    newRoundedPanel:SetColor(newRoundedPanel.options.color)
    newRoundedPanel:SetBorderCornerColor(newRoundedPanel.options.border_color)

    return newRoundedPanel
end

local applyPreset = function(frame, preset)
    if (preset.border_color) then
        frame:SetBorderCornerColor(preset.border_color)
    end

    if (preset.color) then
        frame:SetColor(preset.color)
    end

    if (preset.roundness) then
        frame:SetRoundness(preset.roundness)
    else
        frame:SetRoundness(1)
    end

    if (preset.use_titlebar) then
        frame:CreateTitleBar()
    end
end

---set a frame to have rounded corners following the settings passed by the preset table
---@param frame frame
---@param preset df_roundedpanel_preset?
function detailsFramework:AddRoundedCornersToFrame(frame, preset)
    frame = frame and frame.widget or frame
    assert(frame and frame.GetObjectType and frame.SetPoint, "AddRoundedCornersToFrame(frame): frame must be a frame object.")

    if (frame.__rcorners) then
        return
    end

    if (frame.GetBackdropBorderColor) then
        local red, green, blue, alpha = frame:GetBackdropBorderColor()
        if (alpha and alpha > 0) then
            detailsFramework:MsgWarning("AddRoundedCornersToFrame() applyed to a frame with a backdrop border.")
            detailsFramework:Msg(debugstack(2, 1, 0))
        end
    end

    ---@cast frame +df_roundedcornermixin
    detailsFramework:Mixin(frame, detailsFramework.RoundedCornerPanelMixin)

    if (not frame["BuildOptionsTable"]) then
        ---@cast frame +df_optionsmixin
        detailsFramework:Mixin(frame, detailsFramework.OptionsFunctions)
    end

    frame:BuildOptionsTable(defaultOptions, {})

    frame.options.width = frame:GetWidth()
    frame.options.height = frame:GetHeight()

    frame:RoundedCornerConstructor()
    frame:HookScript("OnSizeChanged", frame.OnSizeChanged)

    frame.__rcorners = true

    --handle preset
    if (preset and type(preset) == "table") then
        frame.options.horizontal_border_size_offset = preset.horizontal_border_size_offset
        applyPreset(frame, preset)
    else
        applyPreset(frame, defaultPreset)
    end
end

---test case:
C_Timer.After(1, function()

    if true then return end

    local DF = DetailsFramework

    local parent = UIParent
    local name = "NewRoundedCornerFrame"
    local optionsTable = {
        use_titlebar = true,
        use_scalebar = true,
        title = "Test",
        scale = 1.0,
    }

    ---@type df_roundedpanel
    local frame = _G[name] or DF:CreateRoundedPanel(parent, name, optionsTable)
    frame:SetSize(800, 600)
    frame:SetPoint("center", parent, "center", 0, 0)

    frame:SetColor(.1, .1, .1, 1)
    frame:SetTitleBarColor(.2, .2, .2, .5)
    frame:SetBorderCornerColor(.2, .2, .2, .5)
    frame:SetRoundness(0)

    local radiusSlider = DF:CreateSlider(frame, 120, 14, 0, 15, 1, frame.cornerRoundness, false, "RadiusBar", nil, nil, DF:GetTemplate("slider", "OPTIONS_SLIDER_TEMPLATE"))
    radiusSlider:SetHook("OnValueChange", function(self, fixedValue, value)
        value = floor(value)
        if (frame.cornerRoundness == value) then
            return
        end
        frame:SetRoundness(value)
    end)

    local radiusText = frame:CreateFontString(nil, "overlay", "GameFontNormal")
    radiusText:SetText("Radius:")
    radiusText:SetPoint("bottomleft", radiusSlider.widget, "topleft", 0, 0)
    radiusSlider:SetPoint(10, -100)
end)