--[[ MODIFIED FOR SVUI BY SVUILUNCH ]]--

--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          = math;
local floor         = math.floor
local ceil         	= math.ceil
local hugeMath 		= math.huge;
local min 			= math.min;
--TABLE
local table 		= _G.table;
local tsort 		= table.sort;
local tinsert 		= _G.tinsert;
--BLIZZARD API
local GetTime       = _G.GetTime;
local CreateFrame   = _G.CreateFrame;
local UnitAura      = _G.UnitAura;
local UnitIsFriend  = _G.UnitIsFriend;
local GameTooltip  	= _G.GameTooltip;
local GetSpellInfo  = _G.GetSpellInfo;
local DebuffTypeColor  = _G.DebuffTypeColor;
local NumberFontNormal  = _G.NumberFontNormal;

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

local DAY, HOUR, MINUTE = 86400, 3600, 60;
local BUFF_FILTER = 'HELPFUL';
local DEBUFF_FILTER = 'HARMFUL';
local VISIBLE = 1;
local HIDDEN = 0;

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

local SORTING_METHODS = {
	["TIME_REMAINING"] = function(a, b)
		local compA = a.noTime and hugeMath or a.expirationTime
		local compB = b.noTime and hugeMath or b.expirationTime 
		return compA > compB 
	end,
	["TIME_REMAINING_REVERSE"] = function(a, b)
		local compA = a.noTime and hugeMath or a.expirationTime
		local compB = b.noTime and hugeMath or b.expirationTime 
		return compA < compB 
	end,
	["TIME_DURATION"] = function(a, b)
		local compA = a.noTime and hugeMath or a.duration
		local compB = b.noTime and hugeMath or b.duration 
		return compA > compB 
	end,
	["TIME_DURATION_REVERSE"] = function(a, b)
		local compA = a.noTime and hugeMath or a.duration
		local compB = b.noTime and hugeMath or b.duration 
		return compA < compB 
	end,
	["NAME"] = function(a, b)
		return a.name > b.name
	end,
}

local SetSorting = function(self, sorting)
	if(sorting) then
		if((type(sorting) == "string") and SORTING_METHODS[sorting]) then 
			self.sort = SORTING_METHODS[sorting];
		else
			self.sort = SORTING_METHODS["TIME_REMAINING"];
		end
	else 
		self.sort = nil;
	end 
end

local genericFilter = function(self, frame, _, name, _, _, _, _, _, _, caster, _, shouldConsolidate)
	local isPlayer

	if(caster == 'player' or caster == 'vehicle') then
		isPlayer = true
	end

	if((self.onlyShowPlayer and isPlayer) or (not self.onlyShowPlayer and name)) then
		if(frame) then
			frame.isPlayer = isPlayer
			frame.owner = caster
		end
		if(not shouldConsolidate) then
			return true
		end
	end
end

local Aura_OnEnter = function(self)
	if(not self:IsVisible()) then return end
	GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT")
	GameTooltip:SetUnitAura(self.unit, self.index, self.filter)
end

local Aura_OnLeave = function()
	GameTooltip:Hide()
end

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

local AuraIcon_OnUpdate = function(self, elapsed)
	self.expiration = self.expiration - elapsed;
	
	if(self.nextUpdate > 0) then 
		self.nextUpdate = self.nextUpdate - elapsed;
		return;
	end

	if(self.expiration <= 0) then 
		self:SetScript("OnUpdate", nil)
		self.text:SetText('')
		return;
	end

	local expires = self.expiration;
	local calc, timeLeft = 0, 0;
	if expires < 4 then
        self.nextUpdate = 0.051
        self.text:SetFormattedText("|cffff0000%.1f|r", expires)
    elseif expires < 60 then 
        self.nextUpdate = 0.51
        self.text:SetFormattedText("|cffffff00%d|r", floor(expires)) 
    elseif expires < 3600 then
        timeLeft = ceil(expires / 60);
        calc = floor((expires / 60) + .5);
        self.nextUpdate = calc > 1 and ((expires - calc) * 29.5) or (expires - 59.5);
        self.text:SetFormattedText("|cffffffff%dm|r", timeLeft)
    elseif expires < 86400 then
        timeLeft = ceil(expires / 3600);
        calc = floor((expires / 3600) + .5);
        self.nextUpdate = calc > 1 and ((expires - calc) * 1799.5) or (expires - 3570);
        self.text:SetFormattedText("|cff66ffff%dh|r", timeLeft)
    else
        timeLeft = ceil(expires / 86400);
        calc = floor((expires / 86400) + .5);
        self.nextUpdate = calc > 1 and ((expires - calc) * 43199.5) or (expires - 85680);
        if(timeLeft > 7) then
            self.text:SetFormattedText("|cff6666ff%s|r", "long")
        else
            self.text:SetFormattedText("|cff6666ff%dd|r", timeLeft)
        end
    end
