local AceOO = AceLibrary("AceOO-2.0")
local L = AceLibrary("AceLocale-2.2"):new("ag_UnitFrames")
local media = AceLibrary("SharedMedia-1.0")
local dewdrop = AceLibrary("Dewdrop-2.0")

local _G = getfenv(0)

local print = function(msg) if msg then DEFAULT_CHAT_FRAME:AddMessage(msg) end end

-- This file contains the class and subclasses that controls the unit frames of ag_UnitFrames.
-- It is divided up in the following sections:
--
-- - System
--		- Creates the object, registers events
--
-- - Frame Setup
--		- Sets up the frame, controls theme, size and scale
--
-- - Updates
--		- Contains the UpdateAll method which updates everything when needed, and serveral other
--			updates like strings, portrait, etc
--
-- - Statusbar
--		- Updates the status bars
--
-- - Icons
--		- Controls various status icons, like leader, loot master, pvp, resting, combat
--
-- - Auras
-- 		- Controls the aura positions and display
--
-- - Combat Events
--		- Displays the combat text when units take damage or receive heals
--
--
-- Furthermore the following subclasses exist:
--
-- - XP Subclasses
-- 		- For the player and pet units, which both have an experience bar
--
-- - OnUpdate Subclass
--		- For main tank frames, target of target and unittarget frames
--
-- - Combopoint Subclass
--		- For the target frame

-- - Vanilla Subclass
--		- For all other types of units

------------
-- SYSTEM --
------------

aUF.classes = {}
aUF.classes.aUFunit = AceOO.Class("AceEvent-2.0","AceHook-2.1")
aUF.classes.aUFunitPlayer = AceOO.Class(aUF.classes.aUFunit)
aUF.classes.aUFunitPet = AceOO.Class(aUF.classes.aUFunit)
aUF.classes.aUFunitVanilla = AceOO.Class(aUF.classes.aUFunit)
aUF.classes.aUFunitCombo = AceOO.Class(aUF.classes.aUFunit)
aUF.classes.aUFunitFocus = AceOO.Class(aUF.classes.aUFunit)
aUF.classes.aUFunitMetro = AceOO.Class(aUF.classes.aUFunit)

function aUF.classes.aUFunit:ToString()
	return "aUFunit"
end

function aUF.classes.aUFunit.prototype:init(frame,unit,unitdb)
	aUF.classes.aUFunit.super.prototype.init(self)

	self.name = frame:GetName()
	self.unitdb = unitdb
	self.frame = frame
	self.frame.object = self

	if self.frame and unit then
		if not self.frame.headerType then
			self:SetAttributes()
		end
		self.frame:SetAttribute("unit", unit)
		self:UpdateAll()
	elseif self.frame.headerType then
		self.grouped = true
		self:TypeReset(self.frame.headerType)
	end
end

function aUF.classes.aUFunit.prototype:AttributeChanged(name,value)
	if name ~= "unit" then return end

	if (value) then
		aUF:RegisterUnit(self, value)
	else
		aUF:UnregisterUnit(self)
	end

	if self.frame then
		local suffix = self.frame:GetAttribute("unitsuffix")
		if self.unit and self.unit ~= value then
			self:UnitReset(aUF:UnitSuffix(value,suffix))
			if self.unit and self.frame:IsShown() then
				self:UpdateAll()
			end
		elseif not self.unit then
			self:UnitReset(aUF:UnitSuffix(value,suffix))
		end
	end
end

function aUF.classes.aUFunit.prototype:Reset(unitType)
	self:TypeReset(self.type)
	self:UnitReset(self.unit)
	self:UpdateAll()
end

function aUF.classes.aUFunit.prototype:TypeReset(unitType)
	if not unitType then
		return
	end
	
	self.type = unitType

	self.flags = {}
	self.strings = {}
	self.disabledFrames = {}
	self.widthFactor = {}
	self.spellCastData = {}
	
	local parent = self.frame:GetParent()
	
	if self.unitdb then
		self.database = self.unitdb
	elseif parent and parent.object and parent.object.unitdb then
		self.header = parent
		self.database = parent.object.unitdb
	else
		self.database = aUF.db.profile.units[self.type]
	end
	
	self:SetAuraCount()
	self:SetupFrame()
	self:ApplyTheme()
	self:BorderBackground()
	self:BarTexture()
	self:LoadScale()
	
	self.eventsRegistered = nil
end

function aUF.classes.aUFunit.prototype:UnitReset(unit)
	if not unit then return end
	
	self.unit = unit

	_, _, self.number = unit:find("(%d+)")
	if not self.number then
		self.number = 1
	end

	local unitType = unit:gsub("%d", "")
	if not self.type or self.type ~= unitType then
		self:TypeReset(unitType)
	end

	if unit:find("pet") then
		if self.type == "partypet" or self.type == "raidpet" then
			self.parent = unit:gsub("pet","")
		else
			self.parent = "player"
		end
	end

	if not InCombatLockdown() then
		self:SetUnitWatch()
	end
end


function aUF.classes.aUFunit.prototype:ObjectRegisterEvent(event,method)
	aUF:ObjectRegisterEvent(self, event, method)
end

function aUF.classes.aUFunit.prototype:ObjectUnregisterEvent(event,method)
	aUF:ObjectUnregisterEvent(self, event, method)
end

function aUF.classes.aUFunit.prototype:RegisterEvents()
	if not (self.eventsRegistered) then
		self:ObjectRegisterEvent("UNIT_HEALTH", "UpdateHealth")
		self:ObjectRegisterEvent("UNIT_MANA",	"UpdatePower")
		self:ObjectRegisterEvent("UNIT_RAGE", "UpdatePower")
		self:ObjectRegisterEvent("UNIT_FOCUS", "UpdatePower")
		self:ObjectRegisterEvent("UNIT_ENERGY", "UpdatePower")
		self:ObjectRegisterEvent("UNIT_DISPLAYPOWER","UpdatePower")

		self:ObjectRegisterEvent("UNIT_AURA", "UpdateAuras")
		self:ObjectRegisterEvent("UNIT_COMBAT","UnitCombat")
		self:ObjectRegisterEvent("UNIT_SPELLMISS","UnitSpellmiss")

		self:ObjectRegisterEvent("UPDATE_FACTION", "UpdatePvP")
		self:ObjectRegisterEvent("PLAYER_FLAGS_CHANGED", "UpdatePvP")
		self:ObjectRegisterEvent("UNIT_FACTION", "UpdatePvP")

		self:ObjectRegisterEvent("PARTY_LEADER_CHANGED", "LabelsCheckLeader")
		self:ObjectRegisterEvent("PARTY_LOOT_METHOD_CHANGED", "LabelsCheckLoot")
		self:ObjectRegisterEvent("RAID_TARGET_UPDATE", "UpdateRaidTargetIcon")

		self:ObjectRegisterEvent("PLAYER_TARGET_CHANGED","UpdateHighlight")
		self:ObjectRegisterEvent("UNIT_PORTRAIT_UPDATE", "UpdatePortrait")
		self:ObjectRegisterEvent("PARTY_MEMBERS_CHANGED", "RosterUpdate")
		self:ObjectRegisterEvent("RAID_ROSTER_UPDATE", "RosterUpdate")

		if not self.inCombatSchedule then
			local s = "aUF-UpdateStatusIcon-" .. tostring(self.name)
			self.inCombatSchedule = s
			self:ScheduleRepeatingEvent(s, self.UpdateStatusIcon, 0.5, self)
		end

		if self.ClassRegisterEvents then
			self:ClassRegisterEvents()
		end
		self:RegisterCasting()
		self.eventsRegistered = true
	end
end

-----------------
-- FRAME SETUP --
-----------------

function aUF.classes.aUFunit.prototype:SetupFrame()
	if self.frameSetup then return end
	self.ClassText = _G[self.name.."_Overlays_ClassText"]
	self.NameLabel = _G[self.name.."_Overlays_NameLabel"]
	self.HitIndicator = _G[self.name.."_Overlays_HitIndicator"]
	self.HealthBar_BG = _G[self.name.."_HealthBar_BG"]
	self.ManaBar_BG = _G[self.name.."_ManaBar_BG"]
	self.XPBar_BG = _G[self.name.."_XPBar_BG"]
	self.HealthBar = _G[self.name.."_HealthBar"]
	self.HealthBar:SetScript("OnValueChanged",function() self:StatusBarsOnValueChanged(arg1) end)
	self.HealthBar:SetMinMaxValues(0,100)
	self.ManaBar = _G[self.name.."_ManaBar"]
	self.ManaBar:SetMinMaxValues(0,100)
	self.XPBar = _G[self.name.."_XPBar"]
	self.XPBar:SetMinMaxValues(0, 100)
	self.HealthText = _G[self.name.."_Overlays_HealthText"]
	self.ManaText = _G[self.name.."_Overlays_ManaText"]
	self.StatusText = _G[self.name.."_Overlays_StatusText"]
	self.ComboText = _G[self.name.."_Overlays_ComboText"]
	self.Highlight = _G[self.name.."_Highlights_Highlight"]
	self.Highlight:SetAlpha(0.5)
	self.DebuffHighlight = _G[self.name.."_Highlights_Debuff"]
	self.DebuffHighlight:SetAlpha(1)
	self.RaidTargetIcon = _G[self.name.."_Overlays_RaidTargetIcon"]
	self.InCombatIcon = _G[self.name.."_Overlays_InCombatIcon"]
	self.PVPIcon = _G[self.name.."_Overlays_PVPIcon"]
	self.LeaderIcon = _G[self.name.."_Overlays_LeaderIcon"]
	self.MasterIcon = _G[self.name.."_Overlays_MasterIcon"]
	self.Highlights = _G[self.name.."_Highlights"]
	self.Overlays = _G[self.name.."_Overlays"]
	self.Highlights:SetFrameLevel(self.Overlays:GetFrameLevel()+5)

	self.CastBar_BG = _G[self.name.."_CastBar_BG"]
	self.CastBar_BG:SetVertexColor(0.25, 0.25, 0.25, 0.5)
	self.CastBar = _G[self.name.."_CastBar"]
	self.CastBar:SetMinMaxValues(0,100)
	self.CastText = _G[self.name.."_Overlays_CastText"]
	self.BarCastText = _G[self.name.."_Overlays_BarCastText"]

	self.frame:SetScript("OnEnter",function() self:OnEnter() end)
	self.frame:SetScript("OnLeave",function() self:OnLeave() end)
	self.frame:SetScript("OnDragStart",function() self:OnDragStart(arg1) end)
	self.frame:SetScript("OnDragStop",function() self:OnDragStop(arg1) end)
	self.frame:SetMovable(true)
	self.frame:RegisterForDrag("LeftButton")
	self.frame:RegisterForClicks("LeftButtonUp","RightButtonUp","MiddleButtonUp","Button4Up","Button5Up")

	self.frame.custom_menu = self.OnShowCustomMenu
	self.frame.menu = self.OnShowMenu

	self.frameSetup = true
