Quantcast
--[[ MODIFIED FOR SVUI BY MUNGLUNCH ]]--

--GLOBAL NAMESPACE
local _G = _G;
--LUA
local unpack        = _G.unpack;
local select        = _G.select;
local assert        = _G.assert;
local type         	= _G.type;
--STRING
local string        = _G.string;
local format        = string.format;
--MATH
local math          = _G.math;
local floor         = math.floor;
local huge          = math.huge;
local min 			= math.min;
--TABLE
local table 		= _G.table;
local tsort 		= table.sort;
--BLIZZARD API
local GetTime       = _G.GetTime;
local CreateFrame   = _G.CreateFrame;
local UnitAura      = _G.UnitAura;
local UnitIsFriend  = _G.UnitIsFriend;
local GameTooltip  	= _G.GameTooltip;
local DebuffTypeColor  = _G.DebuffTypeColor;

local _, ns = ...
local oUF = oUF or ns.oUF
assert(oUF, 'oUF_AuraBars was unable to locate oUF install.')

local function Round(number, decimalPlaces)
	if decimalPlaces and decimalPlaces > 0 then
		local mult = 10^decimalPlaces
		return floor((number * mult) + .5) / mult
	end
	return floor(number + .5)
end

local DAY, HOUR, MINUTE = 86400, 3600, 60
local function FormatTime(s)
	if s < MINUTE then
		return ("%.1fs"):format(s)
	elseif s < HOUR then
		return ("%dm %ds"):format(s/60%60, s%60)
	elseif s < DAY then
		return ("%dh %dm"):format(s/(60*60), s/60%60)
	else
		return ("%dd %dh"):format(s/DAY, (s / HOUR) - (floor(s/DAY) * 24))
	end
end

local function UpdateTooltip(self)
	GameTooltip:SetUnitAura(self.__unit, self:GetParent().aura.name, self:GetParent().aura.rank, self:GetParent().aura.filter)
end

local function OnEnter(self)
	if(not self:IsVisible()) then return end
	GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT")
	self:UpdateTooltip()
end

local function OnLeave(self)
	GameTooltip:Hide()
end

local function SetAnchors(self)
	local bars = self.bars

	for index = 1, #bars do
		local frame = bars[index]
		local anchor = frame.anchor
		frame:SetHeight(self.auraBarHeight or 20)
		frame.statusBar.iconHolder:Size(frame:GetHeight())
		frame:SetWidth((self.auraBarWidth or self:GetWidth()) - (frame:GetHeight() + (self.gap or 0)))
		frame:ClearAllPoints()
		if self.down == true then
			if self == anchor then -- Root frame so indent for icon
				frame:SetPoint('TOPLEFT', anchor, 'TOPLEFT', (frame:GetHeight() + (self.gap or 0) ), -1)
			else
				frame:SetPoint('TOPLEFT', anchor, 'BOTTOMLEFT', 0, (-self.spacing or 0))
			end
		else
			if self == anchor then -- Root frame so indent for icon
				frame:SetPoint('BOTTOMLEFT', anchor, 'BOTTOMLEFT', (frame:GetHeight() + (self.gap or 0)), 1)
			else
				frame:SetPoint('BOTTOMLEFT', anchor, 'TOPLEFT', 0, (self.spacing or 0))
			end
		end
	end
end

local function SetBackground(frame)
	local btop = frame:CreateTexture(nil, "OVERLAY")
	btop:SetTexture(0, 0, 0)
	btop:SetPoint("TOPLEFT")
	btop:SetPoint("TOPRIGHT")
	btop:SetHeight(1)
	local bbottom = frame:CreateTexture(nil, "OVERLAY")
	bbottom:SetTexture(0, 0, 0)
	bbottom:SetPoint("BOTTOMLEFT")
	bbottom:SetPoint("BOTTOMRIGHT")
	bbottom:SetHeight(1)
	local bright = frame:CreateTexture(nil, "OVERLAY")
	bright:SetTexture(0, 0, 0)
	bright:SetPoint("TOPRIGHT")
	bright:SetPoint("BOTTOMRIGHT")
	bright:SetWidth(1)
	local bleft = frame:CreateTexture(nil, "OVERLAY")
	bleft:SetTexture(0, 0, 0)
	bleft:SetPoint("TOPLEFT")
	bleft:SetPoint("BOTTOMLEFT")
	bleft:SetWidth(1)
    frame:SetBackdrop({
        bgFile = [[Interface\BUTTONS\WHITE8X8]],
        edgeFile = [[Interface\BUTTONS\WHITE8X8]],
        tile = false,
        tileSize = 0,
        edgeSize = 1,
        insets =
        {
            left = 0,
            right = 0,
            top = 0,
            bottom = 0,
        },
    })
    frame:SetBackdropColor(0,0,0,0.5)
    frame:SetBackdropBorderColor(0,0,0)
