
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

---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) then
        ---@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 aggressorActor = actorContainer:GetActor(self.actorName)
                ---@type {topValue: number, data: {key1: spellid, key2: number, key3: actorname}[]}
                local spellList = damageClass.BuildDamageTakenSpellListFromAgressor(currentActor, aggressorActor)
                spellsTab.GenericScrollFrameRight:RefreshMe(spellList)

			elseif (subAttribute == DETAILS_SUBATTRIBUTE_FRIENDLYFIRE) then
				--currentActor is the player which inflicted the damage to other players
				--self.actorName is the name of the actor in the hovered bar
				local spellList = damageClass.BuildFriendlySpellListFromAgressor(currentActor, self.actorName)
				spellsTab.GenericScrollFrameRight:RefreshMe(spellList)
            end
        end
    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)
end

---get a generic bar from the scroll box, if it doesn't exist, return nil
---@param scrollFrame table
---@param lineIndex number
---@return breakdownphasebar
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

---@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)
            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)

	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 Hyanda"))
	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:SetTexture(1, 1, 1, 0.2)
	hightlightTexture:SetAllPoints()
	statusBar.highlightTexture = hightlightTexture

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

	--create an icon
	---@type texture
	local icon = statusBar:CreateTexture("$parentTexture", "overlay")
	icon:SetPoint("left", statusBar, "left", 0, 0)
	icon:SetSize(CONST_SPELLSCROLL_LINEHEIGHT-2, CONST_SPELLSCROLL_LINEHEIGHT-2)
	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)
	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()
	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