
local detailsFramework = DetailsFramework

if (not detailsFramework or not DetailsFrameworkCanLoad) then
	return
end

if not detailsFramework.IsMidnightWowAPI() then return end

local unpack = unpack
local G_CreateFrame = _G.CreateFrame
local CreateFrame = function (frameType , name, parent, template, id)
	local frame = G_CreateFrame(frameType , name, parent, template, id)
	detailsFramework:Mixin(frame, detailsFramework.FrameFunctions)
	return frame
end
local PixelUtil = PixelUtil
local GetTime = GetTime
local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end

detailsFramework.GrowDirectionBySide = {
	[1] = 1,
	[2] = 2,
	[3] = 1,
	[4] = 1,
	[5] = 2,
	[6] = 1,
	[7] = 2,
	[8] = 1,
	[9] = 1,
	[10] = 1,
	[11] = 2,
	[12] = 1,
	[13] = 1,
	[14] = 1,
	[15] = 1,
	[16] = 2,
	[17] = 2,
}

detailsFramework.ShouldCenterAlign = {
	[9] = true,
	[10] = true,
	[12] = true,
}

detailsFramework.SideIsCorner = {
	[1] = true,
	[3] = true,
	[5] = true,
	[7] = true,
	[14] = true,
	[15] = true,
	[16] = true,
	[17] = true,
}

---@class df_icon : button
---@field spellId number
---@field startTime number
---@field duration number
---@field count number
---@field width number
---@field height number
---@field debuffType string
---@field caster string
---@field canStealOrPurge boolean
---@field isBuff boolean
---@field spellName string
---@field timeRemaining number
---@field expirationTime number
---@field lastUpdateCooldown number
---@field identifierKey string
---@field endTime number
---@field nextUpdate number
---@field currentCoords table
---@field textureWidth number
---@field textureHeight number
---@field stacks number
---@field Texture texture
---@field Border texture
---@field StackText fontstring
---@field StackTextShadow fontstring
---@field Desc fontstring
---@field Cooldown cooldown
---@field parentIconRow df_iconrow
---@field cooldownLooper timer

---@class df_iconrow : frame
---@field options table
---@field NextIcon number
---@field IconPool df_icon[]
---@field AuraCache table
---@field shownAmount number
---@field CreateIcon fun(self:df_iconrow, iconName:string?):frame
---@field GetIcon fun(self:df_iconrow):frame
---@field AddSpecificIcon fun(self:df_iconrow, identifierKey:string, spellId:number?, borderColor:table?, startTime:number?, duration:number?, forceTexture:string|number|nil, descText:table?, count:number?, debuffType:string?, caster:string?, canStealOrPurge:boolean?, spellName:string?, isBuff:boolean?)
---@field SetIcon fun(self:df_iconrow, spellId:number?, borderColor:table?, startTime:number?, duration:number?, forceTexture:string?, descText:table?, count:number?, debuffType:string?, caster:string?, canStealOrPurge:boolean?, spellName:string?, isBuff:boolean?, modRate:number?):frame
---@field SetStacks fun(self:df_iconrow, iconFrame:table, bIsShown:boolean, stacksAmount:number?) is shown false to hide the stack text
---@field OnIconTick fun(self:df_icon, deltaTime:number)
---@field FormatCooldownTime fun(thisTime:number):string
---@field FormatCooldownTimeDecimal fun(formattedTime:number):string
---@field RemoveSpecificIcon fun(self:df_iconrow, identifierKey:any)
---@field ClearIcons fun(self:df_iconrow, resetBuffs:boolean?, resetDebuffs:boolean?)
---@field AlignAuraIcons fun(self:df_iconrow)
---@field GetIconGrowDirection fun(self:df_iconrow):number
---@field OnOptionChanged fun(self:df_iconrow, optionName:string)

---@class df_icontemplate : table
---@field id any
---@field texture texturepath|textureid
---@field startTime number?
---@field duration number?
---@field count number?
---@field coords table?
---@field width number?
---@field height number?
---@field points table?
---@field borderColor table?
---@field borderTexture texturepath|textureid|nil
---@field scale number?
---@field alpha number?

local spellIconCache = {}
local spellNameCache = {}
local emptyTable = {}
local white = {1, 1, 1, 1}

local iconFrameOnHideScript = function(self)
	if (self.cooldownLooper) then
		self.cooldownLooper:Cancel()
	end