end

local SetBarLayout = function(self, visible, cache)
	local auras = self.Bars;

	local newHeight = 1;
	if(visible > 0) then
		newHeight = 1 + ((self.auraSize + self.spacing) * visible);
	end
	self:SetHeight(newHeight)

	local width = self:GetWidth();
	local height = self.barHeight or 16;
	local growDown = self.down or false;
	local spacing = self.spacing or 0;

	for i = visible + 1, #auras do
		auras[i]:Hide()
	end

	local lastBar;
	if(cache) then
		for i = 1, #cache do
			local info = cache[i]
			local bar = auras[info.ref]
			if(bar and bar:IsShown()) then
				bar:SetHeight(height)
				bar:SetWidth(width)
				bar.iconHolder:SetWidth(height)
				bar:ClearAllPoints()
				if(growDown) then
					if(not lastBar) then
						bar:SetPoint('TOPLEFT', self, 'TOPLEFT', 0, 0)
					else
						bar:SetPoint('TOPLEFT', lastBar, 'BOTTOMLEFT', 0, -spacing)
					end
				else
					if(not lastBar) then
						bar:SetPoint('BOTTOMLEFT', self, 'BOTTOMLEFT', 0, 0)
					else
						bar:SetPoint('BOTTOMLEFT', lastBar, 'TOPLEFT', 0, spacing)
					end
				end
				lastBar = bar
			end
		end
	else
		for index = 1, #auras do
			local bar = auras[index]
			if(bar and bar:IsShown()) then
				bar:SetHeight(height)
				bar:SetWidth(width)
				bar.iconHolder:SetWidth(height)
				bar:ClearAllPoints()
				if(growDown) then
					if(not lastBar) then
						bar:SetPoint('TOPLEFT', self, 'TOPLEFT', 0, 0)
					else
						bar:SetPoint('TOPLEFT', lastBar, 'BOTTOMLEFT', 0, -spacing)
					end
				else
					if(not lastBar) then
						bar:SetPoint('BOTTOMLEFT', self, 'BOTTOMLEFT', 0, 0)
					else
						bar:SetPoint('BOTTOMLEFT', lastBar, 'TOPLEFT', 0, spacing)
					end
				end
				lastBar = bar
			end
		end
	end
end

