--[[
	PartyCastingBars
	 	Adds party member casting bars to their unit frames.
	
	By: AnduinLothar
	
	
	$Id: PartyCastingBars.lua 4157 2006-10-13 20:12:04Z geowar $
	$Rev: 4157 $
	$LastChangedBy: geowar $
	$Date: 2006-10-13 13:12:04 -0700 (Fri, 13 Oct 2006) $
	
]]--

--------------------------------------------------
-- Globals
--------------------------------------------------
PartyCastingBars = {};

PartyCastingBars.ColorResetting = {};
PartyCastingBars.ColorResetting.FriendlyColors = {};
PartyCastingBars.ColorResetting.HostileColors = {};

PartyCastingBars.Bars = {};

PartyCastingBars.DefaultFriendlyColors = {
	["CAST"] = {	--Light Blue
		r=0.0;
		g=0.7;
		b=1.0;		
	};
	["CHANNEL"] = {	--Green
		r=0.0;
		g=1.0;
		b=0.0;
	};
	["SUCCESS"] = {	--Green
		r=0.0;
		g=1.0;
		b=0.0;
	};
	["FAILURE"] = {	--Red
		r=1.0;
		g=0.0;
		b=0.0;
	};	
};
PartyCastingBars.DefaultHostileColors = {
	["CAST"] = {	--Orange
		r=1.0;
		g=0.5;
		b=0.1;
	};
	["CHANNEL"] = {	--Orange
		r=1.0;
		g=0.6;
		b=0.2;
	};
	["SUCCESS"] = {	--Green
		r=0.0;
		g=1.0;
		b=0.0;
	};
	["FAILURE"] = {	--Red
		r=1.0;
		g=0.0;
		b=0.0;
	};	
};

PartyCastingBars.TIME_LEFT = "(%.1fs)";
PartyCastingBars.SPELL_AND_TARGET = "%s - %s";
PartyCastingBars.COMM_FORMAT = "%s,%s,%s"; -- spellname, targetname, hostile/friendly

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

PartyCastingBars_FriendlyColors = {
	["CAST"] = {	--Yellow
		r=1.0;
		g=0.7;
		b=0.0;		
	};
	["CHANNEL"] = {	--Green
		r=0.0;
		g=1.0;
		b=0.0;
	};
	["SUCCESS"] = {	--Green
		r=0.0;
		g=1.0;
		b=0.0;
	};
	["FAILURE"] = {	--Red
		r=1.0;
		g=0.0;
		b=0.0;
	};	
};
PartyCastingBars_HostileColors = {
	["CAST"] = {	--Orange
		r=1.0;
		g=0.5;
		b=0.1;
	};
	["CHANNEL"] = {	--Orange
		r=1.0;
		g=0.6;
		b=0.2;
	};
	["SUCCESS"] = {
		r=0.0;
		g=1.0;
		b=0.0;
	};
	["FAILURE"] = {
		r=1.0;
		g=0.0;
		b=0.0;
	};	
};

--------------------------------------------------
-- Party Member Caching
--------------------------------------------------

PartyCastingBars.PartyMembers = {}
PartyCastingBars.HostilityCache = {}

function PartyCastingBars.CachePartyMembers()
	PartyCastingBars.PartyMembers = {}
	for i=1, 4 do
		local unit = "party"..i
		if (UnitExists(unit)) then
			local name = UnitName(unit)
			PartyCastingBars.PartyMembers[name] = i
			PartyCastingBars.HostilityCache[name] = UnitCanAttack("player", unit)
		end
	end
	if (UnitInRaid("player")) then
		for i=1, 40 do
			local unit = "raid"..i
			if (UnitExists(unit)) then
				local name = UnitName(unit)
				PartyCastingBars.HostilityCache[name] = UnitCanAttack("player", unit)
			end
		end
	end
end


--------------------------------------------------
-- Color Management
--------------------------------------------------

function PartyCastingBars.GetColorArgs(database)
	return database.r, database.g, database.b
end

--------------------------------------------------
-- Events
--------------------------------------------------

function PartyCastingBars.EventFrameOnLoad()
	this:RegisterEvent("VARIABLES_LOADED")
	this:RegisterEvent("PLAYER_ENTERING_WORLD")
	this:RegisterEvent("PARTY_MEMBERS_CHANGED")
	this:RegisterEvent("PLAYER_TARGET_CHANGED")
	this:RegisterEvent("UNIT_SPELLCAST_SENT")
	this:RegisterEvent("CHAT_MSG_ADDON")