end

local function CreateAuraBar(oUF, anchor)
	local auraBarParent = oUF.AuraBars

	local frame = CreateFrame("Frame", nil, auraBarParent)
	frame:SetHeight(auraBarParent.auraBarHeight or 20)
	frame:SetWidth((auraBarParent.auraBarWidth or auraBarParent:GetWidth()) - (frame:GetHeight() + (auraBarParent.gap or 0)))
	frame.anchor = anchor
	SetBackground(frame)

	-- the main bar
	local statusBar = CreateFrame("StatusBar", nil, frame)
	statusBar:SetStatusBarTexture(auraBarParent.barTexture or [[Interface\TargetingFrame\UI-StatusBar]])
	statusBar:SetAlpha(auraBarParent.fgalpha or 1)
	statusBar:SetPoint("TOPLEFT", frame, "TOPLEFT", 1, -1)
	statusBar:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -1, 1)
	frame.statusBar = statusBar

	if auraBarParent.down == true then
		if auraBarParent == anchor then -- Root frame so indent for icon
			frame:SetPoint('TOPLEFT', anchor, 'TOPLEFT', (frame:GetHeight() + (auraBarParent.gap or 0) ), -1)
		else
			frame:SetPoint('TOPLEFT', anchor, 'BOTTOMLEFT', 0, (-auraBarParent.spacing or 0))
		end
	else
		if auraBarParent == anchor then -- Root frame so indent for icon
			frame:SetPoint('BOTTOMLEFT', anchor, 'BOTTOMLEFT', (frame:GetHeight() + (auraBarParent.gap or 0)), 1)
		else
			frame:SetPoint('BOTTOMLEFT', anchor, 'TOPLEFT', 0, (auraBarParent.spacing or 0))
		end
	end

	local spark = statusBar:CreateTexture(nil, "OVERLAY", nil);
	spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]]);
	spark:SetWidth(12);
	spark:SetBlendMode("ADD");
	spark:SetPoint('CENTER', statusBar:GetStatusBarTexture(), 'RIGHT')
	statusBar.spark = spark

	local holder = CreateFrame('Button', nil, statusBar)
	holder:SetHeight(frame:GetHeight())
	holder:SetWidth(frame:GetHeight())
	holder:SetPoint('BOTTOMRIGHT', frame, 'BOTTOMLEFT', -auraBarParent.gap, 0)
	SetBackground(holder)
	holder.__unit = oUF.unit
	holder:SetScript('OnEnter', OnEnter)
	holder:SetScript('OnLeave', OnLeave)
	holder.UpdateTooltip = UpdateTooltip
	statusBar.iconHolder = holder

	statusBar.icon = statusBar.iconHolder:CreateTexture(nil, 'OVERLAY')
	statusBar.icon:SetTexCoord(.1, .9, .1, .9)
	statusBar.icon:SetPoint("TOPLEFT", statusBar.iconHolder, "TOPLEFT", 1, -1)
	statusBar.icon:SetPoint("BOTTOMRIGHT", statusBar.iconHolder, "BOTTOMRIGHT", -1, 1)

	statusBar.spelltime = statusBar:CreateFontString(nil, 'ARTWORK')
	statusBar.spelltime:SetFont(auraBarParent.timeFont or [[Fonts\FRIZQT__.TTF]], auraBarParent.textSize or 10, auraBarParent.textOutline or "NONE")
	statusBar.spelltime:SetTextColor(1 ,1, 1)
	statusBar.spelltime:SetShadowOffset(1, -1)
  	statusBar.spelltime:SetShadowColor(0, 0, 0)
	statusBar.spelltime:SetJustifyH'RIGHT'
	statusBar.spelltime:SetJustifyV'CENTER'
	statusBar.spelltime:SetPoint'RIGHT'

	statusBar.spellname = statusBar:CreateFontString(nil, 'ARTWORK')
	statusBar.spellname:SetFont(auraBarParent.textFont or [[Fonts\FRIZQT__.TTF]], auraBarParent.textSize or 10, auraBarParent.textOutline or "NONE")
	statusBar.spellname:SetTextColor(1, 1, 1)
	statusBar.spellname:SetShadowOffset(1, -1)
  	statusBar.spellname:SetShadowColor(0, 0, 0)
	statusBar.spellname:SetJustifyH'LEFT'
	statusBar.spellname:SetJustifyV'CENTER'
	statusBar.spellname:SetPoint'LEFT'
	statusBar.spellname:SetPoint('RIGHT', statusBar.spelltime, 'LEFT')

	if auraBarParent.PostCreateBar then
		auraBarParent.PostCreateBar(statusBar)
	end

	return frame
