local L = LibStub("AceLocale-3.0"):GetLocale("IceHUD", false)
IceUnitBar = IceCore_CreateClass(IceBarElement)

IceUnitBar.prototype.unit = nil
IceUnitBar.prototype.alive = nil
IceUnitBar.prototype.tapped = nil

IceUnitBar.prototype.health = nil
IceUnitBar.prototype.maxHealth = nil
IceUnitBar.prototype.healthPercentage = nil
IceUnitBar.prototype.mana = nil
IceUnitBar.prototype.maxMana = nil
IceUnitBar.prototype.manaPercentage = nil
IceUnitBar.prototype.scaleHPColorInst = {r = 0, g = 0, b = 0, a = 0}
IceUnitBar.prototype.scaleMPColorInst = {r = 0, g = 0, b = 0, a = 0}

IceUnitBar.prototype.unitClass = nil
IceUnitBar.prototype.hasPet = nil

IceUnitBar.prototype.noFlash = nil

local SPELL_POWER_INSANITY, SPELL_POWER_RAGE, SPELL_POWER_RUNIC_POWER = SPELL_POWER_INSANITY, SPELL_POWER_RAGE, SPELL_POWER_RUNIC_POWER
if Enum and Enum.PowerType then
	SPELL_POWER_INSANITY = Enum.PowerType.Insanity
	SPELL_POWER_RAGE = Enum.PowerType.Rage
	SPELL_POWER_RUNIC_POWER = Enum.PowerType.RunicPower
end

-- Constructor --
function IceUnitBar.prototype:init(name, unit)
	IceUnitBar.super.prototype.init(self, name)
	assert(unit, "IceUnitBar 'unit' is nil")

	self:SetUnit(unit)
	self.noFlash = false

	self:SetDefaultColor("Dead", 0.5, 0.5, 0.5)
	self:SetDefaultColor("Tapped", 0.8, 0.8, 0.8)

	self:SetDefaultColor("MaxHealthColor", 0, 255, 0)
	self:SetDefaultColor("MidHealthColor", 255, 255, 0)
	self:SetDefaultColor("MinHealthColor", 255, 0, 0)

	self:SetDefaultColor("MaxManaColor", 0, 0, 255)
	self:SetDefaultColor("MidManaColor", 125, 0, 255)
	self:SetDefaultColor("MinManaColor", 255, 0, 255)
end

function IceUnitBar.prototype:SetColorCurve()
	if not C_ColorUtil or not C_CurveUtil or not C_CurveUtil.CreateCurve then
		return
	end

	local minHP = self.settings.colors["MinHealthColor"]
	local midHP = self.settings.colors["MidHealthColor"]
	local maxHP = self.settings.colors["MaxHealthColor"]
	-- have to split these up into components to avoid perf impact and massive garbage generation
	-- when trying to pass in a color
	if not self.hpColorCurveR then
		self.hpColorCurveR = C_CurveUtil.CreateCurve()
		self.hpColorCurveR:SetType(Enum.LuaCurveType.Linear)
		self.hpColorCurveG = C_CurveUtil.CreateCurve()
		self.hpColorCurveG:SetType(Enum.LuaCurveType.Linear)
		self.hpColorCurveB = C_CurveUtil.CreateCurve()
		self.hpColorCurveB:SetType(Enum.LuaCurveType.Linear)
	end
	self.hpColorCurveR:ClearPoints()
	self.hpColorCurveR:AddPoint(0, minHP.r)
	self.hpColorCurveR:AddPoint(0.5, midHP.r)
	self.hpColorCurveR:AddPoint(1, maxHP.r)

	self.hpColorCurveG:ClearPoints()
	self.hpColorCurveG:AddPoint(0, minHP.g)
	self.hpColorCurveG:AddPoint(0.5, midHP.g)
	self.hpColorCurveG:AddPoint(1, maxHP.g)

	self.hpColorCurveB:ClearPoints()
	self.hpColorCurveB:AddPoint(0, minHP.b)
	self.hpColorCurveB:AddPoint(0.5, midHP.b)
	self.hpColorCurveB:AddPoint(1, maxHP.b)

	local minMP = self.settings.colors["MinManaColor"]
	local midMP = self.settings.colors["MidManaColor"]
	local maxMP = self.settings.colors["MaxManaColor"]
	if not self.mpColorCurveR then
		self.mpColorCurveR = C_CurveUtil.CreateCurve()
		self.mpColorCurveR:SetType(Enum.LuaCurveType.Linear)
		self.mpColorCurveG = C_CurveUtil.CreateCurve()
		self.mpColorCurveG:SetType(Enum.LuaCurveType.Linear)
		self.mpColorCurveB = C_CurveUtil.CreateCurve()
		self.mpColorCurveB:SetType(Enum.LuaCurveType.Linear)
	end
	self.mpColorCurveR:ClearPoints()
	self.mpColorCurveR:AddPoint(0, minMP.r)
	self.mpColorCurveR:AddPoint(0.5, midMP.r)
	self.mpColorCurveR:AddPoint(1, maxMP.r)

	self.mpColorCurveG:ClearPoints()
	self.mpColorCurveG:AddPoint(0, minMP.g)
	self.mpColorCurveG:AddPoint(0.5, midMP.g)
	self.mpColorCurveG:AddPoint(1, maxMP.g)

	self.mpColorCurveB:ClearPoints()
	self.mpColorCurveB:AddPoint(0, minMP.b)
	self.mpColorCurveB:AddPoint(0.5, midMP.b)
	self.mpColorCurveB:AddPoint(1, maxMP.b)