end

function aUF.classes.aUFunit.prototype:SetAttributes()
	self.frame:SetAttribute("type1","target")
	self.frame:SetAttribute("*type2","custom_menu")
	self.frame:SetAttribute("type2","menu")

	ClickCastFrames = ClickCastFrames or {}
	ClickCastFrames[self.frame] = true
end

local portraitBackdrop = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 1, edgeFile = "", edgeSize = 0, insets = {left = 0, right = 0, top = 0, bottom = 0},}
function aUF.classes.aUFunit.prototype:CreatePortraitFrame()
	if self.Portrait then return self.Portrait end
	self.Portrait = CreateFrame("Button", self.name.."_Portrait", self.frame)
	self.Portrait:SetBackdrop(portraitBackdrop)
	self.Portrait:EnableMouse(false)

	self.PortraitModel = _G[self.name.."_PortraitModel"]
	self.PortraitModel:SetPoint("CENTER", self.Portrait, "CENTER", 0, 0)
	self.PortraitModel:SetScript("OnShow",function() this:SetCamera(0) end)
	self.Portrait:SetAlpha(self.frame:GetAlpha())
end

function aUF.classes.aUFunit.prototype:LoadScale()
	self:ScheduleLeaveCombatAction(self.frame.SetScale, self.frame, self.database.Scale)
end

function aUF.classes.aUFunit.prototype:LoadPosition()
	if self.grouped == true then return end
	self.frame:ClearAllPoints()
	if (aUF.db.profile.Positions[self.name]) then
		local x, y = strsplit(" ", aUF.db.profile.Positions[self.name])
		local scale = self.frame:GetEffectiveScale()
		self.frame:SetPoint("TOPLEFT", UIParent,"TOPLEFT", x/scale, y/scale)
	else
		self.frame:SetPoint("CENTER", UIParent, "CENTER")
	end
end

function aUF.classes.aUFunit.prototype:SavePosition()
	local scale = self.frame:GetEffectiveScale()
	local worldscale = UIParent:GetEffectiveScale()

	local x,y = self.frame:GetLeft()*scale,self.frame:GetTop()*scale - (UIParent:GetTop())*worldscale

	aUF.db.profile.Positions[self.name] = strjoin(" ", x, y)
end

function aUF.classes.aUFunit.prototype:ApplyTheme(reset)
	local themetable = aUF.Layouts[self.database.FrameStyle]
	if not themetable then themetable = aUF.Layouts.ABF end

	self.disabledFrames = {}
	local height,width = 0,0

	self.flags.ResizableBar = themetable.ResizableBar
	self.flags.BackgroundBarColor = themetable.BackgroundBarColor
	self.flags.AlphaBar = themetable.AlphaBar
	self.flags.RaidColorName = themetable.RaidColorName
	self.flags.ThemeName = themetable.Name
	self.flags.PetClassName = themetable.PetClassName
	self.flags.HappinessBar = themetable.HappinessBar
	self.flags.Portrait = themetable.Portrait
	self.flags.ComboGFX = themetable.ComboGFX

	if self.database.HideMana == true then
		self.disabledFrames.ManaBar_BG = true
	else
		self.disabledFrames.ManaBar_BG = nil
	end

	if (self.unit ~= "player" and self.unit ~= "pet") or ((self.unit == "pet" or self.unit == "player") and self.database.ShowXP  == false) then
		self.disabledFrames.XPBar_BG = true
	else
		self.disabledFrames.XPBar_BG = nil
	end

	if self.database.CastBar == true then
		self.disabledFrames.CastBar_BG = nil
	else
		self.disabledFrames.CastBar_BG = true
	end

	if self.database.Portrait and self.flags.Portrait then
		self.disabledFrames.Portrait = nil
		if not self.Portrait then
			self:CreatePortraitFrame()
		end
	else
		self.disabledFrames.Portrait = true
		self.flags.Portrait = false
	end

	height = themetable.ThemeData.all.FrameHeight
	width = themetable.ThemeData.all.FrameWidth

	if themetable.ThemeData[self.type] then
		if themetable.ThemeData[self.type].FrameHeight then
			height = themetable.ThemeData[self.type].FrameHeight
		end
		if themetable.ThemeData[self.type].FrameWidth then
			width = themetable.ThemeData[self.type].FrameWidth
		end
	end
	if InCombatLockdown() then
--		self.frame:SetBackdropColor(0.2,0.7,0.1,0.8)
	end
	for k,v in pairs(themetable.ThemeData.all) do
		if self[k] then self[k]:ClearAllPoints() end
	end
	local index = "all"
	for j = 1,2 do
		if j == 2 then
			index = self.type
		end
		if themetable.ThemeData[index] then
			for key, value in pairs(themetable.ThemeData[index]) do
				if self[key] and value.Hidden ~= true and not (self.disabledFrames[key]) then
					local useSecondary = false

					if value.Hide then
						self.disabledFrames[value.Hide] = true
					end

					if value.HeightAdd then
						height = height + value.HeightAdd
					end
						if value.Font then
						self[key]:SetFont(value.Font,value.FontSize)
					end

					local RelativeTo
					local i = ""
					while value["RelativeTo"..i] and not RelativeTo do
						if (themetable.ThemeData[index][value["RelativeTo"..i]]
						and themetable.ThemeData[index][value["RelativeTo"..i]].Hidden)
						or self.disabledFrames[value["RelativeTo"..i]] then
							if i == "" then
								i = 2
							else
								i = i + 1
							end
							if (tonumber(i) or 0) > 4 then
								RelativeTo = "frame"
								i = ""
								break
							end
						else
							RelativeTo = value["RelativeTo"..i]
						end
					end
					if not RelativeTo then
						RelativeTo = "frame"
					end

					if value.Width then
						if self.flags.Portrait == true and value.WidthP then
							self[key]:SetWidth(value.WidthP)
						elseif i and value["Width"..i] then
							self[key]:SetWidth(value["Width"..i])
						else
							self[key]:SetWidth(value.Width)
						end
					end

					if value.Height then
						self[key]:SetHeight(value.Height)
					end

					if RelativeTo then
						x = value["x"..i] or 0
						y = value["y"..i] or 0
						Point = value["Point"..i] or nil
						RelativePoint = value["RelativePoint"..i] or nil
						local pointAdjust
						if themetable.ResizableBar == true and self.database.LongStatusbars == false and (key == "HealthText" or key == "ManaText") then
							if Point == "RIGHT" then
								pointAdjust = "LEFT"
							elseif Point == "LEFT" then
								pointAdjust = "RIGHT"
							end
						end
						if (pointAdjust or Point) and RelativePoint and x and y then
							self[key]:ClearAllPoints()
							self[key]:SetPoint(pointAdjust or Point, self[RelativeTo], RelativePoint, x, y)
							if value.Endx and value.Endy and value.EndPoint and value.EndRelativePoint and value.EndRelativeTo then
								self[key]:SetPoint(value.EndPoint, self[value.EndRelativeTo], value.EndRelativePoint, value.Endx, value.Endy)
							end
						end
					end

					if value.Visibility then
						for k, v in pairs(value.Visibility) do
							if self[v] and ((themetable.ThemeData[index][v] and not (themetable.ThemeData[index][v].Hidden or self.disabledFrames[v]))or (not themetable.ThemeData[index][v])) then
								self[v]:Show()
							end
						end
					end
					if value.Type then
						if value.Type == "string" then
							self.strings[key] = true
							if value.OverrideTag then
								self[key].OverrideTag = value.OverrideTag
							else
								self[key].OverrideTag = nil
							end
						else
							self.strings[key] = nil
						end
					end
					if value.Justify then
						self[key]:SetJustifyH(value.Justify)
					end
					if value.WidthFactor then
						self.widthFactor[key] = value.WidthFactor
						self[key].DefaultWidth = self[key]:GetWidth()
					else
						self.widthFactor[key] = nil
						self[key].DefaultWidth = nil
					end
				elseif self[key] and (value.Hidden or self.disabledFrames[key] == true ) then
					self[key]:Hide()
					self.disabledFrames[key] = true
					if value.Visibility then
						for k, v in pairs(value.Visibility) do
							if self[v] then
								self.disabledFrames[v] = true
								self[v]:Hide()
							end
						end
					end
				end
			end
		end
	end
	for key,value in pairs(self.disabledFrames) do
		if self[key] then
			self[key]:Hide()
		end
	end

	if self.flags.ResizableBar == true and self.database.LongStatusbars == false then
		self.HealthBar_BG:SetWidth(self.HealthBar_BG:GetWidth() - self.HealthText:GetWidth())
		self.ManaBar_BG:SetWidth(self.ManaBar_BG:GetWidth() - self.ManaText:GetWidth())
		self.HealthBar_BG.DefaultWidth = self.HealthBar_BG:GetWidth()
		self.ManaBar_BG.DefaultWidth = self.ManaBar_BG:GetWidth()
	end
	if self.flags.Portrait then
		self.PortraitModel:SetWidth(self.Portrait:GetWidth()-2)
		self.PortraitModel:SetHeight(self.Portrait:GetHeight()-2)
	end

	self:ScheduleLeaveCombatAction(self.frame.SetHeight, self.frame, height)
	if self.frame.header then
		self.frame.header.height = height
	end

	if reset then
		self.database.Width = width
		if themetable.Border then
			self.database.BorderStyle = themetable.Border
		end
	end
	self:SetWidth()
