local conf
XPerl_RequestConfig(function(new) conf = new end)

------------------------------------------------------------------------------
-- XPerl_Highlight
------------------------------------------------------------------------------

local hotSpells  = {["Rejuvenation"] = 12, ["Regrowth"] = 21, ["Renew"] = 15}
local shieldSpells = {["Power Word: Shield"] = 15}		-- Lasts 30 seconds, but can recast after 15 secs.
local healSpells = {["Flash Heal"] = 1, ["Greater Heal"] = 3, ["Lesser Heal"] = 2, ["Heal"] = 2, ["Healing Touch"] = 2, ["Regrowth"] = 2, ["Healing Wave"] = 3, ["Lesser Healing Wave"] = 2, ["Flash of Light"] = 1, ["Holy Light"] = 2}
local buffSpells = {PRIEST = {["Power Word: Fortitude"] = true, ["Prayer of Fortitude"] = true},
		    DRUID = {["Mark of the Wild"] = true, ["Gift of the Wild"] = true},
		    MAGE = {["Arcane Intellect"] = true, ["Arcane Brilliance"] = true},}
if (GetLocale() == "deDE") then
	hotSpells = {["Verj\195\188ngung"] = 12, ["Nachwachsen"] = 21, ["Erneuerung"] = 15}
	healSpells = {["Blitzheilung"] = 1, ["Gro\195\159e Heilung"] = 3, ["Geringes Heilen"] = 2, ["Heilen"] = 2, ["Heilende Ber\195\188hrung"] = 2, ["Nachwachsen"] = 2, ["Welle der Heilung"] = 3, ["Geringe Welle der Heilung"] = 2, ["Lichtblitz"] = 1, ["Heiliges Licht"] = 2}
	buffSpells = {PRIEST = {["Machtwort: Seelenst\195\164rke"] = true, ["Gebet der Seelenst\195\164rke"] = true},
		      DRUID = {["Mal der Wildnis"] = true, ["Gabe der Wildnis"] = true},
		      MAGE = {["Arkane Intelligenz"] = true, ["Arkane Brillanz"] = true},}
elseif (GetLocale() == "frFR") then
	hotSpells = {["R\195\169cup\195\169ration"] = 12, ["R\195\169tablissement"] = 21, ["R\195\169novation"] = 15}
	healSpells = {["Soins rapides"] = 1, ["Soins sup\195\169rieurs"] = 3, ["Soins inf\195\169rieurs"] = 2, ["Soins"] = 2, ["Toucher gu\195\169risseur"] = 2, ["R\195\169tablissement"] = 2, ["Vague de soins"] = 3, ["Vague de soins inf\195\169rieurs"] = 2, ["Eclair lumineux"] = 1, ["Lumi\195\168re sacr\195\169e"] = 2}
	buffSpells = {PRIEST = {["Mot de pouvoir\194\160: Robustesse"] = true, ["Pri\195\168re de robustesse"] = true},
		      DRUID = {["Marque du fauve"] = true, ["Don du fauve"] = true},
		      MAGE = {["Intelligence des arcanes"] = true, ["Illumination des arcanes"] = true},}
elseif (GetLocale() == "zhCN") then
	hotSpells = {["回春术"] = 12, ["愈合"] = 21, ["恢复"] = 15}
	healSpells = {["快速治疗"] = 1, ["强效治疗术"] = 3, ["次级治疗术"] = 2, ["治疗术"] = 2, ["治疗之触"] = 2, ["愈合"] = 2, ["治疗波"] = 3, ["次级治疗波"] = 2, ["圣光闪现"] = 1, ["圣光术"] = 2}
	buffSpells = {PRIEST = {["真言术：韧"] = true, ["坚韧祷言"] = true},
		      DRUID = {["野性印记"] = true, ["野性赐福"] = true},
		      MAGE = {["奥术智慧"] = true, ["奥术光辉"] = true},}
