--[[

	CombatStats2 - Stat Damage Tracking
	TODO:	Lots, mainly get a decent UI 
	AUTHOR:	Onarne/Onarned (beta server)
	THANKS TO:	The dude(ette) who made DPSPlus for showing me the light on how to parse the combat log
				The dude(ette) who made Clock for the time conversion routines 
	
	Rev 2.04 - (sarf) added DoctorVanGogh's German localization fix
	02.06.05 -  Thanks a lot! :)
			   (sarf) Added dropdown button number checking
	            NOTE: The current system is kinda brute force. 
	            A better version - perhaps with some kind of scrolling - 
	            should be implemented.
	           
	            

	Rev 2.03 - Now really really works w/o cosmos
	11.15.04
	
	Rev 2.02	- Now works w/o cosmos
	11.14.04	- Changed the text display frame
			- Fixed the crit % running out of the frame when it was 100%
			- Put CS_TEXT_ATTACK_DEFAULT attack at top of the list
			- Added in a "Total", put it at the bottom of the list
			- UI is now movalbe
			- UI will update itself while you are attacking if it is visible
			- Fixed a bug where the close "X" button couldn't be pressed
			- Now uses OnEvent and OnUpdate 
			- Pet damage is now tracked
			- DOT damage totals are correctly shown
			- Hopefully got rid of bogus entries
				

	Rev 2.01	- First release w/new UI
	11.05.04
	
  ]]


--
--	Cosmos Cofig variables
--

CombatStats_OnOff = 0;
CombatStats_HideOnNoTarget = 0;

CombatStats_LastUpdate = 0;
CombatStats_UpdateFreq = 1;  -- Update the on screen DPS every x seconds
--CombatStats_Window = 20;    -- Calc DPS over the last x seconds
CombatStats_DPSLen = 20;
CombatStats_UseMouseOver = 0;


defcritCount = 0;
speccritCount= 0;
timestamps = {};
players = {};
dmg = {};
dpstotals = {};
defCrits = {0};
specCrits = {0};
defCritTotals = {};
specCritTotals = {};

bFirstTime = 1;
bInCombat = 0;
bHasDefault = 0;

totalDamage = 0;


overallSwings		=	0;
overallMisses		=	0;
overallDodged		=	0;
overallParried		=	0;
overallEvaded		=	0;
overallBlocked		=	0;
overallResisted		=	0;
overallImmuned		=	0;
overallDeflected	=	0;
overallHits		=	0;
overallNonCrits		=	0;
overallCrits		=	0;
overallmaxCrit		=	0;
overallminCrit		=	0;
overallmaxReg		=	0;
overallminReg		=	0;
overallRegDmg		=	0;
overallCritDmg		=	0;
overallLastcrit		=	0;

attackNames = {};
specialAttacks = {};
specialAttackLog = {};

totalHits = 0;
totalCrits = 0;
specialsCount = 0;

CombatStats_Old_TargetFrame_OnShow = nil;
CombatStats_Old_TargetFrame_OnHide = nil;

CombatStats_ChatCommandHandlers={};
--
--
--

RECENT_KILLS_DISPLAYED = 0;

CombatStatsCreepCount = 0;
CombatStatsCreepCount = 0;
CombatStatsVars = { };
CombatStatsSessionVars = { };
DeathCount = 0;
DeathLog = { };
CombatStatsRecentVars = { };
CombatStatsByName = { };
CombatStatsSessionByName = { };
CombatStatsDisplay = { };
KillDifficultyOrder = { verydifficult = 1, difficult = 2, standard = 3, trivial = 4, unknown = 5 };

function CombatStats_ChatCommandHandler(msg)
	msg = string.lower(msg);
	local firsti, lasti, command, setStr = string.find (msg, "(%w+) ([%w%.]+)");
	if ((not command) and msg) then
		command = msg;
	end
	if (command) then
		for curCommand in CombatStats_ChatCommandHandlers do
			if (command == curCommand) then
				if (setStr) then
					CombatStats_ChatCommandHandlers[curCommand](setStr);
				else
					CombatStats_ChatCommandHandlers[curCommand]();
				end
				return;
			end
		end
	end
	Print("All slash command can start with either /cs or /combatstats example: /cs enable or /combatstats enable");
	Print("Then you can pass on or off to them, or pass a number for ones that need a number ex.");
	Print("/cs enable on");
	Print("/cs enable off");
	Print("");
	Print("enable [on | off] - 'Enable/disabwle CombatStats, on or off'");
	Print("target [on | off] - 'Only show DPS meter when you have a target'");
	Print("reset yes - 'Rest all stats to 0'");
	--Sea.IO.printTable(messages);
end

--
--	Slash command handler
--

function CombatStats_Enable_ChatCommandHandler(msg)

	if (msg) then
		msg = string.upper(msg);
		-- Toggle appropriately
		if (string.find(msg, "ON")) then
			CombatStats_OnOff = 1;
		elseif (string.find(msg, "OFF")) then
			CombatStats_OnOff = 0;		
		end
	end
	
	if(Cosmos_RegisterConfiguration ~= nil) then
		Cosmos_UpdateValue("COMBATSTATS_CONFIG_ONOFF", CSM_CHECKONOFF, CombatStats_OnOff);
	end

	CombatStats_UpdateVisibility();

end
CombatStats_ChatCommandHandlers["enable"] = CombatStats_Enable_ChatCommandHandler;

function CombatStats_Target_ChatCommandHandler(msg)

	if (msg) then
		msg = string.upper(msg);
		-- Toggle appropriately
		if (string.find(msg, "ON")) then
			CombatStats_HideOnNoTarget = 1;
		elseif (string.find(msg, "OFF")) then
			CombatStats_HideOnNoTarget = 0;		
		end
	end
	
	if(Cosmos_RegisterConfiguration ~= nil) then
		Cosmos_UpdateValue("COMBATSTATS_CONFIG_HIDEONNOTARGET", CSM_CHECKONOFF, CombatStats_HideOnNoTarget);
	end

	CombatStats_UpdateVisibility();

end
CombatStats_ChatCommandHandlers["target"] = CombatStats_Target_ChatCommandHandler;

function CombatStats_Reset_ChatCommandHandler(msg)

	if (msg) then
		msg = string.upper(msg);
		-- Toggle appropriately
		if (string.find(msg, "YES")) then
			CombatStats_Reset();
		end		
	end

end
CombatStats_ChatCommandHandlers["reset"] = CombatStats_Reset_ChatCommandHandler;

--
--	Cosmos Config Handlers
--

function CombatStats_HideOnNoTarget_OnOff(toggle)
	CombatStats_HideOnNoTarget = toggle;
	CombatStats_UpdateVisibility();
end

function CombatStats_Watch_OnOff(toggle) 
	CombatStats_OnOff = toggle;
	CombatStats_UpdateVisibility();	
end

function CombatStats_UseMouseOver_OnOff(toggle)
	CombatStats_UseMouseOver = toggle;
	CombatStats_UpdateVisibility();
end


function CombatStats_UpdateVisibility(hasTarget)
	if (not hasTarget) then
		if (TargetFrame:IsVisible()) then
			hasTarget = 1;
		else
			hasTarget = 0;
		end
	end
	if (CombatStats_OnOff == 1) then
		if (CombatStats_HideOnNoTarget == 1) then
			if (hasTarget == 1) then
				CombatStatsFrame:Show();
			else
				CombatStatsFrame:Hide();
			end
		else
			CombatStatsFrame:Show();
		end
		
		--
		--	Register the events to watch
		--
		
		
	else
		CombatStatsFrame:Hide();
	end
end