local SetIconLayout = function(self, visible, cache)
	local auras = self.Icons

	local newHeight = 1;
	if(visible > 0) then
		local visibleRows = ceil(visible / self.maxColumns);
		newHeight = 1 + ((self.auraSize + self.spacing) * visibleRows);
	end
	self:SetHeight(newHeight)

	local col = 0
	local row = 0
	local gap = self.gap
	local size = self.auraSize + self.spacing
	local anchor = self.initialAnchor or "BOTTOMLEFT"
	local growthx = (self["growth-x"] == "LEFT" and -1) or 1
	local growthy = (self["growth-y"] == "DOWN" and -1) or 1
	local cols = floor(self:GetWidth() / size + .5)
	local rows = floor(self:GetHeight() / size + .5)

	for i = visible + 1, #auras do
		auras[i]:Hide()
	end

	if(cache) then
		for i = 1, #cache do
			local info = cache[i]
			local button = auras[info.ref]
			if(button and button:IsShown()) then
				if(gap and button.debuff) then
					if(col > 0) then
						col = col + 1
					end
					gap = false
				end

				if(col >= cols) then
					col = 0
					row = row + 1
				end
				button:ClearAllPoints()
				button:SetPoint(anchor, self, anchor, col * size * growthx, row * size * growthy)
				button:SetWidth(self.auraSize)
				button:SetHeight(self.auraSize)
				col = col + 1
			elseif(not button) then
				break
			end
		end
	else
		for i = 1, #auras do
			local button = auras[i]
			if(button and button:IsShown()) then
				if(gap and button.debuff) then
					if(col > 0) then
						col = col + 1
					end
					gap = false
				end

				if(col >= cols) then
					col = 0
					row = row + 1
				end
				button:ClearAllPoints()
				button:SetPoint(anchor, self, anchor, col * size * growthx, row * size * growthy)
				button:SetWidth(self.auraSize)
				button:SetHeight(self.auraSize)
				col = col + 1
			elseif(not button) then
				break
			end
		end
	end
end

--[[ ICON SPECIFIC ]]--

local CreateAuraIcon = function(self, index)
	local button = CreateFrame("Button", nil, self)
	button:EnableMouse(true)
	button:RegisterForClicks'RightButtonUp'

	button:SetWidth(self.auraSize or 16)
	button:SetHeight(self.auraSize or 16)

	local cd = CreateFrame("Cooldown", nil, button, "CooldownFrameTemplate")
	cd:SetAllPoints(button)

	local icon = button:CreateTexture(nil, "BORDER")
	icon:SetAllPoints(button)

	local count = button:CreateFontString(nil, "OVERLAY")
	count:SetFontObject(NumberFontNormal)
	count:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -1, 0)

	local overlay = button:CreateTexture(nil, "OVERLAY")
	overlay:SetTexture"Interface\\Buttons\\UI-Debuff-Overlays"
	overlay:SetAllPoints(button)
	overlay:SetTexCoord(.296875, .5703125, 0, .515625)
	button.overlay = overlay

	local stealable = button:CreateTexture(nil, 'OVERLAY')
	stealable:SetTexture[[Interface\TargetingFrame\UI-TargetingFrame-Stealable]]
	stealable:SetPoint('TOPLEFT', -3, 3)
	stealable:SetPoint('BOTTOMRIGHT', 3, -3)
	stealable:SetBlendMode'ADD'
	button.stealable = stealable

	button:SetScript("OnEnter", Aura_OnEnter)
	button:SetScript("OnLeave", Aura_OnLeave)

	button.icon = icon
	button.count = count
	button.cooldown = cd

	if(self.PostCreateIcon) then self:PostCreateIcon(button) end

	return button
end

--[[ BAR SPECIFIC ]]--

