
--note: do maintenance on spelltable.ChartData

local addonName, Details222 = ...
local breakdownWindow = Details.BreakdownWindow
local Loc = LibStub("AceLocale-3.0"):GetLocale ( "Details" )
local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0")
local unpack = unpack
local GetTime = GetTime
local CreateFrame = CreateFrame
local GetSpellLink = GetSpellLink
local GetSpellInfo = GetSpellInfo
local _GetSpellInfo = Details.GetSpellInfo
local GameTooltip = GameTooltip
local IsShiftKeyDown = IsShiftKeyDown
local DF = DetailsFramework
local tinsert = table.insert

---@type breakdownspelltab
local spellsTab = {}

--expose the object to the global namespace
DetailsSpellBreakdownTab = spellsTab

local iconTableSummary = {
    texture = [[Interface\AddOns\Details\images\icons]],
    coords = {238/512, 255/512, 0, 18/512},
    width = 16,
    height = 16,
}

local CONST_BAR_HEIGHT = 20
local CONST_SPELLSCROLL_LINEHEIGHT = 20
local CONST_TARGET_TEXTURE = [[Interface\MINIMAP\TRACKING\Target]]
local CONST_SPELLBLOCK_DEFAULT_COLOR = {.4, .4, .4, 1}
local CONST_SPELLBLOCK_HEADERTEXT_COLOR = {.9, .8, 0, 1}
local CONST_SPELLBLOCK_HEADERTEXT_SIZE = 11

Details.SpellGroups = {
	[193473] = 15407, --mind flay
}

---return the actor currently in use by the breakdown window
---@return actor
function spellsTab.GetActor() --this cache must be cleared when the actor changes or the breakdown window is closed
	return spellsTab.currentActor
end

---return the combat currently in use by the breakdown window
---@return combat
function spellsTab.GetCombat() --must be cleared
	return spellsTab.combatObject
end

---@return instance
function spellsTab.GetInstance()
	return spellsTab.instance or Details:GetActiveWindowFromBreakdownWindow()
end

---set the backdrop of scrollframe and container following the settings of the breakdown window
---@param containerFrame frame
---@param scrollFrame frame|nil
function spellsTab.ApplyStandardBackdrop(containerFrame, scrollFrame)
	C_Timer.After(0, function()
		containerFrame:SetBackdrop(Details222.BreakdownWindow.BackdropSettings.backdrop)
		containerFrame:SetBackdropColor(unpack(Details222.BreakdownWindow.BackdropSettings.backdropcolor))
		containerFrame:SetBackdropBorderColor(unpack(Details222.BreakdownWindow.BackdropSettings.backdropbordercolor))

		if (scrollFrame) then
			scrollFrame:SetBackdrop({})
			if (scrollFrame["__background"]) then
				scrollFrame["__background"]:Hide()
			end
		end
	end)
end

---return the breakdownspellscrollframe object, there's only one of this in the breakdown window
---@return breakdownspellscrollframe
function spellsTab.GetSpellScrollFrame()
	return spellsTab.TabFrame.SpellScrollFrame
end

---return the breakdownspellblockframe object, there's only one of this in the breakdown window
---@return breakdownspellblockframe
function spellsTab.GetSpellBlockFrame()
	return spellsTab.TabFrame.SpellBlockFrame
end

---@return breakdowntargetscrollframe
function spellsTab.GetTargetScrollFrame()
	return spellsTab.TargetScrollFrame
end

---@return breakdownphasescrollframe
function spellsTab.GetPhaseScrollFrame()
	return spellsTab.PhaseScrollFrame
end

---@return breakdowngenericscrollframe, breakdowngenericscrollframe
function spellsTab.GetGenericScrollFrame()
	return spellsTab.GenericScrollFrameLeft, spellsTab.GenericScrollFrameRight
end

---@return df_framecontainer
function spellsTab.GetSpellScrollContainer()
	return spellsTab.SpellContainerFrame
end

---@return df_framecontainer
function spellsTab.GetSpellBlockContainer()
	return spellsTab.BlocksContainerFrame
end

---@return df_framecontainer
function spellsTab.GetTargetScrollContainer()
	return spellsTab.TargetsContainerFrame
end

---@return df_framecontainer
function spellsTab.GetPhaseScrollContainer()
	return spellsTab.PhaseContainerFrame
end

---@return df_framecontainer leftContainer, df_framecontainer rightContainer
function spellsTab.GetGenericScrollContainer()
	return spellsTab.GenericContainerFrameLeft, spellsTab.GenericContainerFrameRight
end

function spellsTab.GetScrollFrameByContainerType(containerType)
	if (containerType == "spells") then
		return spellsTab.GetSpellScrollFrame()

	elseif (containerType == "targets") then
		return spellsTab.GetTargetScrollFrame()

	elseif (containerType == "phases") then
		return spellsTab.GetPhaseScrollFrame()

	elseif (containerType == "generic_left") then
		local scrollFrameLeft = spellsTab.GetGenericScrollFrame()
		return scrollFrameLeft

	elseif (containerType == "generic_right") then
		local _, scrollFrameRight = spellsTab.GetGenericScrollFrame()
		return scrollFrameRight
	end
