
local detailsFramework = DetailsFramework

if (not detailsFramework or not DetailsFrameworkCanLoad) then
	return
end

local unpack = unpack
local CreateFrame = CreateFrame
local geterrorhandler = geterrorhandler
local wipe = wipe

--definitions

---@class df_headercolumndata : table
---@field key string?
---@field name string?
---@field icon string?
---@field texcoord table?
---@field text string?
---@field canSort boolean?
---@field selected boolean?
---@field width number?
---@field height number?
---@field align string?
---@field offset number?
---@field columnSpan number?

---@class df_headerchild : uiobject
---@field FramesToAlign table

---@class df_headerframe : frame, df_headermixin, df_optionsmixin
---@field columnOrder string
---@field columnHeadersCreated df_headercolumnframe[]
---@field options table
---@field HeaderTable df_headercolumndata[]
---@field columnSelected number

---@class df_headermixin : table
---@field NextHeader number
---@field HeaderWidth number
---@field HeaderHeight number
---@field OnColumnSettingChangeCallback function
---@field GetColumnWidth fun(self: df_headerframe, columnId: number) : number
---@field SetHeaderTable fun(self: df_headerframe, table)
---@field GetSelectedColumn fun(self: df_headerframe) : number, string, string, string
---@field Refresh fun(self: df_headerframe)
---@field UpdateSortArrow fun(self: df_headerframe, columnHeader: df_headercolumnframe, defaultShown: boolean|nil, defaultOrder: string|nil)
---@field UpdateColumnHeader fun(self: df_headerframe, columnHeader: df_headercolumnframe, headerIndex)
---@field ResetColumnHeaderBackdrop fun(self: df_headerframe, columnHeader: df_headercolumnframe)
---@field SetBackdropColorForSelectedColumnHeader fun(self: df_headerframe, columnHeader: df_headercolumnframe)
---@field ClearColumnHeader fun(self: df_headerframe, columnHeader: df_headercolumnframe)
---@field DoesColumnExists fun(self: df_headerframe, columnId: number) : boolean
---@field GetNextHeader fun(self: df_headerframe) : df_headercolumnframe internal use only
---@field GetHeaderTableByIndex fun(self: df_headerframe, columnId: number) : df_headercolumndata
---@field SetColumnSettingChangedCallback fun(self: df_headerframe, func: function) : boolean
---@field ResetFramesToHeaderAlignment fun(self: df_headerframe)
---@field GetFramesFromHeaderAlignment fun(self: df_headerframe) : table
---@field GetSelectedHeaderColumnData fun(self: df_headerframe) : df_headercolumndata
---@field GetHeaderTable fun(self: df_headerframe) : df_headercolumndata[]
---@field PropagateClicks fun(self: df_headerframe, propagate: boolean) if the header and its children should propagate clicks

---@class df_headercolumnframe : button
---@field Icon texture
---@field Text fontstring
---@field Arrow texture
---@field Separator texture
---@field resizerButton df_headerresizer
---@field bIsRezising boolean
---@field bInUse boolean
---@field columnData table
---@field order string
---@field columnIndex number
---@field columnAlign string
---@field XPosition number
---@field YPosition number
---@field columnOffset number
---@field key string used to sort the values

---@class df_headerresizer : button
---@field texture texture