local CreateAuraBar = function(self, index)
	local frame = CreateFrame("Button", nil, self)

	frame:SetScript('OnEnter', Aura_OnEnter)
	frame:SetScript('OnLeave', Aura_OnLeave)

	local iconHolder = CreateFrame('Frame', nil, frame)
	iconHolder:SetPoint('TOPLEFT', frame, 'TOPLEFT', 0, 0)
	iconHolder:SetPoint('BOTTOMLEFT', frame, 'BOTTOMLEFT', 0, 0)
	iconHolder:SetWidth(frame:GetHeight())
	iconHolder:SetBackdrop({
        bgFile = [[Interface\BUTTONS\WHITE8X8]], 
        edgeFile = [[Interface\BUTTONS\WHITE8X8]], 
        tile = false, 
        tileSize = 0, 
        edgeSize = 1, 
        insets = 
        {
            left = 0, 
            right = 0, 
            top = 0, 
            bottom = 0, 
        }, 
    })
    iconHolder:SetBackdropColor(0,0,0,0.5)
    iconHolder:SetBackdropBorderColor(0,0,0)
	frame.iconHolder = iconHolder

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

	frame.count = frame.iconHolder:CreateFontString(nil, "OVERLAY")
	frame.count:SetFontObject(NumberFontNormal)
	frame.count:SetPoint("BOTTOMRIGHT", frame.iconHolder, "BOTTOMRIGHT", -1, 0)

	local barHolder = CreateFrame('Frame', nil, frame)
	barHolder:SetPoint('BOTTOMLEFT', frame.iconHolder, 'BOTTOMRIGHT', self.gap, 0)
	barHolder:SetPoint('TOPRIGHT', frame, 'TOPRIGHT', 0, 0)
	barHolder:SetBackdrop({
        bgFile = [[Interface\BUTTONS\WHITE8X8]], 
        edgeFile = [[Interface\BUTTONS\WHITE8X8]], 
        tile = false, 
        tileSize = 0, 
        edgeSize = 1, 
        insets = 
        {
            left = 0, 
            right = 0, 
            top = 0, 
            bottom = 0, 
        }, 
    })
    barHolder:SetBackdropColor(0,0,0,0.5)
    barHolder:SetBackdropBorderColor(0,0,0)
	frame.barHolder = barHolder
	
	-- the main bar
	frame.statusBar = CreateFrame("StatusBar", nil, frame.barHolder)
	frame.statusBar:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
	frame.statusBar:SetAlpha(self.fgalpha or 1)
	frame.statusBar:SetPoint("TOPLEFT", frame.barHolder, "TOPLEFT", 1, -1)
	frame.statusBar:SetPoint("BOTTOMRIGHT", frame.barHolder, "BOTTOMRIGHT", -1, 1)

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

	frame.statusBar.spelltime = frame.statusBar:CreateFontString(nil, 'ARTWORK')
	frame.statusBar.spellname = frame.statusBar:CreateFontString(nil, 'ARTWORK')

	--print("New Bar #" .. index)

	if self.PostCreateBar then 
		self.PostCreateBar(frame)
	else
		frame.statusBar.spelltime:SetFont([[Fonts\FRIZQT__.TTF]], 10, "NONE")
		frame.statusBar.spelltime:SetTextColor(1 ,1, 1)
		frame.statusBar.spelltime:SetShadowOffset(1, -1)
	  	frame.statusBar.spelltime:SetShadowColor(0, 0, 0)
		frame.statusBar.spelltime:SetJustifyH'RIGHT'
		frame.statusBar.spelltime:SetJustifyV'CENTER'
		frame.statusBar.spelltime:SetPoint'RIGHT'
		frame.statusBar.spellname:SetFont([[Fonts\FRIZQT__.TTF]], 10, "NONE")
		frame.statusBar.spellname:SetTextColor(1, 1, 1)
		frame.statusBar.spellname:SetShadowOffset(1, -1)
	  	frame.statusBar.spellname:SetShadowColor(0, 0, 0)
		frame.statusBar.spellname:SetJustifyH'LEFT'
		frame.statusBar.spellname:SetJustifyV'CENTER'
		frame.statusBar.spellname:SetPoint'LEFT'
		frame.statusBar.spellname:SetPoint('RIGHT', frame.statusBar.spelltime, 'LEFT')
	end
	return frame
end