function CombatStats_Reset()

	defcritCount = 0;
	speccritCount= 0;
	timestamps = {};
	players = {};
	dmg = {};
	dpstotals = {};
	defCrits = {0};
	specCrits = {0};
	defCritTotals = {};
	specCritTotals = {};

	totalDamage = 0;

	attackNames = {};
	specialAttacks = {};
	specialAttackLog = {};

	totalHits = 0;
	totalCrits = 0;
	specialsCount = 0;
	bHasDefault = 0;
	
	overallSwings		=	0;
	overallMisses		=	0;
	overallDodged		=	0;
	overallParried		=	0;
	overallEvaded		=	0;
	overallBlocked		=	0;
	overallResisted		=	0;
	overallImmuned		=	0;
	overallDeflected	=	0;
	overallHits			=	0;
	overallNonCrits		=	0;
	overallCrits		=	0;
	overallmaxCrit		=	0;
	overallminCrit		=	0;
	overallmaxReg		=	0;
	overallminReg		=	0;
	overallRegDmg		=	0;
	overallCritDmg		=	0;
	overallLastcrit		=	0;
	
	CombatStatsText:SetText(DPS_DISPLAY_INITIAL);
	
	CombatStatsGeneralNameTextLabel:SetText(CS_TEXT_NA);
		
		CombatStatsNonCritHitsStatText:SetText("0");	
		CombatStatsNonCritDamageStatText:SetText("0");		
		CombatStatsNonCritMinMaxStatText:SetText("0 / 0");	
		CombatStatsNonCritAvgStatText:SetText("0.0");	
		CombatStatsNonCritPercentDamageStatText:SetText("0.0 %");	
	
		CombatStatsCritHitsStatText:SetText("0");
		CombatStatsCritDamageStatText:SetText("0");
		CombatStatsCritMinMaxStatText:SetText("0 / 0");
		CombatStatsCritAvgStatText:SetText("0.0");
		CombatStatsCritPercentDamageStatText:SetText("0.0 %");
	
		CombatStatsGeneralTotalHitsHits:SetText("0");	
		CombatStatsGeneralSwingsLabel:SetText("0");
			
		CombatStatsGeneralMissesTextLabel:SetText("0");	
		CombatStatsGeneralMissesPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralDodgesTextLabel:SetText("0");	
		CombatStatsGeneralDodgesPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralParriedTextLabel:SetText("0");	
		CombatStatsGeneralParriedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralBlockedTextLabel:SetText("0");	
		CombatStatsGeneralBlockedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralResistedTextLabel:SetText("0");	
		CombatStatsGeneralResistedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralImmunedTextLabel:SetText("0");	
		CombatStatsGeneralImmunedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralEvadedTextLabel:SetText("0");	
		CombatStatsGeneralEvadedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralPercentDmgPctLabel:SetText("0.0%");
		
		CombatStatsGeneralTimeLastCritTimeLabel:SetText(GREEN_FONT_COLOR_CODE..CS_TEXT_NA);
	
		CombatStatsOverallCritPctLabel:SetText(RED_FONT_COLOR_CODE.."0.0 %");		
		
		CombatStatsAttackNonCritPctLabel:SetText(GREEN_FONT_COLOR_CODE.."0.0 %");		
		CombatStatsAttackCritPctLabel:SetText(RED_FONT_COLOR_CODE.."0.0 %");
		
		UIDropDownMenu_SetText(CS_DROPDOWN_SELECT_TEXT,CombatStatsAttackDropDown);
	
end


function CombatStatDPSLen(checked,value)
	CombatStats_DPSLen = value;	
end

-- Register w/ Cosmos Config and set up a chat watch
function CombatStats_RegisterCosmos()
	if( (Cosmos_RegisterConfiguration ~= nil)) then
		Cosmos_RegisterConfiguration(
			"COS_COMBATSTATS",
			"SECTION",
			COMBATSTATS_CONFIG_HEADER,
			COMBATSTATS_CONFIG_HEADER_INFO
			);
		Cosmos_RegisterConfiguration(
			"COS_COMBATSTATS_HEADER",
			"SEPARATOR",
			COMBATSTATS_CONFIG_HEADER,
			COMBATSTATS_CONFIG_HEADER_INFO
			);
		Cosmos_RegisterConfiguration(
			"COS_COMBATSTATS_ONOFF", 
			"CHECKBOX",
			COMBATSTATS_CONFIG_ONOFF,
			COMBATSTATS_CONFIG_ONOFF_INFO,
			CombatStats_Watch_OnOff,
			1
			);
		Cosmos_RegisterConfiguration(
			"COS_CBSTAT_DPS_LENGTH",  --CVar
			"SLIDER",
			COMBATSTATS_CONFIG_DPS_LENGTH,
			COMBATSTATS_CONFIG_DPS_LENGTH_INFO,
			CombatStatDPSLen,
			0, -- ???
			20, --  default
			-1, -- min value
			60, -- max value
			COMBATSTATS_CONFIG_DPS_LENGTH_TEXT, -- slider text
			1.0,  -- slider increment
			1, -- slider sate text on/off
			COMBATSTATS_CONFIG_DPS_LENGTH_APPEND, -- slider text append
			1  -- slider multiplier
			);
		
		Cosmos_RegisterConfiguration(
			"COS_COMBATSTATS_HIDEONNOTARGET_ONOFF", 
			"CHECKBOX",
			COMBATSTATS_CONFIG_HIDEONNOTARGET,
			COMBATSTATS_CONFIG_HIDEONNOTARGET_INFO,
			CombatStats_HideOnNoTarget_OnOff,
			CombatStats_HideOnNoTarget
			);	

		Cosmos_RegisterConfiguration(
			"COS_COMBATSTATS_USEMOUSEOVER_ONOFF", 
			"CHECKBOX",
			COMBATSTATS_CONFIG_USEMOUSEOVER,
			COMBATSTATS_CONFIG_USEMOUSEOVER_INFO,
			CombatStats_UseMouseOver_OnOff,
			CombatStats_UseMouseOver,
			0
			);	
						
		Cosmos_RegisterChatCommand (
			"COMBATSTATS_COMMANDS",
			{"/cs", "/combatsats"},
			CombatStats_ChatCommandHandler,
			CS_CHAT_COMMAND_INFO
			);					
	end

end






function CombatStats_Encounter(creature)
	if ( not CombatStatsByName[creature] ) then
		CombatStatsCreepCount = CombatStatsCreepCount + 1;
		CombatStatsVars[CombatStatsCreepCount] = {
			index						= CombatStatsCreepCount, 
			name						= creature, 
			kills						= 0,
			deaths						= 0,
			maxLevel					= -1,
			minLevel					= 1000,
			isElite						= 0,
			xpEarned					= 0,
			creatureType				= 'Unknown',
			meleeHitsToMob				= 0,
			meleeHitsToPlayer			= 0,
			meleeCritsToMob				= 0,
			meleeCritsToPlayer			= 0,
			meleeMissesToMob			= 0,
			meleeMissesToPlayer			= 0,
			meleeEvadesToMob			= 0,
			meleeEvadesToPlayer			= 0,
			meleeDamageToMob			= 0,
			meleeDamageToPlayer			= 0,
			zone						= 'Twilight',
			spellHitsToMob				= 0,
			spellHitsToPlayer			= 0,
			spellCritsToMob				= 0,
			spellCritsToPlayer			= 0,
			spellMissesToMob			= 0,
			spellMissesToPlayer			= 0,
			spellEvadesToMob			= 0,
			spellEvadesToPlayer			= 0,
			spellDamageToMob			= 0,
			spellDamageToPlayer			= 0,
			petName						= 'Fluffy',
			petHitsToMob				= 0,
			petHitsToPlayer				= 0,
			petCritsToMob				= 0,
			petCritsToPlayer			= 0,
			petMissesToMob				= 0,
			petMissesToPlayer			= 0,
			petEvadesToMob				= 0,
			petEvadesToPlayer			= 0,
			petDamageToMob				= 0,
			petDamageToPlayer			= 0,
		}
		CombatStatsByName[creature] = CombatStatsVars[CombatStatsCreepCount];
	end
	if ( UnitName('target') == creature ) then
		--CombatStats_UpdateTargetInfo(creature);
	end
	-- record recent kill
	if ( RECENT_KILLS_DISPLAYED ~= 0 ) then
		local priorRecent = RECENT_KILLS_DISPLAYED - 1;
		for i=1, RECENT_KILLS_DISPLAYED do
			if ( CombatStatsRecentVars[i] and CombatStatsRecentVars[i].name == creature ) then
				priorRecent = i - 1;
				break;
			end
		end
		for i=priorRecent, 1, -1 do
			if ( CombatStatsRecentVars[i] ) then
				CombatStatsRecentVars[i+1] = CombatStatsRecentVars[i];
			end
		end
		CombatStatsRecentVars[1] = CombatStatsByName[creature];
	end