end

function aUF.classes.aUFunit.prototype:SetWidth()
	self:ScheduleLeaveCombatAction(self.frame.SetWidth, self.frame, self.database.Width)
	
	local frameWidth = self.frame:GetWidth()
	if frameWidth <= 0 then
		frameWidth = self.frame.initialWidth
	end
	for k,v in pairs(self.widthFactor) do
		if v then
			local aWidth = (frameWidth - 100)*v
			self[k]:SetWidth(self[k].DefaultWidth+aWidth)
		end
	end

	if self.XPBar_Rest then
		self.XPBar_Rest:ClearAllPoints()
		self.XPBar_Rest:SetAllPoints(self.XPBar_BG)
	end
	if self.XPBar then
		self.XPBar:ClearAllPoints()
		self.XPBar:SetAllPoints(self.XPBar_BG)
	end
	if self.HealthBar then
		self.HealthBar:ClearAllPoints()
		self.HealthBar:SetAllPoints(self.HealthBar_BG)
	end
	if self.ManaBar then
		self.ManaBar:ClearAllPoints()
		self.ManaBar:SetAllPoints(self.ManaBar_BG)
	end
	if self.CastBar then
		self.CastBar:ClearAllPoints()
		self.CastBar:SetAllPoints(self.CastBar_BG)
	end

	self:AuraPosition()
	if self.unit then
		self:UpdateTextStrings()
	end
end

function aUF.classes.aUFunit.prototype:BorderBackground()
	local colortable
	local bordercolor = aUF.db.profile.FrameBorderColors
	local borderstyle = self.database.BorderStyle or aUF.db.profile.BorderStyle

	if self.unit == "target" then
		colortable = aUF.db.profile.TargetFrameColors
	else
		colortable = aUF.db.profile.PartyFrameColors
	end
	
	self.frame:SetBackdrop({
				bgFile = "Interface\\ChatFrame\\ChatFrameBackground", tile = true, tileSize = 16,
				edgeFile = aUF.Borders[borderstyle].texture, edgeSize = aUF.Borders[borderstyle].size,
				insets = {left = aUF.Borders[borderstyle].insets, right = aUF.Borders[borderstyle].insets, top = aUF.Borders[borderstyle].insets, bottom = aUF.Borders[borderstyle].insets},
		})

	self.frame:SetBackdropColor(colortable.r,colortable.g,colortable.b,colortable.a)
	self.frame:SetBackdropBorderColor(bordercolor.r,bordercolor.g,bordercolor.b,bordercolor.a)

	if self.flags.Portrait then
		self.Portrait:SetBackdropBorderColor(self.frame:GetBackdropBorderColor())
		self.Portrait:SetBackdropColor(self.frame:GetBackdropColor())
	end
end

function aUF.classes.aUFunit.prototype:BarTexture()
	local m = media:Fetch("statusbar", aUF.db.profile.BarStyle)

	self.HealthBar:SetStatusBarTexture(m)
	self.ManaBar:SetStatusBarTexture(m)
	self.XPBar:SetStatusBarTexture(m)
	self.CastBar:SetStatusBarTexture(m)
	if self.XPBar_Rest then
		self.XPBar_Rest:SetStatusBarTexture(m)
	end
	self.HealthBar_BG:SetTexture(m)
	self.ManaBar_BG:SetTexture(m)
	self.XPBar_BG:SetTexture(m)
	self.CastBar_BG:SetTexture(m)
end

function aUF.classes.aUFunit.prototype:SetUnitWatch()
	if self.database.HideFrame == true then
		self.watched = false
		UnregisterUnitWatch(self.frame)
		self.frame:Hide()
	else
		RegisterUnitWatch(self.frame, false)
		self.watched = true
	end
end

-------------
-- UPDATES --
-------------

function aUF.classes.aUFunit.prototype:UpdateHighlight(entered)
	if (UnitExists("target") and UnitIsUnit("target",self.unit) and not (self.unit:find("target")) and aUF.db.profile.HighlightSelected == true) or (entered == true and aUF.db.profile.HighlightSelected == true) then
		self.Highlight:Show()
	else
		self.Highlight:Hide()
	end
end

function aUF.classes.aUFunit.prototype:UpdateAll(unload)
	if not self.database or not self.unit then return end
	if not unload and not self.database.HideFrame then
		self.exists = true
		aUF:RegisterPool(self)
		if not InCombatLockdown() then
			self:LoadPosition()
		end
		self:StatusBarsColor()
		self:UpdateHealth()
		self:UpdatePower()
		self:UpdateCastBar()
		self:UpdatePvP()
		self:UpdateAuras()
		self:UpdateRaidTargetIcon()
		self:UpdateStatusIcon()
		self:LabelsCheckLeader()
		self:UpdateHighlight()
		self:UpdatePortrait()
		self:GetStringEvents()
		self:UpdateTextStrings()
	else
		self.exists = false
		aUF:RegisterPool(self)
	end
end

function aUF.classes.aUFunit.prototype:RegisterState()
	if self.exists == true then
		self:RegisterEvents()
	else
		aUF:ObjectUnregisterAllEvents(self)
		self.eventsRegistered = nil
		self.castbarEventRegged = nil
	end
end

function aUF.classes.aUFunit.prototype:UpdatePortrait(stop)
	if not self.flags.Portrait then
		if self.PortraitModel then
			self.PortraitModel:Hide()
		end
		return
	end
	if not UnitExists(self.unit) or not UnitIsConnected(self.unit) or not UnitIsVisible(self.unit) then
		self.PortraitModel:SetModelScale(4.25)
		self.PortraitModel:SetPosition(0,0,-1.5)
		self.PortraitModel:SetModel("Interface\\Buttons\\talktomequestionmark.mdx")
	else
		self.PortraitModel:SetUnit(self.unit)
		self.PortraitModel:SetCamera(0)
		self.PortraitModel:Show()
	end
	self.Portrait:SetBackdropBorderColor(self.frame:GetBackdropBorderColor())
	self.Portrait:SetBackdropColor(self.frame:GetBackdropColor())
end

function aUF.classes.aUFunit.prototype:UpdateTextStrings()
	for k,v in pairs(self.strings) do
		local str = self[k]
		if str and str.func then
			str.func(self.unit,str)
		elseif str then
			str:SetText("")
		end
	end
end

local defaultTextFormats = {
	HealthText = {
		Absolute   = "[aghp]",
		Difference = "[agmissinghp]",
		Percent    = "[agpercenthp]",
		Smart      = "[agsmarthp]",
		Hide       = "",
	},
	ManaText = {
		Absolute   = "[agmana]",
		Difference = "[agmissingmana]",
		Percent    = "[agpercentmana]",
		Smart      = "[agsmartmana]",
		Hide       = "",
	},
	NameLabel = {
		Default = "[name]",
		Hide    = "",
	},
	ClassText = {
		Default = "[classification][difficulty][level][white] [raidcolor][agclass][white] [agrace]",
		Hide    = "",
	}
}

function aUF.classes.aUFunit.prototype:GetStringEvents()
	if not aUF.HelperFunctions[self.type] then
		aUF.HelperFunctions[self.type] = {}
	end
	for k,v in pairs(self.strings) do
		local str = self[k]
		if str then
			if self[k].OverrideTag then
				aUF.HelperFunctions[self.type][k] = aUF:Parse(self[k].OverrideTag)
			elseif self.database[k.."Style"] == "Custom" then
				aUF.HelperFunctions[self.type][k] = aUF:Parse(self.database[k.."Format"])
			else
				aUF.HelperFunctions[self.type][k] = aUF:Parse(defaultTextFormats[k][self.database[k.."Style"]])
			end
			if (aUF.HelperFunctions[self.type][k]) then
				if str then
					if str.events then
						for method, event in pairs(str.events) do
							self:ObjectUnregisterEvent(event, method)
						end
					end
					str.events = nil		
					local func = aUF.HelperFunctions[self.type][k].func
					if func then
						str.func = aUF.HelperFunctions[self.type][k].func
					end
					local events = aUF.HelperFunctions[self.type][k].events
					if events then
						for x,y in pairs(events) do
							self:StringRegisterEvent(str, y, k)
						end
					end
				end
			end
		end
	end
end

function aUF.classes.aUFunit.prototype:StringRegisterEvent(str, event, shortName)
	if not str.events then
		str.events = {}
	end
	local unit = self.unit
	self[shortName..event] = function() str.func(unit,str) end
	str.events[shortName..event] = event
	self:ObjectRegisterEvent(event,shortName..event)
end


---------------
-- STATUSBAR --
---------------

function aUF.classes.aUFunit.prototype:UpdateHealth()
	local currValue,maxValue = UnitHealth(self.unit),UnitHealthMax(self.unit)
	local perc = currValue/maxValue * 100

	if ( not UnitExists(self.unit) or UnitIsDead(self.unit) or UnitIsGhost(self.unit) or not UnitIsConnected(self.unit) or maxValue == 1) then
		perc = 0
		self.HealthBar:SetValue(0)
	end
	if self.database.HealthDeficit then
		perc = 100 - perc
	end
	self.HealthBar:SetValue(perc)
end

function aUF.classes.aUFunit.prototype:UpdatePower()
	local currValue,maxValue = UnitMana(self.unit),UnitManaMax(self.unit)
	local perc = currValue/maxValue * 100
	local db = aUF.db.profile

	if ( not UnitExists(self.unit) or UnitIsDead(self.unit) or UnitIsGhost(self.unit) or not UnitIsConnected(self.unit) or (currValue == 1 and maxValue == 1) ) then
		self.ManaBar:SetValue(0)
	else
		if maxValue == 0 then
			self.ManaBar:SetValue(0)
		else
			self.ManaBar:SetValue(perc)
		end
	end

	if not self.powertype or self.powertype ~= UnitPowerType(self.unit) then
		self:StatusBarsColor()
	end
end