end

function IceUnitBar.prototype:SetUnit(unit)
	self.unit = unit
	local _
	_, self.unitClass = UnitClass(self.unit)
end

-- OVERRIDE
function IceUnitBar.prototype:GetDefaultSettings()
	local settings = IceUnitBar.super.prototype.GetDefaultSettings(self)

	settings["lowThreshold"] = 0
	settings["lowThresholdFlash" ] = true
	settings["lowThresholdColor"] = false
	settings["scaleHealthColor"] = true
	settings["scaleManaColor"] = true

	return settings
end


-- OVERRIDE
function IceUnitBar.prototype:GetOptions()
	local opts = IceUnitBar.super.prototype.GetOptions(self)

	if not IceHUD.IsSecretEnv() then
		opts["lowThreshold"] =
		{
			type = 'range',
			name = L["Low Threshold"],
			desc = L["When the bar drops below this amount, it will start flashing (0 means never). For the 'mana' bar this only applies to mana and not rage/energy/focus/runic power."],
			get = function()
				return self.moduleSettings.lowThreshold
			end,
			set = function(info, value)
				self.moduleSettings.lowThreshold = value
				self:Redraw()
			end,
			disabled = function()
				return not self.moduleSettings.enabled or not (self.moduleSettings.lowThresholdFlash or self.moduleSettings.lowThresholdColor)
			end,
			min = 0,
			max = 1,
			step = 0.01,
			isPercent = true,
			order = 30.091
		}

		opts["lowThresholdFlash"] = {
			type = 'toggle',
			name = L["Flash bar below Low Threshold"],
			desc = L["Flashes the bar when it is below the Low Threshold specified above"],
			width = 'double',
			get = function()
				return self.moduleSettings.lowThresholdFlash
			end,
			set = function(info, v)
				self.moduleSettings.lowThresholdFlash = v
				self:Redraw()
			end,
			disabled = function()
				return not self.moduleSettings.enabled
			end,
			hidden = function()
				return self.noFlash
			end,
			order = 30.092
		}
		opts["lowThresholdColor"] = {
			type = "toggle",
			name = L["Low Threshold color"],
			desc = L["Changes the color of this bar to be the minimum health or mana color when it's below the low threshold. See the 'MinHealthColor' and 'MinManaColor' colors in the 'Colors' option page.\n\nThis option only applies to health and mana bars."],
			get = function()
				return self.moduleSettings.lowThresholdColor
			end,
			set = function(info, value)
				self.moduleSettings.lowThresholdColor = value
				self:Redraw()
			end,
			disabled = function()
				return not self.moduleSettings.enabled
			end,
			order = 30.093
		}
	end

	return opts
end


-- 'Public' methods -----------------------------------------------------------

function IceUnitBar.prototype:Enable()
	self:SetColorCurve()

	IceUnitBar.super.prototype.Enable(self)

	self:RegisterEvent("PLAYER_UNGHOST", "Alive")
	self:RegisterEvent("PLAYER_ALIVE", "Alive")
	self:RegisterEvent("PLAYER_DEAD", "Dead")

	self.alive = not UnitIsDeadOrGhost(self.unit)
	self.combat = UnitAffectingCombat(self.unit)
end


-- OVERRIDE
function IceUnitBar.prototype:Redraw()
	IceUnitBar.super.prototype.Redraw(self)

	self:SetColorCurve()

	if (self.moduleSettings.enabled) then
		self:Update()
	end
end





-- 'Protected' methods --------------------------------------------------------

-- OVERRIDE
function IceUnitBar.prototype:CreateFrame()
	IceUnitBar.super.prototype.CreateFrame(self)

	self:CreateFlashFrame()
end

