
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 or C_Spell.GetSpellLink --api local
local GetSpellInfo = GetSpellInfo
local _GetSpellInfo = Details.GetSpellInfo
local GameTooltip = GameTooltip
local DF = DetailsFramework
local tinsert = table.insert

local damageClass = Details.atributo_damage

local spellsTab = DetailsSpellBreakdownTab
local headerContainerType = spellsTab.headerContainerType

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

---@param actorBar breakdowngenericbar?
---@param locked boolean? whether the actor is locked in by clicking on it
local selectActor = function(actorBar, locked)
    local actorName = actorBar and actorBar.actorName or nil
    spellsTab.selectedActor = actorName
    spellsTab.selectedActorBar = actorBar
    spellsTab.actorIsLocked = locked

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

    ---@type actordamage
    local currentActor = spellsTab.GetActor()

    ---@type combat
    local currentCombat = instanceObject:GetCombat()

    ---@type actorcontainer
    local actorContainer = currentCombat:GetContainer(DETAILS_ATTRIBUTE_DAMAGE)

    if (mainAttribute == DETAILS_ATTRIBUTE_DAMAGE) then
        if (subAttribute == DETAILS_SUBATTRIBUTE_DAMAGETAKEN) then
            local spellList
            if (actorName) then
                local aggressorActor = actorContainer:GetActor(actorName)
                ---@type {topValue: number, data: {[1]: spellid, [2]: number, [3]: actorname}[]}
                spellList = currentActor:BuildDamageTakenSpellListFromAgressor(aggressorActor)
            else
                spellList = currentActor:BuildDamageTakenSpellList()
            end
            spellsTab.GenericScrollFrameRight:RefreshMe(spellList)

        elseif (subAttribute == DETAILS_SUBATTRIBUTE_FRIENDLYFIRE) then
            if (not actorName) then
                return
            end
            --currentActor is the player which inflicted the damage to other players
            --actorName is the name of the actor in the hovered bar
            local spellList = currentActor:BuildFriendlySpellListFromAgressor(actorName)
            spellsTab.GenericScrollFrameRight:RefreshMe(spellList)
        end
    end
end

---onEnter function for the generic bars, set the alpha of the bar to one
---@param self breakdowngenericbar
local onEnterGenericBar = function(self) --~onenter ~genericbaronenter
    self:SetAlpha(1)

    if (self.bIsFromLeftScroll and not spellsTab.actorIsLocked) then
        selectActor(self, false)
    end

    --when hovered over, it need to detect which data is shown and call a function within a class to generate the data to show in the right scroll
end

--onLeave function for the generic bars, set the alpha of the bar to 0.9
---@param self breakdowngenericbar
local onLeaveGenericBar = function(self) --~onleave ~genericbaronleave
    self:SetAlpha(0.9)

    if (self.bIsFromLeftScroll and not spellsTab.actorIsLocked) then
        selectActor(nil, false)
    end
end

---@param self breakdowngenericbar
local onClickGenericBar = function(self) --~onclick ~genericbaronclick
    if (self.bIsFromLeftScroll) then
        if (spellsTab.actorIsLocked) then
            if (spellsTab.selectedActorBar == self) then
                self.overlayTexture:Hide()
                selectActor(self, false)
                return
            end
            spellsTab.selectedActorBar.overlayTexture:Hide()
        end

        self.overlayTexture:Show()
        selectActor(self, true)
    end
end

---get a generic bar from the scroll box, if it doesn't exist, return nil
---@param scrollFrame table
---@param lineIndex number
---@return breakdowngenericbar
local getGenericBar = function(scrollFrame, lineIndex)
	---@type breakdowngenericbar
	local genericBar = scrollFrame:GetLine(lineIndex)

	--reset header alignment
	genericBar:ResetFramesToHeaderAlignment()

	--reset columns, hiding them
	genericBar.Icon:Hide()
	for inLineIndex = 1, #genericBar.InLineTexts do
		genericBar.InLineTexts[inLineIndex]:SetText("")
	end

	return genericBar
end

local onEnterSpellIconFrame = function(self)
    ---@type breakdowngenericbar
    local line = self:GetParent()
    if (line.spellId and type(line.spellId) == "number") then
        local spellName = _GetSpellInfo(line.spellId)
        if (spellName) then
            GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
            Details:GameTooltipSetSpellByID(line.spellId)
            GameTooltip:Show()
        end
    end
    line:GetScript("OnEnter")(line)
end