end


-- 
-- Other
--
function CombatStats_UpdateTargetInfo(creature)
	level = UnitLevel('target');
	if ( UnitIsPlusMob('target') and CombatStatsByName[creature].isElite == 0 ) then
		CombatStatsByName[creature].isElite = 1;
	end
	if ( level < CombatStatsByName[creature].minLevel ) then
		CombatStatsByName[creature].minLevel = level;
	end
	if ( level > CombatStatsByName[creature].maxLevel ) then
		CombatStatsByName[creature].maxLevel = level;
	end
--	if ((not CombatStatsByName[creature].portrait) and TargetPortrait) then
--		CombatStatsByName[creature].portrait = TargetPortrait;
--	end
	if CombatStatsByName[creature].creatureType == 'Unknown' then
		if ( getglobal(GameTooltip:GetName().."TextLeft1"):GetText() == CombatStatsByName[creature].name ) then
			for tt_level, CreatureType in string.gfind(getglobal(GameTooltip:GetName().."TextLeft2"):GetText(), "Level (%d+)%+? (%w+)") do
				if CreatureType ~= 'Corpse' then
					CombatStatsByName[creature].creatureType = CreatureType;
				end
			end
		end
	end
	this.lastTarget = creature;
	if ( string.lower(CombatStatsByName[creature].creatureType) == 'critter' or level < TrivialLevel() ) then  
		this.lastTargetTrivial = true;
	else
		this.lastTargetTrivial = false;
	end
end

function CombatStats_OnEvent()
	
	--print2("Event :: "..event);

	--
	--	Don't do all this unless
	--	combat stats is turned on
	--
	
	if ( not CombatStats_OnOff ) then
		return;
	end
	if ( event == "CHAT_MSG_COMBAT_XP_GAIN" ) then
		for creatureName, xp in string.gfind(arg1, TEXT(CS_MSG_XP_GAIN)) do 
			CombatStats_Encounter(creatureName);
			--CombatStats_Encounter(creatureName, xp);
			bInCombat = 0;
			return;
		end
	end
	if (event == "PLAYER_LEAVE_COMBAT") then
		bInCombat = 0;
	end
		
	if (event == "CHAT_MSG_SYSTEM" and arg1) then
		for creatureName in string.gfind(arg1, TEXT(CS_MSG_SLAIN_YOU)) do
			CombatStats_Encounter(creatureName);
	--		CombatStats_RecordDeath(creatureName);
		end
	end
	
	local p, d;
	local curtime = GetTime();
	local a,b,c,d,e;
	
	if(	event == "CHAT_MSG_COMBAT_SELF_HITS" 
		or event == "CHAT_MSG_COMBAT_SELF_MISSES"
		or event == "CHAT_MSG_SPELL_SELF_DAMAGE"
		or event == "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE"
		or event == "CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE"
		) then			
		
		bInCombat = 1;
				
		for type, message in CombatStats_ChatMessages.default.player do
			for a, b, c, d, e in string.gfind(arg1, message.match) do 
				local ordered = CombatStats_ReorderArgs(message.args, a, b, c, d, e);
				if (ordered.creatureName) then
					CombatStats_Encounter(ordered.creatureName);
					if (ordered.damage) then
					--	CombatStats_RecordDamageToMob(ordered.creatureName, ordered.damage);
					--	CombatStats_RecordHitToMob(ordered.creatureName);
						CombatStats_AddSpecialData(CS_TEXT_ATTACK_DEFAULT, type, ordered.damage, curtime);
						CombatStats_AddDPSEntry("Your", ordered.damage);						
					else
					--	CombatStats_RecordEvadeToMob(ordered.creatureName);
						CombatStats_AddSpecialData(CS_TEXT_ATTACK_DEFAULT, type, 0, curtime);
					end	
					return;	
				end
			end
		end
		for type, message in CombatStats_ChatMessages.spell.player do
			for a, b, c, d, e in string.gfind(arg1, message.match) do 
				ordered = CombatStats_ReorderArgs(message.args, a, b, c, d, e);
				if (ordered.creatureName) then
					CombatStats_Encounter(ordered.creatureName);
					if (ordered.damage) then
					--	CombatStats_RecordSpellDamageToMob(ordered.creatureName, ordered.damage);
					--	CombatStats_RecordSpellHitToMob(ordered.creatureName);
						CombatStats_AddSpecialData(ordered.spell, type, ordered.damage, curtime);
						CombatStats_AddDPSEntry("Your", ordered.damage);	
					else
						CombatStats_AddSpecialData(ordered.spell, type, 0, curtime);
					--	CombatStats_RecordSpellEvadeToMob(ordered.creatureName);
					end	
					return;		
				end
			end
		end
	end
	
	if(	event == "CHAT_MSG_COMBAT_PET_HITS" 
		or event == "CHAT_MSG_COMBAT_PET_MISSES"
		or event == "CHAT_MSG_SPELL_PET_DAMAGE") then
		bInCombat = 1;
		
		for type, message in CombatStats_ChatMessages.default.pet do
			for a, b, c, d, e in string.gfind(arg1, message.match) do 
				ordered = CombatStats_ReorderArgs(message.args, a, b, c, d, e);				
				if (ordered.creatureName) then
					CombatStats_Encounter(ordered.creatureName);
					if (ordered.damage) then
					--	CombatStats_RecordDamageToMob(ordered.creatureName, ordered.damage);
					--	CombatStats_RecordHitToMob(ordered.creatureName);
						CombatStats_AddSpecialData("[Pet] "..CS_TEXT_ATTACK_DEFAULT, type, ordered.damage, curtime);
						CombatStats_AddDPSEntry("Your", ordered.damage);	
					else
					--	CombatStats_RecordEvadeToMob(ordered.creatureName);
						CombatStats_AddSpecialData("[Pet] "..CS_TEXT_ATTACK_DEFAULT, type, 0, curtime);
					end	
					return;		
				end
			end
		end
		for type, message in CombatStats_ChatMessages.spell.pet do
			for a, b, c, d, e in string.gfind(arg1, message.match) do 
				ordered = CombatStats_ReorderArgs(message.args, a, b, c, d, e);
				if (ordered.creatureName) then
					CombatStats_Encounter(ordered.creatureName);
					if (ordered.damage) then
					--	CombatStats_RecordSpellDamageToMob(ordered.creatureName, ordered.damage);
					--	CombatStats_RecordSpellHitToMob(ordered.creatureName);
						CombatStats_AddSpecialData("[Pet] "..ordered.spell, type, ordered.damage, curtime);
						CombatStats_AddDPSEntry("Your", ordered.damage);	
					else
						CombatStats_AddSpecialData("[Pet] "..ordered.spell, type, 0, curtime);
					--	CombatStats_RecordSpellEvadeToMob(ordered.creatureName);
					end	
					return;			
				end
			end
		end				
	end
end

function debugPrint(...) 
	Sea.IO.printTable(arg);
end

function CombatStats_ReorderArgs(order, ...)
	local tmp = {};

	for i in order do		
	  	if (order[i]==CS_ARG_CREATURE) then
	  		tmp.creatureName = arg[i];
	  	elseif (order[i]==CS_ARG_DAMAGE) then
	 		tmp.damage = arg[i];
	  	elseif (order[i]==CS_ARG_DAMAGETYPE) then
	 		tmp.damageType = arg[i];
	  	elseif (order[i]==CS_ARG_SPELL) then
	 		tmp.spell = arg[i];
	  	elseif (order[i]==CS_ARG_PET) then
	 		tmp.petName = arg[i];
		end  
	end	
	
	return tmp; 		
end


--
--	Add/Check for attack(s)
--