-- Creates the low amount warning frame
function IceUnitBar.prototype:CreateFlashFrame()
	if not (self.flashFrame) then
		self.flashFrame = CreateFrame("Frame", "IceHUD_"..self.elementName.."_Flash", self.frame)
	end

	self.flashFrame:SetFrameStrata(IceHUD.IceCore:DetermineStrata("BACKGROUND"))
	self.flashFrame:SetWidth(self.settings.barWidth + (self.moduleSettings.widthModifier or 0))
	self.flashFrame:SetHeight(self.settings.barHeight)


	if not (self.flashFrame.flash) then
		self.flashFrame.flash = self.flashFrame:CreateTexture(nil, "BACKGROUND")
	end

	self.flashFrame.flash:SetTexture(self:GetBarTexturePath())
	self.flashFrame.flash:SetBlendMode("ADD")
	self.flashFrame.flash:SetAllPoints(self.flashFrame)

	--self:SetScale(self.flashFrame.flash, 1)
	self.flashFrame:SetAlpha(0)

	self.flashFrame:ClearAllPoints()
	self.flashFrame:SetPoint("BOTTOM", self.frame, "BOTTOM", 0, 0)

	self.flashFrame.flash:SetTexCoord(0, 1, 0, 1)
end

function IceUnitBar.prototype:RotateHorizontal()
	IceUnitBar.super.prototype.RotateHorizontal(self)

	if IceHUD.WowVer < 70000 then
		self:RotateFrame(self.flashFrame)
	end
end

function IceUnitBar.prototype:ResetRotation()
	IceUnitBar.super.prototype.ResetRotation(self)

	if IceHUD.WowVer < 70000 and self.flashFrame and self.flashFrame.anim then
		self.flashFrame.anim:Stop()
	end
end


-- OVERRIDE
function IceUnitBar.prototype:Update()
	IceUnitBar.super.prototype.Update(self)

	if UnitIsTapped then
		self.tapped = UnitIsTapped(self.unit) and (not UnitIsTappedByPlayer(self.unit))
	else
		self.tapped = UnitIsTapDenied(self.unit)
	end

	self.health = UnitHealth(self.unit)
	self.maxHealth = UnitHealthMax(self.unit)
	self.healthPercentage = UnitHealthPercent and UnitHealthPercent(self.unit, true, self:ShouldReverseFill() and CurveConstants.Reverse or CurveConstants.ZeroToOne) or self.maxHealth ~= 0 and (self.health/self.maxHealth) or 0

	-- note that UnitPowerType returns 2 arguments and UnitPower[Max] accepts a third argument to get the values on a different scale
	-- so this technically doesn't get us the answer we want most of the time. too risky to change at this point, though.
	self.mana = UnitPower(self.unit, UnitPowerType(self.unit))
	self.maxMana = UnitPowerMax(self.unit, UnitPowerType(self.unit))
	local powerType = UnitPowerType(self.unit)
	if IceHUD.CanAccessValue(self.mana) then
		if (powerType == SPELL_POWER_RAGE and self.maxMana >= 1000)
			or (powerType == SPELL_POWER_RUNIC_POWER and self.maxMana >= 1000) then
			self.mana = IceHUD:MathRound(self.mana / 10)
			self.maxMana = IceHUD:MathRound(self.maxMana / 10)
		end
		if IceHUD.WowVer >= 70300 and UnitPowerType(self.unit) == SPELL_POWER_INSANITY then
			self.mana = IceHUD:MathRound(self.mana / 100)
			self.maxMana = IceHUD:MathRound(self.maxMana / 100)
		end
	end

	-- account for cases where maxMana is 0, perhaps briefly (during certain spells, for example)
	-- and properly handle it as full. this allows for proper alpha handling during these times.
	if UnitPowerPercent then
		self.manaPercentage = UnitPowerPercent(self.unit, UnitPowerType(self.unit), true, self:ShouldReverseFill() and CurveConstants.Reverse or CurveConstants.ZeroToOne)
	else
		if self.maxMana == self.mana then
			self.manaPercentage = 1
		else
			self.manaPercentage = self.maxMana ~= 0 and (self.mana/self.maxMana) or 0
		end
	end

	local _
	_, self.unitClass = UnitClass(self.unit)

	if self.hpColorCurveR then
		local r = UnitHealthPercent(self.unit, true, self.hpColorCurveR)
		local g = UnitHealthPercent(self.unit, true, self.hpColorCurveG)
		local b = UnitHealthPercent(self.unit, true, self.hpColorCurveB)
		self.scaleHPColorInst.r = r
		self.scaleHPColorInst.g = g
		self.scaleHPColorInst.b = b
	else
		if self.healthPercentage > 0.5 then
			self:SetScaledColor(self.scaleHPColorInst, self.healthPercentage * 2 - 1, self.settings.colors["MaxHealthColor"], self.settings.colors["MidHealthColor"])
		else
			self:SetScaledColor(self.scaleHPColorInst, self.healthPercentage * 2, self.settings.colors["MidHealthColor"], self.settings.colors["MinHealthColor"])
		end
	end

	if self.mpColorCurveR then
		local r = UnitPowerPercent(self.unit, UnitPowerType(self.unit), true, self.mpColorCurveR)
		local g = UnitPowerPercent(self.unit, UnitPowerType(self.unit), true, self.mpColorCurveG)
		local b = UnitPowerPercent(self.unit, UnitPowerType(self.unit), true, self.mpColorCurveB)
		self.scaleMPColorInst.r = r
		self.scaleMPColorInst.g = g
		self.scaleMPColorInst.b = b
	else
		if self.manaPercentage > 0.5 then
			self:SetScaledColor(self.scaleMPColorInst, self.manaPercentage * 2 - 1, self.settings.colors["MaxManaColor"], self.settings.colors["MidManaColor"])
		else
			self:SetScaledColor(self.scaleMPColorInst, self.manaPercentage * 2, self.settings.colors["MidManaColor"], self.settings.colors["MinManaColor"])
		end
	end

	-- This looks slightly quirky. Basically the easiest way for me to achieve this is to have lowThresholdColor override
	-- the scaled color. You'll need to switch them both on to get things to work.
	if self.moduleSettings.lowThresholdColor and IceHUD.CanAccessValue(self.healthPercentage) and IceHUD.CanAccessValue(self.manaPercentage) then
		if( self.healthPercentage <= self.moduleSettings.lowThreshold ) then
			self.scaleHPColorInst.r, self.scaleHPColorInst.g, self.scaleHPColorInst.b = self.settings.colors[ "MinHealthColor" ].r, self.settings.colors[ "MinHealthColor" ].g, self.settings.colors[ "MinHealthColor" ].b
		elseif not self.moduleSettings.scaleHealthColor then
			self.scaleHPColorInst.r, self.scaleHPColorInst.g, self.scaleHPColorInst.b = self.settings.colors[ "MaxHealthColor" ].r, self.settings.colors[ "MaxHealthColor" ].g, self.settings.colors[ "MaxHealthColor" ].b
		end
		if( self.manaPercentage <= self.moduleSettings.lowThreshold ) then
			self.scaleMPColorInst.r, self.scaleMPColorInst.g, self.scaleMPColorInst.b = self.settings.colors[ "MinManaColor" ].r, self.settings.colors[ "MinManaColor" ].g, self.settings.colors[ "MinManaColor" ].b
		elseif not self.moduleSettings.scaleManaColor then
			self.scaleMPColorInst.r, self.scaleMPColorInst.g, self.scaleMPColorInst.b = self.settings.colors[ "MaxManaColor" ].r, self.settings.colors[ "MaxManaColor" ].g, self.settings.colors[ "MaxManaColor" ].b
		end
	end