local onLeaveSpellIconFrame = function(self)
    GameTooltip:Hide()
    self:GetParent():GetScript("OnLeave")(self:GetParent())
end

---@param scrollFrame table
---@param scrollData table
---@param offset number
---@param totalLines number
local refreshGenericRightScrollFunc = function(scrollFrame, scrollData, offset, totalLines) --~refreshgeneric ~refreshfunc ~refresh ~refreshg ~updategenericbar
    local lineIndex = 1
	local combatTime = scrollData.combatTime
	local totalValue = scrollData.totalValue

    for i = 1, totalLines do
		local index = i + offset
		local spellData = scrollData[index]

        if (spellData) then
            local spellId = spellData.spellId
            local spellTotal = spellData.total
            local petName = spellData.petName
            local spellSchool = spellData.spellScholl

            local spellName, _, spellIcon = _GetSpellInfo(spellId)

            --get a bar from the second generic scroll frame
            local genericBar = getGenericBar(scrollFrame, i)
            genericBar.statusBar:SetValue(spellTotal / scrollFrame.topValue * 100)

            local r, g, b = Details:GetSpellSchoolColor(spellSchool)
            genericBar.statusBar:SetStatusBarColor(r, g, b, 1)

			---@type number
			local textIndex = 1

            if (scrollData.headersAllowed.icon) then
				---@type texturetable
				genericBar.Icon:Show()
				genericBar.Icon:SetTexture(spellIcon)
				genericBar.Icon:SetTexCoord(.1, .9, .1, .9)
				genericBar.Icon:SetSize(CONST_SPELLSCROLL_LINEHEIGHT-2, CONST_SPELLSCROLL_LINEHEIGHT-2)
				genericBar:AddFrameToHeaderAlignment(genericBar.Icon)
				genericBar.spellId = spellId
				if spellId then
					genericBar.IconFrame:SetScript("OnEnter", onEnterSpellIconFrame)
					genericBar.IconFrame:SetScript("OnLeave", onLeaveSpellIconFrame)
				else
					genericBar.IconFrame:SetScript("OnEnter", nil)
					genericBar.IconFrame:SetScript("OnLeave", nil)
				end
            end

			if (scrollData.headersAllowed.rank) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				fontString:SetText(index)
				textIndex = textIndex + 1
			end

			if (scrollData.headersAllowed.name) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				if (petName ~= "") then
					--remove the owner name from the pet name
					petName = petName:gsub((" <.*"), "")
					spellName = spellName .. " (" .. petName .. ")"
				end
				fontString:SetText(spellName)
				textIndex = textIndex + 1
                genericBar.actorName = spellName
			end

			if (scrollData.headersAllowed.amount) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				fontString:SetText(Details:Format(spellTotal))
				textIndex = textIndex + 1
			end

			if (scrollData.headersAllowed.persecond) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				fontString:SetText(Details:Format(spellTotal / combatTime))
				textIndex = textIndex + 1
			end

			if (scrollData.headersAllowed.percent) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				fontString:SetText(string.format("%.1f", spellTotal / totalValue * 100) .. "%")
				textIndex = textIndex + 1
			end

            genericBar:Show()

			genericBar:AlignWithHeader(scrollFrame.Header, "left")

			lineIndex = lineIndex + 1
			if (lineIndex > totalLines) then
				break
			end

            --set the amount
            --genericBar.InLineTexts[2]:SetText(Details:ToK(spellTotal))

            --set the amount percent
            --genericBar.InLineTexts[3]:SetText(Details:ToK(spellTotal / totalValue * 100))
        end
    end
end