function CombatStats_AddSpecialData(specialName,type,dmg,time)
	
	--
	--	If we haven't logged this attack
	--	Create a record in the table for it
	--		
		
	totalDamage = totalDamage + dmg;
	
	if (type == "dot") then
		specialName = specialName .." [DOT]";
	end
	
	if(specialName == CS_TEXT_ATTACK_DEFAULT) then
		bHasDefault = 1;
	end
	
	if ( not specialAttacks[specialName] ) then
		specialsCount = specialsCount + 1;
		table.insert(attackNames,specialName);
		
		specialAttackLog[specialsCount] = {
			index				=	specialsCount,
			name				=	specialName,
			isDOT				=	0,
			dotDmg				=	0,
			dotTicks			=	0,
			totalSwings			=	0,
			totalMisses			=	0,
			totalDodged			=	0,
			totalParried		=	0,
			totalEvaded			=	0,
			totalBlocked		=	0,
			totalResisted		=	0,
			totalImmuned		=	0,
			totalDeflected		=	0,
			totalHits			=	0,
			totalNonCrits		=	0,
			totalCrits			=	0,
			maxCrit				=	0,
			minCrit				=	0,
			maxReg				=	0,
			minReg				=	0,
			totalRegDmg			=	0,
			totalCritDmg		=	0,
			lastCrit			=	0,
		}
		
		specialAttacks[specialName]	= specialAttackLog[specialsCount];
		
		local info;
		
		info = {};
		info.text = specialName;
		info.func = CombatStatsAttack_OnClick;
		CombatStats_AddButton(info);
				
	end
	
	--
	--	Now add the data
	--
	
	specialAttacks[specialName].totalSwings = specialAttacks[specialName].totalSwings + 1;
	overallSwings = overallSwings + 1;
	
	if(type == "crit") then
		
		totalHits = totalHits + 1;
		totalCrits = totalCrits + 1;
		specialAttacks[specialName].totalCrits = specialAttacks[specialName].totalCrits +1;
		specialAttacks[specialName].totalCritDmg = specialAttacks[specialName].totalCritDmg + dmg;
		specialAttacks[specialName].lastCrit = time;	
		specialAttacks[specialName].totalHits = specialAttacks[specialName].totalHits + 1;
		
		overallCrits = overallCrits + 1;
		overallCritDmg = overallCritDmg + dmg;
		overallLastcrit = time;
				
		--
		--	Set the initial min/max		
		--
		
		if(specialAttacks[specialName].maxCrit == 0) then
			specialAttacks[specialName].maxCrit = dmg;
		end
		
		if(specialAttacks[specialName].minCrit == 0) then
			specialAttacks[specialName].minCrit = dmg;
		end
		
		if(overallmaxCrit == 0) then
			overallmaxCrit = dmg;
		end
		
		if(overallminCrit == 0) then
			overallminCrit = dmg;
		end
		
		--
		--	Check to see if this dmg
		--	is a new min or max
		--
				
		if(tonumber(dmg) < tonumber(specialAttacks[specialName].minCrit)) then
			specialAttacks[specialName].minCrit = dmg;
		end
		
		if(tonumber(dmg) > tonumber(specialAttacks[specialName].maxCrit)) then
			specialAttacks[specialName].maxCrit = dmg;
		end	
		
		if(tonumber(dmg) < tonumber(overallminCrit)) then
			overallminCrit = dmg;
		end
		
		if(tonumber(dmg) > tonumber(overallmaxCrit)) then
			overallmaxCrit = dmg;
		end	
		
	end
	
	if(type == "hit") then
		
		totalHits = totalHits + 1;
		specialAttacks[specialName].isDOT = 0;
		specialAttacks[specialName].totalNonCrits = specialAttacks[specialName].totalNonCrits +1;
		specialAttacks[specialName].totalRegDmg = specialAttacks[specialName].totalRegDmg + dmg;
		specialAttacks[specialName].totalHits = specialAttacks[specialName].totalHits + 1;
		
		overallNonCrits = overallNonCrits + 1;
		overallRegDmg = overallRegDmg + dmg;
		
		--
		--	Set the initial min/max		
		--
		
		if(specialAttacks[specialName].maxReg == 0) then
			specialAttacks[specialName].maxReg = dmg;
		end
		
		if(specialAttacks[specialName].minReg == 0) then
			specialAttacks[specialName].minReg = dmg;
		end
		
		if(overallmaxReg == 0) then
			overallmaxReg = dmg;
		end
		
		if(overallminReg == 0) then
			overallminReg = dmg;
		end
		
		--
		--	Check to see if this dmg
		--	is a new min or max
		--
		
		if(tonumber(dmg) < tonumber(specialAttacks[specialName].minReg)) then
			specialAttacks[specialName].minReg = dmg;
		end
		
		if(tonumber(dmg) > tonumber(specialAttacks[specialName].maxReg)) then
			specialAttacks[specialName].maxReg = dmg;
		end
		
		if(tonumber(dmg) < tonumber(overallminReg)) then
			overallminReg = dmg;
		end
		
		if(tonumber(dmg) > tonumber (overallmaxReg)) then
			overallmaxReg = dmg;
		end	
		
	end
	
	if(type == "dot") then
		
		totalHits = totalHits + 1;
		specialAttacks[specialName].isDOT = 1;
		specialAttacks[specialName].dotTicks = specialAttacks[specialName].dotTicks +1;
		specialAttacks[specialName].dotDmg = specialAttacks[specialName].dotDmg + dmg;
		specialAttacks[specialName].totalHits = specialAttacks[specialName].totalHits + 1;		
		
		overallNonCrits = overallNonCrits + 1;
		overallRegDmg = overallRegDmg + dmg;
		
		--
		--	Set the initial min/max		
		--
		
		if(specialAttacks[specialName].maxReg == 0) then
			specialAttacks[specialName].maxReg = dmg;
		end
		
		if(specialAttacks[specialName].minReg == 0) then
			specialAttacks[specialName].minReg = dmg;
		end
		
		if(overallmaxReg == 0) then
			overallmaxReg = dmg;
		end
		
		if(overallminReg == 0) then
			overallminReg = dmg;
		end
		
		--
		--	Check to see if this dmg
		--	is a new min or max
		--
		
		if(tonumber(dmg) < tonumber(specialAttacks[specialName].minReg)) then
			specialAttacks[specialName].minReg = dmg;
		end
		
		if(tonumber(dmg) > tonumber(specialAttacks[specialName].maxReg)) then
			specialAttacks[specialName].maxReg = dmg;
		end
		
		if(tonumber(dmg) < tonumber(overallminReg)) then
			overallminReg = dmg;
		end
		
		if(tonumber(dmg) > tonumber (overallmaxReg)) then
			overallmaxReg = dmg;
		end	
		
	end
	
	if(type == "miss") then
		
		specialAttacks[specialName].totalMisses = 	specialAttacks[specialName].totalMisses + 1;
		overallMisses = overallMisses +1;
		
	end
	
	if(type == "dodge") then
		
		specialAttacks[specialName].totalDodged = 	specialAttacks[specialName].totalDodged + 1;
		overallDodged = overallDodged + 1;
		
	end
	
	if(type == "parry") then
		
		specialAttacks[specialName].totalParried = 	specialAttacks[specialName].totalParried + 1;
		overallParried = overallParried + 1;
		
	end
	
	if(type == "block") then
		
		specialAttacks[specialName].totalBlocked = 	specialAttacks[specialName].totalBlocked + 1;
		overallBlocked = overallBlocked + 1;
	
	end
	
	if(type == "evade") then
		
		specialAttacks[specialName].totalEvaded = 	specialAttacks[specialName].totalEvaded + 1;
		overallEvaded = overallEvaded + 1;
		
	end
	
	if(type == "resist") then
		
		specialAttacks[specialName].totalResisted = 	specialAttacks[specialName].totalResisted + 1;
		overallResisted = overallResisted + 1;
		
	end
	
	if(type == "immune") then
		
		specialAttacks[specialName].totalImmuned = 	specialAttacks[specialName].totalImmuned + 1;
		overallImmuned = overallImmuned + 1;
		
	end
	
	if(type == "deflect") then
		
		specialAttacks[specialName].totalDeflected = 	specialAttacks[specialName].totalDeflected + 1;
		overallDeflected = overallDeflected + 1;
		
	end
						
end