function aUF.classes.aUFunit.prototype:StatusBarsColor()
	local db = aUF.db.profile
	local healthColor
	local _,Eclass = UnitClass(self.unit)

	if self.unit:find("target") and not UnitIsFriend(self.unit, "player") and (self.database.TargetShowHostile == true ) then
		healthColor = aUF:UtilFactionColors(self.unit)
		if aUF.db.profile.SmoothHealthBars == true then
			self.flags.BarColor = 2
		else
			self.flags.BarColor = nil
		end
	elseif self.unit == "pet" and self.flags.HappinessBar == true and Eclass ~= "WARLOCK" then
		local happiness = GetPetHappiness()
		if ( happiness == 1 ) then
			healthColor = aUF.ManaColor[1]
		elseif ( happiness == 2 ) then
			healthColor = aUF.ManaColor[3]
		else
			healthColor = db.HealthColor
		end
		self.flags.BarColor = nil
	elseif UnitIsPlayer(self.unit) and self.database.ClassColorBars == true and RAID_CLASS_COLORS[Eclass] then
		healthColor = RAID_CLASS_COLORS[Eclass]
		self.flags.BarColor = nil
	else
		healthColor = db.HealthColor
		if aUF.db.profile.SmoothHealthBars == true then
			self.flags.BarColor = 1
		else
			self.flags.BarColor = nil
		end
	end

	if ( self.flags.BarColor ) then
		self:StatusBarsOnValueChanged(self.HealthBar:GetValue())
	else
		self.HealthBar:SetStatusBarColor(healthColor.r,healthColor.g,healthColor.b, 1)
	end

	if self.flags.BackgroundBarColor == true then
		self.HealthBar_BG:SetVertexColor(healthColor.r,healthColor.g,healthColor.b,0.25)
	else
		self.HealthBar_BG:SetVertexColor(0,0,0,0.25)
	end

	-- mana
	self.powertype = UnitPowerType(self.unit)
	local info = db.ManaColor[self.powertype]
	if self.flags.BackgroundBarColor == true then
		self.ManaBar_BG:SetVertexColor(info.r,info.g,info.b,0.25)
	else
		self.ManaBar_BG:SetVertexColor(0,0,0,0.25)
	end
	if self.flags.AlphaBar == true then
		self.ManaBar:SetStatusBarColor(info.r,info.g,info.b,0.8)
	else
		self.ManaBar:SetStatusBarColor(info.r,info.g,info.b,1)
	end
end

function aUF.classes.aUFunit.prototype:StatusBarsOnValueChanged(value)
	if self.flags.BarColor == 1 then
		self:SmoothHealthBarOnValueChanged(value)
	elseif self.flags.BarColor == 2 then
		self:SmoothTargetHealthBarOnValueChanged(value,aUF:UtilFactionColors(self.unit))
	end
end

function aUF.classes.aUFunit.prototype:SmoothTargetHealthBarOnValueChanged(value,colortable)
	if ( not value) then
		return
	end
	local r, g, b
	local min, max = self.HealthBar:GetMinMaxValues()
	if ( (value < min) or (value > max) ) then
		return
	end
	if ( (max - min) > 0 ) then
		value = (value - min) / (max - min)
	else
		value = 0
	end

	r = colortable.r*(0.35*value+0.65)
	g = colortable.g*(0.35*value+0.65)
	b = colortable.b*(0.35*value+0.65)
	self.HealthBar:SetStatusBarColor(r, g, b)
end

function aUF.classes.aUFunit.prototype:SmoothHealthBarOnValueChanged(value)
	if ( not value ) then
		return
	end
	local db = aUF.db.profile
	local r, g, b
	local min, max = self.HealthBar:GetMinMaxValues()
	if ( (value < min) or (value > max) ) then
		return
	end
	if ( (max - min) > 0 ) then
		value = (value - min) / (max - min)
	else
		value = 0
	end
	if(value > 0.5) then
		r = (db.HealthColor.r) + (((1-value) * 2)* (1-(db.HealthColor.r)))
		g = db.HealthColor.g
	else
		r = 1.0
		g = (db.HealthColor.g) - (0.5 - value)* 2 * (db.HealthColor.g)
	end
	b = db.HealthColor.b
	self.HealthBar:SetStatusBarColor(r, g, b)
end

-------------
-- CASTBAR --
-------------

function aUF.classes.aUFunit.prototype:RegisterCasting()
	if self.database.CastBar and not self.castbarEventRegged and self.CastBar then
		self:ObjectRegisterEvent("UNIT_SPELLCAST_START", "SpellcastStart")
		self:ObjectRegisterEvent("UNIT_SPELLCAST_CHANNEL_START", "SpellcastChannelStart")
		self:ObjectRegisterEvent("UNIT_SPELLCAST_STOP", "SpellcastStop")
		self:ObjectRegisterEvent("UNIT_SPELLCAST_FAILED", "SpellcastFailed")
		self:ObjectRegisterEvent("UNIT_SPELLCAST_DELAYED", "SpellcastDelayed")
		self:ObjectRegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE", "SpellcastChannelUpdate")
		self:ObjectRegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP", "SpellcastChannelStop")
		self:ObjectRegisterEvent("UNIT_SPELLCAST_INTERRUPTED", "SpellcastInterrupted")
		self.frame:SetScript("OnUpdate", function() self:SpellcastUpdate(arg1) end)

		if self.unit == "player" then
			self:ObjectRegisterEvent("UNIT_SPELLCAST_SENT", "SpellcastSent")
		end

		self.castbarEventRegged = true
	elseif not self.database.CastBar then
		local t = {
			"UNIT_SPELLCAST_START","UNIT_SPELLCAST_CHANNEL_START",
			"UNIT_SPELLCAST_STOP","UNIT_SPELLCAST_FAILED","UNIT_SPELLCAST_DELAYED",
			"UNIT_SPELLCAST_CHANNEL_UPDATE","UNIT_SPELLCAST_CHANNEL_STOP","UNIT_SPELLCAST_INTERRUPTED",
			"UNIT_SPELLCAST_SENT"
		}
		for _,v in pairs(t) do
			self:ObjectUnregisterEvent(v)
		end
		self.frame:SetScript("OnUpdate", nil)
		self.castbarEventRegged = nil
	end
end

function aUF.classes.aUFunit.prototype:SpellcastUpdate()
	local currentTime = GetTime()
	if( self.spellCastData.casting) then
		local showTime = math.min(currentTime, self.spellCastData.endTime)
		local percent = ((currentTime - self.spellCastData.startTime) / (self.spellCastData.endTime - self.spellCastData.startTime)) * 100
		self.CastBar:SetValue(percent)
		if self.spellCastData.delay then
			self.CastText:SetText(string.format("|cffFF0000+%.1f|cffffffff %.1f",self.spellCastData.delay, self.spellCastData.endTime - showTime))
		else
			self.CastText:SetText(string.format("%.1f", self.spellCastData.endTime - showTime))
		end
		if currentTime > self.spellCastData.endTime and not UnitIsUnit("player", self.unit) then
			self.spellCastData.casting        = nil
			self.spellCastData.fadeout        = 1
			self.spellCastData.stopTime       = GetTime()
		end
	elseif(self.spellCastData.channelling) then
		local showTime = currentTime
		if(currentTime > self.spellCastData.endTime) then
			showTime = self.spellCastData.endTime
			self.spellCastData.channelling = nil
			self.spellCastData.stopTime = GetTime()
		end
		local remainingTime = self.spellCastData.endTime - showTime
		self.CastBar:SetValue(self.spellCastData.startTime + remainingTime)
		self.CastText:SetText(string.format("%.1f", remainingTime))

		if self.spellCastData.delay then
			self.CastText:SetText(string.format("|cffFF0000-%.1f|cffffffff %.1f", self.spellCastData.delay, remainingTime))
		else
			self.CastText:SetText(string.format("%.1f", remainingTime))
		end
	elseif(self.spellCastData.fadeOut) then
		local alpha = self.spellCastData.stopTime - currentTime + 1
		if(alpha >= 1) then
			alpha = 1
		end
		if(alpha <= 0) then
			self.spellCastData.fadeOut = nil
			self.CastBar:Hide()
			self.BarCastText:Hide()
			self.CastText:Hide()

			-- This is to reduce flicker on subsequent spells
			self.CastBar:SetValue(0)
		else
			self.CastBar:SetAlpha(alpha)
			self.BarCastText:SetAlpha(alpha)
			self.CastText:SetAlpha(alpha)
		end
	end
end

function aUF.classes.aUFunit.prototype:UpdateCastBar()
	local displayName, _, startTime, endTime = select(3, UnitCastingInfo(self.unit))

	if not displayName then
		displayName, _, startTime, endTime = select(3, UnitChannelInfo(self.unit))

		if not displayName then
			if self.spellCastData.casting or self.spellCastData.channelling then
				self.spellCastData.fadeOut		  = 1
				self.spellCastData.casting        = nil
				self.spellCastData.channelling    = nil
				self.spellCastData.stopTime		  = GetTime()
				self.CastText:SetText("")
			end

			return
		end
		self.spellCastData.startTime = startTime * 0.001
		self.spellCastData.endTime = endTime * 0.001

		self.CastBar:SetMinMaxValues(self.spellCastData.startTime, self.spellCastData.endTime)
		self.CastBar:SetValue(self.spellCastData.startTime + self.spellCastData.endTime - GetTime())
		self.CastBar:Show()
		self.spellCastData.casting        = nil
		self.spellCastData.channelling    = 1
	else
		self.spellCastData.startTime = startTime * 0.001
		self.spellCastData.endTime = endTime * 0.001

		self.CastBar:SetMinMaxValues(1, 100)

		local percent = ((GetTime() - startTime) / (endTime - startTime)) * 100
		self.CastBar:SetValue(percent)
		self.CastBar:SetStatusBarColor(1.0, 0.7, 0.0)
		self.CastBar:Show()
		self.spellCastData.casting        = 1
		self.spellCastData.channelling    = nil
	end

	self.CastBar:SetAlpha(1.0)
	self.spellCastData.fadeOut = 0
	self.BarCastText:SetText(displayName)
	self.BarCastText:SetAlpha(1.0)
	self.BarCastText:Show()
	self.CastText:SetAlpha(1.0)
	self.CastText:Show()
	self.spellCastData.delay = nil
end