---@param scrollFrame table
---@param scrollData table
---@param offset number
---@param totalLines number
local refreshGenericLeftScrollFunc = function(scrollFrame, scrollData, offset, totalLines) --~refreshgeneric ~refreshfunc ~refresh ~refreshg ~updategenericbar
	local lineIndex = 1
	local combatTime = scrollData.combatTime
	local totalValue = scrollData.totalValue

	for i = 1, totalLines do
		local index = i + offset
		local dataTable = scrollData[index]

		if (dataTable) then
			local genericBar = getGenericBar(scrollFrame, lineIndex)
			genericBar.statusBar:SetValue(dataTable.total / scrollFrame.topValue * 100)

			local spellSchool = dataTable.spellScholl
			local className = dataTable.class

			if (spellSchool) then
            	local r, g, b = Details:GetSpellSchoolColor(spellSchool)
            	genericBar.statusBar:SetStatusBarColor(r, g, b, 1)

			else
				local red, green, blue = Details:GetClassColor(className)
				genericBar.statusBar:SetStatusBarColor(red, green, blue, 1)
			end

			---@type number
			local textIndex = 1

			if (scrollData.headersAllowed.icon) then
				---@type texturetable
				local dataIcon = dataTable.icon
				genericBar.Icon:Show()
				genericBar.Icon:SetTexture(dataIcon.texture)
				genericBar.Icon:SetTexCoord(dataIcon.coords.left, dataIcon.coords.right, dataIcon.coords.top, dataIcon.coords.bottom)
				genericBar.Icon:SetSize(CONST_SPELLSCROLL_LINEHEIGHT-2, CONST_SPELLSCROLL_LINEHEIGHT-2)
				genericBar:AddFrameToHeaderAlignment(genericBar.Icon)
			end

			if (scrollData.headersAllowed.rank) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				fontString:SetText(index)
				textIndex = textIndex + 1
			end

			if (scrollData.headersAllowed.name) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				local nameWithoutRealm = DF:RemoveRealmName(dataTable.name)
				fontString:SetText(nameWithoutRealm or dataTable.name)
				textIndex = textIndex + 1
                genericBar.actorName = dataTable.name
			end

			if (scrollData.headersAllowed.amount) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				fontString:SetText(Details:Format(dataTable.total))
				textIndex = textIndex + 1
			end

			if (scrollData.headersAllowed.persecond) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				fontString:SetText(Details:Format(dataTable.total / combatTime))
				textIndex = textIndex + 1
			end

			if (scrollData.headersAllowed.percent) then
				---@type fontstring
				local fontString = genericBar.InLineTexts[textIndex]
				genericBar:AddFrameToHeaderAlignment(fontString)
				fontString:SetText(string.format("%.1f", dataTable.total / totalValue * 100) .. "%")
				textIndex = textIndex + 1
			end

			genericBar:AlignWithHeader(scrollFrame.Header, "left")

			lineIndex = lineIndex + 1
			if (lineIndex > totalLines) then
				break
			end
		end
	end
end