end

function PartyCastingBars.EventFrameOnEvent(event, newarg1, newarg2, newarg3, newarg4)
	if ( event == "VARIABLES_LOADED" ) then
		PartyCastingBars.RegisterConfig()
		
	elseif ( event == "PLAYER_ENTERING_WORLD" ) then
		PartyCastingBars.CachePartyMembers()
		
	elseif ( event == "PARTY_MEMBERS_CHANGED" ) then
		PartyCastingBars.CachePartyMembers()
		
	elseif ( event == "PLAYER_TARGET_CHANGED" ) then
		if (UnitExists("target")) then
			PartyCastingBars.HostilityCache[UnitName("target")] = UnitCanAttack("player", "target")
		end
		
	elseif ( event == "UNIT_SPELLCAST_SENT" ) then
		-- "player", spell, rank, target
		--Sea.io.printComma(newarg1, newarg2, newarg3, newarg4)
		if (newarg2 and newarg4) then
			if (PartyCastingBars.HostilityCache[newarg4]) then
				ChatThrottleLib:SendAddonMessage("ALERT", "PartyCastingBars", format(PartyCastingBars.COMM_FORMAT, newarg2, newarg4, "hostile"), "PARTY");
			else
				ChatThrottleLib:SendAddonMessage("ALERT", "PartyCastingBars", format(PartyCastingBars.COMM_FORMAT, newarg2, newarg4, "friendly"), "PARTY");
			end
		end
		
	elseif ( event == "CHAT_MSG_ADDON" ) then
		-- prefix, msg, method, sender
		if (newarg1 == "PartyCastingBars") then
			--Sea.io.printComma(format("[%s]<%s>[%s]: %s", newarg3, newarg1, newarg4, newarg2))
			local partyNum = PartyCastingBars.PartyMembers[newarg4]
			if (partyNum) then
				local _, _, spellName, targetName, relationship = strfind(newarg2, "^(.+),(.+),(.+)$")
				if (spellName and targetName and relationship) then
					local frame = getglobal("PartyMemberFrame"..partyNum.."CastingBarFrame")
					frame.targetName = targetName
					frame.hostileCast = (relationship == "hostile")
					PartyCastingBars.OnEvent(frame, "UNIT_SPELLCAST_TARGET_CHANGED", "party"..partyNum, spellName)
				end
			end
		end
		
	end
end

--------------------------------------------------
-- Bar Scripts
--------------------------------------------------

function PartyCastingBars.OnLoad()
	-- Modified from CastingBarFrame_OnLoad
	this:RegisterEvent("UNIT_SPELLCAST_START");
	this:RegisterEvent("UNIT_SPELLCAST_STOP");
	this:RegisterEvent("UNIT_SPELLCAST_FAILED");
	this:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED");
	this:RegisterEvent("UNIT_SPELLCAST_DELAYED");
	this:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START");
	this:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE");
	this:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP");
	--this:RegisterEvent("PLAYER_ENTERING_WORLD");
	
	this:RegisterForDrag("LeftButton");
	
	this.partyFrame = this:GetParent();
	this.unit = this:GetParent().unit;
	this.showTradeSkills = true;
	this.casting = nil;
	this.channeling = nil;
	this.holdTime = 0;
	this.showCastbar = true;

	local barIcon = getglobal(this:GetName().."Icon");
	barIcon:Hide();
	
	this:SetScale(0.75)
	local text = getglobal(this:GetName().."Text")
	text:ClearAllPoints()
	text:SetPoint("CENTER", this, "CENTER", 0, 0)
	this.barText = text
	
	local border = getglobal(this:GetName().."Border")
	border:SetTexture("Interface\\Tooltips\\UI-StatusBar-Border")
	border:SetWidth(202)
	border:SetHeight(18)
	border:ClearAllPoints()
	border:SetPoint("CENTER", this, "CENTER", 0, 0)
	
	local flash = getglobal(this:GetName().."Flash")
	flash:SetTexture("Interface\\AddOns\\PartyCastingBars\\Skin\\ArcaneBarFlash")
	flash:SetWidth(292)
	flash:SetHeight(34)
	flash:ClearAllPoints()
	flash:SetPoint("CENTER", this, "CENTER", 0, 0)
	this.barFlash = flash
	
	local icon = getglobal(this:GetName().."Icon")
	icon:Show();
	this.barIcon = icon
	
	local spark = getglobal(this:GetName().."Spark")
	flash:ClearAllPoints()
	flash:SetPoint("CENTER", this, "CENTER", 0, 0)
	this.barSpark = spark
	
	this.barTime = getglobal(this:GetName().."Time")
	
	tinsert(PartyCastingBars.Bars, this)