function aUF.classes.aUFunit.prototype:SpellcastSent(_, _, _, _, target)
	if target == "" then target = nil end
	self.castingTarget = target
end

function aUF.classes.aUFunit.prototype:SpellcastStart()
	if arg1 ~= self.unit then return end

	local displayName, _, startTime, endTime = select(3, UnitCastingInfo(self.unit))

	self.spellCastData.startTime = startTime * 0.001
	self.spellCastData.endTime = endTime * 0.001
	self.spellCastData.delay = nil

	local c = aUF.db.profile.CastbarColor

	self.CastBar:SetStatusBarColor(c.r, c.g, c.b)
	self.CastBar:SetAlpha(1.0)
	self.CastBar:SetMinMaxValues(1, 100)
	self.CastBar:SetValue(1)
	self.CastBar:Show()
	self.spellCastData.casting        = 1
	self.spellCastData.channelling    = nil
	self.spellCastData.fadeOut        = 0
	if type(self.castingTarget) == "string" and UnitName(self.unit) ~= self.castingTarget then
		self.BarCastText:SetText(displayName .. " (" .. self.castingTarget .. ")")
	else
		self.BarCastText:SetText(displayName)
	end
	self.BarCastText:SetAlpha(1.0)
	self.BarCastText:Show()
	self.CastText:SetAlpha(1.0)
	self.CastText:Show()
end

function aUF.classes.aUFunit.prototype:SpellcastStop()
	if arg1 ~= self.unit then return end

	if self.spellCastData.casting then
		self.CastBar:SetStatusBarColor(0.0, 1.0, 0.0)
		self.CastBar:SetMinMaxValues(0, 1)
		self.CastBar:SetValue(1)
		self.spellCastData.casting        = nil
		self.spellCastData.fadeout        = 1
		self.spellCastData.stopTime       = GetTime()
		self.CastText:SetText("")
		self.castingTarget = nil
	end
end

function aUF.classes.aUFunit.prototype:SpellcastInterrupted()
	if arg1 ~= self.unit then return end

	if ( self.spellCastData.casting and not self.spellCastData.channelling ) then
		self.CastBar:SetStatusBarColor(1.0, 0.0, 0.0)
		self.CastBar:SetAlpha(1.0)
		self.CastBar:Show()
		self.spellCastData.stopTime   = GetTime()
		self.spellCastData.casting    = nil
		self.spellCastData.channelling = nil
		self.spellCastData.fadeOut    = 1
		self.BarCastText:SetText(L["Interrupted"])
		self.BarCastText:Show()
		self.castingTarget = nil
	end
end

function aUF.classes.aUFunit.prototype:SpellcastFailed()
	if arg1 ~= self.unit then return end

-- To enable failed messages when the spell can't be cast at all, comment the next line
-- The visual appearance of a full failed bar for all spells is consistent with the default blizzard castbar

	if self.spellCastData.casting or self.spellCastData.channelling then
		self.CastBar:SetStatusBarColor(1.0, 0.0, 0.0)
		self.CastBar:SetAlpha(1.0)
		self.CastBar:SetMinMaxValues(0, 1)
		self.CastBar:SetValue(1)
		self.CastBar:Show()
		self.spellCastData.stopTime   = GetTime()
		self.spellCastData.casting    = nil
		self.spellCastData.channelling = nil
		self.spellCastData.fadeOut    = 1
		self.BarCastText:SetText(L["Failed"])
		self.BarCastText:Show()
		self.castingTarget = nil
	end
end

function aUF.classes.aUFunit.prototype:SpellcastChannelStart()
	if arg1 ~= self.unit then return end

	local displayName, _, startTime, endTime = select(3, UnitChannelInfo(self.unit))
	self.spellCastData.startTime = startTime * 0.001
	self.spellCastData.endTime = endTime * 0.001
	self.spellCastData.delay = nil

	local c = aUF.db.profile.CastbarColor

	self.CastBar:SetStatusBarColor(c.r, c.g, c.b)
	self.CastBar:SetAlpha(1.0)
	self.CastBar:SetMinMaxValues(self.spellCastData.startTime, self.spellCastData.endTime)
	self.CastBar:SetValue(self.spellCastData.endTime)
	self.CastBar:Show()
	self.spellCastData.casting        = nil
	self.spellCastData.channelling    = 1
	self.spellCastData.fadeOut        = 0
	self.BarCastText:SetText(displayName)
	self.BarCastText:SetAlpha(1.0)
	self.BarCastText:Show()
	self.CastText:SetAlpha(1.0)
	self.CastText:Show()
end

function aUF.classes.aUFunit.prototype:SpellcastDelayed()
	if arg1 ~= self.unit then return end

	if(self.spellCastData.casting) then
		local startTime, endTime = select(5, UnitCastingInfo("player"))

		if not startTime or not endTime then return end

		local oldStart = self.spellCastData.startTime
		self.spellCastData.startTime = startTime * 0.001
		self.spellCastData.endTime = endTime * 0.001
		self.spellCastData.delay = (self.spellCastData.delay or 0) + (self.spellCastData.startTime-oldStart)
		self.CastBar:SetMinMaxValues(1, 100)
	end
end

function aUF.classes.aUFunit.prototype:SpellcastChannelUpdate()
	if arg1 ~= self.unit then return end

	local spell, _, _, _, startTime, endTime = UnitChannelInfo("player")

	if not spell and self.CastBar then
		self.CastBar:Hide()
		return
	end

	local oldStart = self.spellCastData.startTime
	self.spellCastData.startTime = startTime * 0.001
	self.spellCastData.endTime = endTime * 0.001
	self.spellCastData.delay = (self.spellCastData.delay or 0) + (oldStart - self.spellCastData.startTime)
	self.CastBar:SetMinMaxValues(self.spellCastData.startTime, self.spellCastData.endTime)
end

function aUF.classes.aUFunit.prototype:SpellcastChannelStop()
	if arg1 ~= self.unit then return end

	self.spellCastData.channelling    = nil
	self.spellCastData.casting        = nil
	self.spellCastData.fadeout        = 1
	self.spellCastData.stopTime       = GetTime()
	self.CastText:SetText("")
	self.castingTarget = nil
end

-----------
-- ICONS --
-----------

function aUF.classes.aUFunit.prototype:RosterUpdate()
	self:LabelsCheckLeader()
	self:LabelsCheckLoot()
	self:UpdateRaidTargetIcon()
end

function aUF.classes.aUFunit.prototype:LabelsCheckLeader()
	self.LeaderIcon:Hide()
	if not ( self.type == "raid" or self.type == "party" or self.unit == "player") then return end
	if aUF.db.profile.ShowGroupIcons == true and not self.disabledFrames.LeaderIcon then
		if self.type == "raid" then
			local _, rank = GetRaidRosterInfo(self.number)
			if rank == 2 then
				self.LeaderIcon:Show()
			end
		elseif self.type == "party" and tonumber(GetPartyLeaderIndex()) == tonumber(self.number) then
			self.LeaderIcon:Show()
		elseif self.unit == "player" and IsPartyLeader() then
			self.LeaderIcon:Show()
		end
	end
end

function aUF.classes.aUFunit.prototype:LabelsCheckLoot()
	self.MasterIcon:Hide()
	if not ( self.unit == "player" or self.type == "party" or self.type == "raid") then return end
	if aUF.db.profile.ShowGroupIcons == true and self.number and not self.disabledFrames.MasterIcon then
		local lootMaster
		if self.type == "raid" then
			lootMaster = select(11, GetRaidRosterInfo(self.number))
			if lootMaster then
				self.MasterIcon:Show()
			end
		else
			_, lootMaster = GetLootMethod()
			if lootMaster then
				if self.unit == "player" and lootMaster == 0 then
					self.MasterIcon:Show()
				elseif self.type == "party" and lootMaster > 0 then
					if lootMaster == self.number then
						self.MasterIcon:Show()
					end
				end
			end
		end
	end
end

function aUF.classes.aUFunit.prototype:UpdateRaidTargetIcon()
	local index = GetRaidTargetIndex(self.unit)
	if ( index ) and UnitExists(self.unit) and self.database.ShowRaidTargetIcon == true and not self.disabledFrames.RaidTargetIcon then
		SetRaidTargetIconTexture(self.RaidTargetIcon, index)
		self.RaidTargetIcon:Show()
	else
		self.RaidTargetIcon:Hide()
	end
end

function aUF.classes.aUFunit.prototype:UpdateStatusIcon()
	if self.database.ShowInCombatIcon == true and not self.disabledFrames.InCombatIcon and self.unit then
		if UnitAffectingCombat(self.unit) then
			self.InCombatIcon:Show()
			self.InCombatIcon:SetTexCoord(0.5,1,0,0.5)
		elseif self.unit == "player" and IsResting() then
			self.InCombatIcon:Show()
			self.InCombatIcon:SetTexCoord(0,0.5,0,0.421875)
		else
			self.InCombatIcon:Hide()
		end
	else
		self.InCombatIcon:Hide()
	end
end

function aUF.classes.aUFunit.prototype:UpdatePvP()
	if aUF.db.profile.ShowPvPIcon == true and not self.disabledFrames.PVPIcon then
		if ( UnitIsPVPFreeForAll(self.unit) ) then
			self.PVPIcon:SetTexture("Interface\\TargetingFrame\\UI-PVP-FFA")
			self.PVPIcon:Show()
		elseif ( UnitFactionGroup(self.unit) and UnitIsPVP(self.unit) ) then
			self.PVPIcon:SetTexture("Interface\\TargetingFrame\\UI-PVP-"..UnitFactionGroup(self.unit))
			self.PVPIcon:Show()
		else
			self.PVPIcon:Hide()
		end
	else
		self.PVPIcon:Hide()
	end
end

-----------
-- AURAS --
-----------

local function hideTT()
	GameTooltip:Hide()
end

local function onAuraEnter()
	if (not this:IsVisible()) then return end
	GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT")
	if ( this.buffFilter == "HELPFUL") then
		GameTooltip:SetUnitBuff(this.unit.unit, this.id, this.unit.database.AuraFilter)
	elseif ( this.buffFilter == "HARMFUL") then
		GameTooltip:SetUnitDebuff(this.unit.unit, this.id, 0)
	end