-- CombatStats Event Watcher
--
-- Update Handler
--
function CombatStats_OnUpdate()
	local curtime = GetTime();
	local oldesttime = 0;
	local deleteindex = 0;
	
	--print2("In Combat == " ..bInCombat);
	
	if (bInCombat == 1) then
	
		-- Check to see if its time to update the on screen DPS and only update if there are dps entries to save CPU
		if ( ((curtime - CombatStats_LastUpdate) > CombatStats_UpdateFreq) and (table.getn(timestamps) > 0) ) then
	
			-- TODO: Change this to a high to low loop so we can do our subtracts and deletes in the same run
		
			for k,v in pairs(timestamps) do
			
				-- Get rid of old dps entries and adjust the totals				
				if ( (curtime - CombatStats_DPSLen) > v ) then

					-- Subtract this entry from the totals
					dpstotals[players[k]] = dpstotals[players[k]] - dmg[k];
				
					-- Mark for later removal *We can't do the remove here cause table.remove reindexes the list and bones our loop
					deleteindex = k;
				else
					-- were into the good stuff now, stop
					oldesttime = v;
					break;
				end
			end

			-- Remove entries from deleteindex down so we don't miss em
			while (deleteindex > 0) do
				table.remove(timestamps, deleteindex);
				table.remove(players, deleteindex);
				table.remove(dmg, deleteindex);
				deleteindex = deleteindex -1;
			end
		
			-- NOTE: Everyone calcs off the same oldest time, not their own oldest time.
			--       This should give more accurate dps on a per group fight basis.
		
			-- Update player DPS
			if ( dpstotals["Your"] ~= nil ) then
				--print2("dpsTotals : " ..dpstotals["Your"]);
				CombatStatsText:SetText( format(TEXT(DPS_DISPLAY), (dpstotals["Your"] / (curtime - oldesttime +1) )) );
			end

			CombatStats_LastUpdate = curtime;
		end
		
		--
		--	If the data frame is visible
		--	update the selected attack "Real time"
		--
		
		if (CombatStatsDataFrame:IsVisible()) then
			
			--print2("IsVisible");
			if( UIDropDownMenu_GetText(CombatStatsAttackDropDown) ~= CS_DROPDOWN_SELECT_TEXT) then
				CombatStats_UpdateDetails( UIDropDownMenu_GetText(CombatStatsAttackDropDown));
			end
		end
		
	end
end


-- Called from XML
function CombatStats_OnLoad()
	if ( Cosmos_RegisterConfiguration ~= nil) then 
		CombatStats_RegisterCosmos();
		HookFunction("TargetFrame_OnShow", "CombatStats_TargetFrame_OnShow", "after");
		HookFunction("TargetFrame_OnHide", "CombatStats_TargetFrame_OnHide", "after");
	else
	-- Standalone (chatwatch)
		CombatStats_Old_TargetFrame_OnShow = TargetFrame_OnShow;
		CombatStats_Old_TargetFrame_OnHide = TargetFrame_OnHide;
		TargetFrame_OnShow = CombatStats_TargetFrame_OnShow;
		TargetFrame_OnHide = CombatStats_TargetFrame_OnHide;			
		
		SLASH_CSSLASH1 = "/combatstats";
		SLASH_CSSLASH2 = "/cs";	
		SlashCmdList["CSSLASH"] = CombatStats_ChatCommandHandler;
					
	end
		
		this:RegisterEvent("CHAT_MSG_COMBAT_SELF_HITS");
		this:RegisterEvent("CHAT_MSG_COMBAT_SELF_MISSES");
		this:RegisterEvent("CHAT_MSG_COMBAT_PET_HITS");
		this:RegisterEvent("CHAT_MSG_COMBAT_PET_MISSES");
		this:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE");
		this:RegisterEvent("CHAT_MSG_SPELL_PET_DAMAGE");
		this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE");
		this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE");
		this:RegisterEvent("CHAT_MSG_SYSTEM");
		this:RegisterEvent("PLAYER_ENTER_COMBAT");
		this:RegisterEvent("PLAYER_LEAVE_COMBAT");
		this:RegisterEvent("CHAT_MSG_COMBAT_XP_GAIN");	
			
		CombatStatsText:SetText(DPS_DISPLAY_INITIAL);
		CombatStats_UpdateVisibility();
end

function CombatStatsAttack_OnClick()
		
	local attackID = this:GetID();
			
	UIDropDownMenu_SetSelectedID(CombatStatsAttackDropDown, attackID);	
	--print2("GetID :: ".. UIDropDownMenu_GetSelectedID(CombatStatsAttackDropDown));
	local attackName =  UIDropDownMenu_GetText(CombatStatsAttackDropDown);
	UIDropDownMenu_SetSelectedValue(CombatStatsAttackDropDown, attackName);
	--print2("GetValue :: ".. UIDropDownMenu_GetSelectedValue(CombatStatsAttackDropDown));
	UIDropDownMenu_SetText(attackName,CombatStatsAttackDropDown);
	--this:SetText(CombatStatsDetailTypes[detailID].value);
	--this.text = CombatStatsDetailTypes[detailID].value;
	
	CombatStats_UpdateDetails(attackName);
	
end

function CombatStatsAttackDropDown_OnLoad()
	UIDropDownMenu_Initialize(this, CombatStatsAttackDropDown_Initialize);
end


function CombatStatsAttackDropDown_Initialize()
	CombatStats_Buttons = 0;
	CombatStats_LoadAttackNames();
end

function CombatStats_LoadAttackNames()

	local info;
	
	if(table.getn(attackNames) ~= 0) then	
		if(bHasDefault == 1) then
			info = {};
			info.text = CS_TEXT_ATTACK_DEFAULT;
			info.func = CombatStatsAttack_OnClick;
			CombatStats_AddButton(info);
		end
	end
	
	for i=1, table.getn(attackNames), 1 do
		
		if(attackNames[i] ~= CS_TEXT_ATTACK_DEFAULT) then	
			info = {};
			info.text = attackNames[i];
			info.func = CombatStatsAttack_OnClick;
			CombatStats_AddButton(info);
		end
		
	end
	
	if(table.getn(attackNames) ~= 0) then		
		info = {};
		info.text = CS_TEXT_ATTACK_TOTAL;
		info.func = CombatStatsAttack_OnClick;
		CombatStats_AddButton(info, true);
	end
			
end

--
--	Add the damage to the DPS table
--

function CombatStats_AddDPSEntry(p, d)

	-- print2("Adding Entry[" .. p .. "] = " .. d);	-- debug
	table.insert(timestamps, GetTime());
	table.insert(players, p);
	table.insert(dmg, d);

	-- Keep running totals on group members to shorten calculation times
	-- We'll subtract off the expired entries during OnUpdate				
	if (dpstotals[p] == nil) then
	--	print2("dpstotals[p] == nil");
		dpstotals[p] = d;
	else
	--	print2("dpstotals[p] == " ..dpstotals[p] );
		dpstotals[p] = dpstotals[p] + d;
	--	print2("AFTER : dpstotals[p] == " ..dpstotals[p] );
	end
	
	--print2("Total : " ..dpstotals[p]);
end

--	Show the stats collected
function CombatStatsText_OnClick()
   if (CombatStatsDataFrame:IsVisible()) then 
   	CombatStatsDataFrame:Hide();
   else
		if (CombatStats_UseMouseOver == 0) then
			CombatStatsText_ShowFrame();
		end
	end
end

function CombatStatsText_OnEnter()
	if (CombatStats_UseMouseOver == 1) then
		CombatStatsText_ShowFrame();
	end