end

function spellsTab.OnProfileChange()
	--no need to cache, just call the db from there
	spellsTab.UpdateHeadersSettings("spells")
	spellsTab.UpdateHeadersSettings("targets")
	spellsTab.UpdateHeadersSettings("phases")
	spellsTab.UpdateHeadersSettings("generic_left")
	spellsTab.UpdateHeadersSettings("generic_right")
end

------------------------------------------------------------------------------------------------------------------------------------------------
--Header

---store the header object has key and its type as value, the header type can be 'spell' or 'target'
---@type table<uiobject, string>
local headerContainerType = {}
spellsTab.headerContainerType = headerContainerType

---@type number
local columnOffset = 0

---column header information saved into details database: if is enabaled, its with and align
local settingsPrototype = {
	enabled = true,
	width = 100,
	align = "left",
}

---contains the column settings for each header column, the table key is columnName and the value is headercolumndatasaved
---@class headercolumndatabase : table<string, headercolumndatasaved>

---headercolumndata goes inside the header table which is passed to the header constructor or header:SetHeaderTable()
---@class headercolumndata : {name:string, width:number, text:string, align:string, key:string, selected:boolean, canSort:boolean, dataType:string, order:string, offset:number, key:string}

---columndata is the raw table with all options which can be used to create a headertable, some may not be used due to settings or filtering
---@class columndata : {name:string, width:number, key:string, selected:boolean, label:string, align:string, enabled:boolean, attribute:number, canSort:boolean, dataType:string, order:string, offset:number}

---default settings for the header of the spells container, label is a localized string, name is a string used to save the column settings, key is the key used to get the value from the spell table, width is the width of the column, align is the alignment of the text, enabled is if the column is enabled, canSort is if the column can be sorted, sortKey is the key used to sort the column, dataType is the type of data the column is sorting, order is the order of the sorting, offset is the offset of the column
---@type columndata[]
local spellContainerColumnData = {
	--the align seems to be bugged as the left is aligning in the center and center is on the left side
	{name = "icon", width = 22, label = "", align = "left", enabled = true, offset = columnOffset},
	{name = "target", width = 22, label = "", align = "left", enabled = true, offset = columnOffset},
	{name = "rank", label = "#", width = 16, align = "center", enabled = true, offset = 6, dataType = "number"},
	{name = "expand", label = "^", width = 16, align = "left", enabled = true, offset = -4}, --maybe -3
	{name = "name", label = "spell name", width = 231, align = "left", enabled = true, offset = columnOffset},
	{name = "amount", label = "total", key = "total", selected = true, width = 50, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset},
	{name = "persecond", label = "ps", key = "total", width = 50, align = "left", enabled = false, canSort = true, sortKey = "ps", offset = columnOffset, order = "DESC", dataType = "number"},
	{name = "percent", label = "%", key = "total", width = 50, align = "left", enabled = true, canSort = true, offset = columnOffset, order = "DESC", dataType = "number"},
	{name = "casts", label = "casts", key = "casts", width = 40, align = "left", enabled = false, canSort = true, offset = columnOffset, order = "DESC", dataType = "number"},
	{name = "critpercent", label = "crit %", key = "critpercent", width = 40, align = "left", enabled = false, canSort = true, offset = columnOffset, order = "DESC", dataType = "number"},
	{name = "hits", label = "hits", key = "counter", width = 40, align = "left", enabled = false, canSort = true, offset = columnOffset, order = "DESC", dataType = "number"},
	{name = "castavg", label = "cast avg", key = "castavg", width = 50, align = "left", enabled = false, canSort = true, offset = columnOffset, order = "DESC", dataType = "number"},
	{name = "uptime", label = "uptime", key = "uptime", width = 45, align = "left", enabled = false, canSort = true, offset = columnOffset, order = "DESC", dataType = "number"},
	{name = "overheal", label = "overheal", key = "overheal", width = 70, align = "left", enabled = true, canSort = true, order = "DESC", dataType = "number", attribute = DETAILS_ATTRIBUTE_HEAL, offset = columnOffset},
	{name = "absorbed", label = "absorbed", key = "healabsorbed", width = 55, align = "left", enabled = false, canSort = true, order = "DESC", dataType = "number", attribute = DETAILS_ATTRIBUTE_HEAL, offset = columnOffset},
}

local targetContainerColumnData = {
	{name = "icon", width = 22, label = "", align = "left", enabled = true, offset = columnOffset},
	{name = "rank", label = "#", width = 20, align = "left", enabled = true, offset = columnOffset},
	{name = "name", label = "name", width = 185, align = "left", enabled = true, offset = columnOffset},
	{name = "amount", label = "total", key = "total", selected = true, width = 50, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset},
	{name = "overheal", label = "overheal", key = "overheal", width = 70, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset, attribute = DETAILS_ATTRIBUTE_HEAL},
	{name = "percent", label = "%", key = "total", width = 50, align = "left", enabled = true, canSort = true, offset = columnOffset, order = "DESC", dataType = "number"},
}
spellsTab.targetContainerColumnData = targetContainerColumnData