end

function aUF.classes.aUFunit.prototype:SetAuraCount()
	local db = self.database
	self.AuraRows, self.AuraColumns = db.AuraRows, db.AuraColumns

	local count = self.AuraRows * self.AuraColumns

	-- setup auras
	for i = 1,math.max(count, 20) do
		local aura = "Aura" .. i
		if (not self[aura]) then
			local name = self.name .. "_" .. aura
			self[aura] = _G[name] or CreateFrame("Button", name, self.frame, "AGAuraTemplate")

			self[aura].Icon = _G[name .. "Icon"]
			self[aura].Overlay = _G[name .. "Overlay"]
			self[aura].Count = _G[name .. "Count"]
			self[aura]:SetScript("OnEnter", onAuraEnter)
			self[aura]:SetScript("OnLeave", hideTT)
			self[aura].unit = self

			aUF.hooks.hookShowHide(self[aura])
			aUF.hooks.hookSetTexture(self[aura].Icon)

--			self[aura]:SetParent(self.Overlays)
			self[aura]:Hide()
		end
	end

	local i = count+1
	while (true) do
		local f = self["Aura"..i]
		if (not f) then
			break
		end
		f:Hide()
		i = i+1
	end

	if (not aUF.cache or not aUF.cache.buffs or not aUF.cache.debuffs) then
		self:SetupBuffCache()
	end
end

function aUF.classes.aUFunit.prototype:AuraPosition()
	if not self.frame:GetWidth() or 0 >= self.frame:GetWidth() then
		return
	end
	local position = self.database.AuraPos
	local count = self.AuraRows * self.AuraColumns

	self.Aura1:ClearAllPoints()

	local Point, RelativePoint,x,y,GrowDirection,Scale,Alpha,AdjustAuto
	local auraTable
	if aUF.Layouts[self.database.FrameStyle] then
		auraTable = aUF.Layouts[self.database.FrameStyle].Auras or aUF.Layouts.ABF.Auras
	else
		return
	end

	if auraTable[position] then
		Point = auraTable[position].Point
		RelativePoint = auraTable[position].RelativePoint
		x = auraTable[position].x
		y = auraTable[position].y
		GrowDirection = auraTable[position].GrowDirection
		Scale = auraTable[position].Scale
		Alpha = auraTable[position].Alpha
		AdjustAuto = auraTable[position].AdjustAuto
	else
		return
	end

	if Scale == "auto" then
		Scale = (self.frame:GetWidth()-AdjustAuto)/(16*self.AuraColumns)
		self.Aura1:SetPoint(Point,self.frame,RelativePoint, x/Scale, y/Scale)
	else
		self.Aura1:SetPoint(Point,self.frame,RelativePoint, x*(1/Scale), y*(1/Scale))
	end

	for i=1,count do
		self["Aura"..i]:SetScale(Scale)
		self["Aura"..i]:SetAlpha(Alpha)
	end

	if ( GrowDirection == "left" ) then
		for i=2,count do
			self["Aura"..i]:ClearAllPoints()
			if (math.fmod(i,self.AuraColumns) == 1) then
				self["Aura"..i]:SetPoint("TOP",self["Aura"..i-self.AuraColumns],"BOTTOM",0,-1)
			else
				self["Aura"..i]:SetPoint("RIGHT",self["Aura"..i-1],"LEFT",1,0)
			end
		end
	else
		for i=2,count do
			self["Aura"..i]:ClearAllPoints()
			if (i % self.AuraColumns == 1) then
				if position == "Above" then
					self["Aura"..i]:SetPoint("BOTTOM",self["Aura"..i-self.AuraColumns],"TOP",0,1)
				else
					self["Aura"..i]:SetPoint("TOP",self["Aura"..i-self.AuraColumns],"BOTTOM",0,-1)
				end
			else
				self["Aura"..i]:SetPoint("LEFT",self["Aura"..i-1],"RIGHT",1,0)
			end
		end
	end
end

function aUF.classes.aUFunit.prototype:UpdateAuras(_,_,exec)
	if not exec then
		aUF.auraUpdatePool[self] = true
		return
	end
	local filter,position,AuraDebuffC = self.database.AuraFilter,self.database.position,self.database.AuraDebuffC
	local position = self.database.AuraPos
	local buttons,dFound
	local rows, cols, auracount = self.AuraRows, self.AuraColumns, self.AuraRows * self.AuraColumns

	local hide = position == "Hidden"
	local hidef = self["Aura1"].Hide

	-- hide all auras
	if (not self.hidden) then
		for i=1,auracount do
			hidef(self["Aura"..i])
		end

		self.hidden = true
	end

	if (not self.unit) then
		return
	end

	-- scan for buffs and debuffs
	local bcount, dbcount, highlighttype = self:ScanAuras()
	if (highlighttype and DebuffTypeColor[highlighttype] and AuraDebuffC == true) then
		local h = DebuffTypeColor[highlighttype]
		self.DebuffHighlight:Show()
		self.DebuffHighlight:SetVertexColor(h.r, h.g, h.b)
	else
		self.DebuffHighlight:Hide()
	end

	--[[
		-- look for debuffs
		-- note: not using aUF:UnitBuff, as the third argument is showDispellable
		local i = 1
		local debuff, t, highlight
		repeat
			_, _, debuff, _, t = UnitDebuff(self.unit,i,filter)
			if (debuff) then dbcount = dbcount + 1 end
			if (not highlight and t and DebuffTypeColor[t]) then
				highlight = DebuffTypeColor[t]
			end
			i = i + 1
		until (not debuff)

		-- set highlight
		if (highlight and AuraDebuffC == true and UnitIsFriend("player",self.unit)) then
			self.DebuffHighlight:Show()
			self.DebuffHighlight:SetVertexColor(highlight.r, highlight.g, highlight.b)
		end

		i = 1
		repeat
			debuff = UnitBuff(self.unit, i, filter)
			if (debuff) then bcount = bcount + 1 end
			i = i + 1
		until (not debuff)
	]]

	if hide or bcount + dbcount == 0 then
		self.hidden = true
		return
	end
	self.hidden = false

	if (auracount < bcount + dbcount) then
		if (self.database.AuraPreferBuff) then
			dbcount = auracount - bcount
			if (dbcount < 0) then
				bcount = auracount
				dbcount = 0
			end
		else
			bcount = auracount - dbcount
			if (bcount < 0) then
				dbcount = auracount
				bcount = 0
			end
		end
	end

	local buff, count, class, index, c, id

	for i=1,bcount do
		c = aUF.cache.buffs[i];
		buff, count, id = c.texture, c.count, c.id;
		self:SetAura(i, false, id, buff, count, nil)
	end

	rows = math.ceil((bcount+dbcount) / cols)

	if (rows == 1 and (position == "Left" or position == "Right" or position == "Inside")) then
		if (cols > bcount+dbcount) then
			cols = bcount+dbcount+0
		end
	end

	for i=1,dbcount do
		-- _,_, buff, count, class = UnitDebuff(self.unit,i,filter)
		c = aUF.cache.debuffs[i];
		buff, count, class, id = c.texture, c.count, c.type, c.id
		index = rows * cols - i + 1
		self:SetAura(index, true, id, buff, count, class)
	end
end

function aUF.classes.aUFunit.prototype:SetAura(index, isDebuff, id, buff, count, class)
	local buffFrame = self["Aura" .. index]
	buffFrame.Icon:SetTexture(buff)
	buffFrame:Show()
	buffFrame.id = id

	if count and count > 1 then
		buffFrame.Count:SetText(count)
	else
		buffFrame.Count:SetText("")
	end

	if (isDebuff) then
		buffFrame.buffFilter = "HARMFUL"
		local borderColor
		if class then
			borderColor = DebuffTypeColor[class]
		else
			borderColor = DebuffTypeColor["none"]
		end
		buffFrame.Overlay:SetVertexColor(borderColor.r, borderColor.g, borderColor.b)
	buffFrame.Overlay:Show()
	else
		buffFrame.buffFilter = "HELPFUL"
		buffFrame.Overlay:Hide()
	end
end

function aUF.classes.aUFunit.prototype:ShowAuraPositions()
	local count = self.AuraRows*self.AuraColumns
	if (self.database.AuraPreferBuff) then
		for i = 1,count do
			local b = self["Aura"..i]
			if (i <= 16) then
				b.Icon:SetTexture("Interface\\Icons\\Spell_ChargePositive")
			else
				b.Icon:SetTexture("Interface\\Icons\\Spell_ChargeNegative")
			end
		end
	else
		for i = 1,count do
			local b = self["Aura"..i]
			if (i > count-16) then
				b.Icon:SetTexture("Interface\\Icons\\Spell_ChargeNegative")
			else
				b.Icon:SetTexture("Interface\\Icons\\Spell_ChargePositive")
			end
		end
	end
	for i = 1,count do
		local b = self["Aura"..i]
		b:Show()
		b.Overlay:Show()
	end

	self.hidden = false
	self:ScheduleEvent(self.UpdateAuras, 10, self)
end

function aUF.classes.aUFunit.prototype:SetupBuffCache()
	if (not aUF.cache) then aUF.cache = {} end
	aUF.cache.buffs = {}
	aUF.cache.debuffs = {}

	for i = 1,50 do
		aUF.cache.buffs[i] = {}
		aUF.cache.debuffs[i] = {}
	end
end