--mixed functions
---@class df_headerfunctions : table
detailsFramework.HeaderFunctions = {
    ---comment
    ---@param self df_headerchild
    ---@param frame uiobject
	AddFrameToHeaderAlignment = function(self, frame)
		self.FramesToAlign = self.FramesToAlign or {}
		table.insert(self.FramesToAlign, frame)
	end,

    ---comment
    ---@param self df_headerchild
	ResetFramesToHeaderAlignment = function(self)
		self.FramesToAlign = self.FramesToAlign or {}
		--hide all frames
		for i = 1, #self.FramesToAlign do
			local uiObject = self.FramesToAlign[i]
			uiObject:Hide()
		end
		wipe(self.FramesToAlign)
	end,

	SetFramesToHeaderAlignment = function(self, ...)
		self.FramesToAlign = self.FramesToAlign or {}
        ---@cast self df_headerchild
		wipe(self.FramesToAlign)
		self.FramesToAlign = {...}
	end,

	GetFramesFromHeaderAlignment = function(self)
		self.FramesToAlign = self.FramesToAlign or {}
		return self.FramesToAlign
	end,

	---@param self uiobject
	---@param headerFrame df_headerframe
	---@param anchor string
	AlignWithHeader = function(self, headerFrame, anchor)
		assert(type(headerFrame) == "table" and headerFrame.GetColumnWidth, "Details! Framework: AlignWithHeader(): 'headerFrame' is not of type df_headerframe.\n" .. debugstack(2, 1, 1))

		local columnHeaderFrames = headerFrame.columnHeadersCreated
		anchor = anchor or "topleft"

        ---@cast self df_headerchild

		for i = 1, #self.FramesToAlign do
			---@type uiobject
			local uiObject = self.FramesToAlign[i]
			uiObject:ClearAllPoints()

			---@type df_headercolumnframe
			local columnHeader = columnHeaderFrames[i]
			if (columnHeader) then
				local offset = 0

				if (columnHeader.columnAlign == "right") then
					offset = columnHeader:GetWidth()
				elseif (columnHeader.columnAlign == "center") then
					offset = columnHeader:GetWidth() / 2
				end

				if (uiObject:GetObjectType() == "FontString") then
					---@cast uiObject fontstring
					if (columnHeader.columnAlign == "right") then
						uiObject:SetJustifyH("right")
					elseif (columnHeader.columnAlign == "left") then
						uiObject:SetJustifyH("left")
					elseif (columnHeader.columnAlign == "center") then
						uiObject:SetJustifyH("center")
					end
				end

				uiObject:SetPoint(columnHeader.columnAlign, self, anchor, columnHeader.XPosition + columnHeader.columnOffset + offset, 0)
				uiObject:Show()
			end
		end
	end,

	---comment
	---@param newColumnHeaderToSort df_headercolumnframe
	---@param buttonClicked string
	OnClick = function(newColumnHeaderToSort, buttonClicked)
		--get the header main frame
		local headerFrame = newColumnHeaderToSort:GetParent()
		---@cast headerFrame df_headerframe

		--if this header does not have a clickable header, just ignore
		if (not headerFrame.columnSelected) then
			return
		end

		--check if this column has 'canSort' key, otherwise ignore the click
		if (not newColumnHeaderToSort.columnData.canSort) then
			return
		end

		--get the latest column header selected and reset its backdrop and hide the arrow
		---@type df_headercolumnframe
		local previousColumnHeader = headerFrame.columnHeadersCreated[headerFrame.columnSelected]
		previousColumnHeader.Arrow:Hide()
		headerFrame:ResetColumnHeaderBackdrop(previousColumnHeader)

		headerFrame:SetBackdropColorForSelectedColumnHeader(newColumnHeaderToSort)

		if (headerFrame.columnSelected == newColumnHeaderToSort.columnIndex) then
			newColumnHeaderToSort.order = newColumnHeaderToSort.order ~= "ASC" and "ASC" or "DESC"
		end
		headerFrame.columnOrder = newColumnHeaderToSort.order

		--set the new column header selected
		headerFrame.columnSelected = newColumnHeaderToSort.columnIndex

		headerFrame:UpdateSortArrow(newColumnHeaderToSort)

		if (headerFrame.options.header_click_callback) then
			--callback with the main header frame, column header, column index and column order as payload
			local okay, errortext = xpcall(headerFrame.options.header_click_callback, geterrorhandler(), headerFrame, newColumnHeaderToSort, newColumnHeaderToSort.columnIndex, newColumnHeaderToSort.order)
			if (not okay) then
				print("DF: Header onClick callback error:", errortext)
			end
		end
	end,

	---comment
	---@param self button
	---@param buttonClicked string
	OnMouseDown = function(self, buttonClicked)
		if (buttonClicked == "LeftButton") then

		end
	end,

	---comment
	---@param self button
	---@param buttonClicked string
	OnMouseUp = function(self, buttonClicked)
		if (buttonClicked == "LeftButton") then

		end
	end,

	PropagateClicks = function(self, propagate)
		self.options.propagate_clicks = propagate
		self:Refresh()
	end,
}

