local E, L, V, P, G = unpack(ElvUI)
local UF = E:GetModule('UnitFrames')

local max = max
local wipe = wipe
local next = next
local pairs = pairs
local ipairs = ipairs
local unpack = unpack
local CreateFrame = CreateFrame
local MAX_COMBO_POINTS = MAX_COMBO_POINTS
-- GLOBALS: ElvUF_Player

local _, ns = ...
local ElvUF = ns.oUF
assert(ElvUF, 'ElvUI was unable to locate oUF.')

local AltManaTypes = { Rage = 1 }
if E.Retail then
	AltManaTypes.LunarPower = 8
	AltManaTypes.Maelstrom = 11
	AltManaTypes.Insanity = 13
end

function UF:GetClassPower_Construct(frame)
	frame.ClassPower = UF:Construct_ClassBar(frame)
	frame.ClassBar = 'ClassPower'

	if E.myclass == 'DRUID' then
		frame.AdditionalPower = UF:Construct_AdditionalPowerBar(frame)
	elseif E.Retail then
		if E.myclass == 'PRIEST' or E.myclass == 'SHAMAN' then
			frame.AdditionalPower = UF:Construct_AdditionalPowerBar(frame)
		elseif E.myclass == 'DEATHKNIGHT' then
			frame.Runes = UF:Construct_DeathKnightResourceBar(frame)
			frame.ClassBar = 'Runes'
		elseif E.myclass == 'MONK' then
			frame.Stagger = UF:Construct_Stagger(frame)
		end
	elseif E.myclass == 'SHAMAN' then
		frame.Totems = UF:Construct_Totems(frame)
	end
end

function UF:PostVisibility_ClassBars(frame)
	if not (frame and frame.db) then return end

	UF:Configure_ClassBar(frame)
	UF:Configure_Power(frame)
	UF:Configure_InfoPanel(frame)
end

function UF:ClassPower_UpdateColor(powerType)
	local color, r, g, b = UF.db.colors.classResources[E.myclass] or UF.db.colors.power[powerType]
	if color then
		r, g, b = color.r, color.g, color.b
	else
		color = ElvUF.colors.power[powerType]
		r, g, b = unpack(color)
	end

	local custom_backdrop = UF.db.colors.customclasspowerbackdrop and UF.db.colors.classpower_backdrop

	for i, bar in ipairs(self) do
		local classCombo = (powerType == 'COMBO_POINTS' and UF.db.colors.classResources.comboPoints[i] or powerType == 'CHI' and UF.db.colors.classResources.MONK[i])
		if classCombo then r, g, b = classCombo.r, classCombo.g, classCombo.b end

		bar:SetStatusBarColor(r, g, b)

		if bar.bg then
			if custom_backdrop then
				bar.bg:SetVertexColor(custom_backdrop.r, custom_backdrop.g, custom_backdrop.b)
			else
				bar.bg:SetVertexColor(r * .35, g * .35, b * .35)
			end
		end
	end
end