elseif (GetLocale() == "zhTW") then
	hotSpells = {["回春術"] = 12, ["癒合"] = 21, ["恢復"] = 15}
	healSpells = {["快速治療"] = 1, ["強效治療術"] = 3, ["次級治療術"] = 2, ["治療術"] = 2, ["治療之觸"] = 2, ["癒合"] = 2, ["治療波"] = 3, ["次級治療波"] = 2, ["聖光閃現"] = 1, ["聖光術"] = 2}
	buffSpells = {PRIEST = {["真言術：韌"] = true, ["堅韌禱言"] = true},
		      DRUID = {["野性印記"] = true, ["野性賜福"] = true},
		      MAGE = {["祕法智慧"] = true, ["祕法光輝"] = true},}
elseif (GetLocale() == "koKR") then
	hotSpells = {["회복"] = 12, ["재생"] = 21, ["소생"] = 15}
	healSpells = {["순간 치유"] = 1, ["상급 치유"] = 3, ["하급 치유"] = 2, ["치유"] = 2, ["치유의 손길"] = 2, ["재생"] = 2, ["치유의 물결"] = 3, ["하급 치유의 물결"] = 2, ["빛의 섬광"] = 1, ["성스러운 빛"] = 2}
	buffSpells = {PRIEST = {["신의 권능: 인내"] = true, ["인내의 기원"] = true},
		      DRUID = {["야생의 징표"] = true, ["야생의 선물"] = true},
		      MAGE = {["신비한 지능"] = true, ["신비한 총명함"] = true},}
end

local colours = {HOT = {r = 0.2, g = 0.4, b = 0.8, canFlash = true},
		 SHIELD = {r = 0.6, g = 0.1, b = 0.6, canFlash = true},
		 MISSING = {r = 0.5, g = 0.5, b = 0.5},
		 AGGRO = {r = 0.8, g = 0, b = 0},
		 HEAL = {r = 0.2, g = 0.8, b = 0.2},
		 SEL = {r = 0.86, g = 0.82, b = 0.41},
}

-- XPerl_Highlight(name, highlightType, duration)
local xpHigh = CreateFrame("Frame", "XPerl_Highlight")
xpHigh.list = {}
xpHigh.callbacks = {}
xpHigh.lastExpireCheck = 0
xpHigh.flashers = {}

-- XPerl_Highlight:Add
function xpHigh:Add(name, highlightType, duration)
	if (not self.list[name]) then
		self.list[name] = {}
	end
	local a = self.list[name]

	if (not duration) then
		a[highlightType] = nil
		self:Send(name)
	else
		if (duration == 0) then
			a[highlightType] = 0
			self:Send(name)
		else
			local newEndTime = GetTime() + duration
			local current = a[highlightType]

			if (not current or current < newEndTime) then
				a[highlightType] = newEndTime
				self:Send(name)
			end
		end
	end
end

-- xpHigh:TooltipInfo
function xpHigh:TooltipInfo(name)
	local effects = self.list[name]
	if (effects) then
		local str = ""
		local prefix = XPERL_LOC_STATUSTIP
		for k,v in pairs(effects) do
			local desc = XPERL_LOC_STATUSTIPLIST[k]
			local c = colours[k]
			if (desc and c) then
				str = str..prefix..format("|c00%02X%02X%02X", 255 * c.r, 255 * c.g, 255 * c.b)..desc.."|r"
				prefix = ", "
			end
		end

		if (prefix == ", ") then
			GameTooltip:AddLine("\r"..str)
			GameTooltip:Show()
		end
	end
end

-- xpHigh:Remove
function xpHigh:Remove(name, highlightType)
	local a = self.list[name]
	if (a) then
		a[highlightType] = nil
		self:Send(name)
	end
end

-- xpHigh:HasEffect
function xpHigh:HasEffect(name, effect)
	local list = self.list[name]
	if (list) then
		return list[effect]
	end
end