local phaseContainerColumnData = {
	{name = "icon", width = 22, label = "", align = "left", enabled = true, offset = columnOffset},
	{name = "name", label = "name", width = 90, align = "left", enabled = true, offset = columnOffset},
	{name = "rank", label = "#", width = 30, align = "left", enabled = true, offset = columnOffset},
	{name = "amount", label = "total", key = "total", width = 50, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset, selected = true},
	{name = "persecond", label = "ps", key = "total", width = 44, align = "left", enabled = true, canSort = true, sortKey = "ps", dataType = "number", order = "DESC", offset = columnOffset},
	{name = "percent", label = "%", key = "total", width = 44, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset},
}
spellsTab.phaseContainerColumnData = phaseContainerColumnData

--generic container can show data from any attribute
local genericContainerLeftColumnData = {
	{name = "icon", width = 22, label = "", align = "left", enabled = true, offset = columnOffset},
	{name = "rank", label = "#", width = 30, align = "left", enabled = true, offset = columnOffset},
	{name = "name", label = "name", width = 200, align = "left", enabled = true, offset = columnOffset},
	{name = "amount", label = "total", key = "total", width = 50, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset, selected = true},
	{name = "persecond", label = "ps", key = "total", width = 50, align = "left", enabled = true, canSort = true, sortKey = "ps", dataType = "number", order = "DESC", offset = columnOffset},
	{name = "percent", label = "%", key = "total", width = 50, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset},
}
spellsTab.genericContainerLeftColumnData = genericContainerLeftColumnData

--generic container can show data from any attribute
local genericContainerRightColumnData = {
	{name = "icon", width = 22, label = "", align = "left", enabled = true, offset = columnOffset},
	{name = "rank", label = "#", width = 30, align = "left", enabled = true, offset = columnOffset},
	{name = "name", label = "name", width = 190, align = "left", enabled = true, offset = columnOffset},
	{name = "amount", label = "total", key = "total", width = 50, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset, selected = true},
	{name = "persecond", label = "ps", key = "total", width = 50, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset},
	{name = "percent", label = "%", key = "total", width = 50, align = "left", enabled = true, canSort = true, sortKey = "total", dataType = "number", order = "DESC", offset = columnOffset},
}
spellsTab.genericContainerRightColumnData = genericContainerRightColumnData

---get the header settings from details saved variables and the container column data
---@param containerType "spells"|"targets"|"phases"|"generic_left"|"generic_right
---@return headercolumndatabase
---@return columndata
function spellsTab.GetHeaderSettings(containerType)
	local settings
	---@type headercolumndata
	local containerColumnData

	if (containerType == "spells") then
		settings = Details.breakdown_spell_tab.spellcontainer_headers
		containerColumnData = spellContainerColumnData

	elseif (containerType == "targets") then
		settings = Details.breakdown_spell_tab.targetcontainer_headers
		containerColumnData = targetContainerColumnData

	elseif (containerType == "phases") then
		settings = Details.breakdown_spell_tab.phasecontainer_headers
		containerColumnData = phaseContainerColumnData

	elseif (containerType == "generic_left") then
		settings = Details.breakdown_spell_tab.genericcontainer_headers
		containerColumnData = genericContainerLeftColumnData

		--as the generic data is received, it may have which columns can be shown
		if (spellsTab.headersAllowed) then
			for index, columnData in ipairs(containerColumnData) do
				local newEnabledState = spellsTab.headersAllowed[columnData.name] or false
				columnData.enabled = newEnabledState
				--check if the settings already has the data, and then set the header enabled value to follow the headersAllowed table
				if (settings[columnData.name]) then
					settings[columnData.name].enabled = newEnabledState
				end
			end
		end

	elseif (containerType == "generic_right") then
		settings = Details.breakdown_spell_tab.genericcontainer_headers_right
		containerColumnData = genericContainerRightColumnData

		--as the generic data is received, it may have which columns can be shown
		if (spellsTab.headersAllowed) then
			for index, columnData in ipairs(containerColumnData) do
				local newEnabledState = spellsTab.headersAllowed[columnData.name] or false
				columnData.enabled = newEnabledState
				--check if the settings already has the data, and then set the header enabled value to follow the headersAllowed table
				if (settings[columnData.name]) then
					settings[columnData.name].enabled = newEnabledState
				end
			end
		end
	end

	---@cast settings headercolumndatabase
	return settings, containerColumnData
end