end

function PartyCastingBars.GetTimeLeft(statusBar)
	local min, max = statusBar:GetMinMaxValues();
	local current_time;
	if ( this.channeling ) then
		current_time = statusBar:GetValue() - min;
	else
		current_time = max - statusBar:GetValue();
	end
	return format(PartyCastingBars.TIME_LEFT, math.max(current_time,0));
end

function PartyCastingBars.OnEvent(this, event, unit, spellName)
	--Modified from CastingBarFrame_OnEvent to handle colors, interuptions, frame shortcuts and possible target text
	--[[
	if ( newevent == "PLAYER_ENTERING_WORLD" ) then
		local nameChannel  = UnitChannelInfo(this.unit);
		local nameSpell  = UnitCastingInfo(this.unit);
		if ( nameChannel ) then
			event = "UNIT_SPELLCAST_CHANNEL_START";
			unit = this.unit;
		elseif ( nameSpell ) then
			event = "UNIT_SPELLCAST_START";
			unit = this.unit;
		end
	end
	]]
	
	if ( unit ~= this.unit ) then
		return;
	end

	local barSpark = this.barSpark;
	local barText = this.barText;
	local barFlash = this.barFlash;
	local barIcon = this.barIcon;

	if ( event == "UNIT_SPELLCAST_START" ) then
		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(this.unit);
		if ( not name or (not this.showTradeSkills and isTradeSkill)) then
			this:Hide();
			return;
		end
		
		this.hostileCast = this.nextHostileCast;
		this.targetName = this.nextTargetName;
		
		if (this.hostileCast) then
			this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_HostileColors["CAST"]));
		else
			this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_FriendlyColors["CAST"]));
		end
		barSpark:Show();
		this.startTime = startTime / 1000;
		this.maxValue = endTime / 1000;

		-- startTime to maxValue		no endTime
		this:SetMinMaxValues(this.startTime, this.maxValue);
		this:SetValue(this.startTime);
		if (this.targetName) then
			barText:SetText(format(PartyCastingBars.SPELL_AND_TARGET, text, this.targetName))
		else
			barText:SetText(text);
		end
		barIcon:SetTexture(texture);
		this:SetAlpha(1.0);
		this.holdTime = 0;
		this.casting = 1;
		this.channeling = nil;
		this.fadeOut = nil;
		if ( this.showCastbar ) then
			this:Show();
		end
				
		return;

	elseif ( event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" ) then
		if ( not this:IsVisible() ) then
			this:Hide();
		end
		if ( this:IsShown() ) then
			local min, max = this:GetMinMaxValues();
			local currTimeLeft = max - this:GetValue();
			if (currTimeLeft > 0.1) then
				-- use interupted event handler
				event = "UNIT_SPELLCAST_INTERRUPTED";
			else
				barSpark:Hide();
				barFlash:SetAlpha(0.0);
				barFlash:Show();
				this:SetValue(this.maxValue);
				if ( event == "UNIT_SPELLCAST_STOP" ) then
					if (this.hostileCast) then
						this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_HostileColors["SUCCESS"]));
					else
						this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_FriendlyColors["SUCCESS"]));
					end
					this.casting = nil;
				else
					this.channeling = nil;
				end
				this.flash = 1;
				this.fadeOut = 1;
				this.holdTime = 0;
			end
			
			this.targetName = nil;
			this.hostileCast = nil;
		end
	end
	
	if ( event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" ) then
		if ( this:IsShown() and not this.channeling ) then
			this:SetValue(this.maxValue);
			if (this.hostileCast) then
				this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_HostileColors["FAILURE"]));
			else
				this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_FriendlyColors["FAILURE"]));
			end
			barSpark:Hide();
			if ( event == "UNIT_SPELLCAST_FAILED" ) then
				barText:SetText(FAILED);
			else
				barText:SetText(INTERRUPTED);
			end
			this.casting = nil;
			this.channeling = nil;
			this.fadeOut = 1;
			this.holdTime = GetTime() + CASTING_BAR_HOLD_TIME;
			
			this.targetName = nil;
			this.hostileCast = nil;
		end
	elseif ( event == "UNIT_SPELLCAST_TARGET_CHANGED" ) then
		-- Fake event, generated by chat comm
		if ( not spellName ) then
			return;
		end
		if ( this:IsShown() ) then
			local min, max = this:GetMinMaxValues();
			local currTimeLeft = max - this:GetValue();
			
			local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill; 
			if ( this.casting ) then
				name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(this.unit);
			elseif ( this.channeling ) then
				name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo(this.unit);
			end
			
			if (spellName == name and currTimeLeft > 0.1) then
				-- Spell start event was already recieved, update the current spell
				if ( this.casting ) then
					if (this.hostileCast) then
						this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_HostileColors["CAST"]));
					else
						this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_FriendlyColors["CAST"]));
					end
				elseif ( this.channeling ) then
					if (this.hostileCast) then
						this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_HostileColors["CHANNEL"]));
					else
						this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_FriendlyColors["CHANNEL"]));
					end
				end
				
				if (this.targetName) then
					barText:SetText(format(PartyCastingBars.SPELL_AND_TARGET, text, this.targetName));
				else
					barText:SetText(text);
				end
				return;
			end
		end
		
		this.nextHostileCast = this.hostileCast;
		this.nextTargetName = this.targetName;
		
	elseif ( event == "UNIT_SPELLCAST_DELAYED" ) then
		if ( this:IsShown() ) then
			local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(this.unit);
			if ( not name or (not this.showTradeSkills and isTradeSkill)) then
				-- if there is no name, there is no bar
				this:Hide();
				return;
			end
			this.startTime = startTime / 1000;
			this.maxValue = endTime / 1000;
			this:SetMinMaxValues(this.startTime, this.maxValue);
		end
	elseif ( event == "UNIT_SPELLCAST_CHANNEL_START" ) then
		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo(this.unit);
		if ( not name or (not this.showTradeSkills and isTradeSkill)) then
			-- if there is no name, there is no bar
			this:Hide();
			return;
		end
		
		this.hostileCast = this.nextHostileCast;
		this.targetName = this.nextTargetName;

		if (this.hostileCast) then
			this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_HostileColors["CHANNEL"]));
		else
			this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_FriendlyColors["CHANNEL"]));
		end
		barSpark:Show();
		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);
		barText:SetText(text);
		if (this.targetName) then
			barText:SetText(format(PartyCastingBars.SPELL_AND_TARGET, text, this.targetName))
		else
			barText:SetText(text);
		end
		barIcon:SetTexture(texture);
		this:SetAlpha(1.0);
		this.holdTime = 0;
		this.casting = nil;
		this.channeling = 1;
		this.fadeOut = nil;
		if ( this.showCastbar ) then
			this:Show();
		end
	elseif ( event == "UNIT_SPELLCAST_CHANNEL_UPDATE" ) then
		if ( this:IsShown() ) then
			local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo(this.unit);
			if ( not name or (not this.showTradeSkills and isTradeSkill)) then
				-- if there is no name, there is no bar
				this:Hide();
				return;
			end
			this.startTime = startTime / 1000;
			this.endTime = endTime / 1000;
			this.maxValue = this.startTime;
			this:SetMinMaxValues(this.startTime, this.endTime);
		end
	end