-- xpHigh:OnUpdate
function xpHigh:OnUpdate(elapsed)
	self.lastExpireCheck = self.lastExpireCheck + elapsed
	if (self.lastExpireCheck > 1) then
		self.lastExpireCheck = 0
	else
		return
	end

	-- We expire things 1 second early so people can cast in time for buff to expire
	local now = GetTime() + 1

	for playerName,list in pairs(self.list) do
		local any
		for k,v in pairs(list) do
			any = true
			if (v > 0) then
				if (v <= now) then
					self.flashers[playerName] = nil
					list[k] = nil
					self:Send(playerName)

				elseif (v <= now + 5 and colours[k].canFlash) then
					if (not self.flashers[playerName]) then
						self.flashers[playerName] = k
						self:Send(playerName)
					end
				end
			end
		end

		if (not any) then
			self.list[playerName] = nil
			self.flashers[playerName] = nil
		end
	end
end

-- xpHigh:ValidateInRaidOrParty()
function xpHigh:ValidateInRaidOrParty()
	if (GetNumRaidMembers() > 0 and XPerl_GetRaidPosition) then
		for playerName,list in pairs(self.list) do
			if (not XPerl_GetRaidPosition(playerName)) then
				self.list[playerName] = nil
				self.flashers[playerName] = nil
				self:Send(playerName)
			end
		end
	elseif (GetNumPartyMembers() > 0) then
		local partyNames = {}
		for i = 1,GetNumPartyMembers() do
			partyNames[UnitName("party"..i)] = true
		end
		for playerName,list in pairs(self.list) do
			if (not partyNames[playerName]) then
				self.list[playerName] = nil
				self.flashers[playerName] = nil
				self:Send(playerName)
			end
		end
	end
end

-- xpHigh:Query
function xpHigh:SetHighlight(frame, unitName)
	if (not frame.highlight) then
		XPerl_Notice("No .highlight region for "..frame:GetName()..", unit: "..unitName)
		return
	end

	if (not unitName) then
		local id = SecureButton_GetUnit(frame)
		if (id) then
			unitName = UnitName(id)
		end
	end

	self:OnUpdate(0)

	if (unitName and conf.RaidHighlights == 1) then
		local r = self.list[unitName]
		if (r) then
			local r1, g1, b1, r2, g2, b2
			for k,v in pairs(r) do
				if (not frame.IgnoreHighlightStates or not frame.IgnoreHighlightStates[k]) then
					if (not r1) then
						r1, g1, b1 = colours[k].r, colours[k].g, colours[k].b
					else
						r2, g2, b2 = colours[k].r, colours[k].g, colours[k].b
						break
					end
				end
			end

			if (r1) then
				if (frame.highlight.sel) then
					r1 = min(r1 + 0.2, 1)
					g1 = min(g1 + 0.2, 1)
					b1 = min(b1 + 0.2, 1)
				end

				if (r2) then
					if (frame.highlight.sel) then
						r2 = min(r2 + 0.2, 1)
						g2 = min(g2 + 0.2, 1)
						b2 = min(b2 + 0.2, 1)
					end

					frame.highlight.tex:SetGradient("HORIZONTAL", r1, g1, b1, r2, g2, b2)
				else
					frame.highlight.tex:SetVertexColor(r1, g1, b1)
				end
				frame.highlight:Show()
				frame.highlight:SetAlpha(1)

				if (self.flashers[unitName]) then
					UIFrameFlash(frame.highlight, 0.5, 0.5, 5, 1)
				end
				return
			end
		end
	end

	if (UIFrameIsFlashing(frame.highlight)) then
		UIFrameFlashStop(frame.highlight)
	end

	frame.highlight.tex:SetVertexColor(0.86, 0.82, 0.41)	-- Default selection colour

	if (frame.highlight.sel) then
		frame.highlight:Show()
		frame.highlight:SetAlpha(1)
	else
		frame.highlight:Hide()
	end

end