function UF:Configure_ClassBar(frame)
	local db = frame.db
	if not db then return end

	local bars = frame[frame.ClassBar]
	if not bars then return end

	bars.Holder = frame.ClassBarHolder
	bars.origParent = frame

	local MAX_CLASS_BAR = frame.MAX_CLASS_BAR

	--Fix height in case it is lower than the theme allows, or in case it's higher than 30px when not detached
	if not UF.thinBorders and (frame.CLASSBAR_HEIGHT > 0 and frame.CLASSBAR_HEIGHT < 7) then --A height of 7 means 6px for borders and just 1px for the actual power statusbar
		frame.CLASSBAR_HEIGHT = 7
		if db.classbar then db.classbar.height = 7 end
	elseif UF.thinBorders and (frame.CLASSBAR_HEIGHT > 0 and frame.CLASSBAR_HEIGHT < 3) then --A height of 3 means 2px for borders and just 1px for the actual power statusbar
		frame.CLASSBAR_HEIGHT = 3
		if db.classbar then db.classbar.height = 3 end
	elseif not frame.CLASSBAR_DETACHED and frame.CLASSBAR_HEIGHT > 30 then
		frame.CLASSBAR_HEIGHT = 10
		if db.classbar then db.classbar.height = 10 end
	end

	--We don't want to modify the original frame.CLASSBAR_WIDTH value, as it bugs out when the classbar gains more buttons
	local CLASSBAR_WIDTH = E:Scale(frame.CLASSBAR_WIDTH)
	local SPACING = E:Scale((UF.BORDER + UF.SPACING)*2)

	local color = E.db.unitframe.colors.borderColor
	if not bars.backdrop.forcedBorderColors then
		bars.backdrop:SetBackdropBorderColor(color.r, color.g, color.b)
	end

	if frame.USE_MINI_CLASSBAR and not frame.CLASSBAR_DETACHED then
		if MAX_CLASS_BAR == 1 or frame.ClassBar == 'AdditionalPower' or frame.ClassBar == 'Stagger' or frame.ClassBar == 'AlternativePower' then
			CLASSBAR_WIDTH = CLASSBAR_WIDTH * 2/3
		else
			CLASSBAR_WIDTH = CLASSBAR_WIDTH * (MAX_CLASS_BAR - 1) / MAX_CLASS_BAR
		end
	elseif frame.CLASSBAR_DETACHED then --Detached
		CLASSBAR_WIDTH = db.classbar.detachedWidth
	end

	bars:Width(CLASSBAR_WIDTH - SPACING)
	bars:Height(frame.CLASSBAR_HEIGHT - SPACING)

	if frame.ClassBar == 'ClassPower' or frame.ClassBar == 'Runes' or frame.ClassBar == 'Totems' then
		if E.myclass == 'DEATHKNIGHT' and frame.ClassBar == 'Runes' then
			bars.sortOrder = (db.classbar.sortDirection ~= 'NONE') and db.classbar.sortDirection
		end

		local maxClassBarButtons = max(UF.classMaxResourceBar[E.myclass] or 0, frame.ClassBar == 'Totems' and 4 or MAX_COMBO_POINTS)
		for i = 1, maxClassBarButtons do
			local button = bars[i]
			button.backdrop:Hide()

			if i <= MAX_CLASS_BAR then
				if not button.backdrop.forcedBorderColors then
					button.backdrop:SetBackdropBorderColor(color.r, color.g, color.b)
				end

				button:Height(bars:GetHeight())

				if MAX_CLASS_BAR == 1 then
					button:Width(CLASSBAR_WIDTH)
				elseif frame.USE_MINI_CLASSBAR then
					if frame.CLASSBAR_DETACHED and db.classbar.orientation == 'VERTICAL' then
						button:Width(CLASSBAR_WIDTH)
					else
						button:Width((CLASSBAR_WIDTH - ((5 + (UF.BORDER*2 + UF.SPACING*2))*(MAX_CLASS_BAR - 1)))/MAX_CLASS_BAR) --Width accounts for 5px spacing between each button, excluding borders
					end
				elseif i ~= MAX_CLASS_BAR then
					button:Width((CLASSBAR_WIDTH - ((MAX_CLASS_BAR-1)*(UF.BORDER*2-UF.SPACING))) / MAX_CLASS_BAR) --classbar width minus total width of dividers between each button, divided by number of buttons
				end

				button:GetStatusBarTexture():SetHorizTile(false)
				button:ClearAllPoints()

				if i == 1 then
					button:Point('LEFT', bars)
				else
					local prevButton = bars[i-1]
					if frame.USE_MINI_CLASSBAR then
						if frame.CLASSBAR_DETACHED and db.classbar.orientation == 'VERTICAL' then
							button:Point('BOTTOM', prevButton, 'TOP', 0, (db.classbar.spacing + UF.BORDER*2 + UF.SPACING*2))
						else
							button:Point('LEFT', prevButton, 'RIGHT', (db.classbar.spacing + UF.BORDER*2 + UF.SPACING*2), 0) --5px spacing between borders of each button(replaced with Detached Spacing option)
						end
					elseif i == MAX_CLASS_BAR then
						button:Point('LEFT', prevButton, 'RIGHT', UF.BORDER-UF.SPACING, 0)
						button:Point('RIGHT', bars)
					else
						button:Point('LEFT', prevButton, 'RIGHT', UF.BORDER-UF.SPACING, 0)
					end
				end

				if not frame.USE_MINI_CLASSBAR then
					button.backdrop:Hide()
				else
					button.backdrop:Show()
				end

				if frame.CLASSBAR_DETACHED and db.classbar.verticalOrientation then
					button:SetOrientation('VERTICAL')
				else
					button:SetOrientation('HORIZONTAL')
				end

				if frame.ClassBar == 'ClassPower' or frame.ClassBar == 'Totems' then
					button.bg:SetParent(frame.USE_MINI_CLASSBAR and bars[i].backdrop or bars)
				end
			end
		end

		if (not frame.USE_MINI_CLASSBAR) and frame.USE_CLASSBAR then
			bars.backdrop:Show()
		else
			bars.backdrop:Hide()
		end
	elseif frame.ClassBar == 'AdditionalPower' or frame.ClassBar == 'Stagger' or frame.ClassBar == 'AlternativePower' then
		if frame.CLASSBAR_DETACHED and db.classbar.verticalOrientation then
			bars:SetOrientation('VERTICAL')
		else
			bars:SetOrientation('HORIZONTAL')
		end
	end

	if frame.USE_MINI_CLASSBAR and not frame.CLASSBAR_DETACHED then
		bars:ClearAllPoints()
		bars:Point('CENTER', frame.Health.backdrop, 'TOP', 0, 0)

		bars:SetFrameLevel(50) --RaisedElementParent uses 100, we want it lower than this

		if bars.Holder and bars.Holder.mover then
			E:DisableMover(bars.Holder.mover:GetName())
		end
	elseif frame.CLASSBAR_DETACHED then
		bars.Holder:Size(db.classbar.detachedWidth, db.classbar.height)

		bars:ClearAllPoints()
		bars:Point('BOTTOMLEFT', bars.Holder, 'BOTTOMLEFT', UF.BORDER + UF.SPACING, UF.BORDER + UF.SPACING)

		if not bars.Holder.mover then
			E:CreateMover(bars.Holder, 'ClassBarMover', L["Classbar"], nil, nil, nil, 'ALL,SOLO', nil, 'unitframe,individualUnits,player,classbar')
		else
			E:EnableMover(bars.Holder.mover:GetName())
		end

		if not db.classbar.strataAndLevel.useCustomStrata then
			bars:SetFrameStrata('LOW')
		else
			bars:SetFrameStrata(db.classbar.strataAndLevel.frameStrata)
		end

		if not db.classbar.strataAndLevel.useCustomLevel then
			bars:SetFrameLevel(frame.Health:GetFrameLevel() + 10) --Health uses 10, Power uses (Health + 5) when attached
		else
			bars:SetFrameLevel(db.classbar.strataAndLevel.frameLevel)
		end
	else
		bars:ClearAllPoints()
		if frame.ORIENTATION == 'RIGHT' then
			bars:Point('BOTTOMRIGHT', frame.Health.backdrop, 'TOPRIGHT', -UF.BORDER, UF.SPACING*3)
		else
			bars:Point('BOTTOMLEFT', frame.Health.backdrop, 'TOPLEFT', UF.BORDER, UF.SPACING*3)
		end

		bars:SetFrameStrata('LOW')
		bars:SetFrameLevel(frame.Health:GetFrameLevel() + 10) --Health uses 10, Power uses (Health + 5) when attached

		if bars.Holder and bars.Holder.mover then
			E:DisableMover(bars.Holder.mover:GetName())
		end
	end

	if frame.CLASSBAR_DETACHED and db.classbar.parent == 'UIPARENT' then
		E.FrameLocks[bars] = true
		bars:SetParent(E.UIParent)
	else
		E.FrameLocks[bars] = nil
		bars:SetParent(frame)
	end

	if frame.USE_CLASSBAR then
		if frame.ClassPower and not frame:IsElementEnabled('ClassPower') then
			frame:EnableElement('ClassPower')
		end
		if frame.AdditionalPower then
			local altMana, displayMana = E.db.unitframe.altManaPowers[E.myclass], frame.AdditionalPower.displayPairs[E.myclass]
			wipe(displayMana)

			if altMana then
				for name, value in pairs(altMana) do
					local powerType = AltManaTypes[name]
					if powerType and value then
						displayMana[powerType] = value
					end
				end
			end

			local display = next(displayMana)
			local enabled = frame:IsElementEnabled('AdditionalPower')
			if display and not enabled then
				frame:EnableElement('AdditionalPower')
			elseif enabled and not display then
				frame:DisableElement('AdditionalPower')
			end
		end
		if frame.Runes and not frame:IsElementEnabled('Runes') then
			frame:EnableElement('Runes')
		end
		if frame.Stagger and not frame:IsElementEnabled('Stagger') then
			frame:EnableElement('Stagger')
		end
		if frame.Totems and not frame:IsElementEnabled('Totems') then
			frame:EnableElement('Totems')
		end
		if frame.AlternativePower and not frame:IsElementEnabled('AlternativePower') then
			frame:EnableElement('AlternativePower')
		end
	else
		if frame.ClassPower and frame:IsElementEnabled('ClassPower') then
			frame:DisableElement('ClassPower')
		end
		if frame.AdditionalPower and frame:IsElementEnabled('AdditionalPower') then
			frame:DisableElement('AdditionalPower')
		end
		if frame.Runes and frame:IsElementEnabled('Runes') then
			frame:DisableElement('Runes')
		end
		if frame.Stagger and frame:IsElementEnabled('Stagger') then
			frame:DisableElement('Stagger')
		end
		if frame.Totems and frame:IsElementEnabled('Totems') then
			frame:DisableElement('Totems')
		end
		if frame.AlternativePower and frame:IsElementEnabled('AlternativePower') then
			frame:DisableElement('AlternativePower')
		end
	end

	-- keep after classbar height update
	UF.ToggleResourceBar(bars)