end
function CombatStatsText_ShowFrame()
	
	if(bFirstTime == 1) then
		
		--
		--	Set all the default values
		--
		
		CombatStatsGeneralNameLabel:SetText(TEXT(CS_FRAME_GEN_ATTACK_NAME));	
		CombatStatsGeneralHitsTextLabel:SetText(CS_FRAME_HITS_TEXT);
		CombatStatsGeneralSwingsTextLabel:SetText(CS_FRAME_SWINGS_TEXT);
		CombatStatsGeneralMissesLabel:SetText(CS_FRAME_MISSES_TEXT);
		CombatStatsGeneralDodgesLabel:SetText(CS_FRAME_DODGES_TEXT);
		CombatStatsGeneralParriedLabel:SetText(CS_FRAME_PARRIES_TEXT);
		CombatStatsGeneralBlockedLabel:SetText(CS_FRAME_BLOCKS_TEXT);
		CombatStatsGeneralResistedLabel:SetText(CS_FRAME_RESISTS_TEXT);
		CombatStatsGeneralImmunedLabel:SetText(CS_FRAME_IMMUNE_TEXT);
		CombatStatsGeneralEvadedLabel:SetText(CS_FRAME_EVADES_TEXT );
		CombatStatsGeneralDeflectedLabel:SetText(CS_FRAME_DEFLECTS_TEXT);
		CombatStatsGeneralPercentDmgLabel:SetText(CS_FRAME_PERCENT_OVERALL_TEXT);
		CombatStatsGeneralTimeLastCritLabel:SetText(CS_FRAME_TIME_LASTCRIT_TEXT);
		
		CombatStatsNonCritHitsLabel:SetText(CS_FRAME_TOTAL_TEXT);
		CombatStatsNonCritDamageLabel:SetText(CS_FRAME_DAMAGE_TEXT);
		CombatStatsNonCritMinMaxLabel:SetText(CS_FRAME_MINMAX_TEXT);
		CombatStatsNonCritAvgLabel:SetText(CS_FRAME_AVGDMG_TEXT);
		CombatStatsNonCritPercentDamageLabel:SetText(CS_FRAME_PERCENTDMG_TEXT);
		
		CombatStatsCritHitsLabel:SetText(CS_FRAME_TOTAL_TEXT);
		CombatStatsCritDamageLabel:SetText(CS_FRAME_DAMAGE_TEXT);
		CombatStatsCritMinMaxLabel:SetText(CS_FRAME_MINMAX_TEXT);
		CombatStatsCritAvgLabel:SetText(CS_FRAME_AVGDMG_TEXT);
		CombatStatsCritPercentDamageLabel:SetText(CS_FRAME_PERCENTDMG_TEXT);
		
		CombatStatsGeneralNameTextLabel:SetText(CS_TEXT_NA);
		
		CombatStatsNonCritHitsStatText:SetText("0");	
		CombatStatsNonCritDamageStatText:SetText("0");		
		CombatStatsNonCritMinMaxStatText:SetText("0 / 0");	
		CombatStatsNonCritAvgStatText:SetText("0.0");	
		CombatStatsNonCritPercentDamageStatText:SetText("0.0 %");	
	
		CombatStatsCritHitsStatText:SetText("0");
		CombatStatsCritDamageStatText:SetText("0");
		CombatStatsCritMinMaxStatText:SetText("0 / 0");
		CombatStatsCritAvgStatText:SetText("0.0");
		CombatStatsCritPercentDamageStatText:SetText("0.0 %");
	
		CombatStatsGeneralTotalHitsHits:SetText("0");	
		CombatStatsGeneralSwingsLabel:SetText("0");
			
		CombatStatsGeneralMissesTextLabel:SetText("0");	
		CombatStatsGeneralMissesPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralDodgesTextLabel:SetText("0");	
		CombatStatsGeneralDodgesPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralParriedTextLabel:SetText("0");	
		CombatStatsGeneralParriedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralBlockedTextLabel:SetText("0");	
		CombatStatsGeneralBlockedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralResistedTextLabel:SetText("0");	
		CombatStatsGeneralResistedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralImmunedTextLabel:SetText("0");	
		CombatStatsGeneralImmunedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralEvadedTextLabel:SetText("0");	
		CombatStatsGeneralEvadedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralDeflectedTextLabel:SetText("0");
		CombatStatsGeneralDeflectedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		
		CombatStatsGeneralPercentDmgPctLabel:SetText("0.0%");
		
		CombatStatsGeneralTimeLastCritTimeLabel:SetText(GREEN_FONT_COLOR_CODE..CS_TEXT_NA);
	
		CombatStatsOverallCritPctLabel:SetText(RED_FONT_COLOR_CODE.."0.0 %");		
		
		CombatStatsAttackNonCritPctLabel:SetText(GREEN_FONT_COLOR_CODE.."0.0 %");		
		CombatStatsAttackCritPctLabel:SetText(RED_FONT_COLOR_CODE.."0.0 %");
		
		UIDropDownMenu_SetText(CS_DROPDOWN_SELECT_TEXT,CombatStatsAttackDropDown);
		
		bFirstTime = 0;
		
	end
	
	if( UIDropDownMenu_GetText(CombatStatsAttackDropDown) ~= CS_DROPDOWN_SELECT_TEXT) then
		
		CombatStats_UpdateDetails( UIDropDownMenu_GetText(CombatStatsAttackDropDown));
		
	end
	
	CombatStatsDataFrame:Show();
	

end