end

detailsFramework.IconMixin = {
	---create a new icon frame
	---@param self df_iconrow the parent frame
	---@param iconName string the name of the icon frame
	---@return df_icon
    CreateIcon = function(self, iconName)
		---@type df_icon
        local iconFrame = CreateFrame("button", iconName, self, "BackdropTemplate")

		---@type texture
        iconFrame.Texture = iconFrame:CreateTexture(nil, "artwork")
        PixelUtil.SetPoint(iconFrame.Texture, "topleft", iconFrame, "topleft", 1, -1)
        PixelUtil.SetPoint(iconFrame.Texture, "bottomright", iconFrame, "bottomright", -1, 1)

		---@type texture
        iconFrame.Border = iconFrame:CreateTexture(nil, "background")
        iconFrame.Border:SetAllPoints()
        iconFrame.Border:SetColorTexture(0, 0, 0)

		---@type fontstring
        iconFrame.StackText = iconFrame:CreateFontString(nil, "overlay", "GameFontNormal")
        iconFrame.StackText:SetPoint("bottomright", iconFrame, "bottomright", 0, 0)
        iconFrame.StackText:Hide()
        iconFrame.StackTextShadow = iconFrame:CreateFontString(nil, "artwork", "GameFontNormal")
        iconFrame.StackTextShadow:SetPoint("center", iconFrame.StackText, "center", 0, 0)
		iconFrame.StackTextShadow:SetTextColor(0, 0, 0)
        iconFrame.StackTextShadow:Hide()

		---@type fontstring
        iconFrame.Desc = iconFrame:CreateFontString(nil, "overlay", "GameFontNormal")
        iconFrame.Desc:SetPoint("bottom", iconFrame, "top", 0, 2)
        iconFrame.Desc:Hide()

        local cooldownFrame = CreateFrame("cooldown", "$parentCooldown", iconFrame, "CooldownFrameTemplate, BackdropTemplate")
        cooldownFrame:SetAllPoints()
        cooldownFrame:EnableMouse(false)
        cooldownFrame:SetFrameLevel(iconFrame:GetFrameLevel()+1)
        iconFrame.Cooldown = cooldownFrame

		iconFrame.Cooldown:SetMinimumCountdownDuration(0)
		iconFrame.Cooldown:SetCountdownAbbrevThreshold(60)

		---@type fontstring
        --iconFrame.CountdownText = cooldownFrame:CreateFontString(nil, "overlay", "GameFontNormal")
		iconFrame.CountdownText = iconFrame.Cooldown:GetRegions()
        iconFrame.CountdownText:SetPoint("center", iconFrame, "center", 0, 0)
        iconFrame.CountdownText:Hide()

		iconFrame.stacks = 0
		iconFrame:SetScript("OnHide", iconFrameOnHideScript)

		return iconFrame
    end,

	---get an icon frame from the pool
	---@param self df_iconrow the parent frame
	---@return df_icon
	GetIcon = function(self)
		---@type df_icon
		local iconFrame = self.IconPool[self.NextIcon]

		if (not iconFrame) then
			---@type df_icon
            iconFrame = self:CreateIcon("$parentIcon" .. self.NextIcon)
            iconFrame.parentIconRow = self

			iconFrame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
			iconFrame:SetBackdropBorderColor(0, 0, 0, 0)
			iconFrame:EnableMouse(false)
			--iconFrame.Cooldown:SetHideCountdownNumbers(self.options.surpress_blizzard_cd_timer)
			iconFrame.Cooldown:SetHideCountdownNumbers(not self.options.show_text)
			iconFrame.Cooldown.noCooldownCount = self.options.surpress_tulla_omni_cc
			iconFrame.CountdownText:ClearAllPoints()
			iconFrame.CountdownText:SetPoint(self.options.text_anchor or "center", iconFrame, self.options.text_rel_anchor or "center", self.options.text_x_offset or 0, self.options.text_y_offset or 0)
			iconFrame.Desc:ClearAllPoints()
			iconFrame.Desc:SetPoint(self.options.desc_text_anchor or "bottom", iconFrame, self.options.desc_text_rel_anchor or "top", self.options.desc_text_x_offset or 0, self.options.desc_text_y_offset or 2)


            iconFrame.StackText:ClearAllPoints()
            iconFrame.StackText:SetPoint(self.options.stack_text_anchor or "center", iconFrame, self.options.stack_text_rel_anchor or "center", self.options.stack_text_x_offset or 0, self.options.stack_text_y_offset or 0)

			self.IconPool[self.NextIcon] = iconFrame
			iconFrame = iconFrame
		end

		iconFrame:ClearAllPoints()

		local anchor = self.options.anchor
		local anchorTo = self.NextIcon == 1 and self or self.IconPool[self.NextIcon - 1]
		local xPadding = self.NextIcon == 1 and self.options.left_padding or self.options.icon_padding or 1
		local growDirection = self.options.grow_direction

		if (growDirection == 1) then --grow to right
			if (self.NextIcon == 1) then
				PixelUtil.SetPoint(iconFrame, "left", anchorTo, "left", xPadding, 0)
			else
				PixelUtil.SetPoint(iconFrame, "left", anchorTo, "right", xPadding, 0)
			end

		elseif (growDirection == 2) then --grow to left
			if (self.NextIcon == 1) then
				PixelUtil.SetPoint(iconFrame, "right", anchorTo, "right", xPadding, 0)
			else
				PixelUtil.SetPoint(iconFrame, "right", anchorTo, "left", xPadding, 0)
			end

		end

		detailsFramework:SetFontColor(iconFrame.CountdownText, self.options.text_color)

		self.NextIcon = self.NextIcon + 1
		return iconFrame
	end,

	---adds only if not existing already in the cache
	---@param self df_iconrow the parent frame
	AddSpecificIcon = function(self, identifierKey, spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff, modRate)
		if (not identifierKey or identifierKey == "") then
			return
		end

		if (not self.AuraCache[identifierKey]) then
			---@type df_icon
			local icon = self:SetIcon(spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff or false, modRate)
			icon.identifierKey = identifierKey
			self.AuraCache[identifierKey] = true
		end
	end,

	---set an icon frame
	---@param self df_iconrow the parent frame
	---@return df_icon?
	SetIcon = function(self, spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff, modRate)
		local actualSpellName, _, spellIcon = GetSpellInfo(spellId)

		if forceTexture then
			spellIcon = forceTexture
		end

		spellName = spellName or actualSpellName or "unknown_aura"
		modRate = modRate or 1

		if (spellIcon) then
			---@type df_icon
			local iconFrame = self:GetIcon()
			iconFrame.Texture:SetTexture(spellIcon)
			iconFrame.Texture:SetTexCoord(unpack(self.options.texcoord))

			if (borderColor) then
				iconFrame:SetBackdropBorderColor(unpack(borderColor))
			else
				iconFrame:SetBackdropBorderColor(0, 0, 0 ,0)
			end

			--iconFrame.Border:SetColorTexture(0, 0, 0, 1)

			if (duration) then
				local now = GetTime()

				local durationObject = duration

				iconFrame.timeRemaining = durationObject:GetRemainingDuration()
				iconFrame.expirationTime = durationObject:GetEndTime()
				

				local noExpirationTime = durationObject:IsZero()--C_UnitAuras.DoesAuraHaveExpirationTime(auraIconFrame.unitFrame.namePlateUnitToken, i)
				iconFrame.Cooldown:SetCooldownFromDurationObject(durationObject)
				iconFrame.Cooldown:SetAlphaFromBoolean(noExpirationTime, 0, 1)

				if (self.options.show_text) then
					iconFrame.Cooldown:SetHideCountdownNumbers(false)
					iconFrame.CountdownText:Show()

					detailsFramework:SetFontSize(iconFrame.CountdownText, self.options.text_size)
					detailsFramework:SetFontFace (iconFrame.CountdownText, self.options.text_font)
					detailsFramework:SetFontOutline (iconFrame.CountdownText, self.options.text_outline)

					if self.options.on_tick_cooldown_update then
						iconFrame.lastUpdateCooldown = now
						iconFrame:SetScript("OnUpdate", self.OnIconTick)
					else
						iconFrame:SetScript("OnUpdate", nil)
					end

				else
					iconFrame.Cooldown:SetHideCountdownNumbers(true)
					iconFrame:SetScript("OnUpdate", nil)
					iconFrame.CountdownText:Hide()
				end

				iconFrame.Cooldown:SetReverse(self.options.cooldown_reverse)
				iconFrame.Cooldown:SetDrawSwipe(self.options.cooldown_swipe_enabled)
				iconFrame.Cooldown:SetEdgeTexture(self.options.cooldown_edge_texture)
				--iconFrame.Cooldown:SetHideCountdownNumbers(self.options.surpress_blizzard_cd_timer)
				iconFrame.Cooldown:Show()
			else
				iconFrame.timeRemaining = nil
				iconFrame.expirationTime = nil
				iconFrame:SetScript("OnUpdate", nil)
				iconFrame.CountdownText:Hide()
				iconFrame.Cooldown:Hide()
			end

			if (descText and self.options.desc_text) then
				iconFrame.Desc:Show()
				iconFrame.Desc:SetText(descText.text)
				iconFrame.Desc:SetTextColor(detailsFramework:ParseColors(descText.text_color or self.options.desc_text_color))
				iconFrame.Desc:SetPoint(self.options.desc_text_anchor or "bottom", iconFrame, self.options.desc_text_rel_anchor or "top", self.options.desc_text_x_offset or 0, self.options.desc_text_y_offset or 2)
				detailsFramework:SetFontSize(iconFrame.Desc, descText.text_size or self.options.desc_text_size)
				detailsFramework:SetFontFace(iconFrame.Desc, self.options.desc_text_font)
				detailsFramework:SetFontOutline(iconFrame.Desc, self.options.desc_text_outline)
			else
				iconFrame.Desc:Hide()
			end

			if (count and self.options.stack_text) then
				iconFrame.StackText:Show()
				iconFrame.StackText:SetText(C_StringUtil.TruncateWhenZero(count))
				iconFrame.StackText:SetTextColor(detailsFramework:ParseColors(self.options.stack_text_color))
				iconFrame.StackText:SetPoint(self.options.stack_text_anchor or "center", iconFrame, self.options.stack_text_rel_anchor or "center", self.options.stack_text_x_offset or 0, self.options.stack_text_y_offset or 0)
				detailsFramework:SetFontSize(iconFrame.StackText, self.options.stack_text_size)
				detailsFramework:SetFontFace(iconFrame.StackText, self.options.stack_text_font)
				detailsFramework:SetFontOutline(iconFrame.StackText, self.options.stack_text_outline)
			else
				iconFrame.StackText:Hide()
			end

			iconFrame.stacks = count or 0

			iconFrame.width = self.options.icon_width
			iconFrame.height = self.options.icon_height
			iconFrame.textureWidth = iconFrame.Texture:GetWidth()
			iconFrame.textureHeight = iconFrame.Texture:GetHeight()
			PixelUtil.SetSize(iconFrame, iconFrame.width, iconFrame.height)
			iconFrame:Show()

			--update the size of the frame
			self:SetWidth((self.options.left_padding * 2) + (self.options.icon_padding * (self.NextIcon-2)) + (self.options.icon_width * (self.NextIcon - 1)))
			self:SetHeight(self.options.icon_height + (self.options.top_padding * 2))

			--make information available
			iconFrame.spellId = spellId
			iconFrame.startTime = startTime
			iconFrame.duration = duration
			iconFrame.count = count
			iconFrame.debuffType = debuffType
			iconFrame.caster = caster
			iconFrame.canStealOrPurge = canStealOrPurge
			iconFrame.isBuff = isBuff
			iconFrame.spellName = spellName
			iconFrame.modRate = modRate

			iconFrame.identifierKey = nil -- only used for "specific" add/remove

			--show the frame
			self:Show()

			return iconFrame
		end
	end,

	SetStacks = function(self, iconFrame, bIsShown, stacksAmount)
		if (bIsShown) then
			iconFrame.StackText:Show()
			iconFrame.StackTextShadow:Show()
			iconFrame.StackText:SetText(stacksAmount)
			iconFrame.StackTextShadow:SetText(stacksAmount)
			iconFrame.stacks = stacksAmount
		else
			iconFrame.StackText:Hide()
			iconFrame.StackTextShadow:Hide()
			iconFrame.stacks = 0
		end
	end,

	---@param self df_icon
	---@param deltaTime number
	OnIconTick = function(self, deltaTime)
		local now = GetTime()
		if (self.lastUpdateCooldown + 0.5) <= now then
			-- TODO: room for pandemic
			self.lastUpdateCooldown = now
		end
	end,

	FormatCooldownTime = function(thisTime)
		local abbreviationData = {
			breakpointData =
			{
				{
					breakpoint = 3600,
					abbreviation = "h", 
					significandDivisor = 60,
					fractionDivisor = 60,
					abbreviationIsGlobal = false,
				},
				{
					breakpoint = 60,
					abbreviation = "m", 
					significandDivisor = 60,
					fractionDivisor = 1,
					abbreviationIsGlobal = false,
				},
				{
					breakpoint = 0,
					abbreviation = "s", 
					significandDivisor = 1,
					fractionDivisor = 1,
					abbreviationIsGlobal = false,
				},
			}
		}
		return AbbreviateNumbers(thisTime, abbreviationData) -- TODO: cleanup
	end,

	FormatCooldownTimeDecimal = function(formattedTime)
        local abbreviationData = {
			breakpointData =
			{
				{
					breakpoint = 3600,
					abbreviation = "h", 
					significandDivisor = 60,
					fractionDivisor = 60,
					abbreviationIsGlobal = false,
				},
				{
					breakpoint = 60,
					abbreviation = "m", 
					significandDivisor = 60,
					fractionDivisor = 1,
					abbreviationIsGlobal = false,
				},
				{
					breakpoint = 0,
					abbreviation = "s", 
					significandDivisor = 1,
					fractionDivisor = 1,
					abbreviationIsGlobal = false,
				},
			}
		}
		return AbbreviateNumbers(formattedTime, abbreviationData) -- TODO: cleanup
	end,

	---@param self df_iconrow the parent frame
	---@param identifierKey any
	RemoveSpecificIcon = function(self, identifierKey)
		if (not identifierKey or identifierKey == "") then
			return
		end

		if (not self.AuraCache[identifierKey]) then
			return
		end
		self.AuraCache[identifierKey] = nil

		local iconPool = self.IconPool

		--find and hide the icon frame
		for i = 1, self.NextIcon -1 do
			local iconFrame = iconPool[i]
			if (iconFrame.identifierKey and iconFrame.identifierKey == identifierKey) then
				iconFrame:Hide()
				iconFrame:ClearAllPoints()
				iconFrame.identifierKey = nil
			end
		end

		self:AlignAuraIcons()
	end,

	---@param self df_iconrow the parent frame
	ClearIcons = function(self, resetBuffs, resetDebuffs)
		resetBuffs = resetBuffs ~= false
		resetDebuffs = resetDebuffs ~= false

		local iconPool = self.IconPool
		local iconsActive = 0

		for i = 1, self.NextIcon -1 do
			local iconFrame = iconPool[i]
			if (iconFrame.isBuff == nil) then
				iconFrame:Hide()
				iconFrame:ClearAllPoints()

			elseif (resetBuffs and iconFrame.isBuff) then
				iconFrame:Hide()
				iconFrame:ClearAllPoints()

			elseif (resetDebuffs and not iconFrame.isBuff) then
				iconFrame:Hide()
				iconFrame:ClearAllPoints()

			else
				iconsActive = iconsActive + 1
			end
		end

		-- this is needed in any case, to ensure updated 'self.NextIcon'
		self:AlignAuraIcons()
	end,

	---@param self df_iconrow the parent frame
	AlignAuraIcons = function(self)
		local iconPool = self.IconPool
		local iconAmount = #iconPool
		local countStillShown = 0

		if iconAmount == 0 then
			self:Hide()
		else
			table.sort(iconPool, function(i1, i2) return i1:IsShown() and not i2:IsShown() end)
			local shownAmount = 0
			for i = 1, iconAmount do
				if iconPool[i]:IsShown() then
					shownAmount = shownAmount + 1
				end
			end

			local width = 0

			--re-anchor not hidden
			for i = 1, shownAmount do
				local iconFrame = iconPool[i]
				local anchor = self.options.anchor
				local anchorTo = i == 1 and self or self.IconPool[i - 1]
				local xPadding = i == 1 and self.options.left_padding or self.options.icon_padding or 1
				local growDirection = self.options.grow_direction

				countStillShown = countStillShown + (iconFrame:IsShown() and 1 or 0)

				iconFrame:ClearAllPoints()

				if (growDirection == 1) then --grow to right
					if (i == 1) then
						PixelUtil.SetPoint(iconFrame, "left", anchorTo, "left", xPadding, 0)
					else
						PixelUtil.SetPoint(iconFrame, "left", anchorTo, "right", xPadding, 0)
					end

				elseif (growDirection == 2) then --grow to left
					if (i == 1) then
						PixelUtil.SetPoint(iconFrame, "right", anchorTo, "right", xPadding, 0)
					else
						PixelUtil.SetPoint(iconFrame, "right", anchorTo, "left", xPadding, 0)
					end
				end

				width = width + ((iconFrame.width or iconFrame:GetWidth()) * iconFrame:GetScale()) + xPadding
			end

			if (self.options.center_alignment) then
				self:SetWidth(width)
			end

			self.shownAmount = shownAmount
		end
		
		if countStillShown > 0 then
			self:Show()
		end

		self.NextIcon = countStillShown + 1

		if countStillShown > 0 then
			self:Show()
		end
	end,

	---@param self df_iconrow the parent frame
	GetIconGrowDirection = function(self)
		local side = self.options.anchor.side
		return detailsFramework.GrowDirectionBySide[side]
	end,

	---@param self df_iconrow the parent frame
	OnOptionChanged = function(self, optionName)
		if (self.SetBackdropColor) then
			self:SetBackdropColor(unpack(self.options.backdrop_color))
			self:SetBackdropBorderColor(unpack(self.options.backdrop_border_color))
		end
	end,
}