local UpdateIconAuras = function(self, cache, unit, index, filter, visible, isFriend)
	if not unit then return; end

	local isDebuff = filter == DEBUFF_FILTER
	local timeNow = GetTime()
	local auras = self.Icons;

	local name, rank, texture, count, debuffType, duration, timeLeft, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff;

	if(self.forceShow) then
		spellID = 47540
		name, rank, texture = GetSpellInfo(spellID)
		count, debuffType, duration, timeLeft, caster, isStealable, shouldConsolidate, canApplyAura, isBossDebuff = 5, 'Magic', 0, 60, 'player', nil, nil, nil, nil
	else
		name, rank, texture, count, debuffType, duration, timeLeft, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff = UnitAura(unit, index, filter);
	end

	if(name) then
		local show = true
		if(not self.forceShow) then
			show = (self.CustomFilter or genericFilter) (self, false, unit, name, rank, texture, count, debuffType, duration, timeLeft, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff)
		end

		if(show) then
			local i = visible + 1
			local this = auras[i]
			if(not this) then
				this = (self.CreateAuraIcon or CreateAuraIcon) (self, i)
				auras[i] = this
			end

			duration = duration or 0;
			timeLeft = timeLeft or 0;
			count = count or 0;
			local noTime = (duration == 0 and timeLeft == 0)
			--FOR TOOLTIPS
			this.unit = unit
			this.index = index
			this.filter = filter
			--FOR ONCLICK EVENTS
			this.name = name
			this.spellID = spellID
			--FOR ONUPDATE EVENTS
			this.expirationTime = timeLeft
			this.noTime = noTime

			this.icon:SetTexture(texture)
			this.count:SetText((count > 1 and count))
			
			this:Show()

			--SORTING CACHE
			local cached = {
				ref = i,
				noTime = noTime,
				duration = duration,
				expirationTime = timeLeft
			}
			tinsert(cache, cached)

			local cd = this.cooldown
			if(cd and not self.disableCooldown) then
				if(noTime) then
					cd:Hide()
				else
					cd:SetCooldown(timeLeft - duration, duration)
					cd:Show()
				end
			end

			if(isDebuff) then
				local color = DebuffTypeColor[debuffType] or DebuffTypeColor.none
				if((not isFriend) and caster and (caster ~= "player") and (caster ~= "vehicle")) then
					this:SetBackdropBorderColor(0.9, 0.1, 0.1, 1)
					this.bg:SetBackdropColor(1, 0, 0, 1)
					this.icon:SetDesaturated((unit and not unit:find('arena%d')) and true or false)
				else
					this:SetBackdropBorderColor(color.r * 0.6, color.g * 0.6, color.b * 0.6, 1)
					this.bg:SetBackdropColor(color.r, color.g, color.b, 1)
					this.icon:SetDesaturated(false)
				end

				this.bg:SetBackdropBorderColor(0, 0, 0, 1)

				if(self.showType and this.overlay) then
					this.overlay:SetVertexColor(color.r, color.g, color.b)
					this.overlay:Show()
				else
					this.overlay:Hide()
				end
			else
				if((isStealable) and (not isFriend)) then
					this:SetBackdropBorderColor(0.92, 0.91, 0.55, 1)
					this.bg:SetBackdropColor(1, 1, 0.5, 1)
					this.bg:SetBackdropBorderColor(0, 0, 0, 1)
				else
					this:SetBackdropBorderColor(0, 0, 0, 1)
					this.bg:SetBackdropColor(0, 0, 0, 0)
					this.bg:SetBackdropBorderColor(0, 0, 0, 0)		
				end	
			end
			
			if(noTime) then
				this:SetScript('OnUpdate', nil)
				this.text:SetText('')
			elseif(not this:GetScript('OnUpdate')) then
				this.expirationTime = timeLeft
				this.expiration = timeLeft - timeNow
				this.nextUpdate = -1
				this:SetScript('OnUpdate', AuraIcon_OnUpdate)
			elseif(this.expirationTime ~= timeLeft) then
				this.expirationTime = timeLeft
				this.expiration = timeLeft - timeNow
				this.nextUpdate = -1
			end
			
			return VISIBLE
		else
			return HIDDEN
		end
	end
end