-- xpHigh:ClearAll
function xpHigh:ClearAll(clearType)
	for k,v in pairs(self.list) do
		if (v[clearType]) then
			v[clearType] = nil
			self:Send(k)
		end
	end
end

-- xpHigh:Send
function xpHigh:Send(name)
	for k,v in pairs(self.callbacks) do
		v(name)
	end
end

-- XPerl_RegisterFrameHighlighter
function xpHigh:Register(callback)
	tinsert(self.callbacks, callback)
end

-- xpHigh:OnEvent
function xpHigh:OnEvent(event, a, b, c, d)
	local func = self[event]
	if (func) then
		func(self, a, b, c, d)
	end
end

-- xpHigh:UNIT_SPELLCAST_SENT
function xpHigh:UNIT_SPELLCAST_SENT(unit, spell, rank, targetName)
	if (hotSpells[spell]) then
		self.castSpell = spell
		self.castTarget = targetName
	elseif (shieldSpells[spell]) then
		self.castSpell = spell
		self.castTarget = targetName
	end
end

-- xpHigh:UNIT_SPELLCAST_SUCCEEDED
function xpHigh:UNIT_SPELLCAST_SUCCEEDED(unit, spell, rank)
	if (spell == self.castSpell) then
		if (hotSpells[spell]) then
			if (conf.RaidHighlightHoTs == 1) then
				xpHigh:Add(self.castTarget, "HOT", hotSpells[spell])
			end
		elseif (shieldSpells[spell]) then
			if (conf.RaidHighlightShields == 1) then
				xpHigh:Add(self.castTarget, "SHIELD", shieldSpells[spell])
			end
		end
	end
	self.castSpell = nil
	self.castTarget = nil
end

-- xpHigh:UNIT_SPELLCAST_START
-- Removed for now since we can't determine the heal target
--function xpHigh:UNIT_SPELLCAST_START(unit)
--	local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(unit)
--	if (healSpells[name] and not UnitIsUnit(unit, "player")) then
--		if (UnitName(unit.."target") and not UnitIsDead(unit.."target") and UnitIsFriend("player", unit.."target")) then
--			xpHigh:Add(targetName, "HEAL", healSpells[name])
--		end
--	end
--end

local myBuffSpells

-- xpHigh:UNIT_AURA
function xpHigh:UNIT_AURA(unit)

	local isBuffed
	if (conf.RaidHighlightMissingAll == 1 and GetNumRaidMembers() > 0 and (GetNumWorldStateUI() == 0 or not IsInInstance())) then
		if (UnitInRaid(unit)) then
			local pBuff = not self.buffClasses or not self.buffClasses.PRIEST
			local dBuff = not self.buffClasses or not self.buffClasses.DRUID
			local mBuff = not self.buffClasses or not self.buffClasses.MAGE

			local buffs = 0
			for i = 1,20 do
				local name, rank, tex = UnitBuff(unit, i)
				if (not name) then
					break
				end

				if (buffSpells.PRIEST[name]) then
					pBuff = true
				elseif (buffSpells.DRUID[name]) then
					dBuff = true
				elseif (buffSpells.MAGE[name]) then
					mBuff = true
				end
			end

			-- Special case, don't count non mana users
			local _, class = UnitClass(unit)
			if (class == "ROGUE" or class == "WARRIOR") then
				mBuff = true
			end

			isBuffed = pBuff and dBuff and mBuff
		end
	else
		if (not myBuffSpells) then
			return		-- I can't buff!
		end

		if (GetNumWorldStateUI() > 0 and IsInInstance()) then
			return		-- Don't bother if in a PVP zone
		end

		if (UnitInRaid(unit) or UnitInParty(unit)) then
			if (playerClass == "MAGE") then
				-- Special case, don't count non mana users
				local _, class = UnitClass(unit)
				if (class == "ROGUE" or class == "WARRIOR") then
					isBuffed = true
				end
			end

			if (not isBuffed) then
				for i = 1,20 do
					local name, rank, tex = UnitBuff(unit, i)
					if (not name) then
						break
					end

					-- Check for max rank
					if (myBuffSpells[name]) then
						isBuffed = true
						break
					end
				end
			end
		end
	end

	local name = UnitName(unit)
	if (self:HasEffect(name, "MISSING")) then
		if (isBuffed) then
			self:Remove(name, "MISSING")
		end
	else
		if (not isBuffed) then
			self:Add(name, "MISSING", 0)
		end
	end