end


function IceUnitBar.prototype:Alive()
	-- instead of maintaining a state for 3 different things
	-- (dead, dead/ghost, alive) just afford the extra function call here
	self.alive = not UnitIsDeadOrGhost(self.unit)
	self:Update()
end


function IceUnitBar.prototype:Dead()
	self.alive = false
	self:Update()
end


-- OVERRIDE
function IceUnitBar.prototype:UpdateBar(scale, color, alpha)
	IceUnitBar.super.prototype.UpdateBar(self, scale, color, alpha)

	if (not self.flashFrame) then
		-- skip if flashFrame hasn't been created yet
		return
	end

	if IceHUD.CanAccessValue(scale) and
		self.moduleSettings.lowThreshold > 0 and
		self.moduleSettings.lowThresholdFlash and
		self.moduleSettings.lowThreshold >= scale and self.alive and
		not self.noFlash then
			self.bUpdateFlash = true
			self.flashFrame.flash:SetVertexColor(self:GetColor(color))
	else
		self.bUpdateFlash = nil
		self.flashFrame:SetAlpha(0)
	end
end


function IceUnitBar.prototype:MyOnUpdate()
	IceUnitBar.super.prototype.MyOnUpdate(self)

	self:ConditionalUpdateFlash()
end


function IceUnitBar.prototype:ConditionalUpdateFlash()
	if self.bUpdateFlash then
		local time = GetTime()
		local decimals = time - math.floor(time)

		if (decimals > 0.5) then
			decimals = 1 - decimals
		end

		decimals = decimals*1.1 -- add more dynamic to the color change

		self.flashFrame:SetAlpha(decimals)
	end
end


function IceUnitBar.prototype:SetScaleColorEnabled(enabled)
	self.moduleSettings.scaleColor = enabled
end


function IceUnitBar.prototype:GetColor(color, alpha)
	if color == "ScaledHealthColor" then
		return self.scaleHPColorInst.r, self.scaleHPColorInst.g, self.scaleHPColorInst.b, alpha or self.alpha
	elseif color == "ScaledManaColor" then
		return self.scaleMPColorInst.r, self.scaleMPColorInst.g, self.scaleMPColorInst.b, alpha or self.alpha
	end

	return IceUnitBar.super.prototype.GetColor(self, color, alpha)
end