local default_icon_row_options = {
	icon_width = 20,
	icon_height = 20,
	texcoord = {.1, .9, .1, .9},
	show_text = true,
	text_color = {1, 1, 1, 1},
	text_size = 12,
	text_font = "Arial Narrow",
	text_outline = "NONE",
	text_anchor = "center",
	text_rel_anchor = "center",
	text_x_offset = 0,
	text_y_offset = 0,
	desc_text = true,
	desc_text_color = {1, 1, 1, 1},
	desc_text_size = 7,
	desc_text_font = "Arial Narrow",
	desc_text_outline = "NONE",
	desc_text_anchor = "bottom",
	desc_text_rel_anchor = "top",
	desc_text_x_offset = 0,
	desc_text_y_offset = 2,
	stack_text = true,
	stack_text_color = {1, 1, 1, 1},
	stack_text_size = 10,
	stack_text_font = "Arial Narrow",
	stack_text_outline = "NONE",
	stack_text_anchor = "center",
	stack_text_rel_anchor = "bottomright",
	stack_text_x_offset = 0,
	stack_text_y_offset = 0,
	left_padding = 1, --distance between right and left
	top_padding = 1, --distance between top and bottom
	icon_padding = 1, --distance between each icon
	backdrop = {},
	backdrop_color = {0, 0, 0, 0.5},
	backdrop_border_color = {0, 0, 0, 1},
	anchor = {side = 6, x = 2, y = 0},
	grow_direction = 1, --1 = to right 2 = to left
	center_alignment = false, --if true if will align the icons with grow_direction and then set the iconRow width to match the length used by all icons
	--surpress_blizzard_cd_timer = false,
	surpress_tulla_omni_cc = false,
	on_tick_cooldown_update = true,
	decimal_timer = false,
	cooldown_reverse = false,
	cooldown_swipe_enabled = true,
	cooldown_edge_texture = "Interface\\Cooldown\\edge",
}

---@param parent frame
---@param name string?
---@param options table?
---@return df_iconrow
function detailsFramework:CreateIconRow(parent, name, options)
	local newIconRowFrame = CreateFrame("frame", name, parent, "BackdropTemplate")
	newIconRowFrame.IconPool = {}
	newIconRowFrame.NextIcon = 1
	newIconRowFrame.AuraCache = {}
	newIconRowFrame.shownAmount = 0

	detailsFramework:Mixin(newIconRowFrame, detailsFramework.IconMixin)
	detailsFramework:Mixin(newIconRowFrame, detailsFramework.OptionsFunctions)

	newIconRowFrame:BuildOptionsTable(default_icon_row_options, options)

	newIconRowFrame:SetSize(newIconRowFrame.options.icon_width, newIconRowFrame.options.icon_height + (newIconRowFrame.options.top_padding * 2))

	newIconRowFrame:SetBackdrop(newIconRowFrame.options.backdrop)
	newIconRowFrame:SetBackdropColor(unpack(newIconRowFrame.options.backdrop_color))
	newIconRowFrame:SetBackdropBorderColor(unpack(newIconRowFrame.options.backdrop_border_color))

	return newIconRowFrame
end