local UpdateBarAuras = function(self, cache, unit, index, filter, visible, isFriend)
	if not unit then return; end
	local isDebuff = filter == DEBUFF_FILTER
	local timeNow = GetTime()
	local auras = self.Bars;

	local name, rank, texture, count, debuffType, duration, timeLeft, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff = UnitAura(unit, index, filter);

	if(self.forceShow) then
		spellID = 47540
		name, rank, texture = GetSpellInfo(spellID)
		count, debuffType, duration, timeLeft, caster, isStealable, shouldConsolidate, canApplyAura, isBossDebuff = 5, 'Magic', 0, 60, 'player', nil, nil, nil, nil
	end

	if(name) then
		local show = true
		if(not self.forceShow) then
			show = (self.CustomFilter or genericFilter) (self, false, unit, name, rank, texture, count, debuffType, duration, timeLeft, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff)
		end

		if(show) then
			local i = visible + 1
			local this = auras[i]
			if(not this) then
				this = (self.CreateAuraBar or CreateAuraBar) (self, i)
				auras[i] = this
			end

			duration = duration or 0;
			timeLeft = timeLeft or 0;
			count = count or 0;
			local noTime = (duration == 0 and timeLeft == 0)
			--FOR TOOLTIPS
			this.unit = unit
			this.index = index
			this.filter = filter
			--FOR ONCLICK EVENTS
			this.name = name
			this.spellID = spellID
			--FOR ONUPDATE EVENTS
			this.expirationTime = timeLeft
			this.noTime = noTime

			this.icon:SetTexture(texture)
			this.count:SetText((count > 1 and count))
			
			this:Show()

			--SORTING CACHE
			local cached = {
				ref = i,
				noTime = noTime,
				duration = duration,
				expirationTime = timeLeft
			}
			tinsert(cache, cached)

			local bar = this.statusBar
			if(noTime) then
				bar:SetMinMaxValues(0, 1)
				bar:SetValue(1)
				bar.spelltime:SetText('')
			else
				local value = timeLeft - timeNow
				bar:SetMinMaxValues(0, duration)
				bar:SetValue(value)
				bar.spelltime:SetText(value)
			end
			bar.spellname:SetText(count > 1 and format("%s [%d]", name, count) or name)

			if self.PostBarUpdate then
				self:PostBarUpdate(bar, spellID, isDebuff, debuffType)
			elseif(isDebuff) then
				bar:SetStatusBarColor(.9, 0, 0)
			else
				bar:SetStatusBarColor(.2, .6, 1)
			end
			
			return VISIBLE
		else
			return HIDDEN
		end
	end
end