---@class df_headermixin : table
detailsFramework.HeaderMixin = {
	---@param self df_headerframe
	---@param columnId number
	---@return number
	GetColumnWidth = function(self, columnId)
		assert(self.HeaderTable[columnId], "Header:GetColumnWidth(): 'columnId' does not exist in the HeaderTable.")
		return self.HeaderTable[columnId].width
	end,

	GetHeaderTableByIndex = function(self, columnId)
		return self.HeaderTable[columnId]
	end,

	DoesColumnExists = function(self, columnId)
		return self.HeaderTable[columnId] ~= nil
	end,

	---get header table
	---@param self df_headerframe
	---@return df_headercolumndata[]
	GetHeaderTable = function(self)
		return self.HeaderTable
	end,

	---@param self df_headerframe
	---@param newTable table
	SetHeaderTable = function(self, newTable)
		self.columnHeadersCreated = self.columnHeadersCreated or {}
		self.HeaderTable = newTable
		self.NextHeader = 1
		self.HeaderWidth = 0
		self.HeaderHeight = 0
		self:Refresh()
	end,

	---@param self df_headerframe
	---@param func function
	---@return boolean
	SetColumnSettingChangedCallback = function(self, func)
		if (type(func) ~= "function") then
			self.OnColumnSettingChangeCallback = nil
			return false
		end
		self.OnColumnSettingChangeCallback = func
		return true
	end,

	--return which header is current selected and the the order ASC DESC
	---@param self df_headerframe
	---@return number, string, string, string
	GetSelectedColumn = function(self)
		---@type number
		local columnSelected = self.columnSelected
		---@type df_headercolumnframe
		local columnHeader = self.columnHeadersCreated[columnSelected or 1]
		return columnSelected, columnHeader.order, columnHeader.key, columnHeader.columnData.name
	end,

	---get selected header column data
	---@param self df_headerframe
	---@return df_headercolumndata
	GetSelectedHeaderColumnData = function(self)
		local columnSelected = self.columnSelected
		return self.HeaderTable[columnSelected]
	end,

	--clean up and rebuild the header following the header options
	--@self: main header frame
	---@param self df_headerframe
	Refresh = function(self)
		--refresh background frame
		self:SetBackdrop(self.options.backdrop)
		self:SetBackdropColor(unpack(self.options.backdrop_color))
		self:SetBackdropBorderColor(unpack(self.options.backdrop_border_color))

		--reset all header frames
		for i = 1, #self.columnHeadersCreated do
			local columnHeader = self.columnHeadersCreated[i]
			columnHeader.bInUse = false
			columnHeader:Hide()
		end

		local previousColumnHeader
		local growDirection = string.lower(self.options.grow_direction)

		--amount of headers to be updated
		local headerSize = #self.HeaderTable

		local columnSpan = 0

		if not InCombatLockdown() then
			self:SetPropagateMouseClicks(self.options.propagate_clicks)
		end

		--update header frames
		for headerIndex = 1, headerSize do
			--get the header button, a new one is created if it doesn't exists yet
			local columnHeader = self:GetNextHeader()
			local columnData = self.HeaderTable[headerIndex]
			self:UpdateColumnHeader(columnHeader, headerIndex)

			if not InCombatLockdown() then
				columnHeader:SetPropagateMouseClicks(self.options.propagate_clicks)
			end

			--grow direction
			if (not previousColumnHeader) then
				columnHeader:SetPoint("topleft", self, "topleft", 0, 0)

				if (growDirection == "right") then
					if (self.options.use_line_separators) then
						columnHeader.Separator:Show()
						columnHeader.Separator:SetWidth(self.options.line_separator_width)
						columnHeader.Separator:SetColorTexture(unpack(self.options.line_separator_color))

						columnHeader.Separator:ClearAllPoints()
						if (self.options.line_separator_gap_align) then
							columnHeader.Separator:SetPoint("topleft", columnHeader, "topright", 0, 0)
						else
							columnHeader.Separator:SetPoint("topright", columnHeader, "topright", 0, 0)
						end
						columnHeader.Separator:SetHeight(self.options.line_separator_height)
					end
				end
			else
				if (growDirection == "right") then
					if (columnSpan > 0) then
						columnHeader:Hide()
						columnHeader.Separator:Hide()
						previousColumnHeader:SetWidth(previousColumnHeader:GetWidth() + columnHeader:GetWidth() + self.options.padding)
					else
						columnHeader:SetPoint("topleft", previousColumnHeader, "topright", self.options.padding, 0)

						if (self.options.use_line_separators) then
							columnHeader.Separator:Show()
							columnHeader.Separator:SetWidth(self.options.line_separator_width)
							columnHeader.Separator:SetColorTexture(unpack(self.options.line_separator_color))

							columnHeader.Separator:ClearAllPoints()
							if (self.options.line_separator_gap_align) then
								columnHeader.Separator:SetPoint("topleft", columnHeader, "topright", 0, 0)
							else
								columnHeader.Separator:SetPoint("topleft", columnHeader, "topright", 0, 0)
							end
							columnHeader.Separator:SetHeight(self.options.line_separator_height)

							if (headerSize == headerIndex) then
								columnHeader.Separator:Hide()
							end
						end
					end

				elseif (growDirection == "left") then
					columnHeader:SetPoint("topright", previousColumnHeader, "topleft", -self.options.padding, 0)

				elseif (growDirection == "bottom") then
					columnHeader:SetPoint("topleft", previousColumnHeader, "bottomleft", 0, -self.options.padding)

				elseif (growDirection == "top") then
					columnHeader:SetPoint("bottomleft", previousColumnHeader, "topleft", 0, self.options.padding)
				end
			end

			previousColumnHeader = columnHeader

			if (columnSpan > 0) then
				columnSpan = columnSpan - 1
			else
				columnSpan = columnData.columnSpan or 0
			end
		end

		self:SetSize(self.HeaderWidth, self.HeaderHeight)
	end,

	---@param self df_headerframe
	---@param columnHeader df_headercolumnframe
	---@param defaultShown boolean
	---@param defaultOrder string
	UpdateSortArrow = function(self, columnHeader, defaultShown, defaultOrder)
		local options = self.options
		local order = defaultOrder or columnHeader.order
		local arrowIcon = columnHeader.Arrow

		if (type(defaultShown) ~= "boolean") then
			arrowIcon:Show()
		else
			arrowIcon:SetShown(defaultShown)
			if (defaultShown) then
				self:SetBackdropColorForSelectedColumnHeader(columnHeader)
			end
		end

		arrowIcon:SetAlpha(options.arrow_alpha)

		if (order == "ASC") then
			arrowIcon:SetTexture(options.arrow_up_texture)
			arrowIcon:SetTexCoord(unpack(options.arrow_up_texture_coords))
			arrowIcon:SetSize(unpack(options.arrow_up_size))

		elseif (order == "DESC") then
			arrowIcon:SetTexture(options.arrow_down_texture)
			arrowIcon:SetTexCoord(unpack(options.arrow_down_texture_coords))
			arrowIcon:SetSize(unpack(options.arrow_down_size))
		end
	end,

	---@param self df_headerframe
	---@param columnHeader df_headercolumnframe
	---@param headerIndex number
	UpdateColumnHeader = function(self, columnHeader, headerIndex)
		--this is the data to update the columnHeader
		local columnData = self.HeaderTable[headerIndex]
		columnHeader.key = columnData.key or "total"

		if (columnData.icon) then
			columnHeader.Icon:SetTexture(columnData.icon)

			if (columnData.texcoord) then
				columnHeader.Icon:SetTexCoord(unpack(columnData.texcoord))
			else
				columnHeader.Icon:SetTexCoord(0, 1, 0, 1)
			end

			columnHeader.Icon:SetPoint("left", columnHeader, "left", self.options.padding, 0)
			columnHeader.Icon:Show()
		end

		if (columnData.text) then
			columnHeader.Text:SetText(columnData.text)

			--text options
			detailsFramework:SetFontColor(columnHeader.Text, self.options.text_color)
			detailsFramework:SetFontSize(columnHeader.Text, self.options.text_size)
			detailsFramework:SetFontOutline(columnHeader.Text, self.options.text_shadow)

			--point
			local textAlign = columnData.align or "left"
			columnHeader.Text:ClearAllPoints()

			if (textAlign == "center") then
				columnHeader.Text:SetJustifyH("center")
				columnHeader.Text:SetPoint("center", columnHeader, "center", 0, 0)

			elseif (textAlign == "right") then
				columnHeader.Text:SetJustifyH("right")
				columnHeader.Text:SetPoint("right", columnHeader, "right", -self.options.padding, 0)

			else
				columnHeader.Text:SetJustifyH("left")
				if (not columnData.icon) then
					columnHeader.Text:SetPoint("left", columnHeader, "left", self.options.padding, 0)
				else
					columnHeader.Text:SetPoint("left", columnHeader.Icon, "right", self.options.padding, 0)
				end
			end

			columnHeader.Text:Show()
		end

		--column header index
		columnHeader.columnIndex = headerIndex

		if (columnData.canSort) then
			columnHeader.order = "DESC"
			columnHeader.Arrow:SetTexture(self.options.arrow_up_texture)
		else
			columnHeader.Arrow:Hide()
		end

		if (columnData.selected) then
			columnHeader.Arrow:Show()
			columnHeader.Arrow:SetAlpha(.843)
			self:UpdateSortArrow(columnHeader, true, columnHeader.order)
			self.columnSelected = headerIndex
		else
			if (columnData.canSort) then
				self:UpdateSortArrow(columnHeader, false, columnHeader.order)
			end
		end

		--size
		if (columnData.width) then
			columnHeader:SetWidth(columnData.width)
		end
		if (columnData.height) then
			columnHeader:SetHeight(columnData.height)
		end

		columnHeader.XPosition = self.HeaderWidth -- + self.options.padding
		columnHeader.YPosition = self.HeaderHeight -- + self.options.padding

		columnHeader.columnAlign = columnData.align or "left"
		columnHeader.columnOffset = columnData.offset or 0

		--add the header piece size to the total header size
		local growDirection = string.lower(self.options.grow_direction)

		if (growDirection == "right" or growDirection == "left") then
			self.HeaderWidth = self.HeaderWidth + columnHeader:GetWidth() + self.options.padding
			self.HeaderHeight = math.max(self.HeaderHeight, columnHeader:GetHeight())

		elseif (growDirection == "top" or growDirection == "bottom") then
			self.HeaderWidth =  math.max(self.HeaderWidth, columnHeader:GetWidth())
			self.HeaderHeight = self.HeaderHeight + columnHeader:GetHeight() + self.options.padding
		end

		local bShowColumnHeaderReziser = self.options.reziser_shown
		if (bShowColumnHeaderReziser) then
			local resizerButton = columnHeader.resizerButton
			resizerButton:Show()
			resizerButton.texture:SetVertexColor(unpack(self.options.reziser_color))
			resizerButton:SetWidth(self.options.reziser_width)
			resizerButton:SetHeight(columnHeader:GetHeight())
		else
			columnHeader.resizerButton:Hide()
		end

		columnHeader:Show()
		columnHeader.bInUse = true
		columnHeader.columnData = columnData
	end,

	---reset column header backdrop
	---@param self df_headerframe
	---@param columnHeader df_headercolumnframe
	ResetColumnHeaderBackdrop = function(self, columnHeader)
		columnHeader:SetBackdrop(self.options.header_backdrop)
		columnHeader:SetBackdropColor(unpack(self.options.header_backdrop_color))
		columnHeader:SetBackdropBorderColor(unpack(self.options.header_backdrop_border_color))
	end,

	---@param self df_headerframe
	---@param columnHeader df_headercolumnframe
	SetBackdropColorForSelectedColumnHeader = function(self, columnHeader)
		columnHeader:SetBackdropColor(unpack(self.options.header_backdrop_color_selected))
	end,

	---clear the column header
	---@param self df_headerframe
	---@param columnHeader df_headercolumnframe
	ClearColumnHeader = function(self, columnHeader)
		columnHeader:SetSize(self.options.header_width, self.options.header_height)
		self:ResetColumnHeaderBackdrop(columnHeader)

		columnHeader:ClearAllPoints()

		columnHeader.Icon:SetTexture("")
		columnHeader.Icon:Hide()
		columnHeader.Text:SetText("")
		columnHeader.Text:Hide()
	end,

	---get the next column header, create one if doesn't exists
	---@param self df_headerframe
	GetNextHeader = function(self)
		local nextHeader = self.NextHeader
		local columnHeader = self.columnHeadersCreated[nextHeader]

		if (not columnHeader) then
			--create a new column header
			---@type df_headercolumnframe
			columnHeader = CreateFrame("button", "$parentHeaderIndex" .. nextHeader, self, "BackdropTemplate")

			--store the mouse xy position when onmousedown the column header, when the mouse is up, before calling its onclick function, check if the mouse position is the same, if not ignore the click
			--this prevents the click event when the user is trying to move the parent frame
			columnHeader:SetScript("OnMouseDown", function(columnHeader, buttonClicked)
				columnHeader.mouseDownX, columnHeader.mouseDownY = GetCursorPosition()
			end)
			columnHeader:SetScript("OnMouseUp", function(columnHeader, buttonReleased)
				local mouseUpX, mouseUpY = GetCursorPosition()
				if (columnHeader.mouseDownX == mouseUpX and columnHeader.mouseDownY == mouseUpY) then
					-- Call the OnClick function if the mouse hasn't moved
					detailsFramework.HeaderFunctions.OnClick(columnHeader, buttonReleased)
				end
			end)

			columnHeader:SetMovable(true)
			columnHeader:SetResizable(true)

			--header icon
			detailsFramework:CreateImage(columnHeader, "", self.options.header_height, self.options.header_height, "ARTWORK", nil, "Icon", "$parentIcon")
			--header separator
			detailsFramework:CreateImage(columnHeader, "", 1, 1, "ARTWORK", nil, "Separator", "$parentSeparator")
			--header name text
			detailsFramework:CreateLabel(columnHeader, "", self.options.text_size, self.options.text_color, "GameFontNormal", "Text", "$parentText", "ARTWORK")
			--header selected and order icon
			detailsFramework:CreateImage(columnHeader, self.options.arrow_up_texture, 12, 12, "ARTWORK", nil, "Arrow", "$parentArrow")

			---rezise button
			---@type df_headerresizer
			local resizerButton = CreateFrame("button", "$parentResizer", columnHeader)
			resizerButton:SetWidth(4)
			resizerButton:SetFrameLevel(columnHeader:GetFrameLevel()+2)
			resizerButton:SetPoint("topright", columnHeader, "topright", -1, -1)
			resizerButton:SetPoint("bottomright", columnHeader, "bottomright", -1, 1)
			resizerButton:EnableMouse(true)
			resizerButton:RegisterForClicks("LeftButtonDown", "LeftButtonUp")
			columnHeader.resizerButton = resizerButton

			resizerButton:SetScript("OnEnter", function()
				resizerButton.texture:SetVertexColor(1, 1, 1, 0.9)

				C_Timer.After(0.2, function()  --delay a bit to avoid the cursor flickering when moving fast
					if (not resizerButton:IsMouseOver()) then
						return
					end
					--change the cursor to resize
					if (SetCursor) then
						SetCursor("UI_RESIZE_CURSOR")
					end
				end)
			end)

			resizerButton:SetScript("OnLeave", function()
				resizerButton.texture:SetVertexColor(unpack(self.options.reziser_color))
				if (ResetCursor) then
					ResetCursor()
				end
			end)

			resizerButton:SetScript("OnMouseDown", function() --move this to a single function
				if (not columnHeader.bIsRezising) then
					--get the string length to know the min size
					local textLength = columnHeader.Text:GetStringWidth() + 6
					columnHeader:SetResizeBounds(math.max(textLength, self.options.reziser_min_width), columnHeader:GetHeight(), self.options.reziser_max_width, columnHeader:GetHeight())
					columnHeader.bIsRezising = true
					columnHeader:StartSizing("right")
				end
			end)

			resizerButton:SetScript("OnMouseUp", function()
				if (columnHeader.bIsRezising) then
					columnHeader.bIsRezising = false
					columnHeader:StopMovingOrSizing()

					--callback or modify into a passed by table?
					if (self.OnColumnSettingChangeCallback) then --need to get the header name
						local columnName = columnHeader.columnData.name
						xpcall(self.OnColumnSettingChangeCallback, geterrorhandler(), self, "width", columnName, columnHeader:GetWidth())
					end
				end
			end)

			resizerButton:SetScript("OnHide", function()
				if (columnHeader.bIsRezising) then
					columnHeader:StopMovingOrSizing()
					columnHeader.bIsRezising = false
				end
			end)

			resizerButton.texture = resizerButton:CreateTexture(nil, "overlay")
			resizerButton.texture:SetAllPoints()
			resizerButton.texture:SetColorTexture(1, 1, 1, 1)

			local xOffset = self.options.reziser_shown and -5 or -1
			columnHeader.Arrow:SetPoint("right", columnHeader, "right", xOffset, 0)

			columnHeader.Separator:Hide()
			columnHeader.Arrow:Hide()

			self:UpdateSortArrow(columnHeader, false, "DESC")

			table.insert(self.columnHeadersCreated, columnHeader)
			columnHeader = columnHeader
		end

		self:ClearColumnHeader(columnHeader)
		self.NextHeader = self.NextHeader + 1
		return columnHeader
	end,

	---return a header button by passing its name (.name on the column table)
	---@param self df_headerframe
	---@param columnName string
	---@return df_headercolumnframe|nil
	GetHeaderColumnByName = function(self, columnName)
		for _, headerColumnFrame in ipairs(self.columnHeadersCreated) do
			if (headerColumnFrame.columnData.name == columnName) then
				return headerColumnFrame
			end
		end
	end,

	NextHeader = 1,
	HeaderWidth = 0,
	HeaderHeight = 0,
}

