-----------------------------
-- Configuration Functions --
-----------------------------

local ArcaneBars = {}

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

-- Registers frame to spellcast events.

local XPerl_ArcaneBar_Colors = {
	main = {r = 1.0, g = 0.7, b = 0.0},
	channel = {r = 0.0, g = 1.0, b = 0.0},
	success = {r = 0.0, g = 1.0, b = 0.0},
	failure = {r = 1.0, g = 0.0, b = 0.0}
}

local events = {"UNIT_SPELLCAST_START", "UNIT_SPELLCAST_STOP", "UNIT_SPELLCAST_FAILED", "UNIT_SPELLCAST_INTERRUPTED",
		"UNIT_SPELLCAST_DELAYED", "UNIT_SPELLCAST_CHANNEL_START", "UNIT_SPELLCAST_CHANNEL_UPDATE",
		"UNIT_SPELLCAST_CHANNEL_STOP", "PLAYER_ENTERING_WORLD"}

-- XPerl_ArcaneBar_Register(self)
function XPerl_ArcaneBar_Register(self)
	for i,event in pairs(events) do
		self:RegisterEvent(event)
	end
	self:SetScript("OnUpdate", XPerl_ArcaneBar_OnUpdate)
	if (self.unit == "target") then
		self:RegisterEvent("PLAYER_TARGET_CHANGED")
	elseif (self.unit == "focus") then
		self:RegisterEvent("PLAYER_FOCUS_CHANGED")
	end
end

-- Unregisters from spellcast events.
function XPerl_ArcaneBar_Unregister(self)
	for i,event in pairs(events) do
		self:UnregisterEvent(event)
	end
	self:SetScript("OnUpdate", nil)
end

-- XPerl_ArcaneBar_EnableToggle
function XPerl_ArcaneBar_EnableToggle(self, value)
	if (value == 1) then
		if (self.Enabled ~= 1) then
			XPerl_ArcaneBar_Register(self)
			self.Enabled = 1
		end
	else
		if (self.Enabled == 1) then
			XPerl_ArcaneBar_Unregister(self)
			self.Enabled = 0
			self:Hide()
		end
	end
end

-- XPerl_ArcaneBar_OverrideToggle
function XPerl_ArcaneBar_OverrideToggle(value)
	if (value == 1) then
		if (ArcaneBars.player and ArcaneBars.player.bar.Overrided ~= 1) then
			CastingBarFrame:Hide()
			for i,event in pairs(events) do
				CastingBarFrame:UnregisterEvent(event)
			end
			ArcaneBars.player.bar.Overrided = 1
		end
	else
		if (ArcaneBars.player and ArcaneBars.player.bar.Overrided == 1) then
			for i,event in pairs(events) do
				CastingBarFrame:RegisterEvent(event)
			end
			ArcaneBars.player.bar.Overrided = 0
		end
	end
end

--------------------------------------------------
--
-- Event/Update Handlers
--
--------------------------------------------------