end

-- xpHigh:RAID_ROSTER_UPDATE
function xpHigh:RAID_ROSTER_UPDATE()
	if (self:GetBuffClasses()) then
		self:RefreshAllAuras()
	end
end

-- xpHigh:GetBuffClasses
function xpHigh:GetBuffClasses()
	local oldBuffClasses = self.buffClasses
	self.buffClasses = {}
	if (GetNumRaidMembers() > 0) then
		for i = 1,GetNumRaidMembers() do
			local _, c = UnitClass("raid"..i)
			if (buffSpells[c]) then
				self.buffClasses[c] = true
			end
		end
	else
		if (buffSpells[playerClass]) then
			self.buffClasses[playerClass] = true
		end
		for i = 1,GetNumPartyMembers() do
			local _, c = UnitClass("party"..i)
			if (buffSpells[c]) then
				self.buffClasses[c] = true
			end
		end
	end

	if (oldBuffClasses) then
		return self.buffClasses.PRIEST ~= oldBuffClasses.PRIEST or
			self.buffClasses.DRUID ~= oldBuffClasses.DRUID or
			self.buffClasses.MAGE ~= oldBuffClasses.MAGE
	end

	return true
end

-- xpHigh:RefreshAllAuras
function xpHigh:RefreshAllAuras()

	if (GetNumRaidMembers() > 0) then
		for i = 1,GetNumRaidMembers() do
			self:UNIT_AURA("raid"..i)
		end
	else
		self:UNIT_AURA("player")
		for i = 1,GetNumPartyMembers() do
			self:UNIT_AURA("party"..i)
		end
	end
end

-- xpHigh:OptionChange
function xpHigh:OptionChange()
	local events

	local _
	_, playerClass = UnitClass("player")
	myBuffSpells = buffSpells[playerClass]

	if (conf.RaidHighlights == 1 and (conf.RaidHighlightHoTs == 1 or conf.RaidHighlightShields == 1)) then
		events = true
		self:RegisterEvent("UNIT_SPELLCAST_SENT")
		self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
	else
		self:UnregisterEvent("UNIT_SPELLCAST_SENT")
		self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED")
		self:ClearAll("HOT")
		self:ClearAll("SHIELD")
	end

	if (conf.RaidHighlights == 1 and (conf.RaidHighlightMissingAll == 1 or (conf.RaidHighlightMissing == 1 and myBuffSpells))) then
		events = true
		self:RegisterEvent("UNIT_AURA")
		if (conf.RaidHighlightMissingAll == 1) then
			self:RegisterEvent("RAID_ROSTER_UPDATE")
		else
			self:UnregisterEvent("RAID_ROSTER_UPDATE")
		end
		--self:RegisterEvent("PLAYER_ENTERING_WORLD")
		self:GetBuffClasses()
		self:RefreshAllAuras()
	else
		self:ClearAll("MISSING")
		self:UnregisterEvent("UNIT_AURA")
		self:UnregisterEvent("RAID_ROSTER_UPDATE")
		--self:UnregisterEvent("PLAYER_ENTERING_WORLD")
	end

	if (events) then
		self:SetScript("OnEvent", self.OnEvent)
	end

	if (conf.RaidHighlights == 1 and (conf.RaidHighlightHoTs == 1 or conf.RaidHighlightShields == 1)) then
		self:SetScript("OnUpdate", self.OnUpdate)
	else
		self:SetScript("OnUpdate", nil)
	end
end

XPerl_RegisterOptionChanger(xpHigh.OptionChange, xpHigh)