local ParseIconAuras = function(self, unit)
	local limit = self.maxCount or 0;
	local filter = self.filtering;
	local index = 1;
	local visible = 0;
	local cache = {};
	local isFriend = (UnitIsFriend('player', unit) == 1) and true or false;

	while(visible < limit) do
		if(self.forceShow and visible > 8) then break end
		local result = UpdateIconAuras(self, cache, unit, index, filter, visible, isFriend)
		if(not result) then
			break
		elseif(result == VISIBLE) then
			visible = visible + 1
		end

		index = index + 1
	end

	if(self.sort and type(self.sort) == 'function' and (#cache > 0)) then
		tsort(cache, self.sort)
		SetIconLayout(self, visible, cache)
	else
		SetIconLayout(self, visible)
	end
end

local ParseBarAuras = function(self, unit)
	local limit = self.maxCount or 0;
	local filter = self.filtering;
	local index = 1;
	local visible = 0;
	local cache = {};

	local isFriend = (UnitIsFriend('player', unit) == 1) and true or false;

	while(visible < limit) do
		if(self.forceShow and visible > 8) then break end
		local result = UpdateBarAuras(self, cache, unit, index, filter, visible, isFriend)
		if(not result) then
			break
		elseif(result == VISIBLE) then
			visible = visible + 1
		end

		index = index + 1
	end

	if(self.sort and type(self.sort) == 'function' and (#cache > 0)) then
		tsort(cache, self.sort)
		SetBarLayout(self, visible, cache)
	else
		SetBarLayout(self, visible)
	end
end

--[[ SETUP AND ENABLE/DISABLE ]]--

local Update = function(self, event, unit)
	if((not unit) or (self.unit ~= unit)) then return end

	local buffs = self.Buffs
	if(buffs) then
		--if(self.unit == 'player') then print(event)print(buffs.UseBars) end
		if(buffs.UseBars and (buffs.UseBars == true)) then
			--if(self.unit == 'player') then print('Parsing BUFF BARS') end
			ParseBarAuras(buffs, unit)
		else
			--if(self.unit == 'player') then print('Parsing BUFF ICONS') end
			ParseIconAuras(buffs, unit)
		end
	end

	local debuffs = self.Debuffs
	if(debuffs) then
		--if(self.unit == 'player') then print(event)print(debuffs.UseBars) end
		if(debuffs.UseBars and (debuffs.UseBars == true)) then
			--if(self.unit == 'player') then print('Parsing DEBUFF BARS') end
			ParseBarAuras(debuffs, unit)
		else
			--if(self.unit == 'player') then print('Parsing DEBUFF ICONS') end
			ParseIconAuras(debuffs, unit)
		end
	end
end

local ForceUpdate = function(element)
	return Update(element.__owner, 'ForceUpdate', element.unit)
end

local Enable = function(self)
	if(self.Buffs or self.Debuffs) then
		self:RegisterEvent('UNIT_AURA', Update)

		local barsAvailable = self.AuraBarsAvailable;

		local buffs = self.Buffs
		if(buffs) then
			buffs.__owner 		= self;
			buffs.gap 			= buffs.gap or 2;
			buffs.spacing 		= buffs.spacing or 2;
			buffs.auraSize 		= buffs.auraSize or 16;
			buffs.maxRows 		= buffs.maxRows or 2;
			buffs.maxColumns 	= buffs.maxColumns or 8;
			buffs.maxCount 		= buffs.maxCount or 16;
			buffs.maxHeight 	= buffs.maxHeight or 20;
			buffs.filtering 	= BUFF_FILTER;
			buffs.ForceUpdate 	= ForceUpdate;
			buffs.SetSorting 	= SetSorting;

			buffs:SetHeight(1)

			buffs.Icons = buffs.Icons or CreateFrame("Frame", nil, buffs)
			buffs.Icons:SetAllPoints(buffs)

			if(barsAvailable) then
				buffs.spark = true;
				buffs.UseBars = false;
				buffs.barHeight = buffs.barHeight or 16
				buffs.Bars = buffs.Bars or CreateFrame("Frame", nil, buffs)
				buffs.Bars:SetAllPoints(buffs)
				buffs.Bars:SetScript('OnUpdate', AuraBars_OnUpdate)
			end
		end

		local debuffs = self.Debuffs
		if(debuffs) then
			debuffs.__owner 	= self;
			debuffs.gap 		= debuffs.gap or 2;
			debuffs.spacing 	= debuffs.spacing or 2;
			debuffs.auraSize 	= debuffs.auraSize or 16;
			debuffs.maxRows 	= debuffs.maxRows or 2;
			debuffs.maxColumns 	= debuffs.maxColumns or 8;
			debuffs.maxCount 	= debuffs.maxCount or 16;
			debuffs.maxHeight 	= debuffs.maxHeight or 20;
			debuffs.filtering 	= DEBUFF_FILTER;
			debuffs.ForceUpdate = ForceUpdate;
			debuffs.SetSorting 	= SetSorting;

			debuffs:SetHeight(1)

			debuffs.Icons = debuffs.Icons or CreateFrame("Frame", nil, debuffs)
			debuffs.Icons:SetAllPoints(debuffs)
			
			if(barsAvailable) then
				debuffs.spark = true;
				debuffs.UseBars = false;
				debuffs.barHeight = debuffs.barHeight or 16
				debuffs.Bars = debuffs.Bars or CreateFrame("Frame", nil, debuffs)
				debuffs.Bars:SetAllPoints(debuffs)
				debuffs.Bars:SetScript('OnUpdate', AuraBars_OnUpdate)
			end
		end

		return true
	end
end

local Disable = function(self)
	if(self.Buffs or self.Debuffs) then
		self:UnregisterEvent('UNIT_AURA', Update)
		if(self.Buffs and self.Buffs.Bars) then
			self.Buffs.Bars:SetScript('OnUpdate', nil)
		end
		if(self.Debuffs and self.Debuffs.Bars) then
			self.Debuffs.Bars:SetScript('OnUpdate', nil)
		end
	end
end

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