end

local function ToggleResourceBar(bars)
	local frame = bars.origParent or bars:GetParent()

	local db = frame.db
	if not db then return end

	frame.CLASSBAR_SHOWN = frame[frame.ClassBar]:IsShown()

	if bars.text then bars.text:SetAlpha(frame.CLASSBAR_SHOWN and 1 or 0) end

	frame.CLASSBAR_HEIGHT = frame.USE_CLASSBAR and ((db.classbar and db.classbar.height) or (frame.AlternativePower and db.power.height)) or 0
	frame.CLASSBAR_YOFFSET = (not frame.USE_CLASSBAR or not frame.CLASSBAR_SHOWN or frame.CLASSBAR_DETACHED) and 0 or (frame.USE_MINI_CLASSBAR and ((UF.SPACING+(frame.CLASSBAR_HEIGHT/2))) or (frame.CLASSBAR_HEIGHT - (UF.BORDER-UF.SPACING)))

	UF:Configure_CustomTexts(frame)
	UF:Configure_HealthBar(frame)
	UF:Configure_Portrait(frame)

	-- keep this after the configure_healtbar, we need the one updated before we match the healpred size to -1
	if frame.HealthPrediction then
		UF:SetSize_HealComm(frame)
	end
end
UF.ToggleResourceBar = ToggleResourceBar --Make available to combobar