---create a genericbar within the generic scroll
---@param self breakdowngenericscrollframe
---@param index number
---@return breakdowngenericbar
local createGenericBar = function(self, index) --~create ~generic ~creategeneric ~genericbar
	---@type breakdowngenericbar
	local genericBar = CreateFrame("button", self:GetName() .. "GenericBarButton" .. index, self)
	genericBar.index = index

	--size and positioning
	genericBar:SetHeight(CONST_SPELLSCROLL_LINEHEIGHT)
	local y = (index-1) * CONST_SPELLSCROLL_LINEHEIGHT * -1 + (1 * -index) - 15
	genericBar:SetPoint("topleft", self, "topleft", 0, y)
	genericBar:SetPoint("topright", self, "topright", 0, y)

	genericBar:EnableMouse(true)

	genericBar:SetAlpha(0.9)
	genericBar:SetFrameStrata("HIGH")
	genericBar:SetScript("OnEnter", onEnterGenericBar)
	genericBar:SetScript("OnLeave", onLeaveGenericBar)
	genericBar:SetScript("OnClick", onClickGenericBar)

	DF:Mixin(genericBar, DF.HeaderFunctions)

	---@type breakdownspellbarstatusbar
	local statusBar = CreateFrame("StatusBar", "$parentStatusBar", genericBar)
	statusBar:SetAllPoints()
	statusBar:SetAlpha(0.8)
	statusBar:SetMinMaxValues(0, 100)
	statusBar:SetValue(50)
	statusBar:EnableMouse(false)
	statusBar:SetFrameLevel(genericBar:GetFrameLevel() - 1)
	genericBar.statusBar = statusBar

	---@type texture this is the statusbar texture
	local statusBarTexture = statusBar:CreateTexture("$parentTexture", "artwork")
	statusBarTexture:SetTexture(SharedMedia:Fetch("statusbar", Details.breakdown_general.bar_texture))
	statusBar:SetStatusBarTexture(statusBarTexture)
	statusBar:SetStatusBarColor(1, 1, 1, 1)

	---@type texture shown when the mouse hoverover this bar
	local hightlightTexture = statusBar:CreateTexture("$parentTextureHighlight", "highlight")
	hightlightTexture:SetColorTexture(1, 1, 1, 0.2)
	hightlightTexture:SetAllPoints()
	statusBar.highlightTexture = hightlightTexture

	---@type texture background texture
	local backgroundTexture = statusBar:CreateTexture("$parentTextureBackground", "border")
	backgroundTexture:SetAllPoints()
	backgroundTexture:SetColorTexture(.05, .05, .05)
	backgroundTexture:SetAlpha(1)
	statusBar.backgroundTexture = backgroundTexture

	---@type texture overlay texture to use when the spellbar is selected
	local statusBarOverlayTexture = statusBar:CreateTexture("$parentTextureOverlay", "overlay", nil, 7)
	statusBarOverlayTexture:SetTexture([[Interface/AddOns/Details/images/overlay_indicator_1]])
	statusBarOverlayTexture:SetVertexColor(1, 1, 1, 0.2)
	statusBarOverlayTexture:SetAllPoints()
	statusBarOverlayTexture:Hide()
	genericBar.overlayTexture = statusBarOverlayTexture
	statusBar.overlayTexture = statusBarOverlayTexture

	--frame which will allow showing the spell tooltip
	---@type frame
	local iconFrame = CreateFrame("frame", "$parentIconFrame", genericBar, "BackdropTemplate")
	iconFrame:SetPoint("left", statusBar, "left", 0, 0)
	iconFrame:SetSize(CONST_SPELLSCROLL_LINEHEIGHT-2, CONST_SPELLSCROLL_LINEHEIGHT-2)
	genericBar.IconFrame = iconFrame

	--create the icon to show the spell texture
	---@type texture
	local icon = iconFrame:CreateTexture("$parentTexture", "overlay")
	icon:SetAllPoints()
	icon:SetTexCoord(.1, .9, .1, .9)
	genericBar.Icon = icon

	genericBar:AddFrameToHeaderAlignment(icon)

	genericBar.InLineTexts = {}

	for i = 1, 5 do
		---@type fontstring
		local fontString = genericBar:CreateFontString("$parentFontString" .. i, "overlay", "GameFontHighlightSmall")
		fontString:SetJustifyH("left")
		fontString:SetTextColor(1, 1, 1, 1)
		fontString:SetNonSpaceWrap(true)
		fontString:SetWordWrap(false)
		genericBar["lineText" .. i] = fontString
		genericBar.InLineTexts[i] = fontString
		fontString:SetTextColor(1, 1, 1, 1)
		genericBar:AddFrameToHeaderAlignment(fontString)
	end

	genericBar:AlignWithHeader(self.Header, "left")

	return genericBar
end