end

function PartyCastingBars.OnUpdate()
	if ( PartyCastingBars.draggable ) then
		return;
	end
	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);
			if (this.hostileCast) then
				this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_HostileColors["SUCCESS"]));
			else
				this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_FriendlyColors["SUCCESS"]));
			end
			this.barSpark:Hide();
			this.barFlash:SetAlpha(0.0);
			this.barFlash:Show();
			this.casting = nil;
			this.flash = 1;
			this.fadeOut = 1;
			return;
		end
		this:SetValue(status);
		this.barFlash:Hide();
		local sparkPosition = ((status - this.startTime) / (this.maxValue - this.startTime)) * this:GetWidth();
		if ( sparkPosition < 0 ) then
			sparkPosition = 0;
		end
		this.barSpark:SetPoint("CENTER", this, "LEFT", sparkPosition, 0);
		this.barTime:SetText(PartyCastingBars.GetTimeLeft(this));
	elseif ( this.channeling ) then
		local time = GetTime();
		if ( time > this.endTime ) then
			time = this.endTime;
		end
		if ( time == this.endTime ) then
			if (this.hostileCast) then
				this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_HostileColors["SUCCESS"]));
			else
				this:SetStatusBarColor(PartyCastingBars.GetColorArgs(PartyCastingBars_FriendlyColors["SUCCESS"]));
			end
			this.barSpark:Hide();
			this.barFlash:SetAlpha(0.0);
			this.barFlash:Show();
			this.channeling = nil;
			this.flash = 1;
			this.fadeOut = 1;
			return;
		end
		local barValue = this.startTime + (this.endTime - time);
		this:SetValue( barValue );
		this.barFlash:Hide();
		local sparkPosition = ((barValue - this.startTime) / (this.endTime - this.startTime)) * this:GetWidth();
		this.barSpark:SetPoint("CENTER", this, "LEFT", sparkPosition, 0);
		this.barTime:SetText(PartyCastingBars.GetTimeLeft(this));
	elseif ( GetTime() < this.holdTime ) then
		return;
	elseif ( this.flash ) then
		local alpha = this.barFlash:GetAlpha() + CASTING_BAR_FLASH_STEP;
		if ( alpha < 1 ) then
			this.barFlash:SetAlpha(alpha);
		else
			this.barFlash:SetAlpha(1.0);
			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();
		end
	end