function aUF.classes.aUFunit.prototype:ScanAuras()
	local buffid, id, unit = 1, 1, self.unit
	local c, name, rank, texture, debufftype, count, logdb
	local filter = self.database.AuraFilter
	local dbcount, bcount

	-- record buffs into a temporary area
	-- no special cases needed, just store the data
	name, rank, texture, count = UnitBuff(unit, buffid, filter)
	while (name) do
		-- record buffs
		c = aUF.cache.buffs[buffid]
		c.id, c.name, c.texture, c.count = buffid, name, texture, count

		buffid = buffid + 1
		name, rank, texture, count = UnitBuff(unit, buffid, filter)
	end

	bcount = buffid - 1

	-- record debuffs
	-- special case: priest weakened soul debuff
	local _, playerclass = UnitClass("player")
	id, buffid = 1, 1
	name, rank, texture, count, debufftype = UnitDebuff(unit, buffid)

	while (name) do
		-- record if filter is okay
		if (filter == 0) then
			logdb = true
			-- AceLibrary('AceConsole-2.0'):PrintLiteral("Skipped filtering on " .. name)
		elseif (aUF.CanDispel[playerclass] and aUF.CanDispel[playerclass][debufftype]) then
			logdb = true
			-- AceLibrary('AceConsole-2.0'):PrintLiteral("Can disspell " .. name)
		elseif (playerclass == "PRIEST" and texture == "Interface\\Icons\\Spell_Holy_AshesToAshes") then
			logdb = true
			-- AceLibrary('AceConsole-2.0'):PrintLiteral(name .. " special case")
		else
			-- AceLibrary('AceConsole-2.0'):PrintLiteral("Can't disspell " .. name)
			logdb = false
		end

		if (logdb) then
			c = aUF.cache.debuffs[id]
			c.id, c.name, c.texture, c.count, c.type = buffid, name, texture, count, debufftype
			id = id + 1
		end

		buffid = buffid + 1
		name, rank, texture, count, debufftype = UnitDebuff(unit, buffid)
	end

	dbcount = id - 1

	if (dbcount > 0) then
		return bcount, dbcount, aUF.cache.debuffs[1].type
	else
		return bcount, dbcount
	end
end

-----------------------
-- MOUSE INTERACTION --
-----------------------

function aUF.classes.aUFunit.prototype:OnDragStart(button)
	if self.grouped then
		self.frame:GetParent().object:OnDragStart(button)
		return
	end
	if button == "LeftButton" and (aUF.db.profile.Locked == false or IsAltKeyDown()) then
		self.frame:StartMoving()
	end
end

function aUF.classes.aUFunit.prototype:OnDragStop(button)
	if self.grouped then
		self.frame:GetParent().object:OnDragStop(button)
		return
	end
	self.frame:StopMovingOrSizing()
	self:SavePosition()
	self.frame:SetUserPlaced(false)
end

function aUF.classes.aUFunit.prototype:OnShowMenu(unit)
	self.object:DropDownUnit(self.unit)
end

function aUF.classes.aUFunit.prototype:OnShowCustomMenu(unit)
	if not InCombatLockdown() then
		dewdrop:Open(self, 'children', aUF:CreateDewdrop(self.object.type),'cursorX', true, 'cursorY', true)
	else
		aUF:Print(L["lockdown"])
	end
end

function aUF.classes.aUFunit.prototype:OnEnter()
	self.frame.unit = self.unit
	self:UpdateHighlight(true)
	UnitFrame_OnEnter()
end

function aUF.classes.aUFunit.prototype:OnLeave()
	self:UpdateHighlight()
	UnitFrame_OnLeave()
end

function aUF.classes.aUFunit.prototype:DropDownUnit()
	local type = nil

	if self.unit == "player" then
		type = PlayerFrameDropDown
	elseif self.unit == "target" then
		type = TargetFrameDropDown
	elseif self.unit == "pet" then
		type = PetFrameDropDown
	elseif self.type == "party" then
		type = _G["PartyMemberFrame"..self.number.."DropDown"]
	elseif self.unit:find("raid") then
		type = FriendsDropDown
		this.unit = self.unit
		this.name = UnitName(self.unit)
		this.id = this:GetID()
		FriendsDropDown.displayMode = "MENU"
		FriendsDropDown.initialize = RaidFrameDropDown_Initialize
	end

	if self.number then
		self:ScheduleLeaveCombatAction(this.SetID, this, self.number)
	end

	if type then
		HideDropDownMenu(1)
		type.unit = self.unit
		type.name = UnitName(self.unit)
		ToggleDropDownMenu(1, nil, type,"cursor")
		return true
	end
end

-------------------
-- COMBAT EVENTS --
-------------------

function aUF.classes.aUFunit.prototype:UnitCombat(event, unit)
	if unit ~= self.unit then return end
	if not ( self.database.ShowCombat ) then return end
	self:CombatFeedback_OnCombatEvent(arg1, arg2, arg3, arg4, arg5)
end

function aUF.classes.aUFunit.prototype:UnitSpellmiss(event, unit)
	if unit ~= self.unit then return end
	if not ( self.database.ShowCombat ) then return end
	self:CombatFeedback_OnSpellMissEvent(arg1, arg2)
end

function aUF.classes.aUFunit.prototype:CombatFeedback_OnCombatEvent(unit, event, flags, amount, type)
	local fontHeight = 13
	local text = ""
	local r,g,b = 1,1,1
	if( event == "IMMUNE" ) then
		fontHeight = fontHeight * 0.75
		text = CombatFeedbackText[event]
	elseif ( event == "WOUND" ) then
		if ( amount ~= 0 ) then
			if ( flags == "CRITICAL" or flags == "CRUSHING" ) then
				fontHeight = fontHeight * 1.5
			elseif ( flags == "GLANCING" ) then
				fontHeight = fontHeight * 0.75
			end
			if ( type > 0 ) then
				r = 1.0
				g = 1.0
				b = 0.0
			end
			if UnitInParty(self.unit) or UnitInRaid(self.unit) then
				r = 1.0
				g = 0.0
				b = 0.0
			end
			text = "-"..amount
		elseif ( flags == "ABSORB" ) then
			fontHeight = fontHeight * 0.75
			text = CombatFeedbackText["ABSORB"]
		elseif ( flags == "BLOCK" ) then
			fontHeight = fontHeight * 0.75
			text = CombatFeedbackText["BLOCK"]
		elseif ( flags == "RESIST" ) then
			fontHeight = fontHeight * 0.75
			text = CombatFeedbackText["RESIST"]
		else
			text = CombatFeedbackText["MISS"]
		end
	elseif ( event == "BLOCK" ) then
		fontHeight = fontHeight * 0.75
		text = CombatFeedbackText[event]
	elseif ( event == "HEAL" ) then
		text = "+"..amount
		r = 0.0
		g = 1.0
		b = 0.0
		if ( flags == "CRITICAL" ) then
			fontHeight = fontHeight * 1.3
		end
	elseif ( event == "ENERGIZE" ) then
		text = amount
		r = 0.41
		g = 0.8
		b = 0.94
		if ( flags == "CRITICAL" ) then
			fontHeight = fontHeight * 1.3
		end
	else
		text = CombatFeedbackText[event]
	end

	self.feedbackStartTime = GetTime()

	local font = self.HitIndicator:GetFont()
	self.HitIndicator:SetFont(font,fontHeight,"OUTLINE")
	self.HitIndicator:SetText(text)
	self.HitIndicator:SetTextColor(r, g, b)
	self.HitIndicator:SetAlpha(0)
	self.HitIndicator:Show()
	aUF.feedback[self.name] = true
	aUF:ScheduleRepeatingEvent("agUF_CombatSchedule",aUF.FeedbackUpdate, 0.05, aUF)
end

----------------------
-- UNITPLAYER CLASS --
----------------------

function aUF.classes.aUFunitPlayer.prototype:init(name,unit,unitdb)
	aUF.classes.aUFunitPlayer.super.prototype.init(self,name,unit,unitdb)
end

function aUF.classes.aUFunitPlayer.prototype:ClassRegisterEvents()
	self:ObjectRegisterEvent("PLAYER_XP_UPDATE","UpdateXP")
	self:ObjectRegisterEvent("UPDATE_EXHAUSTION","UpdateXP")
	self:ObjectRegisterEvent("PLAYER_LEVEL_UP","UpdateXP")
	self:ObjectRegisterEvent("PLAYER_REGEN_ENABLED", "UpdateStatusIcon")
	self:ObjectRegisterEvent("PLAYER_REGEN_DISABLED", "UpdateStatusIcon")
	self:ObjectRegisterEvent("PLAYER_UPDATE_RESTING", "UpdateStatusIcon")
	if self.inCombatSchedule then
		self:CancelScheduledEvent(self.inCombatSchedule)
		self.inCombatSchedule = nil
	end
end

local function xpOnEnter() this.unit:XPOnEnter() end

function aUF.classes.aUFunitPlayer.prototype:SetupFrame()
	self.class.super.prototype.SetupFrame(self)
	self.XPBar_Rest = CreateFrame("StatusBar",self.name.."_XPBar_Rest",self.frame)
	self.XPBar_Rest:SetMinMaxValues(0, 100)
	self.XPBar:SetParent(self.XPBar_Rest)

	self.XPBar.unit = self
	self.XPBar:EnableMouse(true)
	self.XPBar:SetScript("OnEnter", xpOnEnter)
	self.XPBar:SetScript("OnLeave", hideTT)
end

function aUF.classes.aUFunitPlayer.prototype:UpdateXP()
	if not self:IsHooked("ReputationWatchBar_Update") then
		self:SecureHook("ReputationWatchBar_Update", "UpdateXP")
	end
	local repname, repreaction, repmin, repmax, repvalue = GetWatchedFactionInfo()
	if ( self.database.ShowXP == true ) then
		if repname then
			local color = aUF.RepColor[repreaction]
			repmax = repmax - repmin
			repvalue = repvalue - repmin
			repmin = 0

			if self.XPBar_Rest then
				self.XPBar_Rest:Hide()
			end

			self.XPBar:SetParent(self.frame)
			self.XPBar:Show()
			if repmax ~= 0 then
				self.XPBar:SetValue((repvalue/repmax)*100)
			else
				self.XPBar:SetValue(0)
			end
			self.XPBar:SetStatusBarColor(color.r, color.g, color.b)
			self.XPBar_BG:SetVertexColor(color.r, color.g, color.b, 0.25)
			self.XPBar_Rest:Hide()
		else
			local priorXP = self.XPBar:GetValue()
			local restXP = GetXPExhaustion()
			local currXP, nextXP = UnitXP(self.unit), UnitXPMax(self.unit)

			if nextXP ~= 0 then
				self.XPBar:SetValue((currXP/nextXP)*100)
			else
				self.XPBar:SetValue(0)
			end

			if restXP then
				self.XPBar_Rest:Show()
				self.Overlays:SetFrameLevel(self.XPBar:GetFrameLevel()+1)
				self.XPBar_Rest:SetFrameLevel(self.XPBar:GetFrameLevel()-1)
				self.XPBar:SetParent(self.XPBar_Rest)
				if nextXP ~= 0 then
					self.XPBar_Rest:SetValue(((currXP+restXP)/nextXP)*100)
				else
					self.XPBar_Rest:SetValue(0)
				end
			else
				self.XPBar:SetParent(self.frame)
				self.XPBar:Show()
				self.XPBar_Rest:Hide()
			end

			self.XPBar:SetStatusBarColor(0.8, 0, 0.7)
			if self.flags.BackgroundBarColor == true then
				self.XPBar_BG:SetVertexColor(0.8, 0, 0.7, 0.25)
			else
				self.XPBar_BG:SetVertexColor(0, 0, 0, 0.25)
			end
		end
	else
		self.XPBar:Hide()
		self.XPBar_BG:Hide()
		self.XPBar_Rest:Hide()
	end