---callback for when the user resizes a column on the header
---@param headerFrame df_headerframe
---@param optionName string
---@param columnName string
---@param value any
function spellsTab.OnHeaderColumnOptionChanged(headerFrame, optionName, columnName, value)
	---@type "spells"|"targets"|"phases"|"generic_left"|"generic_right"
	local containerType = headerContainerType[headerFrame]
	---@type headercolumndatabase
	local settings = spellsTab.GetHeaderSettings(containerType)

	settings[columnName][optionName] = value
	spellsTab.UpdateHeadersSettings(containerType)
end

function spellsTab.OnAnyColumnHeaderClickCallback()
	local instance = spellsTab.GetInstance()
	instance:RefreshWindow(true)
end

---copy settings from the ColumnInfo table which doesn't exists in the details profile
---this is called when the profile changes or when the tab is opened with a different actor than before
---@param containerType "spells"|"targets"|"phases"|"generic_left"|"generic_right"
function spellsTab.UpdateHeadersSettings(containerType)
	---details table which hold the settings for a container header
	local settings, containerColumnData = spellsTab.GetHeaderSettings(containerType)

	--do a loop and check if the column data from columnInfo exists in the details profile settings, if not, add it
	for i = 1, #containerColumnData do
		--default column settings
		local columnData = containerColumnData[i]
		---@type string
		local columnName = columnData.name

		--column settings for the column on details profile
		---@type headercolumndatasaved
		local columnSettings = settings[columnName]

		--check if this column does not have a mirror table in details profile
		if (not columnSettings) then
			--create a table in Details! saved variables to save the column settings
			---@type headercolumndatasaved
			local newColumnSettings = DetailsFramework.table.copy({}, settingsPrototype)
			settings[columnName] = newColumnSettings

			newColumnSettings.enabled = columnData.enabled
			newColumnSettings.width = columnData.width
			newColumnSettings.align = columnData.align
		end
	end

	if (containerType == "spells") then
		spellsTab.spellsHeaderData = spellsTab.BuildHeaderTable(containerType)

		local spellContainer = spellsTab.GetSpellScrollContainer()
		local spellScrollFrame = spellsTab.GetSpellScrollFrame()

		local headerFrame = spellScrollFrame.Header
		headerFrame:SetHeaderTable(spellsTab.spellsHeaderData)

		local width = headerFrame:GetWidth()

		--SetHeaderTable() calls Header:Refresh() which reset its width
		--if the spell container get a resize to be as big as the sum of all columns width, the option to resize the container's width is useless
		spellContainer:SetWidth(width)
		--save the width of the spell container in Details settings
		Details.breakdown_spell_tab.spellcontainer_width = width

	elseif (containerType == "targets") then
		spellsTab.targetsHeaderData = spellsTab.BuildHeaderTable(containerType)
		spellsTab.GetTargetScrollFrame().Header:SetHeaderTable(spellsTab.targetsHeaderData)

		local width = spellsTab.GetTargetScrollFrame().Header:GetWidth()
		spellsTab.GetTargetScrollContainer():SetWidth(width)

		--save the width of the target container in Details settings
		Details.breakdown_spell_tab.targetcontainer_width = width

	elseif (containerType == "phases") then
		spellsTab.phasesHeaderData = spellsTab.BuildHeaderTable(containerType)
		spellsTab.GetPhaseScrollFrame().Header:SetHeaderTable(spellsTab.phasesHeaderData)

	elseif (containerType == "generic_left") then
		spellsTab.genericHeaderData = spellsTab.BuildHeaderTable(containerType)
		spellsTab.GetGenericScrollFrame().Header:SetHeaderTable(spellsTab.genericHeaderData)

	elseif (containerType == "generic_right") then
		spellsTab.genericHeaderData = spellsTab.BuildHeaderTable(containerType)
		spellsTab.GetGenericScrollFrame().Header:SetHeaderTable(spellsTab.genericHeaderData)
	end
end