-------------------------------------------------------------
-- MONK, PALADIN, WARLOCK, MAGE, and COMBOS
-------------------------------------------------------------
function UF:Construct_ClassBar(frame)
	local bars = CreateFrame('Frame', '$parent_ClassBar', frame)
	bars:CreateBackdrop(nil, nil, nil, nil, true)
	bars:Hide()

	local maxBars = max(UF.classMaxResourceBar[E.myclass] or 0, MAX_COMBO_POINTS)
	for i = 1, maxBars do
		local bar = CreateFrame('StatusBar', frame:GetName()..'ClassIconButton'..i, bars)
		bar:SetStatusBarTexture(E.media.blankTex) --Dummy really, this needs to be set so we can change the color
		bar:GetStatusBarTexture():SetHorizTile(false)
		UF.statusbars[bar] = true

		bar:CreateBackdrop(nil, nil, nil, nil, true)
		bar.backdrop:SetParent(bars)

		bar.bg = bars:CreateTexture(nil, 'BORDER')
		bar.bg:SetTexture(E.media.blankTex)
		bar.bg:SetInside(bar.backdrop)

		bars[i] = bar
	end

	bars.PostVisibility = UF.PostVisibilityClassBar
	bars.PostUpdate = UF.UpdateClassBar
	bars.UpdateColor = UF.ClassPower_UpdateColor
	bars.UpdateTexture = E.noop --We don't use textures but statusbars, so prevent errors

	bars:SetScript('OnShow', ToggleResourceBar)
	bars:SetScript('OnHide', ToggleResourceBar)

	return bars