--default options
local default_header_options = {
	backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
	backdrop_color = {0, 0, 0, 0.2},
	backdrop_border_color = {0.1, 0.1, 0.1, .2},

	propagate_clicks = false,

	text_color = {1, 1, 1, 1},
	text_size = 10,
	text_shadow = false,
	grow_direction = "RIGHT",
	padding = 2,

	reziser_shown = false, --make sure to set the callback function with: header:SetOnColumnResizeScript(callbackFunction)
	reziser_width = 2,
	reziser_color = {1, 0.6, 0, 0.6},
	reziser_min_width = 16,
	reziser_max_width = 200,

	--each piece of the header
	header_backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
	header_backdrop_color = {0, 0, 0, 0.5},
	header_backdrop_color_selected = {0.3, 0.3, 0.3, 0.5},
	header_backdrop_border_color = {0, 0, 0, 0},
	header_width = 120,
	header_height = 20,

	arrow_up_texture = [[Interface\Buttons\Arrow-Up-Down]],
	arrow_up_texture_coords = {0, 1, 6/16, 1},
	arrow_up_size = {12, 11},
	arrow_down_texture = [[Interface\Buttons\Arrow-Down-Down]],
	arrow_down_texture_coords = {0, 1, 0, 11/16},
	arrow_down_size = {12, 11},
	arrow_alpha = 0.659,

	use_line_separators = false,
	line_separator_color = {.1, .1, .1, .6},
	line_separator_width = 1,
	line_separator_height = 200,
	line_separator_gap_align = false,
}