function CombatStats_UpdateDetails(attackName)
	
	local x,y,info;
	
	--
	--	Draw the text for the hits/swings 
	--
	
	CombatStatsGeneralHitsTextLabel:SetText(CS_FRAME_HITS_TEXT);
	CombatStatsGeneralSwingsTextLabel:SetText(CS_FRAME_SWINGS_TEXT);
	
	if(attackName ~= CS_TEXT_ATTACK_TOTAL) then
			
		--
		-- Calculate % of hits that are crits
		-- 		
		
		if( (totalCrits) ~= 0) then		
			CombatStatsOverallCritPctLabel:SetText( format(RED_FONT_COLOR_CODE.."%.2f %%", ( totalCrits / totalHits) * 100.0));			
		end
	
		CombatStatsGeneralNameTextLabel:SetText(attackName);
		CombatStatsGeneralTotalHitsHits:SetText(specialAttacks[attackName].totalHits);
		CombatStatsGeneralSwingsLabel:SetText(specialAttacks[attackName].totalSwings);
	
		CombatStatsGeneralMissesTextLabel:SetText(specialAttacks[attackName].totalMisses);
		
		if(specialAttacks[attackName].totalMisses ~= 0) then
			CombatStatsGeneralMissesPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (specialAttacks[attackName].totalMisses / (specialAttacks[attackName].totalSwings) * 100)));
		else
			CombatStatsGeneralMissesPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
	
		CombatStatsGeneralDodgesTextLabel:SetText(specialAttacks[attackName].totalDodged);
		
		if(specialAttacks[attackName].totalDodged ~= 0) then
			CombatStatsGeneralDodgesPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (specialAttacks[attackName].totalDodged / (specialAttacks[attackName].totalSwings) * 100)));
		else
			CombatStatsGeneralDodgesPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
	
		CombatStatsGeneralParriedTextLabel:SetText(specialAttacks[attackName].totalParried);
		
		if(specialAttacks[attackName].totalParried ~= 0) then
			CombatStatsGeneralParriedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (specialAttacks[attackName].totalParried / (specialAttacks[attackName].totalSwings) * 100)));
		else
			CombatStatsGeneralParriedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
	
		CombatStatsGeneralResistedTextLabel:SetText(specialAttacks[attackName].totalResisted);
		
		if(specialAttacks[attackName].totalResisted ~= 0) then
			CombatStatsGeneralResistedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (specialAttacks[attackName].totalResisted / (specialAttacks[attackName].totalSwings) * 100)));
		else
			CombatStatsGeneralResistedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
	
		CombatStatsGeneralImmunedTextLabel:SetText(specialAttacks[attackName].totalImmuned);
		
		if(specialAttacks[attackName].totalImmuned ~= 0) then
			CombatStatsGeneralImmunedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (specialAttacks[attackName].totalImmuned / (specialAttacks[attackName].totalSwings) * 100)));
		else
			CombatStatsGeneralImmunedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
	
		CombatStatsGeneralEvadedTextLabel:SetText(specialAttacks[attackName].totalEvaded);
		
		if(specialAttacks[attackName].totalEvaded ~= 0) then
			CombatStatsGeneralEvadedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (specialAttacks[attackName].totalEvaded / (specialAttacks[attackName].totalSwings) * 100)));
		else
			CombatStatsGeneralEvadedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
	
		CombatStatsGeneralDeflectedTextLabel:SetText(specialAttacks[attackName].totalDeflected);
		
		if(specialAttacks[attackName].totalDeflected ~= 0) then
			CombatStatsGeneralDeflectedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (specialAttacks[attackName].totalDeflected / (specialAttacks[attackName].totalSwings) * 100)));
		else
			CombatStatsGeneralDeflectedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
	
		--
		--	Percent of total damage
		--
					
		if(specialAttacks[attackName].totalHits ~=0) then
			CombatStatsGeneralPercentDmgPctLabel:SetText( format("%.2f %%",  ((specialAttacks[attackName].totalRegDmg + specialAttacks[attackName].totalCritDmg + specialAttacks[attackName].dotDmg) / totalDamage) * 100) );
		else
			CombatStatsGeneralPercentDmgPctLabel:SetText("0.0%");
		end
	
		--
		--	Time since last crit
		--
		
		local timeNow;
		timeNow = GetTime();
		
		if(specialAttacks[attackName].lastCrit ~= 0) then
			CombatStatsGeneralTimeLastCritTimeLabel:SetText(GREEN_FONT_COLOR_CODE ..Clock_FormatTime( (timeNow - specialAttacks[attackName].lastCrit)));
		else
			CombatStatsGeneralTimeLastCritTimeLabel:SetText(GREEN_FONT_COLOR_CODE..CS_TEXT_NA);	
		end
	
		--
		--	Attack Crit %
		--
		
		if (specialAttacks[attackName].totalHits ~= 0) and (specialAttacks[attackName].totalCrits ~= 0) then
			CombatStatsAttackCritPctLabel:SetText(format(RED_FONT_COLOR_CODE.."%.1f%%", ( (specialAttacks[attackName].totalCrits) / (specialAttacks[attackName].totalHits)) * 100.0 ));			
		else
			CombatStatsAttackCritPctLabel:SetText(RED_FONT_COLOR_CODE.."0.0 %");
		end
	
		--
		--	Attack Non Crit %
		--
		
		if (specialAttacks[attackName].totalHits ~= 0) and (specialAttacks[attackName].totalNonCrits ~= 0) then
			CombatStatsAttackNonCritPctLabel:SetText( format(GREEN_FONT_COLOR_CODE.."%.1f%%", ( (specialAttacks[attackName].totalNonCrits) / (specialAttacks[attackName].totalHits)) * 100.0 ));			
		else
			CombatStatsAttackNonCritPctLabel:SetText(GREEN_FONT_COLOR_CODE.."0.0 %");
		end
	
		--
		--	Non Crit stats
		--
		
		CombatStatsNonCritHitsStatText:SetText(specialAttacks[attackName].totalNonCrits);	
		CombatStatsNonCritDamageStatText:SetText(specialAttacks[attackName].totalRegDmg);	
		CombatStatsNonCritMinMaxStatText:SetText(specialAttacks[attackName].minReg .." / "..specialAttacks[attackName].maxReg);
		
		if( specialAttacks[attackName].totalRegDmg ~= 0) then
													
			if( (specialAttacks[attackName].totalHits - specialAttacks[attackName].totalCrits) < 0) then
				CombatStatsNonCritAvgStatText:SetText(format("%.1f",  specialAttacks[attackName].totalRegDmg / (specialAttacks[attackName].totalCrits - specialAttacks[attackName].totalHits ) ));
			end
							
			if(	(specialAttacks[attackName].totalHits - specialAttacks[attackName].totalCrits) == 0 ) then
				CombatStatsNonCritAvgStatText:SetText(format("%1.f", specialAttacks[attackName].totalRegDmg / (specialAttacks[attackName].totalCrits) ));
			end
							
			if(	(specialAttacks[attackName].totalHits - specialAttacks[attackName].totalCrits) > 0 ) then
				CombatStatsNonCritAvgStatText:SetText(format("%1.f",specialAttacks[attackName].totalRegDmg / (specialAttacks[attackName].totalHits - specialAttacks[attackName].totalCrits) ));
			end
							
		else		
			CombatStatsNonCritAvgStatText:SetText("0.0");
		end
	
		if(specialAttacks[attackName].totalNonCrits ~=0) then
			CombatStatsNonCritPercentDamageStatText:SetText( format("%.2f %%", ( specialAttacks[attackName].totalRegDmg   / (specialAttacks[attackName].totalRegDmg + specialAttacks[attackName].totalCritDmg )) * 100.0));
		else
			CombatStatsNonCritPercentDamageStatText:SetText("0.0 %");
		end
	
		--
		--	Crit stats
		--
		
		CombatStatsCritHitsStatText:SetText(specialAttacks[attackName].totalCrits);	
		CombatStatsCritDamageStatText:SetText(specialAttacks[attackName].totalCritDmg);	
		CombatStatsCritMinMaxStatText:SetText(specialAttacks[attackName].minCrit .." / "..specialAttacks[attackName].maxCrit);
		
		if( specialAttacks[attackName].totalCritDmg ~= 0) then
			CombatStatsCritAvgStatText:SetText(format("%1.f", (specialAttacks[attackName].totalCritDmg / specialAttacks[attackName].totalCrits) ));
		else
			CombatStatsCritAvgStatText:SetText("0.0");
		end
		
		if(specialAttacks[attackName].totalCrits ~=0) then
			CombatStatsCritPercentDamageStatText:SetText(format("%.2f %%", (specialAttacks[attackName].totalCritDmg / ( specialAttacks[attackName].totalRegDmg + specialAttacks[attackName].totalCritDmg )) * 100.10 ));
		else
			CombatStatsCritPercentDamageStatText:SetText("0.0 %");
		end	
				
		--
		--	Show DOT Stats
		--
		
		
				
		if (specialAttacks[attackName].isDOT ==1) then
			
			CombatStatsGeneralHitsTextLabel:SetText(CS_FRAME_TICKS_TEXT);
			CombatStatsGeneralSwingsTextLabel:SetText("");
			CombatStatsGeneralSwingsLabel:SetText("");
			
			CombatStatsGeneralTotalHitsHits:SetText(specialAttacks[attackName].dotTicks);
			CombatStatsNonCritHitsStatText:SetText(specialAttacks[attackName].dotTicks);
			CombatStatsNonCritDamageStatText:SetText(specialAttacks[attackName].dotDmg);
			
			CombatStatsNonCritMinMaxStatText:SetText(specialAttacks[attackName].minReg .." / "..specialAttacks[attackName].maxReg);
			CombatStatsNonCritAvgStatText:SetText(format("%.1f",  specialAttacks[attackName].dotDmg /  specialAttacks[attackName].dotTicks  ));
			CombatStatsNonCritPercentDamageStatText:SetText("100.0 %");
			CombatStatsAttackNonCritPctLabel:SetText(GREEN_FONT_COLOR_CODE.."100.0%");
			
			--
			--	Should never be a 0 tick entry but just in case
			--
	--		if(specialAttacks[attackNames[y]].dotTicks ~= 0) then	
	--			text = text.."\n"..format(TEXT(DOT_AVERAGE),(specialAttacks[attackNames[y]].dotDmg / specialAttacks[attackNames[y]].dotTicks));
	--		end
		end
	
	else
		
		--
		-- Calculate % of hits that are crits
		-- 		
		
		if( (totalCrits) ~= 0) then		
			CombatStatsOverallCritPctLabel:SetText( format(RED_FONT_COLOR_CODE.."%.2f %%", ( totalCrits / totalHits) * 100.0));			
		end
		
		CombatStatsGeneralNameTextLabel:SetText(CS_TEXT_ATTACK_TOTAL);
		CombatStatsGeneralTotalHitsHits:SetText(totalHits);
		CombatStatsGeneralSwingsLabel:SetText(overallSwings);
		
		CombatStatsGeneralMissesTextLabel:SetText(overallMisses);
		
		if(overallMisses ~= 0) then
			CombatStatsGeneralMissesPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (overallMisses / (overallSwings) * 100)));
		else
			CombatStatsGeneralMissesPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
		
		CombatStatsGeneralDodgesTextLabel:SetText(overallDodged);
		
		if(overallDodged ~= 0) then
			CombatStatsGeneralDodgesPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (overallDodged / (overallSwings) * 100)));
		else
			CombatStatsGeneralDodgesPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
		
		CombatStatsGeneralParriedTextLabel:SetText(overallParried);
		
		if(overallParried ~= 0) then
			CombatStatsGeneralParriedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (overallParried / (overallSwings) * 100)));
		else
			CombatStatsGeneralParriedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
		
		CombatStatsGeneralResistedTextLabel:SetText(overallResisted);
		
		if(overallResisted ~= 0) then
			CombatStatsGeneralResistedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (overallResisted / (overallSwings) * 100)));
		else
			CombatStatsGeneralResistedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
		
		CombatStatsGeneralImmunedTextLabel:SetText(overallImmuned);
		
		if(overallImmuned ~= 0) then
			CombatStatsGeneralImmunedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (overallImmuned / (overallSwings) * 100)));
		else
			CombatStatsGeneralImmunedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
		
		CombatStatsGeneralEvadedTextLabel:SetText(overallEvaded);
		
		if(overallEvaded ~= 0) then
			CombatStatsGeneralEvadedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (overallEvaded / (overallSwings) * 100)));
		else
			CombatStatsGeneralEvadedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
		
		CombatStatsGeneralDeflectedTextLabel:SetText(overallDeflected);
		
		if(overallDeflected ~= 0) then
			CombatStatsGeneralDeflectedPercentLabel:SetText( format(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.. "%.1f%%" .. NORMAL_FONT_COLOR_CODE.." ]", (overallDeflected / (overallSwings) * 100)));
		else
			CombatStatsGeneralDeflectedPercentLabel:SetText(NORMAL_FONT_COLOR_CODE.."[ "..WHITE_FONT_COLOR_CODE.."0.0%"..NORMAL_FONT_COLOR_CODE.." ]");
		end
		
		--
		--	Percent of total damage
		--
					
		if(totalHits ~=0) then
			CombatStatsGeneralPercentDmgPctLabel:SetText( format("%.2f %%",  ((overallRegDmg + overallCritDmg ) / totalDamage) * 100) );
		else
			CombatStatsGeneralPercentDmgPctLabel:SetText("0.0%");
		end
		
		--
		--	Time since last crit
		--
		
		local timeNow;
		timeNow = GetTime();
		
		if(overallLastcrit ~= 0) then
			CombatStatsGeneralTimeLastCritTimeLabel:SetText(GREEN_FONT_COLOR_CODE ..Clock_FormatTime( (timeNow - overallLastcrit)));
		else
			CombatStatsGeneralTimeLastCritTimeLabel:SetText(GREEN_FONT_COLOR_CODE..CS_TEXT_NA);	
		end
		
		--
		--	Attack Crit %
		--
		
		if (totalHits ~= 0) and (overallCrits ~= 0) then
			CombatStatsAttackCritPctLabel:SetText(format(RED_FONT_COLOR_CODE.."%.1f%%", ( (overallCrits) / (totalHits)) * 100.0 ));			
		else
			CombatStatsAttackCritPctLabel:SetText(RED_FONT_COLOR_CODE.."0.0 %");
		end
		
		--
		--	Attack Non Crit %
		--
		
		if (totalHits ~= 0) and (overallNonCrits ~= 0) then
			CombatStatsAttackNonCritPctLabel:SetText( format(GREEN_FONT_COLOR_CODE.."%.1f%%", ( (overallNonCrits) / (totalHits)) * 100.0 ));			
		else
			CombatStatsAttackNonCritPctLabel:SetText(GREEN_FONT_COLOR_CODE.."0.0 %");
		end
		
		--
		--	Non Crit stats
		--
		
		CombatStatsNonCritHitsStatText:SetText(overallNonCrits);	
		CombatStatsNonCritDamageStatText:SetText(overallRegDmg);	
		CombatStatsNonCritMinMaxStatText:SetText(overallminReg .." / "..overallmaxReg);
		
		if( overallRegDmg ~= 0) then
													
			if( (totalHits - overallCrits) < 0) then
				CombatStatsNonCritAvgStatText:SetText(format("%.1f",  overallRegDmg / (overallCrits - totalHits ) ));
			end
							
			if(	(totalHits - overallCrits) == 0 ) then
				CombatStatsNonCritAvgStatText:SetText(format("%1.f", overallRegDmg / (overallCrits) ));
			end
							
			if(	(totalHits - overallCrits) > 0 ) then
				CombatStatsNonCritAvgStatText:SetText(format("%1.f", overallRegDmg / (totalHits - overallCrits) ));
			end
							
		else		
			CombatStatsNonCritAvgStatText:SetText("0.0");
		end
		
		if(overallNonCrits ~=0) then
			CombatStatsNonCritPercentDamageStatText:SetText( format("%.2f %%", ( overallRegDmg  / (overallRegDmg + overallCritDmg )) * 100.0));
		else
			CombatStatsNonCritPercentDamageStatText:SetText("0.0 %");
		end
		
		--
		--	Crit stats
		--
		
		CombatStatsCritHitsStatText:SetText(overallCrits);	
		CombatStatsCritDamageStatText:SetText(overallCritDmg);	
		CombatStatsCritMinMaxStatText:SetText(overallminCrit .." / "..overallmaxCrit);
		
		if( overallCritDmg ~= 0) then
			CombatStatsCritAvgStatText:SetText(format("%1.f", (overallCritDmg / overallCrits) ));
		else
			CombatStatsCritAvgStatText:SetText("0.0");
		end
		
		if(overallCrits ~=0) then
			CombatStatsCritPercentDamageStatText:SetText(format("%.2f %%", (overallCritDmg / ( overallRegDmg + overallCritDmg )) * 100.10 ));
		else
			CombatStatsCritPercentDamageStatText:SetText("0.0 %");
		end	
	
	end
	
