Quantcast
-- this is for new blizzard style castbars that flash and stuff

-- table to hold castbars for all units
local _casters = {}

local _barFrames = {}

local _updateRaidSetup = true
local _raidMembers,_partyMembers

local _enabled = true
local _disableOnPEW = true

local _castBars = {}

local _redColor = {1,0,0}

local babbleSpells = BabbleLib:GetInstance('Spell 1.1')
local babbleCore = BabbleLib:GetInstance('Core 1.1')

local function _finishSpell(barSpark, barFlash)
	this:SetStatusBarColor(0.0, 1.0, 0.0);
	if ( barSpark ) then
		barSpark:Hide();
	end
	if ( barFlash ) then
		barFlash:SetAlpha(0.0);
		barFlash:Show();
	end
	this.flash = 1;
	this.fadeOut = 1;
	this.casting = nil;
	this.channeling = nil;
end

local function _onUpdate()
	local barSpark = this.spark
	local barFlash = this.barFlash
	local barTime = this.time

	if ( this.casting ) then
		local status = GetTime();
		if ( status > this.maxValue ) then
			status = this.maxValue;
		end
		if ( status == this.maxValue ) then
			this:SetValue(this.maxValue);
			_finishSpell();
			return;
		end
		this:SetValue(status);
		if ( barFlash ) then
			barFlash:Hide();
		end
		local sparkPosition = ((status - this.startTime) / (this.maxValue - this.startTime)) * this:GetWidth();
		if ( sparkPosition < 0 ) then
			sparkPosition = 0;
		end
		if ( barSpark ) then
			barSpark:SetPoint("CENTER", this, "LEFT", sparkPosition, 2);
		end
		barTime:SetText(string.format('%.1f',this.maxValue - status))
	elseif ( this.channeling ) then
		local time = GetTime();
		if ( time > this.endTime ) then
			time = this.endTime;
		end
		if ( time == this.endTime ) then
			_finishSpell();
			return;
		end
		local barValue = this.startTime + (this.endTime - time);
		this:SetValue( barValue );
		if ( barFlash ) then
			barFlash:Hide();
		end
		barTime:SetText(string.format('%.1f',this.endTime - time))
	elseif ( GetTime() < this.holdTime ) then
		return;
	elseif ( this.flash ) then
		local alpha = 0;
		if ( barFlash ) then
			alpha = barFlash:GetAlpha() + CASTING_BAR_FLASH_STEP;
		end
		if ( alpha < 1 ) then
			if ( barFlash ) then
				barFlash:SetAlpha(alpha);
			end
		else
			if ( barFlash ) then
				barFlash:SetAlpha(1.0);
			end
			this.flash = nil;
		end
	elseif ( this.fadeOut ) then
		local alpha = this:GetAlpha() - CASTING_BAR_ALPHA_STEP;
		if ( alpha > 0 ) then
			this:SetAlpha(alpha);
		else
			this.fadeOut = nil;
			this:Hide();
			HealWatch.RemoveCastBar(this.target,this)
			if (not this.unit) then return end
			for i,bar in pairs(_casters[this.unit]) do
				if (bar == this) then
					tremove(_casters[this.unit],i)
				end
			end
			this.unit = nil
			this.target = nil
			tinsert(_castBars,this)
		end
	end
end