---create two generic containers, these containers hold bars that can show any type of data
---an example is damage taken in the left container and the spells which caused the damage in the right container
---@param tabFrame tabframe
---@return breakdowngenericscrollframe, breakdowngenericscrollframe
function spellsTab.CreateGenericContainers(tabFrame) --~create ~generic ~creategenericcontainer ~creategenericscroll ~creategeneric
	local defaultAmountOfLines = 50

	--create a container for the scrollframe
	local optionsLeftScroll = {
		width = Details.breakdown_spell_tab.genericcontainer_width,
		height = Details.breakdown_spell_tab.genericcontainer_height,
		is_locked = Details.breakdown_spell_tab.genericcontainer_islocked,
		can_move = false,
		can_move_children = false,
		use_top_resizer = true,
		use_right_resizer = true,
		use_bottom_resizer = true,
		use_left_resizer = true,
	}

	--create a container for the scrollframe
	local optionsRightScroll = {
		width = Details.breakdown_spell_tab.genericcontainer_right_width,
		height = Details.breakdown_spell_tab.genericcontainer_right_height,
		is_locked = Details.breakdown_spell_tab.genericcontainer_islocked,
		can_move = false,
		can_move_children = false,
		use_top_resizer = true,
		use_right_resizer = true,
		use_bottom_resizer = true,
		use_left_resizer = true,
	}

	---@type df_framecontainer
	local leftContainer = DF:CreateFrameContainer(tabFrame, optionsLeftScroll, tabFrame:GetName() .. "GenericScrollContainerLeft")
	leftContainer:SetPoint("topleft", tabFrame, "topleft", 0, 0)
	leftContainer:SetFrameLevel(tabFrame:GetFrameLevel()+1)
	leftContainer:HookScript("OnShow", function()
		spellsTab.actorIsLocked = false
		if spellsTab.selectedActorBar then
			spellsTab.selectedActorBar.overlayTexture:Hide()
			spellsTab.selectedActor, spellsTab.selectedActorBar = nil, nil
		end
	end)
	spellsTab.GenericContainerFrameLeft = leftContainer

	---@type df_framecontainer
	local rightContainer = DF:CreateFrameContainer(tabFrame, optionsRightScroll, tabFrame:GetName() .. "GenericScrollContainerRight")
	rightContainer:SetPoint("topleft", leftContainer, "topright", 30, 0)
	rightContainer:SetFrameLevel(tabFrame:GetFrameLevel()+1)
	spellsTab.GenericContainerFrameRight = rightContainer

	--when a setting is changed in the container, it will call this function, it is registered below with SetSettingChangedCallback()
	local settingChangedCallbackFunction_Left = function(frameContainer, settingName, settingValue)
		if (frameContainer:IsShown()) then
			if (settingName == "height") then
				---@type number
				local currentHeight = frameContainer.ScrollFrame:GetHeight()
				Details.breakdown_spell_tab.genericcontainer_height = settingValue
				frameContainer.ScrollFrame:SetNumFramesShown(math.floor(currentHeight / CONST_SPELLSCROLL_LINEHEIGHT) - 2)

			elseif (settingName == "width") then
				Details.breakdown_spell_tab.genericcontainer_width = settingValue

			elseif (settingName == "is_locked") then
				Details.breakdown_spell_tab.genericcontainer_islocked = settingValue
			end
		end
	end
	leftContainer:SetSettingChangedCallback(settingChangedCallbackFunction_Left)

	--when a setting is changed in the container, it will call this function, it is registered below with SetSettingChangedCallback()
	local settingChangedCallbackFunction_Right = function(frameContainer, settingName, settingValue)
		if (frameContainer:IsShown()) then
			if (settingName == "height") then
				---@type number
				local currentHeight = frameContainer.ScrollFrame:GetHeight()
				Details.breakdown_spell_tab.genericcontainer_right_height = settingValue
				frameContainer.ScrollFrame:SetNumFramesShown(math.floor(currentHeight / CONST_SPELLSCROLL_LINEHEIGHT) - 2)

			elseif (settingName == "width") then
				Details.breakdown_spell_tab.genericcontainer_right_width = settingValue

			elseif (settingName == "is_locked") then
				Details.breakdown_spell_tab.genericcontainer_islocked = settingValue
			end
		end
	end
	rightContainer:SetSettingChangedCallback(settingChangedCallbackFunction_Right)

	--create the left scrollframe
	local genericScrollFrameLeft = DF:CreateScrollBox(leftContainer, "$parentGenericScrollLeft", refreshGenericLeftScrollFunc, {}, Details.breakdown_spell_tab.genericcontainer_width, Details.breakdown_spell_tab.genericcontainer_height, defaultAmountOfLines, CONST_SPELLSCROLL_LINEHEIGHT)
	DF:ReskinSlider(genericScrollFrameLeft)
	genericScrollFrameLeft:SetBackdrop({})
	genericScrollFrameLeft:SetAllPoints()
    leftContainer:RegisterChildForDrag(genericScrollFrameLeft)
    leftContainer.ScrollFrame = genericScrollFrameLeft
	genericScrollFrameLeft.DontHideChildrenOnPreRefresh = false
	tabFrame.GenericScrollFrameLeft = genericScrollFrameLeft
	spellsTab.GenericScrollFrameLeft = genericScrollFrameLeft

	--create the right scrollframe
	local genericScrollFrameRight = DF:CreateScrollBox(rightContainer, "$parentGenericScrollRight", refreshGenericRightScrollFunc, {}, Details.breakdown_spell_tab.genericcontainer_right_width, Details.breakdown_spell_tab.genericcontainer_right_height, defaultAmountOfLines, CONST_SPELLSCROLL_LINEHEIGHT)
	DF:ReskinSlider(genericScrollFrameRight)
	genericScrollFrameRight:SetBackdrop({})
	genericScrollFrameRight:SetAllPoints()
    rightContainer:RegisterChildForDrag(genericScrollFrameRight)
    rightContainer.ScrollFrame = genericScrollFrameRight
	genericScrollFrameRight.DontHideChildrenOnPreRefresh = false
	tabFrame.GenericScrollFrameRight = genericScrollFrameRight
	spellsTab.GenericScrollFrameRight = genericScrollFrameRight

	function genericScrollFrameLeft:RefreshMe(data) --~refreshme (generic) ~refreshg
		--get which column is currently selected and the sort order
		local columnIndex, order, key = genericScrollFrameLeft.Header:GetSelectedColumn()
		genericScrollFrameLeft.SortKey = key

		---@type string
		local keyToSort = key

		if (order == "DESC") then
			table.sort(data,
			function(t1, t2)
				return t1[keyToSort] > t2[keyToSort]
			end)
			genericScrollFrameLeft.topValue = data[1] and data[1][keyToSort] or 0.00001
		else
			table.sort(data,
			function(t1, t2)
				return t1[keyToSort] < t2[keyToSort]
			end)
			genericScrollFrameLeft.topValue = data[#data] and data[#data][keyToSort] or 0.00001
		end

		genericScrollFrameLeft:SetData(data)
		genericScrollFrameLeft:Refresh()

		--clear the right scrollframe
		genericScrollFrameRight:SetData({})
		genericScrollFrameRight:Refresh()

		local selectedActorFound = false
		for i = 1, #data do
			if spellsTab.selectedActor == data[i].name then
				selectActor(genericScrollFrameLeft:GetLine(i), spellsTab.actorIsLocked)
				selectedActorFound = true
				break
			end
		end
		if not selectedActorFound then
			selectActor(nil)
		end
	end

    function genericScrollFrameRight:RefreshMe(data) --~refreshme (generic) ~refreshg
		--get which column is currently selected and the sort order
		local columnIndex, order, key = genericScrollFrameRight.Header:GetSelectedColumn()
		genericScrollFrameRight.SortKey = key

		---@type string
		local keyToSort = key

		if (order == "DESC") then
			table.sort(data,
			function(t1, t2)
				return t1[keyToSort] > t2[keyToSort]
			end)
			genericScrollFrameRight.topValue = data[1] and data[1][keyToSort] or 0.00001
		else
			table.sort(data,
			function(t1, t2)
				return t1[keyToSort] < t2[keyToSort]
			end)
			genericScrollFrameRight.topValue = data[#data] and data[#data][keyToSort] or 0.00001
		end

		genericScrollFrameRight:SetData(data)
		genericScrollFrameRight:Refresh()
    end

	--~header
	local headerOptions = {
		padding = 2,
		header_height = 14,

		reziser_shown = true,
		reziser_width = 2,
		reziser_color = {.5, .5, .5, 0.7},
		reziser_max_width = 246,

		header_click_callback = spellsTab.OnAnyColumnHeaderClickCallback,

		header_backdrop_color = {0.1, 0.1, 0.1, 0.4},
		text_color = {1, 1, 1, 0.823},
	}

	local headerOptionsRight = {
		padding = 2,
		header_height = 14,

		reziser_shown = true,
		reziser_width = 2,
		reziser_color = {.5, .5, .5, 0.7},
		reziser_max_width = 210,

		header_click_callback = spellsTab.OnAnyColumnHeaderClickCallback,

		header_backdrop_color = {0.1, 0.1, 0.1, 0.4},
		text_color = {1, 1, 1, 0.823},
	}

	---@type df_headerframe
	local headerLeft = DetailsFramework:CreateHeader(leftContainer, spellsTab.genericContainerLeftColumnData, headerOptions)
	headerLeft:SetPoint("topleft", genericScrollFrameLeft, "topleft", 0, 1)
	headerLeft:SetColumnSettingChangedCallback(spellsTab.OnHeaderColumnOptionChanged)
	genericScrollFrameLeft.Header = headerLeft

	---@type df_headerframe
	local headerRight = DetailsFramework:CreateHeader(rightContainer, spellsTab.genericContainerRightColumnData, headerOptionsRight)
	headerRight:SetPoint("topleft", genericScrollFrameRight, "topleft", 0, 1)
	headerRight:SetColumnSettingChangedCallback(spellsTab.OnHeaderColumnOptionChanged)
	genericScrollFrameRight.Header = headerRight

	--cache the type of these headers
	headerContainerType[headerLeft] = "generic_left"
	headerContainerType[headerRight] = "generic_right"

	--create the scroll lines
	for i = 1, defaultAmountOfLines do
		local lineFrame = genericScrollFrameLeft:CreateLine(createGenericBar)
        lineFrame.bIsFromLeftScroll = true
	end

	--create the scroll lines
	for i = 1, defaultAmountOfLines do
		local lineFrame = genericScrollFrameRight:CreateLine(createGenericBar)
        lineFrame:Hide()
        lineFrame.bIsFromRightScroll = true
	end

	--need to create the second scroll frame to show the details about the spelltable/actor hovered over

	return genericScrollFrameLeft, genericScrollFrameRight
end