end

function PartyCastingBars.OnDragStart(button)
	if (not PartyCastingBars.draggable) then
		return;
	end
	this:StartMoving();
end

function PartyCastingBars.OnDragStop()
	this:StopMovingOrSizing();
end

function PartyCastingBars.OnMouseUp()
	this:StopMovingOrSizing();
end

function PartyCastingBars.OnHide()
	this:StopMovingOrSizing();
end

--------------------------------------------------
-- Master Enable
--------------------------------------------------

function PartyCastingBars.EnableToggle(value)
	if (value) then
		if (PartyCastingBars_Enabled) then
			--Do nothing
		else
			for i, barFrame in ipairs(PartyCastingBars.Bars) do
				barFrame:SetScript("OnEvent", PartyCastingBars.OnEvent);
				barFrame:SetScript("OnUpdate", PartyCastingBars.OnUpdate);
			end
			PartyCastingBars_Enabled = true;
		end
	else
		if (PartyCastingBars_Enabled) then
			for i, barFrame in ipairs(PartyCastingBars.Bars) do
				barFrame:SetScript("OnEvent", nil);
				barFrame:SetScript("OnUpdate", nil);
				barFrame:Hide();
			end
			PartyCastingBars_Enabled = false;
		else
			--Do nothing
		end
	end
end

--------------------------------------------------
-- Icon Enable
--------------------------------------------------

function PartyCastingBars.EnableIcons(value)
	if (value) then
		if (PartyCastingBars_IconsEnabled) then
			--Do nothing
		else
			for i, barFrame in ipairs(PartyCastingBars.Bars) do
				barFrame.barIcon:Show();
			end
			PartyCastingBars_IconsEnabled = true;
		end
	else
		if (PartyCastingBars_IconsEnabled) then
			for i, barFrame in ipairs(PartyCastingBars.Bars) do
				barFrame.barIcon:Hide();
			end
			PartyCastingBars_IconsEnabled = false;
		else
			--Do nothing
		end
	end
end

--------------------------------------------------
-- Bar Parenting
--------------------------------------------------

function PartyCastingBars.SetParents(value)
	for i, barFrame in ipairs(PartyCastingBars.Bars) do
		barFrame:SetParent(value and barFrame.partyFrame or UIParent);
	end
end

--------------------------------------------------
-- Bar Scaling
--------------------------------------------------

function PartyCastingBars.SetScales(value)
	for i, barFrame in ipairs(PartyCastingBars.Bars) do
		barFrame:SetScale(value);
	end
end

--------------------------------------------------
-- Draggable Mode
--------------------------------------------------

function PartyCastingBars.EnableDragging(value)
	PartyCastingBars.draggable = (value);
	for i, barFrame in ipairs(PartyCastingBars.Bars) do
		if (PartyCastingBars.draggable) then
			barFrame:Show();
			barFrame.barText:SetText(PCB_DRAGGABLE.." #"..i);
			barFrame.barSpark:Hide();
			barFrame.barTime:SetText(format(PartyCastingBars.TIME_LEFT, 15));
			barFrame.barTime:Show();
			barFrame.barIcon:SetTexture("Interface\\Icons\\Spell_Holy_PowerWordShield");
			barFrame:SetAlpha(1);
			barFrame:EnableMouse(1);
		else
			barFrame:Hide();
			barFrame:EnableMouse();
		end
	end