end

function UF:PostVisibilityClassBar()
	UF:PostVisibility_ClassBars(self.origParent or self:GetParent())
end

function UF:UpdateClassBar(current, maxBars, hasMaxChanged, powerType, chargedPoints)
	local frame = self.origParent or self:GetParent()
	local db = frame.db
	if not db then return end

	local isShown = self:IsShown()
	local stateChanged

	if not frame.USE_CLASSBAR or (current == 0 and db.classbar.autoHide) or maxBars == 0 or not maxBars then
		self:Hide()
		if isShown then
			stateChanged = true
		end
	else
		self:Show()
		if not isShown then
			stateChanged = true
		end
	end

	if hasMaxChanged then
		frame.MAX_CLASS_BAR = maxBars
		UF:Configure_ClassBar(frame, current)
	elseif stateChanged then
		UF:Configure_ClassBar(frame, current)
	end

	for i, bar in ipairs(self) do
		if maxBars and (i <= maxBars) then
			bar.bg:Show()
		else
			bar.bg:Hide()
		end
	end

	if powerType == 'COMBO_POINTS' and E.myclass == 'ROGUE' then
		UF.ClassPower_UpdateColor(self, powerType)

		if chargedPoints then
			local r, g, b = unpack(ElvUF.colors.chargedComboPoint)
			for _, cIndex in next, chargedPoints do
				local cPoint = self[cIndex]
				if cPoint then
					cPoint:SetStatusBarColor(r, g, b)
					cPoint.bg:SetVertexColor(r * .35, g * .35, b * .35)
				end
			end
		end
	end
end

-------------------------------------------------------------
-- DEATHKNIGHT
-------------------------------------------------------------
local function PostUpdateRunes(self, _, hasVehicle, allReady)
	local frame = self.origParent or self:GetParent()
	local db = frame.db

	if hasVehicle or (allReady and db.classbar.autoHide) then
		self:Hide()
	else
		self:Show()
		UF.ClassPower_UpdateColor(self, 'RUNES')
	end
end

function UF:Construct_DeathKnightResourceBar(frame)
	local runes = CreateFrame('Frame', '$parent_Runes', frame)
	runes:CreateBackdrop(nil, nil, nil, nil, true)
	runes.backdrop:Hide()

	for i = 1, UF.classMaxResourceBar[E.myclass] do
		local rune = CreateFrame('StatusBar', frame:GetName()..'RuneButton'..i, runes)
		rune:SetStatusBarTexture(E.media.blankTex)
		rune:GetStatusBarTexture():SetHorizTile(false)
		UF.statusbars[rune] = true

		rune:CreateBackdrop(nil, nil, nil, nil, true)
		rune.backdrop:SetParent(runes)

		rune.bg = rune:CreateTexture(nil, 'BORDER')
		rune.bg:SetTexture(E.media.blankTex)
		rune.bg:SetInside(rune.backdrop)
		rune.bg.multiplier = 0.35

		runes[i] = rune
	end

	runes.PostUpdate = PostUpdateRunes
	runes.UpdateColor = E.noop --We handle colors on our own in Configure_ClassBar
	runes:SetScript('OnShow', ToggleResourceBar)
	runes:SetScript('OnHide', ToggleResourceBar)

	return runes
end

-------------------------------------------------------------
-- ALTERNATIVE MANA BAR
-------------------------------------------------------------
function UF:Construct_AdditionalPowerBar(frame)
	local additionalPower = CreateFrame('StatusBar', '$parent_AdditionalPowerBar', frame)
	additionalPower.colorPower = true
	additionalPower.frequentUpdates = true
	additionalPower.PostUpdate = UF.PostUpdateAdditionalPower
	additionalPower.PostUpdateColor = UF.PostColorAdditionalPower
	additionalPower.PostVisibility = UF.PostVisibilityAdditionalPower
	additionalPower:CreateBackdrop(nil, nil, nil, nil, true)
	additionalPower:SetStatusBarTexture(E.media.blankTex)
	UF.statusbars[additionalPower] = true

	additionalPower.RaisedElementParent = UF:CreateRaisedElement(additionalPower, true)
	additionalPower.text = UF:CreateRaisedText(additionalPower.RaisedElementParent)
	additionalPower.displayPairs = {[E.myclass] = {}} -- display power types

	additionalPower.bg = additionalPower:CreateTexture(nil, 'BORDER')
	additionalPower.bg:SetTexture(E.media.blankTex)
	additionalPower.bg:SetInside(nil, 0, 0)
	additionalPower.bg.multiplier = 0.35

	additionalPower:SetScript('OnShow', ToggleResourceBar)
	additionalPower:SetScript('OnHide', ToggleResourceBar)

	UF:Construct_ClipFrame(frame, additionalPower)

	return additionalPower