local function _CreateCastBar(unit)
	local width = HealWatch.Options.labelWidth-10
	local frame = CreateFrame('StatusBar','HealWatchCastFrame'..#_barFrames+1)
	frame:SetHeight(18)
	frame:SetWidth(width)
	frame.width = width - 10
	frame:SetParent(HealWatchCastFrame)

	frame:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar","ARTWORK")
	frame:SetStatusBarColor(1, 0, 0, 1)
	frame.bar = frame

	-- transparent background
	local bgTexture = frame:CreateTexture(nil, "BACKGROUND")
	bgTexture:SetTexture(0,0,0,0.5)
	bgTexture:SetAllPoints(frame)
	frame.bgTexture = bgTexture

	local border = frame:CreateTexture(frame:GetName().."StatusBarBorder", "ARTWORK")
	border:SetTexture("Interface\\Tooltips\\UI-StatusBar-Border")
	border:SetWidth(width+5)
	border:SetHeight(23)
	border:SetPoint("CENTER", frame, "CENTER", 0, 0)
	frame.border = border

	local caster = frame:CreateFontString(nil, "ARTWORK")
	caster:Show()
	caster:SetFontObject(GameFontHighlight)
	caster:SetWidth(math.floor(0.75*width))
	caster:SetHeight(13)
	caster:ClearAllPoints()
	caster:SetPoint("LEFT", frame, "LEFT", 7, 0)
	caster:SetJustifyH("LEFT")
	caster:SetParent(frame)
	frame.caster = caster

	local time = frame:CreateFontString(nil, "ARTWORK")
	time:Show()
	time:SetFontObject(GameFontHighlight)
	time:SetWidth(math.floor(0.5*width))
	time:SetHeight(13)
	time:ClearAllPoints()
	time:SetPoint("RIGHT", frame, "RIGHT", -10, 0)
	time:SetJustifyH("RIGHT")
	time:SetParent(frame)
	frame.time = time

	local spark = frame:CreateTexture(nil, "OVERLAY")
	spark:SetWidth(32)
	spark:SetHeight(32)
	spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
	spark:Show()
	spark:SetBlendMode("ADD")
	spark:ClearAllPoints()
	spark:SetPoint("CENTER", frame, "LEFT", 0, 0)
	spark:SetParent(frame)
	frame.spark = spark

	local flash = frame:CreateTexture(nil,"OVERLAY")
	flash:SetWidth(width)
	flash:SetHeight(64)
	flash:SetTexture("Interface\\Tooltips\\UI-CastingBar-Flash")
	flash:SetBlendMode("ADD")
	flash:ClearAllPoints()
	flash:SetPoint("TOP",frame,"TOP",0,28)
	frame.barFlash = flash

	local icon = frame:CreateTexture(nil,"ARTWORK")
	icon:SetWidth(16)
	icon:SetHeight(16)
	icon:ClearAllPoints()
	icon:Hide()
	icon:SetPoint("RIGHT",frame,"LEFT",-5,0)
	frame.icon = icon

	frame.casting = nil;
	frame.channeling = nil;
	frame.holdTime = 0;
	frame.showCastbar = true;

	frame:SetScript("OnUpdate", _onUpdate)

	table.insert(_barFrames,frame)
	frame.inUse = true
	return frame
end

function HealWatch.UpdateBarWidths(width)
	local bar
	for i=1,#_barFrames do
		bar = _barFrames[i]
		bar:SetWidth(width-10)
		bar.caster:SetWidth(math.floor(0.75*width))
	end
end

local function _getCastBar()
	return tremove(_castBars) or _CreateCastBar()
end

local function _freeCastBar(bar)
	bar.unit = nil
	tinsert(_castBars,bar)
end

local function _tableCasters()
	local unit
	-- reset the group compositions
	_raidMembers = GetNumRaidMembers()
	_partyMembers = GetNumPartyMembers()
	if (_raidMembers > 0) then
		for i = 1,GetNumRaidMembers() do
			unit = 'raid'..i
			if (not _casters[unit]) then
				_casters[unit] = _CreateCastBar(unit)
			end
		end
		if (not isRaid) then isRaid = true end
	elseif (_partyMembers > 0) then
		for i=1,GetNumPartyMembers() do
			unit = 'party'..i
			if (not _casters[unit]) then
				_casters[unit] = _CreateCastBar(unit)
			end
		end
	end
	if (not _casters['player']) then
		_casters['player'] = _CreateCastBar('player')
	end
end

local function _onEvent(self,event,arg1,spell,rank,target)
	if (not _enabled or arg1 == "target") then return end
	if (event == "PLAYER_LOGIN") then
		--_tableCasters()
		if (_disableOnPEW) then _enabled = false end
		return
	elseif (event == "RAID_ROSTER_UPDATE" or event == "PARTY_MEMBERS_CHANGED") then
		--_tableCasters()
		return
	elseif (event == "UNIT_TARGET") then
		local unit = arg1
		if (unit == "player") then
			if (UnitExists('target') and UnitCanAssist('player','target')) then
				HealWatch.UpdateTarget(unit,UnitName('target'),'target')
			else
				HealWatch.UpdateTarget(unit)
			end
		else
			HealWatch.UpdateTarget(unit,nil,unit.."target")
		end
	end
	local newevent = event
	local newarg1 = arg1
	local uName = UnitName(arg1)

	local unit = arg1
	if (not _casters[unit]) then _casters[unit] = {} end
	if (event == "UNIT_SPELLCAST_SENT") then
		-- we're not reporting information yet, so no need to watch our own heals
		local curSpell = babbleSpells:GetEnglish(spell)
		if (not HealWatch.spells['watch'][curSpell]) then return end
		if (HealWatch.SendHWMessage) then HealWatch.SendHWMessage(target,curSpell) end
		if (HealWatch.spells['unique'][spell]) then
			HealWatch.customTarget = target
			HealWatch.UpdateTarget('player',target,nil,true)
		end
		return
	elseif (event == "UNIT_SPELLCAST_START") then
		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(unit);
		if ( not name or not HealWatch.spells['watch'][name]) then
			--DevTools_Dump("Spell not watched: "..tostring(name))
			return
		end
		local barSpark,barFlash,barIcon,caster,time,color
		if (not HealWatch.spells['unique'][name]) then
			local groupSetup = HealWatch.groupSetup
			if (not isRaid) then
				for i=1,GetNumPartyMembers() do
					local this = _getCastBar()
					this.unit = unit
					this.target = UnitExists('party'..i) and UnitName('party'..i)
					barSpark = this.spark
					barFlash = this.barFlash
					barIcon = this.icon
					caster = this.caster
					time = this.time
					color = HealWatch.spells.spellColors[name]
					if (unit == 'player' and HealWatch.Options.colorMyHeals) then color = _redColor end
					if (color) then this:SetStatusBarColor(color[1],color[2],color[3],1) end

					--this:SetStatusBarColor(1.0, 0.7, 0.0);
					if ( barSpark ) then barSpark:Show(); end
					this.startTime = startTime / 1000;
					this.maxValue = endTime / 1000;

					-- startTime to maxValue no endTime
					this:SetMinMaxValues(this.startTime, this.maxValue);
					this:SetValue(this.startTime);
					if ( caster ) then
						caster:SetText(string.format("%s: %s(%s)",uName,text,nameSubtext))
					end
					if ( barIcon ) then
						barIcon:SetTexture(texture);
					end
					this:SetAlpha(1.0);
					this.holdTime = 0;
					this.casting = 1;
					this.channeling = nil;
					this.fadeOut = nil;
					if ( this.showCastbar ) then
						HealWatch.InsertCastBar(this.target,this)
						this:Show()
					end
					tinsert(_casters[unit],this)
				end
				-- add one for oneself
				local this = _getCastBar()
				this.unit = unit
				this.target = UnitName('player')
				barSpark = this.spark
				barFlash = this.barFlash
				barIcon = this.icon
				caster = this.caster
				time = this.time
				color = HealWatch.spells.spellColors[name]
				if (unit == 'player' and HealWatch.Options.colorMyHeals) then color = _redColor end
				if (color) then this:SetStatusBarColor(color[1],color[2],color[3],1) end

				--this:SetStatusBarColor(1.0, 0.7, 0.0);
				if ( barSpark ) then barSpark:Show(); end
				this.startTime = startTime / 1000;
				this.maxValue = endTime / 1000;

				-- startTime to maxValue no endTime
				this:SetMinMaxValues(this.startTime, this.maxValue);
				this:SetValue(this.startTime);
				if ( caster ) then
					caster:SetText(string.format("%s: %s(%s)",uName,text,nameSubtext))
				end
				if ( barIcon ) then
					barIcon:SetTexture(texture);
				end
				this:SetAlpha(1.0);
				this.holdTime = 0;
				this.casting = 1;
				this.channeling = nil;
				this.fadeOut = nil;
				if ( this.showCastbar ) then
					HealWatch.InsertCastBar(this.target,this)
					this:Show()
				end
				tinsert(_casters[unit],this)
				return
			elseif (groupSetup[caster]) then
				local group = groupSetup[caster]["group"]
				if (groupSetup[group]) then
					for i=1,table.getn(groupSetup[group]) do
						local this = _getCastBar()
						this.unit = unit
						this.target = groupSetup[group][i]
						barSpark = this.spark
						barFlash = this.barFlash
						barIcon = this.icon
						caster = this.caster
						time = this.time
						color = HealWatch.spells.spellColors[name]
						if (unit == 'player' and HealWatch.Options.colorMyHeals) then color = _redColor end
						if (color) then this:SetStatusBarColor(color[1],color[2],color[3],1) end

						--this:SetStatusBarColor(1.0, 0.7, 0.0);
						if ( barSpark ) then barSpark:Show(); end
						this.startTime = startTime / 1000;
						this.maxValue = endTime / 1000;

						-- startTime to maxValue no endTime
						this:SetMinMaxValues(this.startTime, this.maxValue);
						this:SetValue(this.startTime);
						if ( caster ) then
							caster:SetText(string.format("%s: %s(%s)",uName,text,nameSubtext))
						end
						if ( barIcon ) then
							barIcon:SetTexture(texture);
						end
						this:SetAlpha(1.0);
						this.holdTime = 0;
						this.casting = 1;
						this.channeling = nil;
						this.fadeOut = nil;
						if ( this.showCastbar ) then
							HealWatch.InsertCastBar(this.target,this)
							this:Show()
						end
						tinsert(_casters[unit],this)
					end
					return
				end
			end
		end
		local this = _getCastBar()
		this.unit = unit
		barSpark = this.spark
		barFlash = this.barFlash
		barIcon = this.icon
		caster = this.caster
		time = this.time
		color = HealWatch.spells.spellColors[name]
		if (unit == 'player' and HealWatch.Options.colorMyHeals) then color = _redColor end
		if (color) then this:SetStatusBarColor(color[1],color[2],color[3],1) end

		--this:SetStatusBarColor(1.0, 0.7, 0.0);
		if ( barSpark ) then barSpark:Show(); end
		this.startTime = startTime / 1000;
		this.maxValue = endTime / 1000;

		-- startTime to maxValue no endTime
		this:SetMinMaxValues(this.startTime, this.maxValue);
		this:SetValue(this.startTime);
		if ( caster ) then
			caster:SetText(string.format("%s: %s(%s)",uName,text,nameSubtext))
		end
		if ( barIcon ) then
			barIcon:SetTexture(texture);
		end
		this:SetAlpha(1.0);
		this.holdTime = 0;
		this.casting = 1;
		this.channeling = nil;
		this.fadeOut = nil;
		if ( this.showCastbar ) then
			this.target = HealWatch.GetTarget(unit)
			HealWatch.InsertCastBar(this.target,this)
			this:Show()
		end
		tinsert(_casters[unit],this)
	elseif ( newevent == "UNIT_SPELLCAST_STOP" or newevent == "UNIT_SPELLCAST_CHANNEL_STOP" ) then
		local this
		for i,v in pairs(_casters[unit]) do
			this = _casters[unit][i]
			if ( not this:IsVisible() ) then
				this:Hide();
				HealWatch.RemoveCastBar(this.target,this)
				if (not this.unit) then return end
				for i,bar in pairs(_casters[this.unit]) do
					if (bar == this) then
						tremove(_casters[this.unit],i)
					end
				end
				this.unit = nil
				this.target = nil
				tinsert(_castBars,this)
			end
			if ( this:IsShown() ) then
				if ( barSpark ) then
					barSpark:Hide();
				end
				if ( barFlash ) then
					barFlash:SetAlpha(0.0);
					barFlash:Show();
				end
				this:SetValue(this.maxValue);
				if ( newevent == "UNIT_SPELLCAST_STOP" ) then
					this:SetStatusBarColor(0.0, 1.0, 0.0);
					this.casting = nil;
				else
					this.channeling = nil;
				end
				this.flash = 1;
				this.fadeOut = 1;
				this.holdTime = 0;
			end
		end
		if (unit == 'player') then HealWatch.customTarget = nil end
	elseif ( newevent == "UNIT_SPELLCAST_FAILED" or newevent == "UNIT_SPELLCAST_INTERRUPTED" ) then
		local this
		for i,v in pairs(_casters[unit]) do
			this = _casters[unit][i]
			local time = this.time
			if ( this:IsShown() and not this.channeling ) then
				this:SetValue(this.maxValue);
				this:SetStatusBarColor(1.0, 0.0, 0.0);
				if ( barSpark ) then
					barSpark:Hide();
				end
				if ( time ) then
					if ( newevent == "UNIT_SPELLCAST_FAILED" ) then
						time:SetText(FAILED);
					else
						time:SetText(INTERRUPTED);
					end
				end
				this.casting = nil;
				this.channeling = nil;
				this.fadeOut = 1;
				this.holdTime = GetTime() + CASTING_BAR_HOLD_TIME;
			end
		end
	elseif ( newevent == "UNIT_SPELLCAST_DELAYED" ) then
		local this
		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(this.unit);
		local watch = HealWatch.spells['watch'][name]
		for i,v in pairs(_casters[unit]) do
			this = _casters[unit][i]
			if ( this:IsShown() ) then
				if ( not name or not watch) then
					-- if there is no name, there is no bar
					this:Hide();
					HealWatch.RemoveCastBar(this.target,this)
					for i,bar in pairs(_casters[this.unit]) do
						if (bar == this) then
							tremove(_casters[this.unit],i)
						end
					end
					this.unit = nil
					this.target = nil
					tinsert(_castBars,this)
					return;
				end
				this.startTime = startTime / 1000;
				this.maxValue = endTime / 1000;
				this:SetMinMaxValues(this.startTime, this.maxValue);
				if ( not this.casting ) then
					this:SetStatusBarColor(1.0, 0.7, 0.0);
					if ( barSpark ) then
						barSpark:Show();
					end
					if ( barFlash ) then
						barFlash:SetAlpha(0.0);
						barFlash:Hide();
					end
					this.casting = 1;
					this.channeling = nil;
					this.flash = 0;
					this.fadeOut = 0;
				end
			end
		end
	elseif ( newevent == "UNIT_SPELLCAST_CHANNEL_START" ) then
		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo(unit);
		if ( not name or not HealWatch.spells['watch'][name]) then
			return
		end
		if (not _casters[arg1]) then
			_casters[arg1] = {}
		end
		if ( not name or not HealWatch.spells['watch'][name]) then
		-- if there is no name, there is no bar
			for i,bar in pairs(_casters[this.unit]) do
				if (bar == this) then
					tremove(_casters[this.unit],i)
				end
			end
			this.unit = nil
			this.target = nil
			tinsert(_castBars,this)
			return;
		end

		this:SetStatusBarColor(0.0, 1.0, 0.0);
		this.startTime = startTime / 1000;
		this.endTime = endTime / 1000;
		this.duration = this.endTime - this.startTime;
		this.maxValue = this.startTime;

		-- startTime to endTime no maxValue
		this:SetMinMaxValues(this.startTime, this.endTime);
		this:SetValue(this.endTime);
		if ( caster ) then
			caster:SetText(string.format("%s: %s(%s)",uName,text,nameSubtext))
		end
		if ( barIcon ) then
			barIcon:SetTexture(texture);
		end
		this:SetAlpha(1.0);
		this.holdTime = 0;
		this.casting = nil;
		this.channeling = 1;
		this.fadeOut = nil;
		if ( this.showCastbar ) then
			this.target = HealWatch.GetTarget(this.unit)
			HealWatch.InsertCastBar(this.target,this)
			this:Show();
		end
	elseif ( newevent == "UNIT_SPELLCAST_CHANNEL_UPDATE" ) then
		local this
		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo(unit);
		local watch = HealWatch.spells['watch'][name]
		for i,v in pairs(_casters[unit]) do
			this = _casters[unit][i]
			if ( this:IsShown() ) then
				if ( not name or not watch) then
					-- if there is no name, there is no bar
					this:Hide();
					HealWatch.RemoveCastBar(this.target,this)
					for i,bar in pairs(_casters[this.unit]) do
						if (bar == this) then
							tremove(_casters[this.unit],i)
						end
					end
					this.unit = nil
					this.target = nil
					tinsert(_castBars,this)
					return;
				end
				this.startTime = startTime / 1000;
				this.endTime = endTime / 1000;
				this.maxValue = this.startTime;
				this:SetMinMaxValues(this.startTime, this.endTime);
			end
		end
	end
end

function HealWatch.HW2Toggle(state)
	_enabled = state or not _enabled
	return _enabled
end

function HealWatch.DumpHW2Status()
	return _enabled
end

local tFrame = CreateFrame("Frame")
tFrame:SetScript("OnEvent",_onEvent)
tFrame:RegisterEvent("UNIT_SPELLCAST_SENT")
tFrame:RegisterEvent("UNIT_SPELLCAST_START")
tFrame:RegisterEvent("UNIT_SPELLCAST_STOP")
tFrame:RegisterEvent("UNIT_SPELLCAST_FAILED")
tFrame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
tFrame:RegisterEvent("UNIT_SPELLCAST_DELAYED")
tFrame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
tFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
tFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
tFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
tFrame:RegisterEvent("RAID_ROSTER_UPDATE")
tFrame:RegisterEvent("PARTY_MEMBERS_CHANGED")
tFrame:RegisterEvent("PLAYER_LOGIN")
tFrame:RegisterEvent("UNIT_TARGET")