end					


--
--  Time functions
--  from Clock.lua
--

local function Clock_FormatPart(fmtOne, fmtSeveral, val)
	local part;

	if( val ~= 1 ) then
		part = format(TEXT(fmtSeveral), val);
	else
		part = format(TEXT(fmtOne), val);	
	end

	return part;
end

function Clock_FormatTime(time)
	local d, h, m, s;
	local text = "";
	local skip = 1;

	d, h, m, s = ChatFrame_TimeBreakDown(time);
	if( d > 0 ) then
		text = text..Clock_FormatPart(CLOCK_TIME_DAY, CLOCK_TIME_DAYS, d)..", ";
		skip = 0;
	end
	if( (skip == 0) or (h > 0) ) then
		text = text..Clock_FormatPart(CLOCK_TIME_HOUR, CLOCK_TIME_HOURS, h)..", ";
		skip = 0;
	end
	if( (skip == 0) or (m > 0) ) then
		text = text..Clock_FormatPart(CLOCK_TIME_MINUTE, CLOCK_TIME_MINUTES, m)..", ";
		skip = 0;
	end
	if( (skip == 0) or (s > 0) ) then
		text = text..Clock_FormatPart(CLOCK_TIME_SECOND, CLOCK_TIME_SECONDS, s);
	end

	return text;
end

function CombatStats_TargetFrame_OnShow()
	if (CombatStats_Old_TargetFrame_OnShow) then
		CombatStats_Old_TargetFrame_OnShow();
	end
	CombatStats_UpdateVisibility(1);
end

function CombatStats_TargetFrame_OnHide()
	if (CombatStats_Old_TargetFrame_OnHide) then
		CombatStats_Old_TargetFrame_OnHide();
	end
	CombatStats_UpdateVisibility(0);
end

-- START Sarf button patch 2005-02-06

local CombatStats_Buttons = 0;

local CombatStats_ReservedBypassButtons = 1;

-- info contains the button information to add, 
-- bypass should be true if the current button should bypass the artificial restriction
-- Currently, that is only used by the "Total" damage counter,
--  but update CombatStats_ReservedBypassButtons if more bypasses are added.
function CombatStats_AddButton(info, bypass)
	if ( not bypass ) and 
		( CombatStats_Buttons >= 
		(UIDROPDOWNMENU_MAXBUTTONS-CombatStats_ReservedBypassButtons) ) then
		return;
	else
		UIDropDownMenu_AddButton(info);
		CombatStats_Buttons = CombatStats_Buttons + 1;
	end
end

function CombatStats_ResetButtons()
	CombatStats_Buttons = 0;
end

-- END Sarf button patch 