end

function UF:PostColorAdditionalPower()
	local frame = self.origParent or self:GetParent()
	if frame.USE_CLASSBAR then
		local custom_backdrop = UF.db.colors.customclasspowerbackdrop and UF.db.colors.classpower_backdrop
		if custom_backdrop then
			self.bg:SetVertexColor(custom_backdrop.r, custom_backdrop.g, custom_backdrop.b)
		end
	end
end

function UF:PostUpdateAdditionalPower(CUR, MAX, event)
	local frame = self.origParent or self:GetParent()
	local db = frame.db

	if frame.USE_CLASSBAR and event ~= 'ElementDisable' and (CUR ~= MAX or not db.classbar.autoHide) then
		self:Show()
	else
		self:Hide()
	end
end

function UF:PostVisibilityAdditionalPower(enabled)
	local frame = self.origParent or self:GetParent()

	frame.ClassBar = (enabled and 'AdditionalPower') or 'ClassPower'

	UF:PostVisibility_ClassBars(frame)
end

-----------------------------------------------------------
-- Stagger Bar
-----------------------------------------------------------
function UF:Construct_Stagger(frame)
	local stagger = CreateFrame('Statusbar', '$parent_Stagger', frame)
	stagger:CreateBackdrop(nil,nil, nil, nil, true)
	stagger.PostUpdate = UF.PostUpdateStagger
	stagger.PostVisibility = UF.PostUpdateVisibilityStagger
	UF.statusbars[stagger] = true

	stagger:SetScript('OnShow', ToggleResourceBar)
	stagger:SetScript('OnHide', ToggleResourceBar)

	return stagger
end

function UF:PostUpdateStagger(stagger)
	local frame = self.origParent or self:GetParent()
	local db = frame.db

	if not frame.USE_CLASSBAR or (stagger == 0 and db.classbar.autoHide) then
		self:Hide()
	else
		self:Show()
	end
end

function UF:PostUpdateVisibilityStagger(_, _, isShown, stateChanged)
	self.ClassBar = (isShown and 'Stagger') or 'ClassPower'

	if stateChanged then
		UF:PostVisibility_ClassBars(self)
	end
end

-----------------------------------------------------------
-- Totems
-----------------------------------------------------------
local TotemColors = {
	[1] = {.58,.23,.10},
	[2] = {.23,.45,.13},
	[3] = {.19,.48,.60},
	[4] = {.42,.18,.74},
}

function UF:Construct_Totems(frame)
	local totems = CreateFrame('Frame', nil, frame)
	totems:CreateBackdrop(nil, nil, nil, UF.thinBorders, true)
	totems.Destroy = {}

	for i = 1, 4 do
		local r, g, b = unpack(TotemColors[i])
		local totem = CreateFrame('StatusBar', frame:GetName()..'Totem'..i, totems)
		totem:CreateBackdrop(nil, nil, nil, UF.thinBorders, true)
		totem.backdrop:SetParent(totems)

		totem:SetStatusBarTexture(E.media.blankTex)
		totem:SetStatusBarColor(r, g, b)

		UF.statusbars[totem] = true

		totem:SetMinMaxValues(0, 1)
		totem:SetValue(0)

		totem.bg = totem:CreateTexture(nil, 'BORDER')
		totem.bg:SetTexture(E.media.blankTex)
		totem.bg:SetInside(totem, 0, 0)
		totem.bg.multiplier = 0.3

		totem.bg:SetVertexColor(r * .3, g * .3, b * .3)

		totems[i] = totem
	end

	frame.MAX_CLASS_BAR = 4
	frame.ClassBar = 'Totems'

	return totems
end
