--[[
	BCastBar
		A Bongos based cast bar
--]]

--[[ Casting Bar Functions ]]--

local function CastingBar_AdjustWidth(castingBar)
	local textWidth = getglobal(castingBar:GetName() .. "Text"):GetStringWidth();
	if getglobal(castingBar:GetName() .. "Time"):IsShown() then
		textWidth = textWidth + 36;
	end
	
	local diff = textWidth * 1.25 - castingBar.normalWidth;
	if diff > 0 then
		diff = textWidth * 1.25 - castingBar:GetWidth();
	else
		diff = castingBar.normalWidth - castingBar:GetWidth();
	end
		
	if diff ~= 0 then
		castingBar:GetParent():SetWidth(castingBar:GetParent():GetWidth() + diff);
		castingBar:SetWidth(castingBar:GetWidth() + diff);
		getglobal(castingBar:GetName() .. "Border"):SetWidth( getglobal(castingBar:GetName() .. "Border"):GetWidth() + (diff * 1.32) );
		getglobal(castingBar:GetName() .. "Flash"):SetWidth( getglobal(castingBar:GetName() .. "Flash"):GetWidth() + (diff * 1.32) );
	end
end

local function CastingBar_OnEvent()
	if( event == "SPELLCAST_START" ) then
		this:SetStatusBarColor(1.0, 0.7, 0.0);
		getglobal(this:GetName() .. "Spark"):Show();
		this.startTime = GetTime();
		this.maxValue = this.startTime + (arg2 / 1000);
		this:SetMinMaxValues(this.startTime, this.maxValue);
		this:SetValue(this.startTime);
		getglobal(this:GetName() .. "Text"):SetText(arg1);
		this:SetAlpha( this:GetParent():GetAlpha() );
		this.holdTime = 0;
		this.casting = 1;
		this.fadeOut = nil;
		this:Show();
		this.mode = "casting";
	elseif ( event == "SPELLCAST_STOP" or event == "SPELLCAST_CHANNEL_STOP" ) then
		if not this:IsVisible() then
			this:Hide();
			return;
		end
		if this:IsShown() then
			this:SetValue(this.maxValue);
			this:SetStatusBarColor(0.0, 1.0, 0.0);
			getglobal(this:GetName() .. "Spark"):Hide();
			getglobal(this:GetName() .. "Flash"):SetAlpha(0.0);
			getglobal(this:GetName() .. "Flash"):Show();
			if ( event == "SPELLCAST_STOP" ) then
				this.casting = nil;
			else
				this.channeling = nil;
			end
			this.flash = 1;
			this.fadeOut = 1;

			this.mode = "flash";
		end
	elseif ( event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED" ) then
		if this:IsShown() and not this.channeling then
			this:SetValue(this.maxValue);
			this:SetStatusBarColor(1.0, 0.0, 0.0);
			getglobal(this:GetName() .. "Spark"):Hide();
			if ( event == "SPELLCAST_FAILED" ) then
				getglobal(this:GetName() .. "Text"):SetText(FAILED);
			else
				getglobal(this:GetName() .. "Text"):SetText(INTERRUPTED);
			end
			this.casting = nil;
			this.fadeOut = 1;
			this.holdTime = GetTime() + CASTING_BAR_HOLD_TIME;
		end
	elseif ( event == "SPELLCAST_DELAYED" ) then
		if( this:IsShown() ) then
			this.startTime = this.startTime + (arg1 / 1000);
			this.maxValue = this.maxValue + (arg1 / 1000);
			this:SetMinMaxValues(this.startTime, this.maxValue);
		end
	elseif ( event == "SPELLCAST_CHANNEL_START" ) then
		this:SetStatusBarColor(1.0, 0.7, 0.0);
		getglobal(this:GetName() .. "Spark"):Show();
		this.maxValue = 1;
		this.startTime = GetTime();
		this.endTime = this.startTime + (arg1 / 1000);
		this.duration = arg1 / 1000;
		this:SetMinMaxValues(this.startTime, this.endTime);
		this:SetValue(this.endTime);
		getglobal(this:GetName() .. "Text"):SetText(arg2);
		this:SetAlpha( this:GetParent():GetAlpha() );
		this.holdTime = 0;
		this.casting = nil;
		this.channeling = 1;
		this.fadeOut = nil;
		this:Show();
	elseif ( event == "SPELLCAST_CHANNEL_UPDATE" ) then
		if ( this:IsShown() ) then
			local origDuration = this.endTime - this.startTime
			this.endTime = GetTime() + (arg1 / 1000)
			this.startTime = this.endTime - origDuration
			this:SetMinMaxValues(this.startTime, this.endTime);
		end
	end
end

local function CastingBar_OnUpdate()
	if ( this.casting ) then
		local status = GetTime();
		if ( status > this.maxValue ) then
			status = this.maxValue
		end
		this:SetValue(status);
		getglobal(this:GetName() .. "Time"):SetText(string.format("%.1f", this.maxValue - status));
		CastingBar_AdjustWidth(this);
		getglobal(this:GetName() .. "Flash"):Hide();
		local sparkPosition = ((status - this.startTime) / (this.maxValue - this.startTime)) * this:GetWidth();
		if ( sparkPosition < 0 ) then
			sparkPosition = 0;
		end
		getglobal(this:GetName() .. "Spark"):SetPoint("CENTER", this, "LEFT", sparkPosition, 2);
	elseif ( this.channeling ) then
		local time = GetTime();
		if time > this.endTime then
			time = this.endTime
		end
		if time == this.endTime then
			this.channeling = nil;
			this.fadeOut = 1;
			return;
		end
		getglobal(this:GetName() .. "Time"):SetText(string.format("%.1f", this.endTime - time));
		CastingBar_AdjustWidth(this);
		local barValue = this.startTime + (this.endTime - time);
		this:SetValue( barValue );
		getglobal(this:GetName() .. "Flash"):Hide();
		local sparkPosition = ((barValue - this.startTime) / (this.endTime - this.startTime)) * this:GetWidth();
		getglobal(this:GetName() .. "Spark"):SetPoint("CENTER", this, "LEFT", sparkPosition, 2);
	elseif GetTime() < this.holdTime then
		return;
	elseif this.flash then
		local alpha = getglobal(this:GetName() .. "Flash"):GetAlpha() + CASTING_BAR_FLASH_STEP * this:GetParent():GetAlpha();
		if alpha < this:GetParent():GetAlpha() then
			getglobal(this:GetName() .. "Flash"):SetAlpha(alpha);
		else
			getglobal(this:GetName() .. "Flash"):SetAlpha( this:GetParent():GetAlpha() );
			this.flash = nil;
		end
	elseif this.fadeOut then
		local alpha = this:GetAlpha() - CASTING_BAR_ALPHA_STEP * this:GetParent():GetAlpha();
		if alpha > 0 then
			this:SetAlpha(alpha);
		else
			this.fadeOut = nil;
			this:Hide();
		end
	end
end

local function CastingBar_RegisterEvents(castingBar)
	castingBar:RegisterEvent("SPELLCAST_START");
	castingBar:RegisterEvent("SPELLCAST_STOP");
	castingBar:RegisterEvent("SPELLCAST_FAILED");
	castingBar:RegisterEvent("SPELLCAST_INTERRUPTED");
	castingBar:RegisterEvent("SPELLCAST_DELAYED");
	castingBar:RegisterEvent("SPELLCAST_CHANNEL_START");
	castingBar:RegisterEvent("SPELLCAST_CHANNEL_UPDATE");
	castingBar:RegisterEvent("SPELLCAST_CHANNEL_STOP");
end

local function CreateCastingBar(parent)
	local castingBar = CreateFrame("StatusBar", parent:GetName() .. "CastBar", parent, "BongosCastingBarTemplate");
	castingBar.normalWidth = castingBar:GetWidth();
	castingBar.holdTime = 0;
	castingBar:SetScript("OnEvent", CastingBar_OnEvent);
	castingBar:SetScript("OnUpdate", CastingBar_OnUpdate);
	CastingBar_RegisterEvents(castingBar);
	
	return castingBar;
end

--[[ Config Functions ]]--

local function ShowText(castingBar)
	getglobal(castingBar:GetName() .. "Time"):Show();
	CastingBar_AdjustWidth(castingBar);
	BCastBar.sets.showText = 1;
end

local function HideText(castingBar)
	getglobal(castingBar:GetName() .. "Time"):Hide();
	CastingBar_AdjustWidth(castingBar);
	BCastBar.sets.showText = nil;
end

local function CreateConfigMenu(name)
	local menu = CreateFrame("Button", name, UIParent, "BongosRightClickMenu");
	menu:SetWidth(220);
	menu:SetHeight(150);
	menu:SetText("Cast Bar");
	
	local timeButton = CreateFrame("CheckButton", name .. "Time", menu, "BongosCheckButtonTemplate");
	timeButton:SetScript("OnClick", function()
		if this:GetChecked() then
			ShowText(getglobal("BCastBarCastBar"));
		else
			HideText(getglobal("BCastBarCastBar"));
		end
	end);
	timeButton:SetPoint("TOPLEFT", menu, "TOPLEFT", 8, -28);
	timeButton:SetText("Show Time");
	
	local scaleSlider = CreateFrame("Slider", name .. "Scale", menu, "BongosScaleSlider");
	scaleSlider:SetPoint("TOPLEFT", timeButton, "BOTTOMLEFT", 2, -10);
	
	local opacitySlider = CreateFrame("Slider", name .. "Opacity", menu, "BongosOpacitySlider");
	opacitySlider:SetPoint("TOP", scaleSlider, "BOTTOM", 0, -24);
end

local function ShowMenu(bar)
	if not BongosCastBarMenu then
		CreateConfigMenu("BongosCastBarMenu");
	end
	
	BongosCastBarMenu.onShow = 1;
	BongosCastBarMenu.frame = bar;
	
	BongosCastBarMenuTime:SetChecked(bar.sets.showText);
	BongosCastBarMenuScale:SetValue(bar:GetScale() * 100);
	BongosCastBarMenuOpacity:SetValue(bar:GetAlpha() * 100);
	
	BMenu.ShowMenuForBar(BongosCastBarMenu, bar);
	BongosCastBarMenu.onShow = nil;
end

--[[ Startup ]]--

BScript.AddStartupAction(function()
	--get rid of the default casting bar
	CastingBarFrame:UnregisterAllEvents();
	CastingBarFrame:Hide();
		
	local bar = BBar.Create("cast", "BCastBar", "BCastBarSets", ShowMenu, 1);
	bar:SetWidth(212);
	bar:SetHeight(30);
	
	if not getglobal("BCastBarCastingBar") then
		local castingBar = CreateCastingBar(bar);
		castingBar:SetPoint("TOPLEFT", bar, "TOPLEFT", 8, -12);
	end
	
	if not bar:IsUserPlaced() then
		bar:SetPoint("CENTER", UIParent, "CENTER", 0, 20);
	end
end)