end

function aUF.classes.aUFunitPlayer.prototype:XPOnEnter()
	if (not this:IsVisible()) then return end

	GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT")
	GameTooltip:ClearLines()
	local _,eClass = UnitClass("player")

	local totalXP = UnitXPMax("player")
	local currentXP = UnitXP("player")
	local toLevelXP = totalXP - currentXP
	local restXP = GetXPExhaustion() or 0

	GameTooltip:AddLine(format(L["XP: %d/%d (%.1f%%)"], currentXP, totalXP, currentXP/totalXP*100))
	if restXP == 0 then
		restXP = ""
	else
		restXP = format(L["|c0064e1ff+%d rested|r"], restXP)
	end
	GameTooltip:AddLine(format(L["%d to level%s"], toLevelXP, restXP))

	local repname, repreaction, repmin, repmax, repvalue = GetWatchedFactionInfo()
	if (repname) then
		GameTooltip:AddLine(repname .. " (" .. _G["FACTION_STANDING_LABEL" .. repreaction] .. ")")
		GameTooltip:AddLine(format("%d/%d (%.1f%%)", repvalue-repmin, repmax-repmin, (repvalue-repmin)/(repmax-repmin) * 100))
	end
	GameTooltip:Show()
end

function aUF.classes.aUFunitPlayer.prototype:UpdateAll()
	self.class.super.prototype.UpdateAll(self)
	self:UpdateXP()
end

------------------
-- UNITPET CLASS --
------------------

function aUF.classes.aUFunitPet.prototype:init(name,unit,unitdb)
	aUF.classes.aUFunitPet.super.prototype.init(self,name,unit,unitdb)
end

function aUF.classes.aUFunitPet.prototype:ClassRegisterEvents()
	self:ObjectRegisterEvent("UNIT_PET_EXPERIENCE","UpdateXP")
	self:ObjectRegisterEvent("UNIT_HAPPINESS","StatusBarsColor")
end

function aUF.classes.aUFunitPet.prototype:SetupFrame()
	self.class.super.prototype.SetupFrame(self)
	self.XPBar:EnableMouse(true)
	self.XPBar.unit = self
	self.XPBar:SetScript("OnEnter", xpOnEnter)
	self.XPBar:SetScript("OnLeave", hideTT)
end

function aUF.classes.aUFunitPet.prototype:UpdateXP()
	local _,eClass = UnitClass("player")
	if eClass ~= "HUNTER" then
		return
	end
	if ( self.database.ShowXP == true and eClass == "HUNTER") then
		local priorXP = self.XPBar:GetValue()
		local currXP, nextXP = GetPetExperience()

		if nextXP ~= 0 then
			self.XPBar:SetValue((currXP/nextXP)*100)
		else
			self.XPBar:SetValue(0)
		end

		self.XPBar:SetStatusBarColor(0.8, 0, 0.7)
		if self.flags.BackgroundBarColor == true then
			self.XPBar_BG:SetVertexColor(0.8, 0, 0.7, 0.25)
		else
			self.XPBar_BG:SetVertexColor(0, 0, 0, 0.25)
		end
	else
		self.XPBar:Hide()
		self.XPBar_BG:Hide()
	end
end

function aUF.classes.aUFunitPet.prototype:XPOnEnter()
	if (not this:IsVisible()) then return end

	GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT")
	GameTooltip:ClearLines()
	local _,eClass = UnitClass("player")

	if ( eClass == "HUNTER") then
		currXP, nextXP = GetPetExperience()
		local toLevelXP = nextXP - currXP

		GameTooltip:AddLine(string.format("Pet XP: %d/%d (%.1f%%)", currXP, nextXP, currXP/nextXP*100))
		GameTooltip:AddLine("" .. toLevelXP .. " to level")
		GameTooltip:AddLine()

		local happiness, damagePercentage, loyaltyRate = GetPetHappiness()
		happiness = ({"|cffff0000Unhappy|r", "|cffffff00Content|r", "|cff00ff00Happy|r"})[happiness]
		local loyalty = loyaltyRate > 0 and "Gaining" or "Losing"

		GameTooltip:AddLine("Pet is " .. happiness .. ", doing " .. damagePercentage .. "% damage")
		GameTooltip:AddLine(loyalty .. " loyalty")
	end
	GameTooltip:Show()
end

function aUF.classes.aUFunitPet.prototype:UpdateAll()
	self.class.super.prototype.UpdateAll(self)
	self:UpdateXP()
end

---------------------
-- TARGET SUBCLASS --
---------------------

function aUF.classes.aUFunitCombo.prototype:init(name,unit,unitdb)
	aUF.classes.aUFunitCombo.super.prototype.init(self,name,unit,unitdb)

end

function aUF.classes.aUFunitCombo.prototype:ClassRegisterEvents(register)
	self:ObjectRegisterEvent("PLAYER_COMBO_POINTS","UpdateComboPoints")
	self:ObjectUnregisterEvent("PLAYER_TARGET_CHANGED")
	self:ObjectRegisterEvent("PLAYER_TARGET_CHANGED","TargetChanged")
end

function aUF.classes.aUFunitCombo.prototype:SetupFrame()
	self.class.super.prototype.SetupFrame(self)
	for i=1,5 do
		local frame = "Combo" .. i
		self[frame] = self.Overlays:CreateTexture(nil,"OVERLAY")
		self[frame]:SetTexture("Interface\\AddOns\\ag_UnitFrames\\images\\combo.tga")
		self[frame]:SetHeight(10)
		self[frame]:SetWidth(10)
		self[frame]:Hide()
		if i > 1 then
			self[frame]:SetPoint("BOTTOMRIGHT",self["Combo"..i-1],"BOTTOMLEFT")
		else
			self[frame]:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",-2,-1)
		end
	end
end

function aUF.classes.aUFunitCombo.prototype:UpdateComboPoints()
	local points = GetComboPoints()
	if self.flags.ComboGFX == true then
		for i=0,4 do
			if points > i then
				self["Combo"..i+1]:Show()
			else
				self["Combo"..i+1]:Hide()
			end
		end
	else
		if points == 0 then
			points = ""
		end
		self.ComboText:SetText(points)
	end
end

function aUF.classes.aUFunitCombo.prototype:TargetChanged()
	if UnitExists(self.unit) and self.frame.visible then
		self:UpdateAll()
	end
	self:UpdateComboPoints()
end

---------------------
-- FOCUS SUBCLASS --
---------------------

function aUF.classes.aUFunitFocus.prototype:init(name,unit,unitdb)
	aUF.classes.aUFunitFocus.super.prototype.init(self,name,unit,unitdb)
end

function aUF.classes.aUFunitFocus.prototype:ClassRegisterEvents()
	self:ObjectRegisterEvent("PLAYER_FOCUS_CHANGED","FocusChanged")
end

function aUF.classes.aUFunitFocus.prototype:FocusChanged()
	if self.exists then
		self:UpdateAll()
	end
end

-----------------------
-- ONUPDATE SUBCLASS --
-----------------------

function aUF.classes.aUFunitMetro.prototype:init(name,unit,unitdb)
	aUF.classes.aUFunitMetro.super.prototype.init(self,name,unit,unitdb)
	self.metro = true
end

function aUF.classes.aUFunitMetro.prototype:RegisterCasting()
	if self.database.CastBar and not self.castbarEventRegged then
		self.frame:SetScript("OnUpdate", function() self:SpellcastUpdate(arg1) end)
		self.castbarEventRegged = true
	elseif not self.database.CastBar then
		self.frame:SetScript("OnUpdate", nil)
		self.castbarEventRegged = nil
	end
end

function aUF.classes.aUFunitMetro.prototype:UpdateMetro(force)
	if force or self.frame:IsVisible() then
		self:UpdateHealth(true)
		self:UpdatePower(true)
		self:UpdateAuras(true)
		self:UpdateRaidTargetIcon(true)
		self:StatusBarsColor()
		self:UpdateTextStrings()
		self:UpdateCastBar()
		if self.database.Portrait and self.flags.Portrait and self.unitName ~= UnitName(self.unit) then
			self:UpdatePortrait()
		end
	end
	self.unitName = UnitName(self.unit)
end

function aUF.classes.aUFunitMetro.prototype:Start()
	if not self.database.HideFrame == true then
		if not self.schedule then
			self.schedule = "aUFunitMetro-" .. math.random()
			self:ScheduleRepeatingEvent(self.schedule, self.UpdateMetro, 0.5, self)
		end
		self:UpdateMetro()
	end
end

function aUF.classes.aUFunitMetro.prototype:Stop()
	if self.schedule and self:IsEventScheduled(self.schedule) then
		self:CancelScheduledEvent(self.schedule)
		self.schedule = nil
	end
	self:UpdateMetro()
end

function aUF.classes.aUFunitMetro.prototype:UpdateHighlight(entered)
	self.class.super.prototype.UpdateHighlight(self,entered)
	self:UpdateMetro(true)
end
--------------------------
-- UNITVANILLA SUBCLASS --
--------------------------

function aUF.classes.aUFunitVanilla.prototype:init(name,unit,unitdb)
	aUF.classes.aUFunitVanilla.super.prototype.init(self,name,unit,unitdb)
end