---create a df_headerframe, alias 'header'.
---a header is a frame that can hold multiple columns which are also frames, each column is a df_headercolumnframe, these columns are arranged in horizontal form.
---a header is used to organize columns giving them a name/title, a way to sort and align them.
---each column is placed on the right side of the previous column.
---@param parent frame
---@param headerTable df_headercolumndata[]
---@param options table?
---@param frameName string?
---@return df_headerframe
function detailsFramework:CreateHeader(parent, headerTable, options, frameName)
	---create the header frame which is returned by this function
	---@type df_headerframe
	local newHeader = CreateFrame("frame", frameName or ("$parentHeaderLine" .. math.random(100000000)), parent, "BackdropTemplate")

	detailsFramework:Mixin(newHeader, detailsFramework.OptionsFunctions)
	detailsFramework:Mixin(newHeader, detailsFramework.HeaderMixin)

	options = options or {}
	newHeader:BuildOptionsTable(default_header_options, options)

	--set the backdrop and backdrop color following the values in the options table
	newHeader:SetBackdrop(newHeader.options.backdrop)
	newHeader:SetBackdropColor(unpack(newHeader.options.backdrop_color))
	newHeader:SetBackdropBorderColor(unpack(newHeader.options.backdrop_border_color))

	newHeader:SetHeaderTable(headerTable)

	return newHeader
end


--[=[example:
C_Timer.After(1, function()


	local parent = UIParent

	--declare the columns the headerFrame will have
	---@type df_headercolumndata[]
	local headerTable = {
		{name = "playername", text = "Player Name", width = 120, align = "left", canSort = true},
		{name = "damage", text = "Damage Done", width = 80, align = "right", canSort = true},
		{name = "points", text = "Total Points", width = 80, align = "right", canSort = false},
	}
	local frameName = "MyAddOnOptionsFrame"
	local options = {}

	local headerFrame = DetailsFramework:CreateHeader(parent, headerTable, options, frameName)
	headerFrame:SetPoint("center", parent, "center", 10, -10)


end)
--]=]