-- XPerl_ArcaneBar_OnEvent
function XPerl_ArcaneBar_OnEvent(self, event, newarg1)
	if (event == "PLAYER_ENTERING_WORLD" or event == "PLAYER_TARGET_CHANGED" or event == "PLAYER_FOCUS_CHANGED") then
		local nameChannel = UnitChannelInfo(self.unit)
		local nameSpell = UnitCastingInfo(self.unit)
		if (nameChannel) then
			event = "UNIT_SPELLCAST_CHANNEL_START"
			newarg1 = self.unit
		elseif (nameSpell) then
			event = "UNIT_SPELLCAST_START"
			newarg1 = self.unit
		else
			self:Hide()
			self.castTimeText:Hide()
			self.barParentName:SetAlpha(conf.TextTransparency)
			self.barParentName:Show()
			return
		end
	end

	if (newarg1 ~= self.unit) then
		return
	end

	if (event == "UNIT_SPELLCAST_START") then
		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(self.unit)
		if ( not name or (not self.showTradeSkills and isTradeSkill)) then
			self:Hide()
			return
		end

		self:SetStatusBarColor(XPerl_ArcaneBar_Colors.main.r, XPerl_ArcaneBar_Colors.main.g, XPerl_ArcaneBar_Colors.main.b, conf.Transparency)
		self.spellText:SetText(name)
		self.barParentName:Hide()
		self.barSpark:Show()
		self.startTime = startTime / 1000
		self.maxValue = endTime / 1000
		self:SetMinMaxValues(self.startTime, self.maxValue)
		self:SetValue(self.startTime)
		self:SetAlpha(0.8)
		self.holdTime = 0
		self.casting = 1
		self.channeling = nil
		self.fadeOut = nil
		self.flash = nil
		self:Show()
		self.delaySum = 0
		if (conf.CastTime == 1) then
			self.castTimeText:Show()
		else
			self.castTimeText:Hide()
		end

	elseif ((event == "UNIT_SPELLCAST_STOP" and self.casting) or (event == "UNIT_SPELLCAST_CHANNEL_STOP" and self.channeling)) then
		self.delaySum = 0
		self.sign = "+"
		self.castTimeText:Hide()
		if (not self:IsVisible()) then
			self:Hide()
		end
		if (self:IsShown()) then
			self:SetValue(self.maxValue)
			self:SetStatusBarColor(XPerl_ArcaneBar_Colors.success.r, XPerl_ArcaneBar_Colors.success.g, XPerl_ArcaneBar_Colors.success.b, conf.Transparency)
			self.barSpark:Hide()
			self.barFlash:SetAlpha(0.0)
			self.barFlash:Show()
			self.casting = nil
			self.channeling = nil
			if (not self.fadeOut or event == "UNIT_SPELLCAST_CHANNEL_STOP") then
				self.flash = 1
			end
			self.fadeOut = 1
			self.holdTime = 0
		end

	elseif (event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED") then
		if (this:IsShown()) then
			if (event == "UNIT_SPELLCAST_FAILED") then
				self.spellText:SetText(FAILED)
			else
				self.spellText:SetText(SPELL_FAILED_INTERRUPTED)
			end

			self:SetValue(self.maxValue)
			self:SetStatusBarColor(XPerl_ArcaneBar_Colors.failure.r, XPerl_ArcaneBar_Colors.failure.g, XPerl_ArcaneBar_Colors.failure.b, conf.Transparency)
			self.barSpark:Hide()
			self.casting = nil
			self.channeling = nil
			if (not self.fadeOut) then
				self.flash = 1
			end
			self.fadeOut = 1
			self.holdTime = GetTime() + CASTING_BAR_HOLD_TIME
		end

	elseif (event == "UNIT_SPELLCAST_DELAYED") then
		if (self:IsShown()) then
			local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(self.unit)
			if (not name or (not self.showTradeSkills and isTradeSkill)) then
				-- if there is no name, there is no bar
				self:Hide()
				return;
			end
			self.startTime = startTime / 1000
			self.maxValue = endTime / 1000
			self:SetMinMaxValues(self.startTime, self.maxValue)
		end

	elseif (event == "UNIT_SPELLCAST_CHANNEL_START") then
		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo(self.unit)
		if (not name or (not self.showTradeSkills and isTradeSkill)) then
			-- if there is no name, there is no bar
			self:Hide()
			return
		end

		self:SetStatusBarColor(XPerl_ArcaneBar_Colors.channel.r, XPerl_ArcaneBar_Colors.channel.g, XPerl_ArcaneBar_Colors.channel.b, conf.Transparency)
		self.barSpark:Show()
		self.barParentName:Hide()
		self.spellText:SetText(name)
		self.maxValue = 1
		self.startTime = startTime / 1000
		self.endTime = endTime / 1000
		self:SetMinMaxValues(self.startTime, self.endTime)
		self:SetValue(self.endTime)
		self:SetAlpha(1.0)
		self.holdTime = 0
		self.casting = nil
		self.channeling = 1
		self.fadeOut = nil
		self.flash = nil
		self:Show()
		self.delaySum = 0
		if (conf.CastTime == 1) then
			self.castTimeText:Show()
		else
			self.castTimeText:Hide()
		end

	elseif (event == "UNIT_SPELLCAST_CHANNEL_UPDATE") then
		if (self:IsShown()) then
			local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo(self.unit)
			if (not name or (not self.showTradeSkills and isTradeSkill)) then
				-- if there is no name, there is no bar
				self:Hide()
				return;
			end
			self.startTime = startTime / 1000
			self.endTime = endTime / 1000
			self.maxValue = self.startTime
			self:SetMinMaxValues(self.startTime, self.endTime)
		end
	end

	if (not self:IsShown()) then
		self.castTimeText:Hide()
		self.barParentName:SetAlpha(conf.TextTransparency)
		self.barParentName:Show()
	end
end

-- XPerl_ArcaneBar_OnUpdate
function XPerl_ArcaneBar_OnUpdate(self)
	local getTime = GetTime()
	local current_time = self.maxValue - getTime
	if (self.channeling) then
		current_time = self.endTime - getTime
	end
	if (current_time < 0) then
		current_time = 0
	end
	local text = format("%.1f", current_time)

	self.castTimeText:SetText(text)

	if (self.casting) then
		local status = getTime
		if (status > self.maxValue) then
			status = self.maxValue

			self.casting = nil
			self.barFlash:SetAlpha(0.0)
			self.barFlash:Show()
			if (not self.fadeOut) then
				self.flash = 1
			end
			self.holdTime = 0
			self.fadeOut = 1
			return
		end
		self:SetValue(status)
		self.barFlash:Hide()

		local sparkPosition = ((status - self.startTime) / (self.maxValue - self.startTime)) * self:GetWidth()
		if (sparkPosition < 0) then
			sparkPosition = 0
		end
		self.barSpark:SetPoint("CENTER", self, "LEFT", sparkPosition, 1)

	elseif (self.channeling) then
		local time = getTime
		if (time > self.endTime) then
			time = self.endTime
		end
		if (time == self.endTime) then
			self.channeling = nil
			self.barFlash:SetAlpha(0.0)
			self.barFlash:Show()
			if (not self.fadeOut) then
				self.flash = 1
			end
			self.holdTime = 0
			self.fadeOut = 1
			return
		end
		local barValue = self.startTime + (self.endTime - time)
		self:SetValue( barValue )
		self.barFlash:Hide()

		local sparkPosition = ((barValue - self.startTime) / (self.endTime - self.startTime)) * self:GetWidth()
		self.barSpark:SetPoint("CENTER", self, "LEFT", sparkPosition, 1)

	elseif (getTime < self.holdTime) then
		return

	elseif (self.flash) then
		local alpha = self.barFlash:GetAlpha() + CASTING_BAR_FLASH_STEP
		if (alpha < 1) then
			self.barFlash:SetAlpha(alpha)
		else
			self.flash = nil
		end
	elseif (self.fadeOut) then
		local alpha = self:GetAlpha() - CASTING_BAR_ALPHA_STEP
		if (alpha > 0) then
			self:SetAlpha(alpha)
			self.barParentName:SetAlpha((1 - alpha) * conf.TextTransparency)
			self.barParentName:Show()
		else
			self.fadeOut = nil
			self:Hide()
		end
	end

	if (not self:IsShown()) then
		self.castTimeText:Hide()
		self.barParentName:SetAlpha(conf.TextTransparency)
		self.barParentName:Show()
	end
end

-- XPerl_ArcaneBar_OnLoad
function XPerl_ArcaneBar_OnLoad(self)
	XPerl_SetChildMembers(self)

	self.barFlash.tex:SetTexture("Interface\\AddOns\\XPerl\\Images\\XPerl_ArcaneBarFlash")
	self.tex:SetTexture("Interface\\AddOns\\XPerl\\Images\\XPerl_StatusBar")
	self.casting = nil
	self.holdTime = 0
	self.Enabled = 0
	self.Overrided = 0

	XPerl_RegisterBar(self)
end

-- XPerl_ArcaneBar_Set
function XPerl_ArcaneBar_Set()

	if (conf) then
		for k,v in pairs(ArcaneBars) do
			XPerl_ArcaneBar_EnableToggle(v.bar, conf[v.opt])
		end

		if (conf.OldCastBar == 0) then
			XPerl_ArcaneBar_OverrideToggle(1)
		else
			XPerl_ArcaneBar_OverrideToggle(0)
		end
	end
end

-- SetArcaneBar
local function SetArcaneBar(value, new)
	for k,v in pairs(ArcaneBars) do
		if (v.bar == new.bar) then
			ArcaneBars[k] = nil
		end
	end

	ArcaneBars[value] = new
end

-- XPerl_ArcaneBar_RegisterFrame
function XPerl_ArcaneBar_RegisterFrame(frame, unit, optionName)

	local function Setup(self, unit, sts)
		self.unit = unit
		self.showTradeSkills = sts
		self.barParentName = frame.text
		self:SetPoint("TOPLEFT", 4, -4)
		self:SetPoint("BOTTOMRIGHT", -4, 4)
	end

	local frameName = frame:GetName().."CastBar"
	local newFrame = getglobal(frameName)
	if (not newFrame) then
		newFrame = CreateFrame("StatusBar", frameName, frame, "XPerl_ArcaneBarTemplate")
	end

	Setup(newFrame, unit, true)

	SetArcaneBar(unit, {bar = newFrame, opt = optionName})

	XPerl_ArcaneBar_Set()
end

XPerl_RegisterOptionChanger(XPerl_ArcaneBar_Set, "ArcaneBar")