end

--------------------------------------------------
-- Reset Bars
--------------------------------------------------

function PartyCastingBars.ResetBarLocations()
	for i, barFrame in ipairs(PartyCastingBars.Bars) do
		barFrame:ClearAllPoints();
		barFrame:SetPoint("BOTTOMLEFT", barFrame.partyFrame, "BOTTOMRIGHT", 17, 43);
	end
end

--------------------------------------------------
-- Khaos Config
--------------------------------------------------

function PartyCastingBars.RegisterConfig()
	--------------------------------------------------
	-- Configuration Functions
	--------------------------------------------------
	
	if (not PartyCastingBars_FriendlyColors) then PartyCastingBars_FriendlyColors = {} end;
	if (not PartyCastingBars_HostileColors) then PartyCastingBars_HostileColors = {} end;
	
	if ( Khaos ) then 
		-- Khaos will save our vars, not the toc!
		PartyCastingBars_Enabled = true;
		PartyCastingBars_IconsEnabled = true;
		-- W0000!
		local optionSet = {};
		local commandSet = {};
		local configurationSet = {
			id="PartyCastingBars";
			text=PCB_SECTION_TEXT;
			helptext=PCB_SECTION_TIP;
			difficulty=1;
			options=optionSet;
			commands=commandSet;
			default=false;
			callback=PartyCastingBars.EnableToggle;
		};
	
		-- Register Basics
		table.insert(
			optionSet,
			{
				id="Header";
				text=PCB_HEADER_TEXT;
				helptext=PCB_HEADER_TIP;
				difficulty=1;
				type=K_HEADER;
			}
		);
		table.insert(
			optionSet,
			{
				id="ShowSpellIcon";
				text=PCB_SHOW_ICON_TEXT;
				helptext=PCB_SHOW_ICON_TIP;
				difficulty=1;
				callback=function(state)
					PartyCastingBars.EnableIcons(state.checked)
				end;
				feedback=function(state)
					if ( state.checked ) then
						return PCB_SHOWNICONFEEDBACK_NEGATIVE;
					else
						return PCB_SHOWNICONFEEDBACK_POSITIVE;
					end
				end;
				check=true;
				type=K_TEXT;
				setup= {						
				};
				default={
					checked=true;
				};
				disabled={
					disabled=false;
				};					
			}
		);
		table.insert(
			optionSet,
			{
				id="PartyMemberFrameParents";
				text=PCB_PARTYMEMBERFRAME_PARENT_TEXT;
				helptext=PCB_PARTYMEMBERFRAME_PARENT_TIP;
				difficulty=3;
				callback=function(state)
					PartyCastingBars.SetParents(state.checked)
				end;
				feedback=function(state)
					if ( state.checked ) then
						return PCB_PARTYMEMBERFRAME_PARENT_NEGATIVE;
					else
						return PCB_PARTYMEMBERFRAME_PARENT_POSITIVE;
					end
				end;
				check=true;
				type=K_TEXT;
				setup= {						
				};
				default={
					checked=true;
				};
				disabled={
					disabled=true;
				};					
			}
		);
		table.insert(
			optionSet,
			{
				id="Draggable";
				text=PCB_DRAGGABLE_TEXT;
				helptext=PCB_DRAGGABLE_TIP;
				difficulty=3;
				callback=function(state)
					PartyCastingBars.EnableDragging(state.checked)
				end;
				feedback=function(state)
					if ( state.checked ) then
						return PCB_DRAGGABLE_NEGATIVE;
					else
						return PCB_DRAGGABLE_POSITIVE;
					end
				end;
				check=true;
				type=K_TEXT;
				setup= {						
				};
				default={
					checked=false;
				};
				disabled={
					disabled=false;
				};					
			}
		);
		table.insert(
			optionSet,
			{
				id="Scale";
				text=PCB_SCALE_TEXT;
				helptext=PCB_SCALE_TIP;
				difficulty=3;
				check=true;
				type=K_SLIDER;
				callback=function(state)
					PartyCastingBars.SetScales(state.slider)
				end;
				feedback=function(state)
					if ( state.checked ) then
						return format(PCB_SCALE_POSITIVE, state.slider);
					else
						return PCB_SCALE_NEGATIVE;
					end
				end;
				setup = {
					sliderMin = .5;
					sliderMax = 2;
					sliderStep = .1;
					sliderText = PCB_SCALE;
				};
				default = {
					checked = false;
					slider = 1;
				};
				disabled = {
					checked = false;
					slider = 1;
				};
				dependencies = {
					Scale = {checked=true;};
				};
			}
		);
		table.insert(
			optionSet,
			{
				id="ResetLocations";
				text=PCB_RESET_TEXT;
				helptext=PCB_RESET_TIP;
				difficulty=3;
				callback=PartyCastingBars.ResetBarLocations;
				feedback=function(state)
					return PCB_LOCATIONS_RESET;
				end;
				type=K_BUTTON;
				setup = {
					buttonText=PCB_RESETTEXT;
				};
			}
		);
	
		-- Register for each cast type
		
		local reaction = "FriendlyColors"
		local defaultColorString = "Default"..reaction;
		local capsReaction = strupper(reaction);
		
		table.insert(
			optionSet,
			{
				id=reaction.."Header";
				text=getglobal("PCB_HEADER_"..capsReaction.."_TEXT");
				helptext=getglobal("PCB_HEADER_"..capsReaction.."_TIP");
				difficulty=3;
				type=K_HEADER;
			}
		);
		
		for i, castType in ipairs({"CAST","CHANNEL","SUCCESS","FAILURE"}) do 
			local typeString = castType;
			local niceType = Sea.string.capitalizeWords(castType);
			local colorChangeFeedback = function(state)
				return format(PCB_COLOR_CHANGED, Sea.string.colorToString(state.color), niceType );
			end;
			local colorResetFeedback = function(state)
				return format(PCB_COLOR_RESET, Sea.string.colorToString(state.color), niceType );
			end;
			table.insert(
				optionSet,
				{
					id=reaction..niceType.."ColorSetter";
					text=getglobal("PCB_"..typeString.."COLOR_"..capsReaction.."_SET");
					helptext=getglobal("PCB_"..typeString.."COLOR_SET_TIP");
					difficulty=3;
					callback=function(state)
						if (PartyCastingBars.ColorResetting[reaction][typeString]) then
							state.color = PartyCastingBars[defaultColorString][typeString];
							PartyCastingBars.ColorResetting[reaction][typeString] = false;
						end
						getglobal("PartyCastingBars_"..reaction)[typeString] = state.color;
					end;
					feedback=colorChangeFeedback;
					type=K_COLORPICKER;
					setup= {
						hasOpacity=false;
					};
					default={
						color=PartyCastingBars[defaultColorString][castType];
					};
					disabled={
						color=PartyCastingBars[defaultColorString][castType];
					};					
				}
			);
			table.insert(
				optionSet,
				{
					id=reaction..niceType.."Reset";
					text=getglobal("PCB_"..typeString.."COLOR_RESET");
					helptext=getglobal("PCB_"..typeString.."COLOR_RESET_TIP");
					difficulty=3;
					callback=function(state)
						PartyCastingBars.ColorResetting[reaction][typeString] = true;
						-- The khaos config's color has to be reset too or else it will just get refreshed
						-- with its state and override any values we change here.
					end;
					feedback=colorResetFeedback;
					type=K_BUTTON;
					setup = {
						buttonText=PCB_RESETTEXT;
					};
				}
			);
		end
		
		local reaction = "HostileColors"
		local defaultColorString = "Default"..reaction;
		local capsReaction = strupper(reaction);
		
		table.insert(
			optionSet,
			{
				id=reaction.."Header";
				text=getglobal("PCB_HEADER_"..capsReaction.."_TEXT");
				helptext=getglobal("PCB_HEADER_"..capsReaction.."_TIP");
				difficulty=3;
				type=K_HEADER;
			}
		);
			
		for i, castType in ipairs({"CAST","CHANNEL","SUCCESS","FAILURE"}) do 
			local typeString = castType;
			local niceType = Sea.string.capitalizeWords(castType);
			local colorChangeFeedback = function(state)
				return format(PCB_COLOR_CHANGED, Sea.string.colorToString(state.color), niceType );
			end;
			local colorResetFeedback = function(state)
				return format(PCB_COLOR_RESET, Sea.string.colorToString(state.color), niceType );
			end;
			table.insert(
				optionSet,
				{
					id=reaction..niceType.."ColorSetter";
					text=getglobal("PCB_"..typeString.."COLOR_"..capsReaction.."_SET");
					helptext=getglobal("PCB_"..typeString.."COLOR_SET_TIP");
					difficulty=3;
					callback=function(state)
						if (PartyCastingBars.ColorResetting[reaction][typeString]) then
							state.color = PartyCastingBars[defaultColorString][typeString];
							PartyCastingBars.ColorResetting[reaction][typeString] = false;
						end
						getglobal("PartyCastingBars_"..reaction)[typeString] = state.color;
					end;
					feedback=colorChangeFeedback;
					type=K_COLORPICKER;
					setup= {
						hasOpacity=false;
					};
					default={
						color=PartyCastingBars[defaultColorString][castType];
					};
					disabled={
						color=PartyCastingBars[defaultColorString][castType];
					};					
				}
			);
			table.insert(
				optionSet,
				{
					id=reaction..niceType.."Reset";
					text=getglobal("PCB_"..typeString.."COLOR_RESET");
					helptext=getglobal("PCB_"..typeString.."COLOR_RESET_TIP");
					difficulty=3;
					callback=function(state)
						PartyCastingBars.ColorResetting[reaction][typeString] = true;
						-- The khaos config's color has to be reset too or else it will just get refreshed
						-- with its state and override any values we change here.
					end;
					feedback=colorResetFeedback;
					type=K_BUTTON;
					setup = {
						buttonText=PCB_RESETTEXT;
					};
				}
			);
		end
		
		Khaos.registerOptionSet(
			"combat",
			configurationSet
		);			
	else
		--[[
		-- Command System FTW
		PartyCastingBars.EnableToggle(PartyCastingBars_Enabled);
		PartyCastingBars.EnableIcons(PartyCastingBars_IconsEnabled);
	
		SlashCmdList["PARTYCASTINGBARS"] = function(msg) 
			if (string.lower(msg) == "enable") then
				PartyCastingBars.EnableToggle(true);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_ENABLED,0,1,0);
			elseif (string.lower(msg) == "disable") then
				PartyCastingBars.EnableToggle(false);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_DISABLED,1,0,0);
			elseif (string.lower(msg) == "icon") then
				if (PartyCastingBars_IconsEnabled) then
					PartyCastingBars.EnableIcons(true);
					DEFAULT_CHAT_FRAME:AddMessage(PCB_SHOWNICONFEEDBACK_NEGATIVE,1,1,0);
				else
					PartyCastingBars.EnableIcons(false);
					DEFAULT_CHAT_FRAME:AddMessage(PCB_SHOWNICONFEEDBACK_POSITIVE,0,1,1);
				end
			elseif (string.lower(msg) == "set failure") then
				PartyCastingBars_FailureColorSet();
			elseif (string.lower(msg) == "set success") then
				PartyCastingBars_SuccessColorSet();
			elseif (string.lower(msg) == "set channel") then
				PartyCastingBars_ChannelColorSet();
			elseif (string.lower(msg) == "set cast") then
				PartyCastingBars_MainColorSet();
			elseif (string.lower(msg) == "reset failure") then
				PartyCastingBars_FailureColorReset();
			elseif (string.lower(msg) == "reset success") then
				PartyCastingBars_SuccessColorReset();
			elseif (string.lower(msg) == "reset channel") then
				PartyCastingBars_ChannelColorReset();
			elseif (string.lower(msg) == "reset cast") then
				PartyCastingBars_MainColorReset();
			elseif (string.lower(msg) == "help") then
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP1);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP2);
				DEFAULT_CHAT_FRAME:AddMessage("|c00995555--------|r");
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP3);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP4);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP5);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP6);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP7);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP8);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP9);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP10);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP11);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP12);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP13);
				DEFAULT_CHAT_FRAME:AddMessage(PCB_HELP14);
			else
				DEFAULT_CHAT_FRAME:AddMessage(PCB_INVALID_COMMAND,.5,.5,.5);
			end
		end;
		
	 	SLASH_PARTYCASTINGBARS1 = "/partycastingbars";
	 	SLASH_PARTYCASTINGBARS2 = "/pcb";
	 	]]--
	end
end