end

local function UpdateBars(auraBars)
	local bars = auraBars.bars
	local timenow = GetTime()

	for index = 1, #bars do
		local frame = bars[index]
		local bar = frame.statusBar
		if not frame:IsVisible() then
			break
		end
		if bar.aura.noTime then
			bar.spelltime:SetText()
			bar.spark:Hide()
		else
			local timeleft = bar.aura.expirationTime - timenow
			bar:SetValue(timeleft)
			bar.spelltime:SetText(FormatTime(timeleft))
			if auraBars.spark == true then
				bar.spark:Show()
			end
		end
	end
end

local function DefaultFilter(self, unit, name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate)
	if unitCaster == 'player' and not shouldConsolidate then
		return true
	end
end

local sort = function(a, b)
	local compa, compb = a.noTime and huge or a.expirationTime, b.noTime and huge or b.expirationTime
	return compa > compb
end


local function Update(self, event, unit)
	if self.unit ~= unit then return end
	local auraBars = self.AuraBars
	local helpOrHarm
	local isFriend = UnitIsFriend('player', unit)

	if auraBars.friendlyAuraType and auraBars.enemyAuraType then
		if isFriend then
			helpOrHarm = auraBars.friendlyAuraType
		else
			helpOrHarm = auraBars.enemyAuraType
		end
	else
		helpOrHarm = isFriend and 'HELPFUL' or 'HARMFUL'
	end

	-- Create a table of auras to display
	local auras = {}
	local lastAuraIndex = 0
	for index = 1, 40 do
		local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff = UnitAura(unit, index, helpOrHarm);

		if auraBars.forceShow then
			spellID = 47540
			name, rank, icon = GetSpellInfo(spellID)
			count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, canApplyAura, isBossDebuff = 5, 'Magic', 0, 60, 'player', nil, nil, nil, nil
		end

		if not name then break end

		local show = true
		if not auraBars.forceShow then
			show = (auraBars.filter or DefaultFilter)(self, unit, name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellID)
		else
			show = lastAuraIndex < 5;
		end

		if (show) then
			lastAuraIndex = lastAuraIndex + 1
			auras[lastAuraIndex] = {}
			auras[lastAuraIndex].spellID = spellID
			auras[lastAuraIndex].name = name
			auras[lastAuraIndex].rank = rank
			auras[lastAuraIndex].icon = icon
			auras[lastAuraIndex].count = count
			auras[lastAuraIndex].debuffType = debuffType
			auras[lastAuraIndex].duration = duration
			auras[lastAuraIndex].expirationTime = expirationTime
			auras[lastAuraIndex].unitCaster = unitCaster
			auras[lastAuraIndex].isStealable = isStealable
			auras[lastAuraIndex].noTime = (duration == 0 and expirationTime == 0)
			auras[lastAuraIndex].filter = helpOrHarm
			auras[lastAuraIndex].shouldConsolidate = shouldConsolidate
		end
	end

	if auraBars.sort then
		tsort(auras, type(auraBars.sort) == 'function' and auraBars.sort or sort)
	end

	-- Show and configure bars for buffs/debuffs.
	local bars = auraBars.bars
	if lastAuraIndex == 0 then
		auraBars:SetHeight(1)
	end

	for index = 1 , lastAuraIndex do
		if auraBars:GetWidth() == 0 then break; end
		local aura = auras[index]
		local frame = bars[index]

		if not frame then
			frame = CreateAuraBar(self, index == 1 and auraBars or bars[index - 1])
			bars[index] = frame
		end

		if index == lastAuraIndex then
			if(auraBars.down and (auraBars:GetTop() and frame:GetBottom())) then
				auraBars:SetHeight(auraBars:GetTop() - frame:GetBottom())
			elseif(frame:GetTop() and auraBars:GetBottom()) then
				auraBars:SetHeight(frame:GetTop() - auraBars:GetBottom())
			else
				auraBars:Height(20)
			end
		end

		local bar = frame.statusBar
		frame.index = index

		-- Backup the details of the aura onto the bar, so the OnUpdate function can use it
		bar.aura = aura

		-- Configure
		if bar.aura.noTime then
			bar:SetMinMaxValues(0, 1)
			bar:SetValue(1)
		else
			if auraBars.scaleTime then
				local maxvalue = min(auraBars.scaleTime, bar.aura.duration)
				bar:SetMinMaxValues(0, maxvalue)
				bar:SetWidth(
					( maxvalue / auraBars.scaleTime ) *
					(	( auraBars.auraBarWidth or auraBars:GetWidth() ) -
						( bar:GetHeight() + (auraBars.gap or 0) ) ) ) 				-- icon size + gap
			else
				bar:SetMinMaxValues(0, bar.aura.duration)
			end
			bar:SetValue(bar.aura.expirationTime - GetTime())
		end

		bar.icon:SetTexture(bar.aura.icon)

		bar.spellname:SetText(bar.aura.count > 1 and format("%s [%d]", bar.aura.name, bar.aura.count) or bar.aura.name)
		bar.spelltime:SetText(not bar.noTime and FormatTime(bar.aura.expirationTime-GetTime()))

		-- Colour bars
		local r, g, b = .2, .6, 1 -- Colour for buffs
		if auraBars.buffColor then
			r, g, b = unpack(auraBars.buffColor)
		end

		if helpOrHarm == 'HARMFUL' then
			local debuffType = bar.aura.debuffType and bar.aura.debuffType or 'none'

			r, g, b = DebuffTypeColor[debuffType].r, DebuffTypeColor[debuffType].g, DebuffTypeColor[debuffType].b
			if auraBars.debuffColor then
				r, g, b = unpack(auraBars.debuffColor)
			else
				if debuffType == 'none' and auraBars.defaultDebuffColor then
					r, g, b = unpack(auraBars.defaultDebuffColor)
				end
			end
		end
		bar:SetStatusBarColor(r, g, b)
		frame:Show()
	end

	-- Hide unused bars.
	for index = lastAuraIndex + 1, #bars do
		bars[index]:Hide()
	end

	if auraBars.PostUpdate then
		auraBars:PostUpdate(event, unit)
	end
end

local function Enable(self)
	if self.AuraBars then
		self:RegisterEvent('UNIT_AURA', Update)
		self.AuraBars:SetHeight(1)
		self.AuraBars.bars = self.AuraBars.bars or {}
		self.AuraBars.SetAnchors = SetAnchors
		self.AuraBars:SetScript('OnUpdate', UpdateBars)
		return true
	end
end

local function Disable(self)
	local auraFrame = self.AuraBars
	if auraFrame then
		self:UnregisterEvent('UNIT_AURA', Update)
		auraFrame:SetScript('OnUpdate', nil)
	end
end

oUF:AddElement('AuraBars', Update, Enable, Disable)