---get the header settings from details profile and build a header table using the table which store all headers columns information
---the data for each header is stored on 'spellContainerColumnInfo' and 'targetContainerColumnInfo' variables
---@param containerType "spells"|"targets"|"phases"|"generic_left"|"generic_right"
---@return {name: string, width: number, text: string, align: string}[]
function spellsTab.BuildHeaderTable(containerType)
	---@type headercolumndata[]
	local headerTable = {}

    ---@type instance
    local instance = spellsTab.GetInstance()

	---@type number, number
	local mainAttribute, subAttribute = instance:GetDisplay()

	--settings from profile | updated at UpdateHeadersSettings() > called on OnProfileChange() and when the tab is opened
	local settings, containerColumnData = spellsTab.GetHeaderSettings(containerType)

	---result of the sum of all columns width
	---@type number
	local totalWidth = 0

	for i = 1, #containerColumnData do
		local columnData = containerColumnData[i]
		---@type headercolumndatasaved
		local columnSettings = settings[columnData.name]

		if (columnSettings.enabled) then
			local bCanAdd = true
			if (columnData.attribute) then
				if (columnData.attribute ~= mainAttribute) then
					bCanAdd = false
				end
			end

			if (bCanAdd) then
				---@type headercolumndata
				local headerColumnData = {
					width = columnSettings.width,
					text = columnData.label,
					name = columnData.name,

					--these values may be nil
					selected = columnData.selected,
					align = columnData.align,
					canSort = columnData.canSort,
					dataType = columnData.dataType,
					order = columnData.order,
					offset = columnData.offset,
					key = columnData.key,
				}

				totalWidth = totalWidth + headerColumnData.width
				headerTable[#headerTable+1] = headerColumnData
			end
		end
	end

	return headerTable
end

------------------------------------------------------------------------------------------------------------------------------------------------
--Bar Selection

--store the current spellbar selected, this is used to lock the spellblock container to the spellbar selected
spellsTab.selectedSpellBar = nil

---selected a breakdownspellbar, locking into the bar
---when a breakdownspellbar is selected, all the other breakdownspellbar has it's hover over disabled
---@param spellBar breakdownspellbar
function spellsTab.SelectSpellBar(spellBar)
	--if already has a spellbar selected, unselect it
	if (spellsTab.HasSelectedSpellBar()) then --unselect and stop the function if the bar selected is the same as the one being selected
		if (spellsTab.GetSelectedSpellBar() == spellBar) then
			spellsTab.UnSelectSpellBar()
			return
		else
			spellsTab.UnSelectSpellBar()
		end
	end

	--as the spell block container get an update when hovering over
	--update the spell block container for the breakdownspellbar just selected
	--this is necessary since a previous breakdownspellbar could have been selected and prevented this breakdownspellbar to update on hover over
	---@type function
	local onEnterScript = spellBar:GetScript("OnEnter")
	if (onEnterScript) then
		onEnterScript(spellBar)
	end

	--set the new breakdownspellbar as selected
	spellsTab.selectedSpellBar = spellBar
	spellsTab.selectedSpellBar.overlayTexture:Show()
end

---deselect the breakdownspellbar
function spellsTab.UnSelectSpellBar()
	if (spellsTab.selectedSpellBar) then
		spellsTab.selectedSpellBar.overlayTexture:Hide()
	end
	spellsTab.selectedSpellBar = nil
end

---get the spellbar currently selected
---@return breakdownspellbar
function spellsTab.GetSelectedSpellBar()
	return spellsTab.selectedSpellBar
end

---return true if there's a spell bar selected
---@return boolean
function spellsTab.HasSelectedSpellBar()
	return spellsTab.selectedSpellBar ~= nil
end

function spellsTab.OnShownTab()
	--unselect any selected breakdownspellbar
	spellsTab.UnSelectSpellBar()
	--reset the spell blocks
	spellsTab.GetSpellBlockFrame():ClearBlocks()
	--update spells and target header frame (spellscroll and targetscroll)
	spellsTab.UpdateHeadersSettings("spells")
	spellsTab.UpdateHeadersSettings("targets")
	spellsTab.UpdateHeadersSettings("phases")
	spellsTab.UpdateHeadersSettings("generic_left")
	spellsTab.UpdateHeadersSettings("generic_right")
end

---called when the tab is getting created, run only once
---@param tabButton button
---@param tabFrame breakdownspellstab
function spellsTab.OnCreateTabCallback(tabButton, tabFrame) --~init
	spellsTab.TabFrame = tabFrame

	--initialize the allowed headers for generic data container
	spellsTab.headersAllowed = {icon = true, name = true, rank = true, amount = true, persecond = true, percent = true}

    --create the scrollbar to show the spells in the breakdown window
    spellsTab.CreateSpellScrollContainer(tabFrame) --finished
    --create the 6 spell blocks in the right side of the breakdown window, these blocks show the spell info like normal hits, critical hits, average, etc
    spellsTab.CreateSpellBlockContainer(tabFrame)
    --create the targets container
    spellsTab.CreateTargetContainer(tabFrame)
	--create phases container
	spellsTab.CreatePhasesContainer(tabFrame)
	--create generic container
	spellsTab.CreateGenericContainers(tabFrame)

    --create the report buttons for each container
    --spellsTab.CreateReportButtons(tabFrame)

	--create a button in the breakdown window to open the options for this tab
	local optionsButton = DF:CreateButton(tabFrame, Details.OpenSpellBreakdownOptions, 130, 18, "options", 14)
	--optionsButton:SetTemplate(DF:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE"))
	optionsButton:SetPoint("bottomright", tabFrame, "bottomright", -10, -19)
	optionsButton.textsize = 12
	optionsButton.textcolor = "orange"

	---@type df_roundedpanel_preset
	local preset = {
		roundness = 5,
		color = {.2, .2, .2, 0.98},
		border_color = {.1, .1, .1, 0.834},
	}
	DF:AddRoundedCornersToFrame(optionsButton, preset)

	--open the breakdown window at startup for testing
	--[=[ debug
	C_Timer.After(1, function()
		Details:OpenPlayerDetails(1)
		C_Timer.After(1, function()
			Details:OpenPlayerDetails(1)
			Details:OpenPlayerDetails(1)
		end)
	end)
	--]=]
end

function spellsTab.UpdateBarSettings(bar)
	if (bar.statusBar) then
		bar.statusBar:SetAlpha(Details.breakdown_spell_tab.statusbar_alpha) --could be moved to when the bar is updated
		bar.statusBar:GetStatusBarTexture():SetTexture(Details.breakdown_spell_tab.statusbar_texture)
		bar.statusBar.backgroundTexture:SetColorTexture(unpack(Details.breakdown_spell_tab.statusbar_background_color))
		bar.statusBar.backgroundTexture:SetAlpha(Details.breakdown_spell_tab.statusbar_background_alpha)
	end
end

-----------------------------------------------------------------------------------------------------------------------
--> create the new tab
function Details.InitializeSpellBreakdownTab()
	local tabButton, tabFrame = Details:CreatePlayerDetailsTab(
		"Summary", --[1] tab name
		Loc ["STRING_SPELLS"], --[2] localized name
		function(tabOBject, playerObject) --[3] condition
			if (playerObject) then
				return true
			else
				return false
			end
		end,

		function() --[4] fill function | passing a fill function, it'll set a OnShow() script on the tabFrame | only run if the actor is different
			spellsTab.OnShownTab()
		end,

		function(tabButton, tabFrame) --[5] onclick
			tabFrame:Show()
		end,

		spellsTab.OnCreateTabCallback, --[6] oncreate
		iconTableSummary, --[7] icon table
		nil, --[8] replace tab
		true --[9] is default tab
	)

	spellsTab.TabButton = tabButton
	spellsTab.TabFrame = tabFrame

	---on receive data from a class
	---@param data breakdownspelldatalist
	---@param actorObject actor
	---@param combatObject combat
	---@param instance instance
	function tabButton.OnReceiveSpellData(data, actorObject, combatObject, instance)
		spellsTab.currentActor = actorObject
		spellsTab.combatObject = combatObject
		spellsTab.instance = instance

		---@type number, number
		local mainAttribute, subAttribute = instance:GetDisplay()
		spellsTab.mainAttribute = mainAttribute
		spellsTab.subAttribute = subAttribute

		--show the regular containers
		spellsTab.GetSpellScrollContainer():Show()
		spellsTab.GetPhaseScrollContainer():Show()
		spellsTab.GetTargetScrollContainer():Show()
		spellsTab.GetSpellBlockContainer():Show()

		--hide the generic container
		local genericScrollContainerLeft, genericScrollContainerRight = spellsTab.GetGenericScrollContainer()
		genericScrollContainerLeft:Hide()
		genericScrollContainerRight:Hide()

		--refresh the data
		spellsTab.GetSpellScrollFrame():RefreshMe(data)
		spellsTab.GetPhaseScrollFrame():RefreshMe(data)
	end

	---called right after the OnReceiveSpellData() call
	---@param data breakdowntargettablelist
	---@param actorObject actor
	---@param combatObject combat
	---@param instance instance
	function tabButton.OnReceiveTargetData(data, actorObject, combatObject, instance)
		---@type number, number
		local mainAttribute, subAttribute = instance:GetDisplay()
		spellsTab.mainAttribute = mainAttribute
		spellsTab.subAttribute = subAttribute
		spellsTab.GetTargetScrollFrame():RefreshMe(data)
	end

	---when the window handler sends data which is not a spell or target data
	---@param data table
	---@param actorObject actor
	---@param combatObject combat
	---@param instance instance
	function tabButton.OnReceiveGenericData(data, actorObject, combatObject, instance)
		---@type number, number
		local mainAttribute, subAttribute = instance:GetDisplay()
		spellsTab.mainAttribute = mainAttribute
		spellsTab.subAttribute = subAttribute

		spellsTab.currentActor = actorObject
		spellsTab.combatObject = combatObject
		spellsTab.instance = instance

		if (spellsTab.headersAllowed ~= data.headersAllowed) then
			--refresh the header frame
			spellsTab.UpdateHeadersSettings("generic_left")
			--bug: now allowing to sort
		end

		--when generic data is shown, the damage-healing-targets-scrolls / spell details blocks/ can be removed
		spellsTab.GetSpellScrollContainer():Hide()
		spellsTab.GetPhaseScrollContainer():Hide()
		spellsTab.GetTargetScrollContainer():Hide()
		spellsTab.GetSpellBlockContainer():Hide()

		--show the generic scroll
		local genericScrollContainerLeft, genericScrollContainerRight = spellsTab.GetGenericScrollContainer()
		genericScrollContainerLeft:Show()
		genericScrollContainerRight:Show()

		--refresh the data
		spellsTab.GetGenericScrollFrame():RefreshMe(data)
	end

	---@type detailseventlistener
	local eventListener = Details:CreateEventListener()
	eventListener:RegisterEvent("DETAILS_PROFILE_APPLYED", function()
		--this event don't trigger at details startup
		spellsTab.OnProfileChange()
	end)
end

-----------------------------------------------------------------------------------------------------------------------
--> report data

function spellsTab.CreateReportButtons(tabFrame) --deprecated?
    --spell list report button
	tabFrame.report_esquerda = Details.gump:NewDetailsButton(tabFrame, tabFrame, nil, Details.Reportar, tabFrame, 1, 16, 16, "Interface\\COMMON\\VOICECHAT-ON", "Interface\\COMMON\\VOICECHAT-ON", "Interface\\COMMON\\VOICECHAT-ON", "Interface\\COMMON\\VOICECHAT-ON", nil, "DetailsJanelaInfoReport2")
	tabFrame.report_esquerda:SetPoint("bottomleft", spellsTab.GetSpellScrollFrame(), "TOPLEFT",  33, 3)
	tabFrame.report_esquerda:SetFrameLevel(tabFrame:GetFrameLevel()+2)
	tabFrame.topleft_report = tabFrame.report_esquerda

	--targets report button
	tabFrame.report_alvos = Details.gump:NewDetailsButton(tabFrame, tabFrame, nil, Details.Reportar, tabFrame, 3, 16, 16,	"Interface\\COMMON\\VOICECHAT-ON", "Interface\\COMMON\\VOICECHAT-ON", "Interface\\COMMON\\VOICECHAT-ON", "Interface\\COMMON\\VOICECHAT-ON", nil, "DetailsJanelaInfoReport3")
	tabFrame.report_alvos:SetPoint("bottomright", tabFrame.container_alvos, "TOPRIGHT",  -2, -1)
	tabFrame.report_alvos:SetFrameLevel(3) --solved inactive problem

	--special barras in the right report button
	tabFrame.report_direita = Details.gump:NewDetailsButton(tabFrame, tabFrame, nil, Details.Reportar, tabFrame, 2, 16, 16, "Interface\\COMMON\\VOICECHAT-ON", "Interface\\COMMON\\VOICECHAT-ON", "Interface\\COMMON\\VOICECHAT-ON", "Interface\\COMMON\\VOICECHAT-ON", nil, "DetailsJanelaInfoReport4")
	tabFrame.report_direita:SetPoint("TOPRIGHT", tabFrame, "TOPRIGHT",  -10, -70)
	tabFrame.report_direita:Show()
end

function spellsTab.monta_relatorio(botao) --deprecated?
    ---@type attributeid
	local mainSection = breakdownWindow.atributo
    ---@type attributeid
	local subSection = breakdownWindow.sub_atributo
    ---@type actor
	local player = breakdownWindow.jogador
    ---@type instance
	local instance = breakdownWindow.instancia
    ---@type number
    local amt = Details.report_lines

    local tabFrame = spellsTab.TabFrame

	if (not player) then
		Details:Msg("Player not found.")
		return
	end

	local report_lines

	if (botao == 1) then --spell data
		if (mainSection == 1 and subSection == 4) then --friendly fire
			report_lines = {"Details!: " .. player.nome .. " " .. Loc ["STRING_ATTRIBUTE_DAMAGE_FRIENDLYFIRE"] .. ":"}

		elseif (mainSection == 1 and subSection == 3) then --damage taken
			report_lines = {"Details!: " .. player.nome .. " " .. Loc ["STRING_ATTRIBUTE_DAMAGE_TAKEN"] .. ":"}

		else
			report_lines = {"Details!: " .. player.nome .. " - " .. Details.sub_atributos [mainSection].lista [subSection] .. ""}
		end

		for index, barra in ipairs(tabFrame.barras1) do
			if (barra:IsShown()) then
				local spellid = barra.show
				if (mainSection == 1 and subSection == 4) then --friendly fire
					report_lines [#report_lines+1] = barra.lineText1:GetText() .. ": " .. barra.lineText4:GetText()

				elseif (type(spellid) == "number" and spellid > 10) then
					local link = GetSpellLink(spellid)
					report_lines [#report_lines+1] = index .. ". " .. link .. ": " .. barra.lineText4:GetText()
				else
					local spellname = barra.lineText1:GetText():gsub((".*%."), "")
					spellname = spellname:gsub("|c%x%x%x%x%x%x%x%x", "")
					spellname = spellname:gsub("|r", "")
					report_lines [#report_lines+1] = index .. ". " .. spellname .. ": " .. barra.lineText4:GetText()
				end
			end

			if (index == amt) then
				break
			end
		end

	elseif (botao == 3) then --targets
		if (mainSection == 1 and subSection == 3) then
			Details:Msg(Loc ["STRING_ACTORFRAME_NOTHING"])
			return
		end

		report_lines = {"Details! " .. Loc ["STRING_ACTORFRAME_REPORTTARGETS"] .. " " .. Details.sub_atributos [1].lista [1] .. " " .. Loc ["STRING_ACTORFRAME_REPORTOF"] .. " " .. player.nome}

		for index, barra in ipairs(tabFrame.barras2) do
			if (barra:IsShown()) then
				report_lines[#report_lines+1] = barra.lineText1:GetText().. " -> " .. barra.lineText4:GetText()
			end
			if (index == amt) then
				break
			end
		end

	elseif (botao == 2) then --spell blocks
        --dano --damage done --dps --heal
		if ((mainSection == 1 and (subSection == 1 or subSection == 2)) or (mainSection == 2)) then
			if (not player.detalhes) then
				Details:Msg(Loc ["STRING_ACTORFRAME_NOTHING"])
				return
			end

			local nome = _GetSpellInfo(player.detalhes)

			report_lines = {"Details! " .. Loc ["STRING_ACTORFRAME_REPORTTO"] .. " " .. Details.sub_atributos [mainSection].lista [subSection] .. " " .. Loc ["STRING_ACTORFRAME_REPORTOF"] .. " " .. player.nome,
			Loc ["STRING_ACTORFRAME_SPELLDETAILS"] .. ": " .. nome}

			for i = 1, 5 do
				local caixa = Details.BreakdownWindowFrame.grupos_detalhes[i]
				if (caixa.bg:IsShown()) then

					local linha = ""

					local nome2 = caixa.nome2:GetText() --golpes
					if (nome2 and nome2 ~= "") then
						if (i == 1) then
							linha = linha..nome2 .. " / "
						else
							linha = linha .. caixa.nome:GetText() .. " " .. nome2 .. " / "
						end
					end

					local dano = caixa.dano:GetText() --dano
					if (dano and dano ~= "") then
						linha = linha .. dano .. " / "
					end

					local media = caixa.dano_media:GetText() --media
					if (media and media ~= "") then
						linha = linha..media .. " / "
					end

					local dano_dps = caixa.dano_dps:GetText()
					if (dano_dps and dano_dps ~= "") then
						linha = linha..dano_dps.." / "
					end

					local dano_porcento = caixa.dano_porcento:GetText()
					if (dano_porcento and dano_porcento ~= "") then
						linha = linha..dano_porcento.." "
					end

					report_lines [#report_lines+1] = linha
				end

				if (i == amt) then
					break
				end
			end

		--dano --damage tanken
		elseif ( (mainSection == 1 and subSection == 3) or mainSection == 3) then
			if (player.detalhes) then
				report_lines = {"Details! " .. Loc ["STRING_ACTORFRAME_REPORTTO"] .. " " .. Details.sub_atributos [1].lista [1] .. " " .. Loc ["STRING_ACTORFRAME_REPORTOF"] .. " " .. player.detalhes.. " " .. Loc ["STRING_ACTORFRAME_REPORTAT"] .. " " .. player.nome}
				for index, barra in ipairs(tabFrame.barras3) do
					if (barra:IsShown()) then
						report_lines [#report_lines+1] = barra.lineText1:GetText() .. " ....... " .. barra.lineText4:GetText()
					end
					if (index == amt) then
						break
					end
				end
			else
				report_lines = {}
			end
		end

	elseif (botao >= 11) then --primeira caixa dos detalhes
		botao =  botao - 10

		local nome
		if (type(spellid) == "string") then --unknown spellid value
			--is a pet
		else
			nome = _GetSpellInfo(player.detalhes)
			local spelllink = GetSpellLink(player.detalhes)
			if (spelllink) then
				nome = spelllink
			end
		end

		if (not nome) then
			nome = ""
		end

		report_lines = {"Details! " .. Loc ["STRING_ACTORFRAME_REPORTTO"] .. " " .. Details.sub_atributos [mainSection].lista [subSection].. " " .. Loc ["STRING_ACTORFRAME_REPORTOF"] .. " " .. player.nome,
		Loc ["STRING_ACTORFRAME_SPELLDETAILS"] .. ": " .. nome}

		local caixa = Details.BreakdownWindowFrame.grupos_detalhes[botao]

		local linha = ""
		local nome2 = caixa.nome2:GetText() --golpes
		if (nome2 and nome2 ~= "") then
			if (botao == 1) then
				linha = linha .. nome2 .. " / "
			else
				linha = linha .. caixa.nome:GetText() .. " " .. nome2 .. " / "
			end
		end

		local dano = caixa.dano:GetText() --dano
		if (dano and dano ~= "") then
			linha = linha..dano.." / "
		end

		local media = caixa.dano_media:GetText() --media
		if (media and media ~= "") then
			linha = linha..media.." / "
		end

		local dano_dps = caixa.dano_dps:GetText()
		if (dano_dps and dano_dps ~= "") then
			linha = linha..dano_dps.." / "
		end

		local dano_porcento = caixa.dano_porcento:GetText()
		if (dano_porcento and dano_porcento ~= "") then
			linha = linha..dano_porcento.." "
		end

		--remove a cor da school
		linha = linha:gsub("|c%x?%x?%x?%x?%x?%x?%x?%x?", "")
		linha = linha:gsub("|r", "")

		report_lines [#report_lines+1] = linha
	end

	return instance:envia_relatorio(report_lines)
end
