--[[
		Recap 3.32 - Gello

		An AddOn for WoW to track and summarize all damage done around the user.
]]

Recap_Version = 3.32

--[[ local tables ]]

-- colors for the gauges
local gaugecolor = { ["DmgIn"] = { r=.66,g=.25,b=.25 }, ["DmgOut"] = { r=.25,g=.66,b=.35 }, ["Heal"] = { r=.25,g=.5,b=.85 },
					 ["Time"] = { r=.75, g=.75, b=.75 }, ["Kills"] = { r=.75, g=.75, b=.75 }, ["MaxHit"] = { r=.75, g=.75, b=.75 },
					 ["DPS"] = { r=.25, g=.66, b=.35 }, ["Over"] = { r=.25, g=.5, b=.85 } }

-- keys for (det)ails and (inc)oming data sets
local detkey =	{ ["HitsDmg"]="d", ["Hits"]="n", ["HitsMax"]="x", ["CritsDmg"]="D", ["Crits"]="N",
				  ["CritsMax"]="X", ["CritsEvents"]="e", ["Missed"]="m", ["TicksDmg"]="t", ["TicksMax"]="T" }
local inckey = { ["MeleeDamage"]="a", ["MeleeMax"]="b", ["MeleeHits"]="c", ["MeleeCrits"]="d", ["MeleeMissed"]="e",
				 ["MeleeDodged"]="f", ["MeleeParried"]="g", ["MeleeBlocked"]="h", ["NonMeleeDamage"]="i",
				 ["NonMeleeMax"]="j", ["NonMeleeHits"]="k", ["NonMeleeCrits"]="l", ["NonMeleeMissed"]="m" }

-- .Opt.x and its buttons
local recapbuttons = { ["Minimized"] = "RecapMinimizeButton", ["Pinned"] = "RecapPinButton",
					   ["Paused"] = "RecapPauseButton", ["SelfView"] = "RecapSelfViewButton" }


--[[ Saved Variables ]]

recap = {} -- options and current data
recap_set = {} -- fight data sets

--[[ Callback functions ]]

function Recap_OnLoad()

	this:RegisterEvent("VARIABLES_LOADED")
	this:RegisterEvent("PLAYER_ENTERING_WORLD")
	this:RegisterEvent("UNIT_NAME_UPDATE")

	-- function overrides: recap.NormalExit is true on a successful /quit or /logout
	Recap_OldQuit = Quit
	Recap_OldLogout = Logout
	Recap_OldCancelLogout = CancelLogout
	Quit = function() recap.NormalExit = true; Recap_OldQuit(); end
	Logout = function() recap.NormalExit = true; Recap_OldLogout(); end
	CancelLogout = function() recap.NormalExit = nil; Recap_OldCancelLogout(); end

end

function Recap_OnEvent()

	if event=="VARIABLES_LOADED" then

		SlashCmdList["RecapCOMMAND"] = Recap_SlashHandler
		SLASH_RecapCOMMAND1 = "/Recap"

	elseif event=="PLAYER_ENTERING_WORLD" or event=="UNIT_NAME_UPDATE" then
		Recap_Initialize()

	elseif event=="PLAYER_REGEN_DISABLED" then
		if recap.Opt.LimitFights.value then
			Recap_StartFight()
		end

	elseif event=="PLAYER_REGEN_ENABLED" then
		Recap_EndFight()

	elseif event=="PARTY_MEMBERS_CHANGED" or event=="RAID_ROSTER_UPDATE" then
		Recap_MakeFriends()

	elseif event=="CHAT_MSG_CHANNEL_NOTICE" then
		if string.find(arg4,"recaplog"..string.lower(UnitName("player"))) and arg1=="YOU_JOINED" then
			RecapOpt_WriteLogLabel:SetText(RECAP_CHANNEL_JOINED_WRITING)
			ChatFrameLog(1)
			RecapFrame:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
			RecapFrame:RegisterEvent("CHAT_MSG_CHANNEL")
			Recap_WriteLog()
		end
	elseif event=="CHAT_MSG_CHANNEL" then
		if string.find(string.lower(arg4),"recaplog") and arg2==UnitName("player") and arg1=="EOF" then
			ChatFrameLog(0)
			RecapOpt_WriteLogLabel:SetText(RECAP_SAVE_TO_WOWCHATLOG_TXT)
			LeaveChannelByName("recaplog"..UnitName("player"))
			RecapFrame:UnregisterEvent("CHAT_MSG_CHANNEL")
			DEFAULT_CHAT_FRAME:AddMessage(recap_temp.Local.LogWritten)
		end
	end
end

function Recap_Idle_OnUpdate(arg1)

	-- timer to end idle fights if option checked
	if recap_temp.IdleTimer~=-1 and recap_temp.Loaded and recap.Opt.IdleFight.value then
		recap_temp.IdleTimer = recap_temp.IdleTimer + arg1
		if recap_temp.IdleTimer > recap.Opt.IdleFightTimer.value then
			recap_temp.IdleTimer = -1
			Recap_EndFight()
		end
	end

	-- if fighting and a plugin loaded or we're minimized, update min dps every half second
	if recap_temp.Loaded and recap_temp.UpdateTimer~=-1 and recap.Opt.State.value=="Active" and
	  ((IB_Recap_Update or TitanPanelRecap_Update) or recap.Opt.Minimized.value) then
		recap_temp.UpdateTimer = recap_temp.UpdateTimer + arg1
		if recap_temp.UpdateTimer>.5 then
			recap_temp.UpdateTimer = 0
			Recap_UpdateMinimizedDPS()
		end
	end
end

function Recap_OnUpdate(arg1)

	-- update fade timer if option checked
	if recap_temp.Loaded and recap.Opt.AutoFade.value and not recap.Opt.Minimized.value and MouseIsOver(RecapFrame) and recap_temp.FadeTimer<recap.Opt.AutoFadeTimer.value then
		recap_temp.FadeTimer = 0
	elseif recap_temp.Loaded and recap_temp.FadeTimer~=-1 and recap.Opt.AutoFade.value and not recap.Opt.Minimized.value then
		recap_temp.FadeTimer = recap_temp.FadeTimer + arg1
		if recap_temp.FadeTimer > recap.Opt.AutoFadeTimer.value then
			RecapFrame:SetAlpha( max(1-min(2*(recap_temp.FadeTimer-recap.Opt.AutoFadeTimer.value),1),.1))
			if recap_temp.FadeTimer > (recap.Opt.AutoFadeTimer.value+.5) then
				RecapFrame_Hide()
			end
		end
	end

	if recap_temp.Loaded and recap.Opt.AutoMinimize.value and recap_temp.Selected==0 then
		if not recap.Opt.Minimized.value and not MouseIsOver(RecapFrame) and not IsShiftKeyDown() then
			Recap_Minimize()
		elseif recap.Opt.Minimized.value and MouseIsOver(RecapFrame) and not IsShiftKeyDown() then
			Recap_Maximize()
		end
	end

end

function Recap_SlashHandler(arg1)

	Recap_Initialize()

	if arg1 and arg1=="reset" then
		RecapFrame:ClearAllPoints()
		RecapFrame:SetPoint("CENTER","UIParent","CENTER")
		RecapOptFrame:ClearAllPoints()
		RecapOptFrame:SetPoint("CENTER","UIParent","CENTER")
		Recap_SetView()
	elseif arg1=="debug" then
		if recap.debug then
			DEFAULT_CHAT_FRAME:AddMessage("recap.debug is ON. To turn it off: /recap debug off")
			for i=1,150 do
				if recap.debug_Filter[i] then
					DEFAULT_CHAT_FRAME:AddMessage("["..i.."] \""..recap.debug_Filter[i].pattern.."\" hits: "..recap.debug_Filter[i].hits..", total: "..recap.debug_Filter[i].total)
				end
			end
			DEFAULT_CHAT_FRAME:AddMessage("__ Filters not used __")
			Recap_DebugUnusedFilters("Filter")
			Recap_DebugUnusedFilters("HealFilter")
			Recap_DebugUnusedFilters("DeathFilter")
		else
			DEFAULT_CHAT_FRAME:AddMessage("recap.debug is OFF. To turn it on: /recap debug on")
		end
	elseif arg1=="debug on" then
		recap.debug = true
		DEFAULT_CHAT_FRAME:AddMessage("recap.debug is now ON.")
	elseif arg1=="debug off" then
		recap.debug = false
		DEFAULT_CHAT_FRAME:AddMessage("recap.debug is now OFF.")
	elseif arg1=="debug reset" then
		recap.debug_Filter = {}
		DEFAULT_CHAT_FRAME:AddMessage("recap.debug_Filter is RESET.")
	elseif string.find(arg1,"opt") then
		if RecapOptFrame:IsShown() then
			RecapOptFrame:Hide()
		else
			RecapOptFrame:Show()
		end
	elseif RecapFrame:IsShown() then
		RecapFrame_Hide()
		RecapOptFrame:Hide()
	else
		RecapFrame_Show()
		Recap_CheckWindowBounds()
		if recap.Opt.State.value == "Stopped" then
			Recap_SetState("Idle")
			recap.Opt.Paused.value = false
			Recap_SetButtons()
		end
	end
end

--[[ Window appearance functions ]]

function Recap_SetState(arg1)
	if arg1 then
		recap.Opt.State.value = arg1
	end
	if recap.Opt.State.value=="Stopped" then
		RecapStatusTexture:SetVertexColor(1,0,0)
		Recap_Unregister_CombatEvents()
	elseif recap.Opt.State.value=="Active" then
		RecapStatusTexture:SetVertexColor(0,1,0)
		Recap_Register_CombatEvents()
	else
		RecapStatusTexture:SetVertexColor(.5,.5,.5)
		Recap_Register_CombatEvents()
	end
	Recap_UpdatePlugins()
end		

function RecapFrame_Hide()

	if not recap_temp.Loaded then
		Recap_Initialize()
	end
	recap_temp.FadeTimer = -1
	RecapFrame:SetAlpha(1)
	RecapFrame:Hide()
	recap.Opt.Visible.value = false
end

function RecapFrame_Show()

	if not recap_temp.Loaded then
		Recap_Initialize()
	end
	RecapFrame:Show()
	recap.Opt.Visible.value = true
end

function RecapOptFrame_Hide()
--[[
	RecapSetEditBox:SetText("")
	RecapSetEditBox:ClearFocus()
	if RecapOptClipFrame:IsVisible() then
		RecapClipEditBox:ClearFocus()
		RecapOptClipFrame:Hide()
		RecapOptSubFrame4:Show()
	end
]]
	RecapOptFrame:Hide()
end

function RecapFrame_Toggle()
    if RecapFrame:IsShown() then
        RecapFrame_Hide()
        RecapOptFrame:Hide()
    else
        RecapFrame_Show()
    end
end

function Recap_Shutdown()

	recap.Opt.Paused.value = true
	Recap_EndFight()
	Recap_SetState("Stopped")
	Recap_SetButtons()
	RecapOptFrame:Hide()
	RecapFrame_Hide()
	RecapCombatEvents:UnregisterAll()
end
 
function Recap_ToggleView()

	if recap.Opt.View.value=="All" then
		Recap_SetView("Last")
	else
		Recap_SetView("All")
	end
end

-- changes button textures depending on their state
function Recap_SetButtons()

	local i

	for i in recapbuttons do
		if recap.Opt[i].value then
			getglobal(recapbuttons[i]):SetNormalTexture("Interface\\AddOns\\Recap\\RecapButtons-Down")
			getglobal(recapbuttons[i]):SetPushedTexture("Interface\\AddOns\\Recap\\RecapButtons-Up")
		else
			getglobal(recapbuttons[i]):SetNormalTexture("Interface\\AddOns\\Recap\\RecapButtons-Up")
			getglobal(recapbuttons[i]):SetPushedTexture("Interface\\AddOns\\Recap\\RecapButtons-Down")
		end
	end

	if recap.Opt.View.value=="Last" then
		RecapShowAllButton:SetNormalTexture("Interface\\AddOns\\Recap\\RecapButtons-Up")
		RecapShowAllButton:SetPushedTexture("Interface\\AddOns\\Recap\\RecapButtons-Down")
	else
		RecapShowAllButton:SetNormalTexture("Interface\\AddOns\\Recap\\RecapButtons-Down")
		RecapShowAllButton:SetPushedTexture("Interface\\AddOns\\Recap\\RecapButtons-Up")
	end
end

-- this resizes the window width to the columns defined in options
function Recap_SetColumns()

	local cx = 168 -- 8(edge)+120(name)+32(scroll)+8(edge)
	
	local i,j,icx,item

	RecapHeader_Name:Show()
	RecapTotals:Show()

	if recap.Opt.SelfView.value then
		for i in recap.Opt do
			if recap.Opt[i].ewidth then
				if recap.Opt[i].value then
					getglobal("RecapHeader_"..i):SetWidth(recap_temp.DefaultOpt[i].ewidth)
					getglobal("RecapHeader_"..i):Show()
					for j=1,15 do
						getglobal("RecapSelf"..j.."_"..i):SetWidth(recap_temp.DefaultOpt[i].ewidth)
						getglobal("RecapSelf"..j.."_"..i):Show()
					end
					cx = cx + recap_temp.DefaultOpt[i].ewidth
				else
					getglobal("RecapHeader_"..i):SetWidth(1)
					getglobal("RecapHeader_"..i):Hide()
					for j=1,15 do
						getglobal("RecapSelf"..j.."_"..i):SetWidth(1)
						getglobal("RecapSelf"..j.."_"..i):Hide()
					end
					cx = cx + 1
				end
			elseif recap.Opt[i].width then
				getglobal("RecapHeader_"..i):Hide()
			end
		end

		RecapHeader_EName:Show()
		RecapHeader_Name:Hide()
		RecapTotals:Hide()

		for i=1,15 do
			getglobal("RecapList"..i):Hide()
			getglobal("RecapSelf"..i):Show()
			getglobal("RecapSelf"..i):SetWidth(cx-48)
		end
		RecapTotals_DPSIn:Hide()

	else

		for i in recap.Opt do
			if recap.Opt[i].width then
				if recap.Opt[i].value then
					item = getglobal("RecapHeader_"..i)
					item:SetWidth(recap_temp.DefaultOpt[i].width)
					item:Show()
					item = getglobal("RecapTotals_"..i)
					item:SetWidth(recap_temp.DefaultOpt[i].width)
					item:Show()
					for j=1,15 do
						item = getglobal("RecapList"..j.."_"..i)
						item:SetWidth(recap_temp.DefaultOpt[i].width)
						item:Show()
					end
					cx = cx + recap_temp.DefaultOpt[i].width
				else
					item = getglobal("RecapHeader_"..i)
					item:SetWidth(1)
					item:Hide()
					item = getglobal("RecapTotals_"..i)
					item:SetWidth(1)
					item:Hide()
					for j=1,15 do
						item = getglobal("RecapList"..j.."_"..i)
						item:SetWidth(1)
						item:Hide()
					end
					cx = cx + 1
				end
			elseif recap.Opt[i].ewidth then
				getglobal("RecapHeader_"..i):Hide()
			end
		end

		for i=1,15 do
			getglobal("RecapSelf"..i):Hide()
			getglobal("RecapList"..i):Show()
			getglobal("RecapList"..i):SetWidth(cx-48)
		end
		RecapHeader_EName:Hide()

		if recap.Opt.DPSIn.value then
			RecapTotals_DPSIn:Show()
		else
			RecapTotals_DPSIn:Hide()
		end

	end

	RecapTotals:SetWidth(cx-48)

	if recap.Opt.GrowLeftwards.value and RecapFrame then
		i = RecapFrame:GetRight()
		j = RecapFrame:GetTop()
		if i and j then
			RecapFrame:ClearAllPoints()
			RecapFrame:SetPoint("TOPLEFT","UIParent","BOTTOMLEFT",i-cx,j) -- *** i not defined sometimes :( **
		end
	end

	RecapTopBar:SetWidth(cx-8)
	RecapBottomBar:SetWidth(cx-16)
	RecapFrame:SetWidth(cx)
	RecapScrollBar:SetWidth(cx-16)

	recap_temp.GaugeWidth = cx-51 - ( (not recap.Opt.SelfView.value) and ((((recap.Opt.Faction.value and recap.Opt.Faction.width) or 1) + ((recap.Opt.Class.value and recap.Opt.Class.width) or 1) + ((recap.Opt.Ranks.value and recap.Opt.Ranks.width) or 1)) or 0) or 0)

	Recap_CheckWindowBounds()
	Recap_SetView()
	RecapScrollBar_Update()

end

-- changes view mode
function Recap_SetView(arg1)

	local text

		if arg1 then
			recap.Opt.View.value = arg1
		end

		Recap_SetButtons()
		Recap_ConstructList()

		cx = RecapFrame:GetWidth()

		-- change titlebar
		if cx and cx>300 and recap.Opt.SelfView.value then
			text = recap_temp.Player.."'s Details"
		elseif cx and cx>230 and recap.Opt.SelfView.value then
			text = recap_temp.Player
		elseif cx and cx<230 or recap.Opt.Minimized.value then
			text = ""
			if cx>180 and not recap.Opt.SelfView.value then
				text = recap.Opt.View.value
			end		
		elseif cx and cx<260 then
			text = recap_temp.Local.LastAll[recap.Opt.View.value]
		else
			text = "Recap of "..recap_temp.Local.LastAll[recap.Opt.View.value]
		end

		RecapTitle:SetText(text)
		RecapHeader_Name:SetText((recap_temp.ListSize-1).." Combatants")
		Recap_ChangeBack()
end

function Recap_SetColors()

	if recap.Opt.UseColor.value then
		recap_temp.ColorDmgIn = recap_temp.ColorRed
		recap_temp.ColorDmgOut = recap_temp.ColorGreen
		recap_temp.ColorHeal = recap_temp.ColorBlue
		recap_temp.ColorHits = recap_temp.ColorCyan
		recap_temp.ColorTicks = recap_temp.ColorYellow
		recap_temp.ColorCrits = recap_temp.ColorLime
		recap_temp.ColorMiss = recap_temp.ColorPink
	else
		recap_temp.ColorDmgIn = recap_temp.ColorWhite
		recap_temp.ColorDmgOut = recap_temp.ColorNone
		recap_temp.ColorHeal = recap_temp.ColorWhite
		recap_temp.ColorHits = recap_temp.ColorWhite
		recap_temp.ColorTicks = recap_temp.ColorWhite
		recap_temp.ColorCrits = recap_temp.ColorWhite
		recap_temp.ColorMiss = recap_temp.ColorWhite
	end

	RecapTotals_DmgIn:SetTextColor(recap_temp.ColorDmgIn.r,recap_temp.ColorDmgIn.g,recap_temp.ColorDmgIn.b)
	RecapTotals_DmgOut:SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
	RecapTotals_DPS:SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
	RecapTotals_DPSvsAll:SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
	RecapTotals_DPSIn:SetTextColor(recap_temp.ColorDmgIn.r,recap_temp.ColorDmgIn.g,recap_temp.ColorDmgIn.b)
	RecapTotals_Heal:SetTextColor(recap_temp.ColorHeal.r,recap_temp.ColorHeal.g,recap_temp.ColorHeal.b)
	RecapTotals_Over:SetTextColor(recap_temp.ColorHeal.r,recap_temp.ColorHeal.g,recap_temp.ColorHeal.b)
	RecapMinDPSIn_Text:SetTextColor(recap_temp.ColorDmgIn.r,recap_temp.ColorDmgIn.g,recap_temp.ColorDmgIn.b)
	RecapMinDPSOut_Text:SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
	for i=1,15 do
		getglobal("RecapList"..i.."_HealP"):SetTextColor(recap_temp.ColorHeal.r,recap_temp.ColorHeal.g,recap_temp.ColorHeal.b)
		getglobal("RecapList"..i.."_Over"):SetTextColor(recap_temp.ColorHeal.r,recap_temp.ColorHeal.g,recap_temp.ColorHeal.b)
		getglobal("RecapList"..i.."_DmgInP"):SetTextColor(recap_temp.ColorDmgIn.r,recap_temp.ColorDmgIn.g,recap_temp.ColorDmgIn.b)
		getglobal("RecapList"..i.."_DmgOutP"):SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
		getglobal("RecapSelf"..i.."_EHits"):SetTextColor(recap_temp.ColorHits.r,recap_temp.ColorHits.g,recap_temp.ColorHits.b)
		getglobal("RecapSelf"..i.."_EHitsAvg"):SetTextColor(recap_temp.ColorHits.r,recap_temp.ColorHits.g,recap_temp.ColorHits.b)
		getglobal("RecapSelf"..i.."_EHitsMax"):SetTextColor(recap_temp.ColorHits.r,recap_temp.ColorHits.g,recap_temp.ColorHits.b)
		getglobal("RecapSelf"..i.."_ETicks"):SetTextColor(recap_temp.ColorTicks.r,recap_temp.ColorTicks.g,recap_temp.ColorTicks.b)
		getglobal("RecapSelf"..i.."_ETicksAvg"):SetTextColor(recap_temp.ColorTicks.r,recap_temp.ColorTicks.g,recap_temp.ColorTicks.b)
		getglobal("RecapSelf"..i.."_ETicksMax"):SetTextColor(recap_temp.ColorTicks.r,recap_temp.ColorTicks.g,recap_temp.ColorTicks.b)
		getglobal("RecapSelf"..i.."_ECrits"):SetTextColor(recap_temp.ColorCrits.r,recap_temp.ColorCrits.g,recap_temp.ColorCrits.b)
		getglobal("RecapSelf"..i.."_ECritsAvg"):SetTextColor(recap_temp.ColorCrits.r,recap_temp.ColorCrits.g,recap_temp.ColorCrits.b)
		getglobal("RecapSelf"..i.."_ECritsMax"):SetTextColor(recap_temp.ColorCrits.r,recap_temp.ColorCrits.g,recap_temp.ColorCrits.b)
		getglobal("RecapSelf"..i.."_ECritsP"):SetTextColor(recap_temp.ColorCrits.r,recap_temp.ColorCrits.g,recap_temp.ColorCrits.b)
		getglobal("RecapSelf"..i.."_EMiss"):SetTextColor(recap_temp.ColorMiss.r,recap_temp.ColorMiss.g,recap_temp.ColorMiss.b)
		getglobal("RecapSelf"..i.."_EMissP"):SetTextColor(recap_temp.ColorMiss.r,recap_temp.ColorMiss.g,recap_temp.ColorMiss.b)
	end
	Recap_ChangeBack()
end

function Recap_ChangeBack()

	if recap.Opt.UseColor.value and (recap.Opt.MinBack.value or not recap.Opt.Minimized.value) then

		if recap.Opt.SelfView.value and not recap.Opt.Minimized.value then
			RecapTitleBack:SetVertexColor(0,0,.8)
		elseif recap.Opt.View.value=="Last" then
			RecapTitleBack:SetVertexColor(0,.66,0)
		else
			RecapTitleBack:SetVertexColor(.66,0,0)
		end
		RecapTitleBack:Show()
	else
		RecapTitleBack:Hide()
	end
end


--[[ Initialization ]]

-- Can be called often, will immediately dump out if it ran through successfully
function Recap_Initialize()

	local i,x
	local init_time

	if not recap_temp.Loaded then

		if UnitName("player") and UnitName("player")~=UNKNOWNOBJECT and RecapFrame and RecapOptFrame then

			init_time = GetTime()

			recap_temp.Player = UnitName("player")
			recap_temp.Server = GetCVar("realmName")

			-- recap index for this player
			recap_temp.p = recap_temp.Player..recap_temp.Server -- use for stuff that can be made global
			recap_temp.s = recap_temp.Player..recap_temp.Server -- use for stuff that should never be global

			if recap and recap.UseOneSettings and recap.User then
				recap_temp.p = recap.User
			end

			-- sources of damage/heals otherwise not declared in chat spam
			recap_temp.FilterIndex[6] = recap_temp.Player
			recap_temp.FilterIndex[7] = "Melee"
			recap_temp.FilterIndex[8] = "Damage Shields"
			recap_temp.FilterIndex[13] = 0

			Recap_InitializeData()

			if not recap[recap_temp.p].WindowTop and RecapFrame then
				RecapFrame:ClearAllPoints()
				RecapFrame:SetPoint("CENTER","UIParent","CENTER")
				RecapOptFrame:ClearAllPoints()
				RecapOptFrame:SetPoint("CENTER", "UIParent", "CENTER")
			elseif recap[recap_temp.p].WindowTop and RecapFrame then
				RecapFrame:ClearAllPoints()
				RecapFrame:SetPoint("TOPLEFT","UIParent","BOTTOMLEFT",recap[recap_temp.p].WindowLeft,recap[recap_temp.p].WindowTop)
				RecapFrame:SetWidth(recap[recap_temp.p].WindowWidth)
				RecapFrame:SetHeight(recap[recap_temp.p].WindowHeight)
			end

			for i in recap.Combatant do
				recap.Combatant[i].WasInLast = false
				recap.Combatant[i].WasInCurrent = false
			end
			recap[recap_temp.p].LastDuration = 0

			Recap_SetColors()

			-- if we loaded out of stopped state, force into idle state
			if recap.Opt.State.value ~= "Stopped" then
				recap.Opt.State.value = "Idle"
			end
			Recap_SetState()

			RecapTotals_Name:SetWidth(82)

			Recap_InitDataSets()

			if recap.Opt.LightData.value then
				recap.Opt.SelfView.value = false
				RecapSelfViewButton:Hide()
			end

			if recap.Opt.Minimized.value then
				Recap_SetButtons()
				Recap_ConstructList()
				Recap_Minimize()
			else
				Recap_Maximize()
			end

			-- we're in the world (in theory), register active events
			this:RegisterEvent("PLAYER_REGEN_DISABLED")
			this:RegisterEvent("PLAYER_REGEN_ENABLED")
			this:RegisterEvent("PARTY_MEMBERS_CHANGED")
			this:RegisterEvent("RAID_ROSTER_UPDATE")

			Recap_Register_CombatEvents()

			recap_temp.Loaded = true

			Recap_InitializeOptions()
			Recap_MoveMinimize()

			-- initialize panel
			recap_temp.Selected = 0
			if recap.Opt.LightData.value then
				recap.Opt.PanelView.value = 3
				RecapPanelTab1:SetAlpha(.5)
				RecapPanelTab2:SetAlpha(.5)
			end
			RecapPanel_SwitchPanels(recap.Opt.PanelView.value)
			if recap.Opt.PanelAnchor.value then
				RecapPanel:ClearAllPoints()
				RecapPanel:SetPoint(recap.Opt.PanelAnchor.Panel,"RecapFrame",recap.Opt.PanelAnchor.Main,Recap_PanelOffset("x"),Recap_PanelOffset("y"))
			end
			RecapPanelFaction:SetAlpha(.75)

			for i=1,15 do
				getglobal("RecapList"..i.."_Faction"):SetAlpha(.75)
			end

			if recap.Opt.Visible.value then
				RecapFrame_Show()
			else
				RecapFrame_Hide()
			end

			if recap.Opt.Paused.value then
				RecapFrame_Hide()
			end

			if recap.debug then
				DEFAULT_CHAT_FRAME:AddMessage("[recap.debug] Time to initialize: "..string.format("%.4f",GetTime()-init_time).." seconds.")
			end

			-- warn if user has a lot of data (over 500 combatants)
			x = 0
			for i in recap.Combatant do
				x = x + 1
			end
			if x>500 and recap.Opt.WarnData.value then
				StaticPopupDialogs["RECAP_LOTS_OF_DATA_WARNING"] = {
					text = TEXT(string.format(recap_temp.Local.LotsOfDataWarning,x)),
					button1 = TEXT(OKAY),
					timeout = 0,
					showAlert = 1,
					whileDead = 1
				}
				StaticPopup_Show("RECAP_LOTS_OF_DATA_WARNING")
			end

			table.insert(UISpecialFrames,"RecapOptFrame")
			table.insert(UISpecialFrames,"RecapPanel")

		end

	end

end

-- arg1==1 to hide buttons, 16 to show
function Recap_ShowButtons(arg1)

	RecapCloseButton:SetWidth(arg1)
	RecapPinButton:SetWidth(arg1)
	RecapPauseButton:SetWidth(arg1)
	RecapOptionsButton:SetWidth(arg1)
	RecapShowAllButton:SetWidth(arg1)

	if arg1==16 then
		RecapCloseButton:Show()
		RecapPinButton:Show()
		RecapPauseButton:Show()
		RecapOptionsButton:Show()
		RecapShowAllButton:Show()
	else
		RecapCloseButton:Hide()
		RecapPinButton:Hide()
		RecapPauseButton:Hide()
		RecapOptionsButton:Hide()
		RecapShowAllButton:Hide()
	end
end
		
function Recap_Minimize()

	local i,j
	local cx = 20 -- 8(edge)+4(space right of status)+8

	recap.Opt.Minimized.value = true

	for i in recap.Opt do
		if recap.Opt[i].minwidth then
			if recap.Opt[i].value then
				item = getglobal("Recap"..i)
				item:SetWidth(recap.Opt[i].minwidth)
				item:Show()
				cx = cx + recap.Opt[i].minwidth
			else
				item = getglobal("Recap"..i)
				item:SetWidth(1)
				item:Hide()
				cx = cx + 1
			end
		elseif recap.Opt[i].width or recap.Opt[i].ewidth then
			getglobal("RecapHeader_"..i):Hide()
		end
	end

	if recap.Opt.MinButtons.value then
		cx = cx + 108
		Recap_ShowButtons(16)
	else
		Recap_ShowButtons(1)
		cx = cx + 23 -- 5 + 18
	end

	RecapTitle:SetText(recap.Opt.View.value)

	RecapTopBar:Hide()
	RecapBottomBar:Hide()
	RecapTotals_DPSIn:Hide()
	RecapResetButton:Hide()
	for i=1,15 do
		getglobal("RecapList"..i):Hide()
		getglobal("RecapSelf"..i):Hide()
	end
	RecapTotals:Hide()
	RecapHeader_EName:Hide()
	RecapHeader_Name:Hide()
	RecapScrollBar:Hide()

	if recap.Opt.AutoMinimize.value then
		cx = cx-15
	end

	if recap.Opt.GrowLeftwards.value and RecapFrame then
		i = RecapFrame:GetRight()
		j = RecapFrame:GetTop()
		if i and j then
			RecapFrame:ClearAllPoints()
			RecapFrame:SetPoint("TOPLEFT","UIParent","BOTTOMLEFT",i-cx,j)
		end
	end
	RecapFrame:SetWidth(cx)
	Recap_SetHeight(28)

	if recap.Opt.MinBack.value then
		RecapFrame:SetBackdropColor(1,1,1,1)
		RecapFrame:SetBackdropBorderColor(.5,.5,.5,1)
	else
		RecapFrame:SetBackdropColor(0,0,0,0)
		RecapFrame:SetBackdropBorderColor(0,0,0,0)
	end

	RecapSelfViewButton:Hide()
	RecapPanel_Hide(1)
	Recap_MoveMinimize()
	Recap_ChangeBack()

	RecapMinView:GetLeft()

end

function Recap_Maximize()

	recap.Opt.Minimized.value = false

	for i in recap.Opt do
		if recap.Opt[i].minwidth then
			item = getglobal("Recap"..i)
			item:SetWidth(recap.Opt[i].minwidth)
			item:Hide()
		end
	end
	RecapMinStatus:Show()
	RecapMinView:Show()
	Recap_ShowButtons(16)

	RecapTopBar:Show()
	RecapBottomBar:Show()
	RecapResetButton:Show()

	RecapTotals:Show()
	RecapHeader_Name:Show()

	RecapScrollBar:Show()
	Recap_SetColumns()

	RecapFrame:SetBackdropColor(1,1,1,1)
	RecapFrame:SetBackdropBorderColor(.5,.5,.5,1)
	if not recap.Opt.LightData.value then
		RecapSelfViewButton:Show()
	end
	Recap_MoveMinimize()
end

function Recap_SetHeight(newcy)

	local i,j

	if recap.Opt.GrowUpwards.value and RecapFrame then
		i = RecapFrame:GetBottom()
		j = RecapFrame:GetLeft()
		if i and j then
			RecapFrame:ClearAllPoints()
			RecapFrame:SetPoint("TOPLEFT","UIParent","BOTTOMLEFT",j,i+newcy)
		end
	end

	RecapFrame:SetHeight(newcy)
	Recap_CheckWindowBounds()
end

function RecapScrollBar_Update()

	local i, index, item
	local listsize = recap.Opt.SelfView.value and recap_temp.SelfListSize or recap_temp.ListSize


	if not recap.Opt.Minimized.value then

		i = 72 + ( math.max( math.min(listsize-1,recap.Opt.MaxRows.value),1 )*14 ) 
		Recap_SetHeight(i)
		RecapScrollBar:SetHeight(i-63)
		FauxScrollFrame_Update(RecapScrollBar,listsize-1,recap.Opt.MaxRows.value,10)

		for i=1,recap.Opt.MaxRows.value do
			index = i + FauxScrollFrame_GetOffset(RecapScrollBar)

			if not recap.Opt.SelfView.value then

				if index < listsize then

					getglobal("RecapList"..i.."_Ranks"):SetText(index..".")

					Recap_SetFactionIcon(i,recap.Combatant[recap_temp.List[index].Name].Faction)
					Recap_SetClassIcon(i,recap.Combatant[recap_temp.List[index].Name].Class)
					if recap.Opt.Faction.value and recap.Combatant[recap_temp.List[index].Name].Level==-1 then
						getglobal("RecapList"..i.."_Level"):SetText("??")
					elseif recap.Opt.Faction.value and recap.Combatant[recap_temp.List[index].Name].Level and tonumber(recap.Combatant[recap_temp.List[index].Name].Level)>0 then
						getglobal("RecapList"..i.."_Level"):SetText(recap.Combatant[recap_temp.List[index].Name].Level)
					else
						getglobal("RecapList"..i.."_Level"):SetText(" ")
					end

					item = getglobal("RecapList"..i.."_Name")
					item:SetText(recap_temp.List[index].Name)
					if recap.Combatant[recap_temp.List[index].Name].Friend then
						item:SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
					else
						item:SetTextColor(recap_temp.ColorWhite.r,recap_temp.ColorWhite.g,recap_temp.ColorWhite.b)
					end

					item = getglobal("RecapList"..i.."_Gauge")
					if recap.Opt.ShowGauges.value and recap_temp.GaugeMax>0 and recap.Combatant[recap_temp.List[index].Name].Friend and recap_temp.GaugeBy then
						item:SetWidth(1+recap_temp.GaugeWidth*recap_temp.List[index][recap_temp.GaugeBy]/recap_temp.GaugeMax)
						item:Show()
					else
						item:Hide()
					end

					getglobal("RecapList"..i.."_Kills"):SetText(recap_temp.List[index].Kills)

					getglobal("RecapList"..i.."_Time"):SetText(Recap_FormatTime(recap_temp.List[index].Time))

					item = getglobal("RecapList"..i.."_Heal")
					item:SetText(recap_temp.List[index].Heal)
					if recap.Combatant[recap_temp.List[index].Name].Friend then
						item:SetTextColor(recap_temp.ColorHeal.r,recap_temp.ColorHeal.g,recap_temp.ColorHeal.b)
					else
						item:SetTextColor(recap_temp.ColorWhite.r,recap_temp.ColorWhite.g,recap_temp.ColorWhite.b)
					end

					getglobal("RecapList"..i.."_HealP"):SetText(recap.Combatant[recap_temp.List[index].Name].Friend and recap_temp.List[index].HealP.."%" or "")
					getglobal("RecapList"..i.."_Over"):SetText(recap.Combatant[recap_temp.List[index].Name].Friend and recap_temp.List[index].Over.."%" or "")

					getglobal("RecapList"..i.."_MaxHit"):SetText(recap_temp.List[index].MaxHit)

					item = getglobal("RecapList"..i.."_DmgIn")
					item:SetText(recap_temp.List[index].DmgIn)
					if recap.Combatant[recap_temp.List[index].Name].Friend then
						item:SetTextColor(recap_temp.ColorDmgIn.r,recap_temp.ColorDmgIn.g,recap_temp.ColorDmgIn.b)
					else
						item:SetTextColor(recap_temp.ColorWhite.r,recap_temp.ColorWhite.g,recap_temp.ColorWhite.b)
					end

					getglobal("RecapList"..i.."_DmgInP"):SetText(recap.Combatant[recap_temp.List[index].Name].Friend and recap_temp.List[index].DmgInP.."%" or "")

					item = getglobal("RecapList"..i.."_DmgOut")
					item:SetText(recap_temp.List[index].DmgOut)
					if recap.Combatant[recap_temp.List[index].Name].Friend then
						item:SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
					else
						item:SetTextColor(recap_temp.ColorWhite.r,recap_temp.ColorWhite.g,recap_temp.ColorWhite.b)
					end

					getglobal("RecapList"..i.."_DmgOutP"):SetText(recap.Combatant[recap_temp.List[index].Name].Friend and recap_temp.List[index].DmgOutP.."%" or "")

					item = getglobal("RecapList"..i.."_DPS")
					item:SetText(format(recap_temp.List[index].DPS<1000 and "%.1f" or "%d",recap_temp.List[index].DPS))
					if recap.Combatant[recap_temp.List[index].Name].Friend then
						item:SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
					else
						item:SetTextColor(recap_temp.ColorWhite.r,recap_temp.ColorWhite.g,recap_temp.ColorWhite.b)
					end
					item = getglobal("RecapList"..i.."_DPSvsAll")
					item:SetText(format(recap_temp.List[index].DPSvsAll<1000 and "%.1f" or "%d",recap_temp.List[index].DPSvsAll))
					if recap.Combatant[recap_temp.List[index].Name].Friend then
						item:SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
					else
						item:SetTextColor(recap_temp.ColorWhite.r,recap_temp.ColorWhite.g,recap_temp.ColorWhite.b)
					end

					item = getglobal("RecapList"..i)
					item:Show()
					if recap_temp.Selected == index then
						item:LockHighlight()
					else
						item:UnlockHighlight()
					end

				else
					getglobal("RecapList"..i):Hide()
					getglobal("RecapList"..i):UnlockHighlight()
				end

				for i=recap.Opt.MaxRows.value+1,15 do
					getglobal("RecapList"..i):Hide()
				end

			elseif recap.Opt.SelfView.value then

				if index < listsize then

					getglobal("RecapSelf"..i.."_EName"):SetText(string.sub(recap_temp.SelfList[index].EName,2))
					getglobal("RecapSelf"..i.."_ETotal"):SetText(recap_temp.SelfList[index].ETotal)
					getglobal("RecapSelf"..i.."_EHits"):SetText(recap_temp.SelfList[index].EHits)
					getglobal("RecapSelf"..i.."_ETicks"):SetText(recap_temp.SelfList[index].ETicks)
					getglobal("RecapSelf"..i.."_ECrits"):SetText(recap_temp.SelfList[index].ECrits)
					getglobal("RecapSelf"..i.."_EHitsAvg"):SetText(recap_temp.SelfList[index].EHitsAvg)
					getglobal("RecapSelf"..i.."_ETicksAvg"):SetText(recap_temp.SelfList[index].ETicksAvg)
					getglobal("RecapSelf"..i.."_ECritsAvg"):SetText(recap_temp.SelfList[index].ECritsAvg)
					getglobal("RecapSelf"..i.."_EHitsMax"):SetText(recap_temp.SelfList[index].EHitsMax)
					getglobal("RecapSelf"..i.."_ETicksMax"):SetText(recap_temp.SelfList[index].ETicksMax)
					getglobal("RecapSelf"..i.."_ECritsMax"):SetText(recap_temp.SelfList[index].ECritsMax)
					getglobal("RecapSelf"..i.."_ECritsP"):SetText(recap_temp.SelfList[index].ECritsP)
					getglobal("RecapSelf"..i.."_EMiss"):SetText(recap_temp.SelfList[index].EMiss)
					getglobal("RecapSelf"..i.."_EMissP"):SetText(recap_temp.SelfList[index].EMissP)
					getglobal("RecapSelf"..i.."_EMaxAll"):SetText(recap_temp.SelfList[index].EMaxAll)
					getglobal("RecapSelf"..i.."_ETotalP"):SetText(recap_temp.SelfList[index].ETotalP)
					if string.sub(recap_temp.SelfList[index].EName,1,1)<"3" then
						getglobal("RecapSelf"..i.."_EName"):SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
						getglobal("RecapSelf"..i.."_ETotal"):SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
						getglobal("RecapSelf"..i.."_ETotalP"):SetTextColor(recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
					else
						getglobal("RecapSelf"..i.."_EName"):SetTextColor(recap_temp.ColorHeal.r,recap_temp.ColorHeal.g,recap_temp.ColorHeal.b)
						getglobal("RecapSelf"..i.."_ETotal"):SetTextColor(recap_temp.ColorHeal.r,recap_temp.ColorHeal.g,recap_temp.ColorHeal.b)
						getglobal("RecapSelf"..i.."_ETotalP"):SetTextColor(recap_temp.ColorHeal.r,recap_temp.ColorHeal.g,recap_temp.ColorHeal.b)
					end						
					getglobal("RecapSelf"..i):Show()
				else
					getglobal("RecapSelf"..i):Hide()
				end

				for i=recap.Opt.MaxRows.value+1,15 do
					getglobal("RecapSelf"..i):Hide()
				end

			end

		end

	end
end

--[[ Fight functions ]]

function Recap_CreateCombatant(arg1,is_source)

	if not arg1 then
		return
	end

	-- if arg1 not a .Combatant, add an entry for them
	if not recap.Combatant[arg1] then
		Recap_CreateBlankCombatant(arg1)
	end
	-- if they haven't fought this session, add a .Last entry for them
	if not recap_temp.Last[arg1] then
		recap_temp.Last[arg1] = { Start = 0, End = 0, DmgIn = 0, DmgOut = 0, MaxHit = 0, Kills = 0, Heal = 0 }
	end
	-- if arg1 is hitting others, update their start or end time
	if is_source then

		if recap_temp.FightStart==0 then
			recap_temp.FightStart = GetTime()
			recap_temp.FightEnd = recap_temp.FightStart
		else
			recap_temp.FightEnd = GetTime()
		end

		if recap_temp.Last[arg1].Start==0 then
			recap_temp.Last[arg1].Start = GetTime()
			recap_temp.Last[arg1].End = recap_temp.Last[arg1].Start
		else
			recap_temp.Last[arg1].End = GetTime()
		end
	end

	recap.Combatant[arg1].WasInCurrent = true
end

function Recap_StartFight()

	local i

	if recap.Opt.State.value=="Idle" then
		if recap.Opt.AutoHide.value and not recap.Opt.Minimized.value then
			RecapFrame_Hide()
		end
		for i in recap.Combatant do
			recap.Combatant[i].WasInCurrent = false
		end
		if not recap.Opt.LimitFights.value and recap.Opt.IdleFight.value then
			recap_temp.IdleTimer = 0
		end
		RecapIdleFrame:Show() -- start idle OnUpdates
		Recap_SetState("Active")
	end
end

-- returns the current DPS of combatant named arg1 if it exists. UpdateDuration/DamageOut reused to save allocations
function Recap_GetCurrentDPS(arg1)

	if arg1 and recap_temp.Last[arg1] and recap_temp.Last[arg1].DmgOut>0 then
		recap_temp.UpdateDuration = recap_temp.Last[arg1].End - recap_temp.Last[arg1].Start
		if recap.Opt.View.value=="All" then
			recap_temp.UpdateDuration = recap_temp.UpdateDuration + recap.Combatant[arg1].TotalTime
		end
		recap_temp.UpdateDamageOut = recap_temp.Last[arg1].DmgOut
		if recap.Opt.View.value=="All" then
			recap_temp.UpdateDamageOut = recap_temp.UpdateDamageOut + recap.Combatant[arg1].TotalDmgOut
		end

		if recap_temp.UpdateDuration > recap_temp.MinTime then
			return (recap_temp.UpdateDamageOut / recap_temp.UpdateDuration)
		end
	end

	return 0
end

function Recap_UpdateMinimizedDPS()

	if recap.Opt.State.value=="Active" then

		if recap_temp.Last[recap_temp.Player] and recap_temp.Last[recap_temp.Player].DmgOut>0 then
			i = Recap_GetCurrentDPS(recap_temp.Player)+Recap_GetCurrentDPS(UnitName("pet"))
			RecapMinYourDPS_Text:SetText(string.format("%.1f",i))
		end

		-- calculate running total DPS In and Out
		recap_temp.UpdateDuration = 0
		if recap_temp.FightEnd>0 and recap_temp.FightStart>0 then
			recap_temp.UpdateDuration = recap_temp.FightEnd - recap_temp.FightStart
		end
		if recap.Opt.View.value=="All" then
			recap_temp.UpdateDuration = recap_temp.UpdateDuration + recap[recap_temp.p].TotalDuration
		end

		if recap_temp.UpdateDuration>recap_temp.MinTime then

			recap_temp.UpdateDmgIn = 0
			recap_temp.UpdateDmgOut = 0
			for i in recap_temp.Last do
				if recap.Combatant[i].Friend then
					recap_temp.UpdateDmgIn = recap_temp.UpdateDmgIn + recap_temp.Last[i].DmgIn
					recap_temp.UpdateDmgOut = recap_temp.UpdateDmgOut + recap_temp.Last[i].DmgOut
				end
			end

			if recap.Opt.View.value=="All" then
				for i in recap.Combatant do
					if recap.Combatant[i].Friend then
						recap_temp.UpdateDmgIn = recap_temp.UpdateDmgIn + recap.Combatant[i].TotalDmgIn
						recap_temp.UpdateDmgOut = recap_temp.UpdateDmgOut + recap.Combatant[i].TotalDmgOut
					end
				end
			end

			RecapMinDPSIn_Text:SetText(string.format("%.1f",recap_temp.UpdateDmgIn/recap_temp.UpdateDuration))
			RecapMinDPSOut_Text:SetText(string.format("%.1f",recap_temp.UpdateDmgOut/recap_temp.UpdateDuration))

		end

		Recap_UpdatePlugins()
	end
end

function Recap_AddPetToOwner(pet,owner)

	local earliest,latest
	local peteffect

	-- times are only for DAMAGE
	if (recap.Combatant[pet].WasInCurrent and recap_temp.Last[pet].DmgOut>0) and (recap.Combatant[owner].WasInCurrent and recap_temp.Last[owner].DmgOut>0) then
		-- both pet and owner were in fight
		earliest = math.min(recap_temp.Last[pet].Start,recap_temp.Last[owner].Start)
		latest = math.max(recap_temp.Last[pet].End,recap_temp.Last[owner].End)
		recap_temp.Last[owner].Start = earliest
		recap_temp.Last[owner].End = latest
	elseif (recap.Combatant[pet].WasInCurrent and recap_temp.Last[pet].DmgOut>0) and (not recap.Combatant[owner].WasInCurrent or recap_temp.Last[owner].DmgOut==0) then
		-- just the pet was in fight
		recap_temp.Last[owner].Start = recap_temp.Last[pet].Start
		recap_temp.Last[owner].End = recap_temp.Last[pet].End
	else
		return -- temp fix for attempt to index field ? (a nil value) in line below ***
	end

	recap_temp.Last[owner].DmgIn = recap_temp.Last[owner].DmgIn + recap_temp.Last[pet].DmgIn
	recap_temp.Last[owner].DmgOut = recap_temp.Last[owner].DmgOut + recap_temp.Last[pet].DmgOut
	recap_temp.Last[owner].MaxHit = math.max(recap_temp.Last[owner].MaxHit,recap_temp.Last[pet].MaxHit)

	Recap_AccumulateCombatant(owner)

	recap_temp.Last[pet].Start = 0
	recap_temp.Last[pet].End = 0
	recap_temp.Last[pet].DmgIn = 0
	recap_temp.Last[pet].DmgOut = 0
	recap_temp.Last[pet].MaxHit = 0
	recap_temp.Last[pet].Kills = 0
	recap_temp.Last[pet].Heal = 0
	recap.Combatant[pet].WasInLast = false
	recap.Combatant[owner].WasInLast = true
	recap.Combatant[pet].WasInCurrent = false
	recap.Combatant[owner].WasInCurrent = false

	if not recap.Opt.LightData.value then
		for i in recap.Combatant[pet].Detail do
			if string.sub(i,1,1)=="1" then
				peteffect = "2"..pet.."'s "..string.sub(i,2)
			else
				peteffect = "4"..pet.."'s "..string.sub(i,2)
			end
			recap.Combatant[owner].Detail[peteffect] = {
				Hits = recap.Combatant[pet].Detail[i].Hits, HitsDmg = recap.Combatant[pet].Detail[i].HitsDmg,
				HitsMax = recap.Combatant[pet].Detail[i].HitsMax, Crits = recap.Combatant[pet].Detail[i].Crits,
				CritsEvents = recap.Combatant[pet].Detail[i].CritsEvents, CritsDmg = recap.Combatant[pet].Detail[i].CritsDmg,
				CritsMax = recap.Combatant[pet].Detail[i].CritsMax, Missed = recap.Combatant[pet].Detail[i].Missed,
				TicksDmg = recap.Combatant[pet].Detail[i].TicksDmg, TicksMax = recap.Combatant[pet].Detail[i].TicksMax }
			if owner==recap_temp.Player then
				recap.Self[recap_temp.s][peteffect] = {
					Hits = recap.Combatant[pet].Detail[i].Hits, HitsDmg = recap.Combatant[pet].Detail[i].HitsDmg,
					HitsMax = recap.Combatant[pet].Detail[i].HitsMax, Crits = recap.Combatant[pet].Detail[i].Crits,
					CritsEvents = recap.Combatant[pet].Detail[i].CritsEvents, CritsDmg = recap.Combatant[pet].Detail[i].CritsDmg,
					CritsMax = recap.Combatant[pet].Detail[i].CritsMax, Missed = recap.Combatant[pet].Detail[i].Missed,
					TicksDmg = recap.Combatant[pet].Detail[i].TicksDmg, TicksMax = recap.Combatant[pet].Detail[i].TicksMax }
			end
		end
	end

end

function Recap_AccumulateCombatant(i)

	recap.Combatant[i].LastDmgIn = recap_temp.Last[i].DmgIn
	recap.Combatant[i].LastDmgOut = recap_temp.Last[i].DmgOut
	recap.Combatant[i].LastTime = recap_temp.Last[i].End-recap_temp.Last[i].Start
	recap.Combatant[i].LastMaxHit = recap_temp.Last[i].MaxHit
	recap.Combatant[i].LastKills = recap_temp.Last[i].Kills
	recap.Combatant[i].LastHeal = recap_temp.Last[i].Heal

	recap_temp.Last[i].Start = 0
	recap_temp.Last[i].End = 0
	recap_temp.Last[i].DmgIn = 0
	recap_temp.Last[i].DmgOut = 0
	recap_temp.Last[i].MaxHit = 0
	recap_temp.Last[i].Kills = 0
	recap_temp.Last[i].Heal = 0

	recap.Combatant[i].TotalDmgIn = recap.Combatant[i].TotalDmgIn + recap.Combatant[i].LastDmgIn
	recap.Combatant[i].TotalDmgOut = recap.Combatant[i].TotalDmgOut + recap.Combatant[i].LastDmgOut
	recap.Combatant[i].TotalTime = recap.Combatant[i].TotalTime + recap.Combatant[i].LastTime
	recap.Combatant[i].TotalKills = recap.Combatant[i].TotalKills + recap.Combatant[i].LastKills
	recap.Combatant[i].TotalHeal = recap.Combatant[i].TotalHeal + recap.Combatant[i].LastHeal

	if recap.Combatant[i].LastTime > recap_temp.MinTime then
		recap.Combatant[i].LastDPS = recap.Combatant[i].LastDmgOut/recap.Combatant[i].LastTime
	else
		recap.Combatant[i].LastDPS = 0
	end
	if recap.Combatant[i].TotalTime > recap_temp.MinTime then
		recap.Combatant[i].TotalDPS = recap.Combatant[i].TotalDmgOut/recap.Combatant[i].TotalTime
	else
		recap.Combatant[i].TotalDPS = 0
	end

	recap.Combatant[i].TotalMaxHit = math.max(recap.Combatant[i].LastMaxHit,recap.Combatant[i].TotalMaxHit)

	recap.Combatant[i].WasInLast = true
end

function Recap_EndFight()

	recap_temp.IdleTimer = -1

	RecapIdleFrame:Hide()

	if recap.Opt.State.value=="Active" then

		recap.Opt.State.value = "Stopped" -- suspend logging to calculate

		local debug_time = GetTime()

		Recap_MakeFriends()

		if recap_temp.FightEnd>recap_temp.FightStart then
			recap[recap_temp.p].LastDuration = recap_temp.FightEnd-recap_temp.FightStart
		else
			recap[recap_temp.p].LastDuration = 0
		end
		recap[recap_temp.p].TotalDuration = recap[recap_temp.p].TotalDuration + recap[recap_temp.p].LastDuration
		recap_temp.FightStart = 0
		recap_temp.FightEnd = 0

		recap_temp.ListSize = 1
		for i in recap_temp.Last do

			if recap.Combatant[i].WasInCurrent then
				if recap.Combatant[i].OwnedBy and recap.Opt.MergePets.value then
					Recap_AddPetToOwner(i,recap.Combatant[i].OwnedBy)
				elseif recap.Combatant[i].OwnsPet and recap.Opt.MergePets.value then
					Recap_AddPetToOwner(recap.Combatant[i].OwnsPet,i)
				else
					Recap_AccumulateCombatant(i)
				end
			else
				recap.Combatant[i].WasInLast = false
			end

		end

		if recap.Opt.AutoPost.value then
			recap.Opt.SortBy.value = Recap_AutoPostGetStatID(recap.Opt.AutoPost.Stat)
			recap.Opt.SortDir.value = false
		end

		Recap_ConstructList()
		Recap_SetState("Idle")

		if not recap.Opt.Minimized.value and recap.Opt.AutoHide.value then
			RecapFrame_Show()
			if recap.Opt.AutoFade.value then
				recap_temp.FadeTimer = 0
			end
		end

		if recap.Opt.AutoPost.value then
			Recap_PostFight()
		end

		if recap.Opt.AutoLeader.value then
			Recap_PostLeader()
		end

		if recap.debug then
			DEFAULT_CHAT_FRAME:AddMessage("[recap.debug] Fight processed in "..string.format("%.4f",GetTime()-debug_time).." seconds for "..(recap_temp.ListSize-1).." combatants.")
		end

	end
end

-- parses CHAT_MSG_SPELL/COMBAT messages to .Last running totals
function Recap_DamageEvent(arg1)

	local i, source, dest, damage, miss, effect
	local found = false
	local blocked = false

	if recap.Opt.State.value~="Stopped" then

		blocked = string.find(arg1,string.gsub(BLOCK_TRAILER,"%%d","%%d+")) -- true if (%d blocked)
		arg1 = string.gsub(arg1," %(.+%)","") -- strip trailing ()'s we don't use
		arg1 = string.gsub(arg1,"%.$","") -- strip trailing .'s

		for i=1,recap_temp.FilterSize do
			found,_,recap_temp.FilterIndex[1],recap_temp.FilterIndex[2],recap_temp.FilterIndex[3],recap_temp.FilterIndex[4],recap_temp.FilterIndex[5] = string.find(arg1,recap_temp.Filter[i].pattern)
			if found then

				source=recap_temp.FilterIndex[recap_temp.Filter[i].source]
				dest=recap_temp.FilterIndex[recap_temp.Filter[i].dest]
				if recap_temp.Local.SelfPronouns[string.lower(dest)] then
					dest = recap_temp.Player
				end
				if recap_temp.Filter[i].damage then
					damage=tonumber(recap_temp.FilterIndex[recap_temp.Filter[i].damage])
				end
				if recap_temp.Filter[i].effect then
					effect = recap_temp.FilterIndex[recap_temp.Filter[i].effect]
				end

				if (recap.Combatant[source] and recap.Combatant[source].Ignore) or (recap.Combatant[dest] and recap.Combatant[dest].Ignore) then
					return -- this is an ignored combatant, pretend the hit didn't happen at all
				end

				if not recap.Opt.LimitFights.value then
					if recap.Opt.IdleFight.value then
						recap_temp.IdleTimer = 0
					end
					if recap.Opt.State.value=="Idle" then
						Recap_StartFight()
					end
				end

				if recap.debug then
					if not recap.debug_Filter then
						recap.debug_Filter = {}
					end
					if not recap.debug_Filter[i] then
						recap.debug_Filter[i] = { hits=1, total=(damage or 1), pattern=recap_temp.Filter[i].pattern }
					else
						recap.debug_Filter[i].hits = recap.debug_Filter[i].hits + 1
						recap.debug_Filter[i].total = recap.debug_Filter[i].total + (damage or 1)
					end
					if recap_temp.Filter[i].watch then
						DEFAULT_CHAT_FRAME:AddMessage("["..i.."] \""..recap_temp.Filter[i].pattern.."\" triggered!")
						DEFAULT_CHAT_FRAME:AddMessage("source="..tostring(source)..", dest="..tostring(dest)..", damage="..tostring(damage)..", miss="..tostring(miss))
					end
				end

				if damage~=0 then -- if this wasn't just a cast
					Recap_CreateCombatant(source,1)
					Recap_CreateCombatant(dest)
					-- remove friend flag from hostiles no longer charmed
					if recap.Combatant[source].Friend and recap.Combatant[dest].Friend then
						recap.Combatant[source].Friend = false
					end
				else
					if (not recap.Opt.LightData.value) and recap.Combatant[source] and recap.Combatant[source].Detail["1"..effect] then
						Recap_CreateCombatant(source,1) -- if this was a cast that's done damage before, start timer
					end
				end

				if damage and damage>0 then
					-- process totals
					recap_temp.Last[source].DmgOut = recap_temp.Last[source].DmgOut + damage
					if damage > recap_temp.Last[source].MaxHit then
						recap_temp.Last[source].MaxHit = damage
					end
					recap_temp.Last[dest].DmgIn = recap_temp.Last[dest].DmgIn + damage

					if recap_temp.Last[dest].HP then
						recap_temp.Last[dest].HP = recap_temp.Last[dest].HP - damage
					end

					if not recap.Opt.LightData.value then
						if recap_temp.Filter[i].effect==7 then -- melee damage
							recap.Combatant[dest].Incoming.MeleeDamage = (recap.Combatant[dest].Incoming.MeleeDamage or 0) + damage
							if recap_temp.Filter[i].crit==1 then
								recap.Combatant[dest].Incoming.MeleeCrits = (recap.Combatant[dest].Incoming.MeleeCrits or 0) + 1
							else
								recap.Combatant[dest].Incoming.MeleeHits = (recap.Combatant[dest].Incoming.MeleeHits or 0) + 1
							end
							recap.Combatant[dest].Incoming.MeleeMax = math.max(damage,recap.Combatant[dest].Incoming.MeleeMax or 0)
							if blocked then
								recap.Combatant[dest].Incoming.MeleeBlocked = (recap.Combatant[dest].Incoming.MeleeBlocked or 0)+ 1
							end
						else
							recap.Combatant[dest].Incoming.NonMeleeDamage = (recap.Combatant[dest].Incoming.NonMeleeDamage or 0) + damage
							if recap_temp.Filter[i].crit==1 then
								recap.Combatant[dest].Incoming.NonMeleeCrits = (recap.Combatant[dest].Incoming.NonMeleeCrits or 0) + 1
							else
								recap.Combatant[dest].Incoming.NonMeleeHits = (recap.Combatant[dest].Incoming.NonMeleeHits or 0) + 1
							end
							recap.Combatant[dest].Incoming.NonMeleeMax = math.max(damage,recap.Combatant[dest].Incoming.NonMeleeMax or 0)
						end
						Recap_CreateEffect(source,effect,1,damage,recap_temp.Filter[i].crit)
					end

				elseif recap_temp.Filter[i].miss and not recap.Opt.LightData.value then
					if recap_temp.Filter[i].miss==9 and recap_temp.Filter[i].effect==7 then -- melee normal miss
						recap.Combatant[dest].Incoming.MeleeMissed = (recap.Combatant[dest].Incoming.MeleeMissed or 0) + 1
					elseif recap_temp.Filter[i].miss==9 then -- non-melee miss
						recap.Combatant[dest].Incoming.NonMeleeMissed = (recap.Combatant[dest].Incoming.NonMeleeMissed or 0) + 1
					elseif recap_temp.Filter[i].miss==10 then -- melee dodge
						recap.Combatant[dest].Incoming.MeleeDodged = (recap.Combatant[dest].Incoming.MeleeDodged or 0) + 1
					elseif recap_temp.Filter[i].miss==11 then -- melee parry
						recap.Combatant[dest].Incoming.MeleeParried = (recap.Combatant[dest].Incoming.MeleeParried or 0) + 1
					elseif recap_temp.Filter[i].miss==12 then -- melee block
						recap.Combatant[dest].Incoming.MeleeBlocked = (recap.Combatant[dest].Incoming.MeleeBlocked or 0) + 1
					end
					Recap_CreateEffect(source,effect,1,0)
				end

				i=recap_temp.FilterSize+1
			end
		end
	end
end

-- did_crit=1 for a crit, =0 for a normal hit but it could crit
-- total = amount of hit/heal, 0 for a miss
function Recap_CreateEffect(source,effect,type,total,did_crit)

	effect = type..effect

	-- skip misses that aren't a stored effect yet, to prevent logging debuff resists
	if not (total==0 and not recap.Combatant[source].Detail[effect]) then

--		ChatFrame2:AddMessage("effect: "..effect..", source: "..source..", total: "..total..", did_crit: "..tostring(did_crit))

		if not recap.Combatant[source].Detail[effect] then
			recap.Combatant[source].Detail[effect] = {} -- Hits=0, HitsDmg=0, HitsMax=0, Crits=0, CritsEvents=0, CritsDmg=0, CritsMax=0, Missed=0, TicksDmg, TicksMax }
		end

		if source==recap_temp.Player and not recap.Self[recap_temp.s][effect] then
			recap.Self[recap_temp.s][effect] = {}
		end

		if did_crit==1 then
			recap.Combatant[source].Detail[effect].CritsEvents = (recap.Combatant[source].Detail[effect].CritsEvents or 0) + 1
			recap.Combatant[source].Detail[effect].Crits = (recap.Combatant[source].Detail[effect].Crits or 0) + did_crit
			recap.Combatant[source].Detail[effect].CritsDmg = (recap.Combatant[source].Detail[effect].CritsDmg or 0) + total
			recap.Combatant[source].Detail[effect].CritsMax = math.max(total,recap.Combatant[source].Detail[effect].CritsMax or 0)
			if source==recap_temp.Player then
				recap.Self[recap_temp.s][effect].CritsEvents = (recap.Self[recap_temp.s][effect].CritsEvents or 0) + 1
				recap.Self[recap_temp.s][effect].Crits = (recap.Self[recap_temp.s][effect].Crits or 0) + did_crit
				recap.Self[recap_temp.s][effect].CritsDmg = (recap.Self[recap_temp.s][effect].CritsDmg or 0) + total
				recap.Self[recap_temp.s][effect].CritsMax = math.max(total,recap.Self[recap_temp.s][effect].CritsMax or 0)
			end
		elseif did_crit==0 then
			recap.Combatant[source].Detail[effect].CritsEvents = (recap.Combatant[source].Detail[effect].CritsEvents or 0) + 1
			recap.Combatant[source].Detail[effect].Hits = (recap.Combatant[source].Detail[effect].Hits or 0) + 1
			recap.Combatant[source].Detail[effect].HitsDmg = (recap.Combatant[source].Detail[effect].HitsDmg or 0) + total
			recap.Combatant[source].Detail[effect].HitsMax = math.max(total,recap.Combatant[source].Detail[effect].HitsMax or 0)
			if source==recap_temp.Player then
				recap.Self[recap_temp.s][effect].CritsEvents = (recap.Self[recap_temp.s][effect].CritsEvents or 0) + 1
				recap.Self[recap_temp.s][effect].Hits = (recap.Self[recap_temp.s][effect].Hits or 0) + 1
				recap.Self[recap_temp.s][effect].HitsDmg = (recap.Self[recap_temp.s][effect].HitsDmg or 0) + total
				recap.Self[recap_temp.s][effect].HitsMax = math.max(total,recap.Self[recap_temp.s][effect].HitsMax or 0)
			end
		elseif total==0 then
			recap.Combatant[source].Detail[effect].Missed = (recap.Combatant[source].Detail[effect].Missed or 0) + 1
			if source==recap_temp.Player then
				recap.Self[recap_temp.s][effect].Missed = (recap.Self[recap_temp.s][effect].Missed or 0) + 1
			end
		else
			recap.Combatant[source].Detail[effect].Hits = (recap.Combatant[source].Detail[effect].Hits or 0) + 1
			recap.Combatant[source].Detail[effect].TicksDmg = (recap.Combatant[source].Detail[effect].TicksDmg or 0) + total
			recap.Combatant[source].Detail[effect].TicksMax = math.max(total,recap.Combatant[source].Detail[effect].TicksMax or 0)
			if source==recap_temp.Player then
				recap.Self[recap_temp.s][effect].Hits = (recap.Self[recap_temp.s][effect].Hits or 0) + 1
				recap.Self[recap_temp.s][effect].TicksDmg = (recap.Self[recap_temp.s][effect].TicksDmg or 0) + total
				recap.Self[recap_temp.s][effect].TicksMax = math.max(total,recap.Self[recap_temp.s][effect].TicksMax or 0)
			end
		end
	end
end

function Recap_DeathEvent(arg1)

	local i, victim, spot
	local found = false

	if recap.Opt.State.value~="Stopped" then

		for i=1,recap_temp.DeathFilterSize do
			found,_,recap_temp.FilterIndex[1] = string.find(arg1,recap_temp.DeathFilter[i].pattern)

			if found then

				victim = recap_temp.FilterIndex[recap_temp.DeathFilter[i].victim]

				-- check for FD
				if recap.Combatant[victim] and recap.Combatant[victim].Class=="Hunter" then
					if UnitName("player")==victim then
						spot = "player"
					end
					for j=1,4 do
						if UnitName("party"..j)==victim then
							spot = "party"..j
						end
					end
					for j=1,40 do
						if UnitName("raid"..j)==victim then
							spot = "raid"..j
						end
					end
					if spot then
						for j=1,16 do
							if UnitBuff(spot,j)=="Interface\\Icons\\Ability_Rogue_FeignDeath" then
								victim = nil
							end
						end
					end
				end
					
				if victim and recap_temp.Last[victim] then
					recap_temp.Last[victim].Kills = recap_temp.Last[victim].Kills + 1
				end

				if recap.debug then
					if not recap.debug_Filter then
						recap.debug_Filter = {}
					end
					if not recap.debug_Filter[i+60] then
						recap.debug_Filter[i+60] = { hits=1, total=1, pattern=recap_temp.DeathFilter[i].pattern }
					else
						recap.debug_Filter[i+60].hits = recap.debug_Filter[i+60].hits + 1
						recap.debug_Filter[i+60].total = recap.debug_Filter[i+60].total + 1
					end
					if recap_temp.Filter[i].watch then
						DEFAULT_CHAT_FRAME:AddMessage("["..i.."] \""..recap_temp.DeathFilter[i].pattern.."\" triggered!")
						DEFAULT_CHAT_FRAME:AddMessage("victim="..tostring(victim))
					end
				end

				i = recap_temp.DeathFilterSize+1

			end
		end
	end
end

function Recap_HealEvent(arg1)

	local i, source, dest, heal, effect, fullheal
	local found = false

	if recap.Opt.State.value~="Stopped" then

		-- strip out period at end of chat line, if one exists
		arg1 = string.gsub(arg1,"%.$","")

		for i=1,recap_temp.HealFilterSize do
			found,_,recap_temp.FilterIndex[1],recap_temp.FilterIndex[2],recap_temp.FilterIndex[3],recap_temp.FilterIndex[4],recap_temp.FilterIndex[5] = string.find(arg1,recap_temp.HealFilter[i].pattern)

			if found then

				if not recap.Opt.LimitFights.value then
					if recap.Opt.IdleFight.value then
						recap_temp.IdleTimer = 0
					end
					if recap.Opt.State.value=="Idle" then
						Recap_StartFight()
					end
				end

				source=recap_temp.FilterIndex[recap_temp.HealFilter[i].source]
				dest=recap_temp.FilterIndex[recap_temp.HealFilter[i].dest]
				if recap_temp.Local.SelfPronouns[string.lower(dest)] then
					dest = recap_temp.Player
				end
				heal=tonumber(recap_temp.FilterIndex[recap_temp.HealFilter[i].heal])
				if recap_temp.HealFilter[i].effect then
					effect = recap_temp.FilterIndex[recap_temp.HealFilter[i].effect]
				end

				Recap_CreateCombatant(source)

				if recap_temp.Last[dest] and recap_temp.Last[dest].HP then
					fullheal = heal
					heal = math.min(recap_temp.Last[dest].MaxHP-recap_temp.Last[dest].HP,heal)
					recap_temp.Last[dest].HP = recap_temp.Last[dest].HP + heal
					if recap.debug and heal~=fullheal then
						ChatFrame2:AddMessage("|cff3fbfff"..source.."'s "..effect.." actually healed "..dest.." for "..heal..". ("..fullheal.." reported)")
					end
				end
				recap_temp.Last[source].Heal = recap_temp.Last[source].Heal + heal

				if recap.debug then
					if not recap.debug_Filter then
						recap.debug_Filter = {}
					end
					if not recap.debug_Filter[i+75] then
						recap.debug_Filter[i+75] = { hits=1, total=heal, pattern=recap_temp.HealFilter[i].pattern }
					else
						recap.debug_Filter[i+75].hits = recap.debug_Filter[i+75].hits + 1
						recap.debug_Filter[i+75].total = recap.debug_Filter[i+75].total + heal
					end
					if recap_temp.Filter[i].watch then
						DEFAULT_CHAT_FRAME:AddMessage("["..i.."] \""..recap_temp.HealFilter[i].pattern.."\" triggered!")
						DEFAULT_CHAT_FRAME:AddMessage("source="..tostring(source)..", dest="..tostring(dest)..", heal="..tostring(heal))
					end
				end

				if not recap.Opt.LightData.value then
					Recap_CreateEffect(source,effect,3,heal,recap_temp.HealFilter[i].crit)

					if fullheal and heal~=fullheal and recap.Combatant[source].Detail["3"..effect] then
						-- reuse .Missed detail to store overhealing since heals never miss
						recap.Combatant[source].Detail["3"..effect].Missed = (recap.Combatant[source].Detail["3"..effect].Missed or 0) + math.max(0,fullheal-heal)
					end
				end

				i = recap_temp.HealFilterSize+1

			end
		end
	end
end

local function Recap_SelfDamageSort(e1,e2)

	local effect1,effect2 = string.sub(e1.EName,1,1),string.sub(e2.EName,1,1)

	if e1 and e2 and ( ((e1.ETotal>e2.ETotal) and (effect1==effect2)) or (effect1<effect2) ) then
		return true
	else
		return false
	end
end

local function Recap_ConstructSelf()

	local idx,i,j = 1
	local damage, heal = 0,0

	recap_temp.SelfListSize = 1
	
	for i in recap.Self[recap_temp.s] do
		if not recap_temp.SelfList[idx] then
			recap_temp.SelfList[idx] = {}
		end
		recap_temp.SelfList[idx].EName = i
		recap_temp.SelfList[idx].ETotal = (recap.Self[recap_temp.s][i].HitsDmg or 0) + (recap.Self[recap_temp.s][i].TicksDmg or 0) + (recap.Self[recap_temp.s][i].CritsDmg or 0)
		recap_temp.SelfList[idx].EHits = (recap.Self[recap_temp.s][i].CritsEvents or 0)-(recap.Self[recap_temp.s][i].Crits or 0)
		recap_temp.SelfList[idx].ETicks = (recap.Self[recap_temp.s][i].Hits or 0) - recap_temp.SelfList[idx].EHits
		recap_temp.SelfList[idx].ECrits = recap.Self[recap_temp.s][i].Crits or 0
		recap_temp.SelfList[idx].EHitsMax = recap.Self[recap_temp.s][i].HitsMax or 0
		recap_temp.SelfList[idx].ETicksMax = recap.Self[recap_temp.s][i].TicksMax or 0
		recap_temp.SelfList[idx].ECritsMax = recap.Self[recap_temp.s][i].CritsMax or 0
		recap_temp.SelfList[idx].EHitsAvg = string.format("%d",((recap_temp.SelfList[idx].EHits>0) and ((recap.Self[recap_temp.s][i].HitsDmg or 0)/recap_temp.SelfList[idx].EHits) or 0))
		recap_temp.SelfList[idx].ETicksAvg = string.format("%d",((recap_temp.SelfList[idx].ETicks>0) and ((recap.Self[recap_temp.s][i].TicksDmg or 0)/recap_temp.SelfList[idx].ETicks) or 0))
		recap_temp.SelfList[idx].ECritsAvg = string.format("%d",((recap_temp.SelfList[idx].ECrits>0) and ((recap.Self[recap_temp.s][i].CritsDmg or 0)/recap_temp.SelfList[idx].ECrits) or 0))
		recap_temp.SelfList[idx].ECritsP = string.format("%.1f%%",((recap.Self[recap_temp.s][i].CritsEvents or 0)>0) and (100*recap_temp.SelfList[idx].ECrits/recap.Self[recap_temp.s][i].CritsEvents) or 0)
		recap_temp.SelfList[idx].EMaxAll = math.max( recap_temp.SelfList[idx].EHitsMax, math.max(recap_temp.SelfList[idx].ETicksMax,recap_temp.SelfList[idx].ECritsMax) )
		if tonumber(string.sub(i,1,1))<3 then -- damage event
			recap_temp.SelfList[idx].EMiss = recap.Self[recap_temp.s][i].Missed or 0
			j = recap_temp.SelfList[idx].EHits + recap_temp.SelfList[idx].ECrits + recap_temp.SelfList[idx].EMiss
			recap_temp.SelfList[idx].EMissP = string.format("%.1f%%",(j>0) and (100*(recap_temp.SelfList[idx].EMiss/j)) or 0)
			damage = damage + recap_temp.SelfList[idx].ETotal
		else
			recap_temp.SelfList[idx].EMiss = "--"
			recap_temp.SelfList[idx].EMissP = "--"
			heal = heal + recap_temp.SelfList[idx].ETotal
		end

		if recap_temp.SelfList[idx].ETicks==0 then
			recap_temp.SelfList[idx].ETicks = "--"
			recap_temp.SelfList[idx].ETicksAvg = "--"
			recap_temp.SelfList[idx].ETicksMax = "--"
		end
		if not recap.Self[recap_temp.s][i].CritsEvents then
			recap_temp.SelfList[idx].ECrits = "--"
			recap_temp.SelfList[idx].ECritsAvg = "--"
			recap_temp.SelfList[idx].ECritsMax = "--"
			recap_temp.SelfList[idx].ECritsP = "--"
			recap_temp.SelfList[idx].EHits = "--"
			recap_temp.SelfList[idx].EHitsAvg = "--"
			recap_temp.SelfList[idx].EHitsMax = "--"
		end

		idx = idx + 1
	end

	recap_temp.SelfListSize = idx

	if recap_temp.SelfListSize>1 then
		for i=1,recap_temp.SelfListSize-1 do
			if string.sub(recap_temp.SelfList[i].EName,1,1)<"3" and damage>0 then
				recap_temp.SelfList[i].ETotalP = string.format("%d%%",100*recap_temp.SelfList[i].ETotal/damage)
			elseif string.sub(recap_temp.SelfList[i].EName,1,1)>"2" and heal>0 then
				recap_temp.SelfList[i].ETotalP = string.format("%d%%",100*recap_temp.SelfList[i].ETotal/heal)
			else
				recap_temp.SelfList[i].ETotalP = "--"
			end
		end
	end

	table.sort(recap_temp.SelfList,Recap_SelfDamageSort)

end

function Recap_ConstructList()

	local i, j, dpsin, dpsout, entry, duration, tempname, over
	local maxhit = 0
	local dmgin = 0
	local dmgout = 0
	local kills = 0
	local heal = 0
	local allover = 0


	if recap.Opt.SelfView.value then
		Recap_ConstructSelf()
	end

	recap_temp.ListSize = 1
	if recap_temp.Selected~=0 then
		tempname = recap_temp.List[recap_temp.Selected].Name
	end

	if recap.Opt.View.value=="Last" then
		duration = recap[recap_temp.p].LastDuration
	else
		duration = recap[recap_temp.p].TotalDuration
	end
	
	for i in recap.Combatant do
		if not recap_temp.List[recap_temp.ListSize] then
			recap_temp.List[recap_temp.ListSize] = {}
		end
		entry = false
		if (recap.Combatant[i].LastDmgIn==0 and recap.Combatant[i].LastDmgOut==0 and
			recap.Combatant[i].TotalDmgOut==0 and recap.Combatant[i].TotalDmgIn==0) or
		   (recap.Opt.HideFoe.value and not recap.Combatant[i].Friend) or
		   (recap.Opt.HideYardTrash.value and (not recap.Combatant[i].Friend and recap.Combatant[i].TotalKills~=1)) or
		   (recap.Combatant[i].Ignore) then
			entry = false -- strip out unwanted combatants
		elseif recap.Opt.View.value=="Last" and recap.Combatant[i].WasInLast then
			if not recap.Opt.HideZero.value or recap.Combatant[i].LastTime>recap_temp.MinTime then
				recap_temp.List[recap_temp.ListSize].Name = i
				recap_temp.List[recap_temp.ListSize].Time = recap.Combatant[i].LastTime
				recap_temp.List[recap_temp.ListSize].MaxHit = recap.Combatant[i].LastMaxHit
				recap_temp.List[recap_temp.ListSize].DmgIn = recap.Combatant[i].LastDmgIn
				recap_temp.List[recap_temp.ListSize].DmgOut = recap.Combatant[i].LastDmgOut
				recap_temp.List[recap_temp.ListSize].DPS = recap.Combatant[i].LastDPS
				recap_temp.List[recap_temp.ListSize].DPSvsAll = duration>recap_temp.MinTime and recap.Combatant[i].LastDmgOut/duration or 0
				recap_temp.List[recap_temp.ListSize].Kills = recap.Combatant[i].LastKills
				recap_temp.List[recap_temp.ListSize].Heal = recap.Combatant[i].LastHeal
				entry = true
			end
		elseif recap.Opt.View.value=="All" then
			if not recap.Opt.HideZero.value or recap.Combatant[i].TotalTime>recap_temp.MinTime then
				recap_temp.List[recap_temp.ListSize].Name = i
				recap_temp.List[recap_temp.ListSize].Time = recap.Combatant[i].TotalTime
				recap_temp.List[recap_temp.ListSize].MaxHit = recap.Combatant[i].TotalMaxHit
				recap_temp.List[recap_temp.ListSize].DmgIn = recap.Combatant[i].TotalDmgIn
				recap_temp.List[recap_temp.ListSize].DmgOut = recap.Combatant[i].TotalDmgOut
				recap_temp.List[recap_temp.ListSize].DPS = recap.Combatant[i].TotalDPS
				recap_temp.List[recap_temp.ListSize].DPSvsAll = duration>recap_temp.MinTime and recap.Combatant[i].TotalDmgOut/duration or 0
				recap_temp.List[recap_temp.ListSize].Kills = recap.Combatant[i].TotalKills
				recap_temp.List[recap_temp.ListSize].Heal = recap.Combatant[i].TotalHeal
				entry = true
			end
		end
		if entry then
			recap_temp.List[recap_temp.ListSize].Over = 0 -- overhealing
			if recap.Combatant[recap_temp.List[recap_temp.ListSize].Name].Friend then
				if recap_temp.List[recap_temp.ListSize].MaxHit > maxhit then
					maxhit = recap_temp.List[recap_temp.ListSize].MaxHit
				end
				dmgin = dmgin + recap_temp.List[recap_temp.ListSize].DmgIn
				dmgout = dmgout + recap_temp.List[recap_temp.ListSize].DmgOut
				kills = kills + recap_temp.List[recap_temp.ListSize].Kills
				heal = heal + recap_temp.List[recap_temp.ListSize].Heal
				if (recap_temp.List[recap_temp.ListSize].Heal>0) and not recap.Opt.LightData.value then
					over = 0
					for j in recap.Combatant[i].Detail do
						if string.sub(j,1,1)=="3" then -- a heal
							over = over + (recap.Combatant[i].Detail[j].Missed or 0)
						end
					end
					recap_temp.List[recap_temp.ListSize].Over = math.floor(((100*over)/(over+recap.Combatant[i].TotalHeal))+.5)
					allover = allover + over
				end
			end
			recap_temp.ListSize = recap_temp.ListSize + 1
		end
	end

	if not recap_temp.List[recap_temp.ListSize] then
		recap_temp.List[recap_temp.ListSize] = {}
	end
	-- the final entry is the totals
	recap_temp.List[recap_temp.ListSize].Name = "Totals"
	recap_temp.List[recap_temp.ListSize].Time = duration
	RecapTotals_Time:SetText(Recap_FormatTime(duration))
	recap_temp.List[recap_temp.ListSize].MaxHit = maxhit
	RecapTotals_MaxHit:SetText(maxhit)
	recap_temp.List[recap_temp.ListSize].DmgIn = dmgin
	RecapTotals_DmgIn:SetText(dmgin)
	recap_temp.List[recap_temp.ListSize].DmgOut = dmgout
	RecapTotals_DmgOut:SetText(dmgout)
	recap_temp.List[recap_temp.ListSize].Kills = kills
	RecapTotals_Kills:SetText(kills)
	recap_temp.List[recap_temp.ListSize].Heal = heal
	RecapTotals_Heal:SetText(heal)
	recap_temp.List[recap_temp.ListSize].Over = recap.Opt.View.value=="All" and ((100*allover)/(allover+heal)) or 0
	RecapTotals_Over:SetText(string.format("%d%%",recap_temp.List[recap_temp.ListSize].Over))
	if duration > recap_temp.MinTime then
		recap_temp.List[recap_temp.ListSize].DPSIn = dmgin/duration
		recap_temp.List[recap_temp.ListSize].DPSOut = dmgout/duration
	else
		recap_temp.List[recap_temp.ListSize].DPSIn = 0
		recap_temp.List[recap_temp.ListSize].DPSOut = 0
	end
	RecapTotals_DPS:SetText(string.format("%.1f",recap_temp.List[recap_temp.ListSize].DPSOut))
	RecapTotals_DPSvsAll:SetText(string.format("%.1f",recap_temp.List[recap_temp.ListSize].DPSOut))
	RecapTotals_DPSIn:SetText(string.format("%.1f",recap_temp.List[recap_temp.ListSize].DPSIn))

	RecapMinDPSIn_Text:SetText(string.format("%.1f",recap_temp.List[recap_temp.ListSize].DPSIn))
	RecapMinDPSOut_Text:SetText(string.format("%.1f",recap_temp.List[recap_temp.ListSize].DPSOut))
	if recap.Combatant[recap_temp.Player] and recap.Opt.View.value=="Last" then
		i = recap.Combatant[recap_temp.Player].LastDPS
		if UnitName("pet") and UnitName("pet")~=UNKNOWNOBJECT and recap.Combatant[UnitName("pet")] then
			i = i + recap.Combatant[UnitName("pet")].LastDPS
		end
		RecapMinYourDPS_Text:SetText(string.format("%.1f",i))
	elseif recap.Combatant[recap_temp.Player] and recap.Opt.View.value=="All" then
		i = recap.Combatant[recap_temp.Player].TotalDPS
		if UnitName("pet") and UnitName("pet")~=UNKNOWNOBJECT and recap.Combatant[UnitName("pet")] then
			i = i + recap.Combatant[UnitName("pet")].TotalDPS
		end
		RecapMinYourDPS_Text:SetText(string.format("%.1f",i))
	else
		RecapMinYourDPS_Text:SetText("n/a")
	end

	-- calculate percentages
	for i=1,recap_temp.ListSize-1 do
		recap_temp.List[i].HealP = 0
		recap_temp.List[i].DmgInP = 0
		recap_temp.List[i].DmgOutP = 0
		if recap.Combatant[recap_temp.List[i].Name].Friend then
			if recap_temp.List[recap_temp.ListSize].Heal>0 then
				recap_temp.List[i].HealP = math.floor(.5+100*recap_temp.List[i].Heal/recap_temp.List[recap_temp.ListSize].Heal)
			end
			if recap_temp.List[recap_temp.ListSize].DmgIn>0 then
				recap_temp.List[i].DmgInP = math.floor(.5+100*recap_temp.List[i].DmgIn/recap_temp.List[recap_temp.ListSize].DmgIn)
			end
			if recap_temp.List[recap_temp.ListSize].DmgOut>0 then
				recap_temp.List[i].DmgOutP = math.floor(.5+100*recap_temp.List[i].DmgOut/recap_temp.List[recap_temp.ListSize].DmgOut)
			end
		end
	end

	Recap_SortList()

	if tempname then -- follow name to pre-sort selected
		recap_temp.Selected = Recap_GetSelectedByName(tempname)
		if recap_temp.Selected~=0 then
			RecapPanel_Show(tempname)
		else
			RecapPanel_Hide()
		end
	end

	RecapScrollBar_Update()
	Recap_UpdatePlugins()

end

function Recap_DefineGauges()

	local i

	recap_temp.GaugeMax = 0

	if recap_temp.ListSize>1 then

		recap_temp.GaugeBy = string.gsub(recap.Opt.SortBy.value,"P$","")
		recap_temp.GaugeBy = recap_temp.GaugeBy=="DPSvsAll" and "DmgOut" or recap_temp.GaugeBy
		if not gaugecolor[recap_temp.GaugeBy] then
			recap_temp.GaugeBy = nil
		else
			for i=1,recap_temp.ListSize-1 do
				recap_temp.GaugeMax = math.max(recap.Combatant[recap_temp.List[i].Name].Friend and recap_temp.List[i][recap_temp.GaugeBy] or 0,recap_temp.GaugeMax)
			end
			for i=1,15 do
				getglobal("RecapList"..i.."_Gauge"):SetVertexColor(gaugecolor[recap_temp.GaugeBy].r,gaugecolor[recap_temp.GaugeBy].g,gaugecolor[recap_temp.GaugeBy].b)
			end
		end
	end
end


function Recap_GetSelectedByName(arg1)

	local sel,i = 0

	if arg1 and recap_temp.ListSize>1 then
		for i=1,recap_temp.ListSize-1 do
			if recap_temp.List[i].Name==arg1 then
				sel = i
			end
		end
	end

	return sel
end

--[[ Sorting functions ]]

-- initial sort by field done with table.sort for speed, then
-- friends shifted to top with an insertion sort for "stable" list
function Recap_SortList()

	local temp,tempname

	if recap_temp.Selected~=0 then
		tempname = recap_temp.List[recap_temp.Selected].Name
	end

	temp = recap_temp.List[recap_temp.ListSize]
	recap_temp.List[recap_temp.ListSize] = nil

	if recap.Opt.SortDir.value then
		table.sort(recap_temp.List,function(e1,e2) if e1 and e2 and ( e1[recap.Opt.SortBy.value] < e2[recap.Opt.SortBy.value] ) then return true else return false end end)
	else
		table.sort(recap_temp.List,function(e1,e2) if e1 and e2 and ( e1[recap.Opt.SortBy.value] > e2[recap.Opt.SortBy.value] ) then return true else return false end end)
	end
 	Recap_SortFriends()

	recap_temp.List[recap_temp.ListSize] = temp

	Recap_DefineGauges()
	recap_temp.Selected = Recap_GetSelectedByName(tempname)

end

function Recap_SortDown(e1,e2)

	if e1 and e2 and ( e1[recap.Opt.SortBy.value] < e2[recap.Opt.SortBy.value] ) then
		return true
	else
		return false
	end
end

function Recap_SortUp(e1,e2)

	if e1 and e2 and ( e1[recap.Opt.SortBy.value] > e2[recap.Opt.SortBy.value] ) then
		return true
	else
		return false
	end
end

-- perform stable insertion sort on the list
function Recap_SortFriends()

	local i,j
	local changed = true
	local temp = {}

	if recap_temp.ListSize>2 then

		for i=2,(recap_temp.ListSize-1) do
			temp = recap_temp.List[i]
			j=i
			while (j>1) and (not recap.Combatant[recap_temp.List[j-1].Name].Friend and recap.Combatant[temp.Name].Friend) do
				recap_temp.List[j] = recap_temp.List[j-1]
				j = j - 1
			end
			recap_temp.List[j] = temp
		end

	end
end

--[[ Window movement functions ]]

function Recap_OnMouseDown(arg1)

	if recap_temp.Loaded and arg1=="LeftButton" and not recap.Opt.Pinned.value then
		RecapFrame:StartMoving()
	end
end

function Recap_OnMouseUp(arg1)

	if recap_temp.Loaded and arg1=="LeftButton" and not recap.Opt.Pinned.value then
		RecapFrame:StopMovingOrSizing()
		Recap_CheckWindowBounds()
	elseif arg1=="RightButton" and recap.Opt.Minimized.value then
		RecapCreateMenu(recap_temp.MinMenu)
	end		
end

-- repositions RecapFrame if any part of it goes off the screen
function Recap_CheckWindowBounds()

	local x1, y1, x2, y2, cx, cy
	local needs_moved = false

	if recap_temp.Loaded and RecapFrame then

		x1 = RecapFrame:GetLeft()
		y1 = RecapFrame:GetTop()
		cx = RecapFrame:GetWidth()
		cy = RecapFrame:GetHeight()

		if x1 and y1 and cx and cy then

			x2 = RecapFrame:GetRight()
			y2 = RecapFrame:GetBottom()
			uix2 = UIParent:GetRight()
			uix1 = UIParent:GetLeft()
			uiy1 = UIParent:GetTop()
			uiy2 = UIParent:GetBottom()

			if x2 and uix2 and x2 > uix2 then
				x1 = x1 + uix2 - x2
				needs_moved = true
			elseif uix1 and uix1 > x1 then
				x1 = x1 + uix1 - x1
				needs_moved = true
			end

			if y1 and y1 > uiy1 then
				y1 = y1 + uiy1 - y1
				needs_moved = true
			elseif uiy2 and uiy2 > y2 then
				y1 = y1 + uiy2 - y2
				needs_moved = true
			end

			if needs_moved then
				RecapFrame:ClearAllPoints()
				RecapFrame:SetPoint("TOPLEFT","UIParent","BOTTOMLEFT",x1,y1)
			end

			recap[recap_temp.p].WindowTop = RecapFrame:GetTop()
			recap[recap_temp.p].WindowLeft = RecapFrame:GetLeft()
			recap[recap_temp.p].WindowHeight = RecapFrame:GetHeight()
			recap[recap_temp.p].WindowWidth = RecapFrame:GetWidth()

		end

	end
end

--[[ Dialog control functions ]]

function Recap_OnClick(arg1)

	if not recap_temp.Loaded then
		return
	end

	local fight_count = 0
	local total_fight_count = 0
	local filen = RecapSetEditBox:GetText()
	local set_friend, set_dmgin, set_dmgout, set_maxhit, set_kills, set_heal, set_time

	PlaySound("GAMEGENERICBUTTONPRESS")

	if arg1=="Close" then
		RecapFrame_Hide()
	elseif arg1=="Minimize" then

		if recap.Opt.Minimized.value then
			Recap_Maximize()
		else
			Recap_Minimize()
		end
		RecapFrame:SetAlpha(1)
		recap_temp.FadeTimer = -1
		Recap_SetButtons()
		Recap_OnTooltip("Minimize")
	elseif arg1=="Pin" then
		recap.Opt.Pinned.value = not recap.Opt.Pinned.value
		Recap_SetButtons()
		Recap_OnTooltip("Pin")
	elseif arg1=="Pause" then
		if recap.Opt.Paused.value then
			recap.Opt.Paused.value = false
			Recap_SetState("Idle")
		else
			recap.Opt.Paused.value = true
			Recap_EndFight()
			Recap_SetState("Stopped")
		end
		Recap_SetButtons()
		Recap_OnTooltip("Pause")
	elseif arg1=="ShowAll" then
		if recap.Opt.SelfView.value then
			recap.Opt.SelfView.value = false
			if not recap.Opt.Minimized.value then
				Recap_SetColumns()
			end
		end
		if recap.Opt.View.value=="Last" then
			Recap_SetView("All")
		else
			Recap_SetView("Last")
		end
		Recap_OnTooltip("ShowAll")
	elseif arg1=="Options" then
		if RecapOptFrame:IsShown() then
			RecapOptFrame:Hide()
		else
			RecapOptFrame:Show()
		end
	elseif arg1=="Reset" then

		if recap.Opt.SelfView.value then
			recap.Self[recap_temp.s] = {}
			Recap_SetView()
		elseif recap.Opt.View.value=="All" then
			-- wipe everything --
			Recap_ResetAllCombatants()
			Recap_SetView()
			Recap_MakeFriends()
		elseif recap.Opt.View.value=="Last" then
			-- remove just the last fight --
			for i in recap.Combatant do
				if recap.Combatant[i].WasInLast then
					Recap_ResetLastFight(i)				
				end
			end
			recap[recap_temp.p].TotalDuration = recap[recap_temp.p].TotalDuration - recap[recap_temp.p].LastDuration
			recap[recap_temp.p].LastDuration = 0
			Recap_SetView()
		end

	elseif arg1=="SaveSet" then
		RecapSetEditBox:ClearFocus()
		if filen and filen~="" then
			filen = string.gsub(filen,"\"","'") -- convert "s to 's
			filen = string.gsub(filen,"%[","(") -- convert [s to (s
			filen = string.gsub(filen,"%]",")") -- convert ]s to )s
		end
		RecapSetEditBox:SetText("")
		Recap_SaveCombatants(filen,recap.Opt.ReplaceSet.value)
		Recap_BuildFightSets()
		recap_temp.FightSetSelected = 0
		RecapFightSetsScrollBar_Update()

	elseif arg1=="LoadSet" then
		RecapSetEditBox:SetText("")
		RecapSetEditBox:ClearFocus()
		Recap_LoadCombatants(filen,recap.Opt.ReplaceSet.value)
		Recap_MakeFriends()
		Recap_SetView("All")

	elseif arg1=="DeleteSet" then
		RecapSetEditBox:SetText("")
		RecapSetEditBox:ClearFocus()
		recap_set[filen] = nil
		Recap_BuildFightSets()

	elseif arg1=="SelfView" then
		recap.Opt.SelfView.value = not recap.Opt.SelfView.value
		RecapPanel_Hide(1)
		Recap_SetColumns()
		Recap_OnTooltip("SelfView")
	end
end

-- removes combatant i from last fight
function Recap_ResetLastFight(i)
	recap.Combatant[i].TotalTime = recap.Combatant[i].TotalTime - recap.Combatant[i].LastTime
	recap.Combatant[i].TotalDmgIn = recap.Combatant[i].TotalDmgIn - recap.Combatant[i].LastDmgIn
	recap.Combatant[i].TotalDmgOut = recap.Combatant[i].TotalDmgOut - recap.Combatant[i].LastDmgOut
	recap.Combatant[i].TotalHeal = recap.Combatant[i].TotalHeal - recap.Combatant[i].LastHeal
	recap.Combatant[i].TotalKills = recap.Combatant[i].TotalKills - recap.Combatant[i].LastKills
	if recap.Combatant[i].TotalTime> recap_temp.MinTime then
		recap.Combatant[i].TotalDPS = recap.Combatant[i].TotalDmgOut/recap.Combatant[i].TotalTime
	else
		recap.Combatant[i].TotalDPS = 0
	end
	recap.Combatant[i].WasInLast = false
	recap.Combatant[i].LastTime = 0
	recap.Combatant[i].LastDmgIn = 0
	recap.Combatant[i].LastDmgOut = 0
	recap.Combatant[i].LastDPS = 0
	recap.Combatant[i].LastMaxHit = 0
	recap.Combatant[i].LastKills = 0
	recap.Combatant[i].LastHeal = 0
end

function RecapHeader_OnEnter()

	local msg=string.gsub(this:GetName(),"RecapHeader_","Header")
	Recap_OnTooltip(msg)
end

function RecapHeader_OnMouseUp(arg1)

	if arg1=="RightButton" then
		RecapCreateMenu(recap_temp.ColumnMenu)
	end
end

-- sort column, toggle dir for new headers, name=ascending, rest=descending
function RecapHeader_OnClick()

	local text=""
	local channel = "" -- "Guild" "Say" etc or "Channel"
	local chatnumber = 0 -- if channel is "Channel", this is the channel numbers
	local arg1= string.gsub(this:GetName(),"RecapHeader_","")

	if not IsShiftKeyDown() then
		if recap.Opt.SortBy.value==string.gsub(arg1,"P$","") then
			recap.Opt.SortDir.value = not recap.Opt.SortDir.value
		else
			recap.Opt.SortBy.value = string.gsub(arg1,"P$","")

			if arg1=="Name" then
				recap.Opt.SortDir.value = true
			else
				recap.Opt.SortDir.value = false
			end
		end
		Recap_SortList()
		RecapScrollBar_Update()
	elseif ChatFrameEditBox:IsVisible() and recap_temp.ListSize>1 and arg1~="Name" then

		recap.Opt.SortBy.value = arg1
		recap.Opt.SortDir.value = false
		Recap_SortList()
		RecapScrollBar_Update()

		if recap.Opt.SpamRows.value then

			channel = ChatFrameEditBox.chatType
			chatnumber = nil

			if channel=="WHISPER" then
				chatnumber = ChatFrameEditBox.tellTarget
			elseif channel=="CHANNEL" then
				chatnumber = ChatFrameEditBox.channelTarget
			end

			Recap_PostSpamRows(arg1,channel,chatnumber)

		else
			Recap_InsertChat(Recap_PostSpamLine(arg1))
		end

	elseif not ChatFrameEditBox:IsVisible() then
		DEFAULT_CHAT_FRAME:AddMessage(recap_temp.Local.RankUsage)
	end

end

function Recap_List_OnClick(arg1)

	local id, index
	local text = ""

	id = this:GetID()
	index = id + FauxScrollFrame_GetOffset(RecapScrollBar)

	if index<recap_temp.ListSize then
		recap_temp.DetailSelected = 0
		if recap_temp.Selected==index and arg1=="LeftButton" then
			recap_temp.Selected = 0
			getglobal("RecapList"..id):UnlockHighlight()
			RecapPanel_Hide()
		else
			recap_temp.Selected = index
			RecapScrollBar_Update()
			RecapPanel_Show(recap_temp.List[index].Name)
		end
	end

	if arg1=="RightButton" and index<recap_temp.ListSize and IsShiftKeyDown() and ChatFrameEditBox:IsVisible() then
		ChatFrameEditBox:Insert(recap_temp.List[index].Name.." "..getglobal("RecapList"..id.."_DPS"):GetText().." DPS ")
	elseif arg1=="LeftButton" and index<recap_temp.ListSize and IsShiftKeyDown() and ChatFrameEditBox:IsVisible() then
		ChatFrameEditBox:Insert(string.format(recap_temp.Local.VerboseLinkStart,		
								recap_temp.List[index].Name,
								getglobal("RecapList"..id.."_DmgIn"):GetText(),
								getglobal("RecapList"..id.."_DmgOut"):GetText(),
								getglobal("RecapList"..id.."_Time"):GetText(),
								getglobal("RecapList"..id.."_DPS"):GetText(),
								getglobal("RecapList"..id.."_MaxHit"):GetText() ).." for "..
								recap_temp.Local.LastAll[recap.Opt.View.value])
	elseif arg1=="RightButton" and index<recap_temp.ListSize and not IsShiftKeyDown() then
		if recap.Combatant[recap_temp.List[index].Name].Friend then
			RecapCreateMenu(recap_temp.DropMenu,1)
		else
			RecapCreateMenu(recap_temp.AddMenu,1)
		end
	end
end

--[[ Tooltip functions ]]

function Recap_Tooltip(arg1,arg2)

	if recap_temp.Loaded and recap.Opt.ShowTooltips.value then
		if not recap.Opt.TooltipFollow.value then
			GameTooltip_SetDefaultAnchor(GameTooltip,this)
		else
			GameTooltip:SetOwner(this,Recap_TooltipAnchor())
		end
		GameTooltip:SetText(arg1)
		GameTooltip:AddLine(arg2, .75, .75, .75, 1)
		GameTooltip:Show()
	end
end

-- returns line1,line2 of a tooltip for arg1 in OptList (for generic static tooltips)
function Recap_GetTooltip(arg1)

	local i

	if arg1 then
		for i in recap_temp.OptList do
			if recap_temp.OptList[i][1]==arg1 then
				return recap_temp.OptList[i][2],recap_temp.OptList[i][3]
			end
		end
	end
end

-- arg1=tooltip index, arg2=optional addition to header
function Recap_OnTooltip(arg1,arg2)

	local line1, line2

	if arg1=="Status" then
		Recap_Tooltip("Recap Status: "..recap.Opt.State.value,recap_temp.Local.StatusTooltip)
	elseif arg1=="Close" then
		if recap.Opt.State.value=="Stopped" then
			Recap_OnTooltip("ExitRecap")
		else
			Recap_OnTooltip("HideWindow")
		end
	elseif arg1=="Minimize" then
		if recap.Opt.Minimized.value then
			Recap_OnTooltip("ExpandWindow")
		else
			Recap_OnTooltip("MinimizeWindow")
		end
	elseif arg1=="Pin" then
		if recap.Opt.Pinned.value then
			Recap_OnTooltip("UnPinWindow")
		else
			Recap_OnTooltip("PinWindow")
		end
	elseif arg1=="Pause" then
		if recap.Opt.State.value=="Stopped" then
			Recap_OnTooltip("Resume")
		else
			Recap_OnTooltip("PauseMonitoring")
		end
	elseif arg1=="ShowAll" then
		if recap.Opt.View.value=="Last" then
			Recap_OnTooltip("ShowAllFights")
		else
			Recap_OnTooltip("ShowLastFight")
		end
	elseif arg1=="HeaderName" then
		if recap.Opt.View.value=="Last" then
			Recap_OnTooltip("CombatLast")
		else
			Recap_OnTooltip("CombatAll")
		end
	elseif arg1=="Reset" then
		if recap.Opt.SelfView.value then
			Recap_OnTooltip("ResetSelfView")
		elseif recap.Opt.View.value=="Last" then
			Recap_OnTooltip("ResetLastFight")
		else
			Recap_OnTooltip("ResetAllTotals")
		end
	elseif arg1=="SelfView" then
		if recap.Opt.SelfView.value then
			Recap_OnTooltip("HideSelfView")
		else
			Recap_OnTooltip("ShowSelfView")
		end
	else
		line1,line2 = Recap_GetTooltip(arg1)
		if line1 and line2 then
			if arg2 then
				Recap_Tooltip(line1..": "..arg2,line2)
			else
				Recap_Tooltip(line1, line2)
			end
		end
		
	end
end

function Recap_List_OnEnter()

	local index, id

	id = this:GetID()
	index = id + FauxScrollFrame_GetOffset(RecapScrollBar)

	if getglobal("RecapList"..id.."_Name"):GetStringWidth()>110 then
		Recap_Tooltip(recap_temp.List[index].Name)
	end

	if (index<recap_temp.ListSize) and recap_temp.Selected==0 then
		RecapPanel_Show(recap_temp.List[index].Name)
	end
end

function Recap_Totals_OnEnter()

	local r = recap_temp.ColorWhite.r
	local g = recap_temp.ColorWhite.g
	local b = recap_temp.ColorWhite.b
	local other_kills = 0

	if recap.Opt.ShowTooltips.value then

		if recap.Opt.TooltipFollow.value then
			GameTooltip:SetOwner(this,Recap_TooltipAnchor())
		else
			GameTooltip_SetDefaultAnchor(GameTooltip,this)
		end

		GameTooltip:AddLine("Totals for "..recap_temp.Local.LastAll[recap.Opt.View.value])

		if recap_temp.ListSize>1 then

			for i=1,(recap_temp.ListSize-1) do
				if not recap.Combatant[recap_temp.List[i].Name].Friend then
					other_kills = other_kills + recap_temp.List[i].Kills
				end
			end
			GameTooltip:AddDoubleLine("Combatants:",recap_temp.ListSize-1,r,g,b,r,g,b)
			GameTooltip:AddDoubleLine("Time Fighting:",Recap_FormatTime(recap_temp.List[recap_temp.ListSize].Time),r,g,b,r,g,b)
			GameTooltip:AddDoubleLine("Max Hit:",recap_temp.List[recap_temp.ListSize].MaxHit,r,g,b,r,g,b)
			GameTooltip:AddDoubleLine("Deaths:",recap_temp.List[recap_temp.ListSize].Kills,r,g,b,r,g,b)
			GameTooltip:AddDoubleLine("Kills:",other_kills,r,g,b,r,g,b)
			GameTooltip:AddDoubleLine("Heals:",recap_temp.List[recap_temp.ListSize].Heal,r,g,b,r,g,b)
			GameTooltip:AddDoubleLine("Damage In:",recap_temp.List[recap_temp.ListSize].DmgIn,recap_temp.ColorDmgIn.r,recap_temp.ColorDmgIn.g,recap_temp.ColorDmgIn.b,recap_temp.ColorDmgIn.r,recap_temp.ColorDmgIn.g,recap_temp.ColorDmgIn.b)
			GameTooltip:AddDoubleLine("DPS In:",string.format("%.1f",recap_temp.List[recap_temp.ListSize].DPSIn),recap_temp.ColorDmgIn.r,recap_temp.ColorDmgIn.g,recap_temp.ColorDmgIn.b,recap_temp.ColorDmgIn.r,recap_temp.ColorDmgIn.g,recap_temp.ColorDmgIn.b)
			GameTooltip:AddDoubleLine("Damage Out:",recap_temp.List[recap_temp.ListSize].DmgOut,recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b,recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
			GameTooltip:AddDoubleLine("DPS Out:",string.format("%.1f",recap_temp.List[recap_temp.ListSize].DPSOut),recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b,recap_temp.ColorDmgOut.r,recap_temp.ColorDmgOut.g,recap_temp.ColorDmgOut.b)
		else
			GameTooltip:AddLine("Combatants: none",r,g,b)
		end

		GameTooltip:Show()
	end
end

function Recap_TooltipAnchor()

	local anchor = "ANCHOR_LEFT"

	if GetCursorPosition("UIParent")<(UIParent:GetWidth()/2) then
		anchor = "ANCHOR_RIGHT"
	end
	return anchor
end

--[[ Plug-in functions ]]

function Recap_GetIB_Tooltip()
	return recap_temp.Local.LastAll[recap.Opt.View.value]..":\nYour DPS: "..RecapMinYourDPS_Text:GetText().."\nMax hit: "..RecapTotals_MaxHit:GetText().."\nTotal DPS Out: "..RecapTotals_DPS:GetText().."\nTotal DPS In: "..RecapTotals_DPSIn:GetText()
end

function Recap_UpdatePlugins()

	local yourdps = RecapMinYourDPS_Text:GetText()
	local dpsin = RecapMinDPSIn_Text:GetText()
	local dpsout = RecapMinDPSOut_Text:GetText()
	
	if IB_Recap_Update then
		IB_Recap_Update("DPS: "..yourdps)
	end

	if TitanPanelRecap_Update then
		TitanPanelRecap_Update(recap.Opt.State.value,yourdps,dpsin,dpsout)
	end
end

--[[ Misc functions ]]

-- if this is a pet, pass owner's name to ownedby
function Recap_AddFriend(arg1,ownedby)

	if arg1 and arg~=UNKNOWNOBJECT and recap.Combatant[arg1] then
		recap.Combatant[arg1].Friend = true
		if recap.Combatant[arg1] and ownedby then
			-- pet could potentially do damage without owner, add owner if so
			if not recap.Combatant[ownedby] then
				Recap_CreateBlankCombatant(ownedby)
				recap.Combatant[ownedby].Friend = true
			end
			if not recap_temp.Last[ownedby] then
				recap_temp.Last[ownedby] = { Start = 0, End = 0, DmgIn = 0, DmgOut = 0, MaxHit = 0, Kills = 0, Heal = 0 }
			end
			recap.Combatant[arg1].OwnedBy = ownedby
			recap.Combatant[ownedby].OwnsPet = arg1
		end
	end
end

function Recap_MakeFriends()

	local i,item,u

	Recap_GetUnitInfo("player")
	Recap_AddFriend(recap_temp.Player)
	if UnitExists("pet") then
		Recap_GetUnitInfo("pet")
		Recap_AddFriend(UnitName("pet"),UnitName("player"))
	end
	for i=1,4 do
		if UnitExists("party"..i) then
			Recap_GetUnitInfo("party"..i)
			Recap_AddFriend(UnitName("party"..i))
			if UnitExists("partypet"..i) then
				Recap_GetUnitInfo("partypet"..i)
				Recap_AddFriend(UnitName("partypet"..i),UnitName("party"..i))
			end
		end
	end

	if (GetNumRaidMembers()>0) then
		for i=1,40 do
			if UnitExists("raid"..i) then
				Recap_GetUnitInfo("raid"..i)
				Recap_AddFriend(UnitName("raid"..i))
				if UnitExists("raidpet"..i) then
					Recap_GetUnitInfo("raidpet"..i)
					Recap_AddFriend(UnitName("raidpet"..i),UnitName("raid"..i))
				end
			end
		end
	end
end

-- returns a string of seconds converted to 0:00:00 format
function Recap_FormatTime(arg1)

	local hours, minutes, seconds
	local text

	seconds = math.floor(arg1+.5)
	hours = math.floor(seconds/3600)
	seconds = seconds - hours*3600
	minutes = math.floor(seconds/60)
	seconds = seconds - minutes*60

	return ((hours>0) and (hours..":") or "") .. string.format("%02d:",minutes) .. string.format("%02d",seconds)
end

function Recap_SetClassIcon(id,class)
	if class and recap_temp.ClassIcons[class] then
		getglobal("RecapList"..id.."_Class"):SetTexCoord(recap_temp.ClassIcons[class].left,recap_temp.ClassIcons[class].right,recap_temp.ClassIcons[class].top,recap_temp.ClassIcons[class].bottom)
	else
		getglobal("RecapList"..id.."_Class"):SetTexCoord(.9,1,.9,1)
	end
end

function Recap_SetFactionIcon(id,faction)

	item = getglobal("RecapList"..id.."_Faction")

	if faction and recap_temp.FactionIcons[faction] then
		item:SetTexture(recap_temp.FactionIcons[faction])
	else
		item:SetTexture("")
	end

end

function Recap_GetUnitInfo(unit)

	local u=UnitName(unit)

	if u then
		if recap.Combatant[u] then
			_,recap.Combatant[u].Class = UnitClass(unit)
			_,recap.Combatant[u].Faction = UnitFactionGroup(unit)
			if UnitCreatureFamily(unit) and recap.Combatant[u].Faction then
				recap.Combatant[u].Class = "Pet"
			end
			recap.Combatant[u].Level = UnitLevel(unit)
		end
	end

	if unit~="target" and unit~="mouseover" and recap_temp.Last[u] then
		if UnitIsVisible(unit) then
			recap_temp.Last[u].HP = UnitHealth(unit)
			recap_temp.Last[u].MaxHP = UnitHealthMax(unit)
		else
			recap_temp.Last[u].HP = nil -- discredit
		end
	end

end

-- creates a new .Combatant[arg1] that is a copy of DefaultCombatant
function Recap_CreateBlankCombatant(arg1)

	if not recap.Combatant[arg1] then

		recap.Combatant[arg1] = {
			TotalTime = 0,
			TotalDmgIn = 0,
			TotalDmgOut = 0,
			TotalDPS = 0,
			TotalMaxHit = 0,
			TotalKills = 0,
			TotalHeal = 0,
			LastTime = 0,
			LastDmgIn = 0,
			LastDmgOut = 0,
			LastDPS = 0,
			LastMaxHit = 0,
			LastKills = 0,
			LastHeal = 0,
			WasInLast = false,
			Friend = false,
			Ignore = nil,
			Owner = nil,
			Incoming = {},
			Detail = {}
		}

	end

end

-- populates .Opt with a copy of DefaultOpt
function Recap_LoadDefaultOpt()

	recap.Opt = {}
	for i in recap_temp.DefaultOpt do
		recap.Opt[i] = {}
		for j in recap_temp.DefaultOpt[i] do
			recap.Opt[i][j] = recap_temp.DefaultOpt[i][j]
		end
	end
end

function Recap_SaveOpt(user)

	if user and string.len(user)>0 then
		recap[user].Opt = {}
    	for i in recap.Opt do
			text = ""
			for j in recap.Opt[i] do
				text = text..tostring(j).."="..tostring(recap.Opt[i][j]).." "
			end
			recap[user].Opt[i] = text
		end
	end
end

function Recap_LoadOpt(user)

    if user and recap[user] and recap[user].Opt then
		for i in recap[user].Opt do
			if recap_temp.DefaultOpt[i] then
				for t,v in string.gfind(recap[user].Opt[i],"(%w+)=(%d-%--%w+) ") do
					if v=="true" then
						v = true
					elseif v=="false" then
						v = false
					elseif tonumber(v) then
						v = tonumber(v)
					end
					if t=="type" and recap_temp.DefaultOpt[i][t] then
						v = recap_temp.DefaultOpt[i][t]
					end
					if recap.Opt[i] then
						recap.Opt[i][t] = v
					end
				end
			end
		end
	end
end

-- saves current data to data set "filen", overwrite~=nil to overwrite existing data
function Recap_SaveCombatants(filen,overwrite)

	local sd = {}

	if not filen or string.len(filen)<1 then
		return
	end

	if not recap_set[filen] then
		recap_set[filen] = {}
		recap_set[filen].TimeStamp = date()
		recap_set[filen].TotalDuration = 0
		recap_set[filen].Combatant = {}
	end

	recap_set[filen] = {}
	recap_set[filen].TimeStamp = date()
	recap_set[filen].TotalDuration = recap[recap_temp.p].TotalDuration
	recap_set[filen].Combatant = {}
	for i in recap.Combatant do
		if (not recap.Opt.SaveFriends.value or recap.Combatant[i].Friend) then
			recap_set[filen].Combatant[i] = string.format("%s %d %d %d %d %d %.3f ~%d %d %d",
											tostring(recap.Combatant[i].Friend),
											recap.Combatant[i].TotalDmgIn,
											recap.Combatant[i].TotalDmgOut,
											recap.Combatant[i].TotalMaxHit,
											recap.Combatant[i].TotalHeal,
											recap.Combatant[i].TotalKills,
											recap.Combatant[i].TotalTime,
											Recap_MakeKey(recap.Combatant[i].Faction),
											Recap_MakeKey(recap.Combatant[i].Class),
											recap.Combatant[i].Level or 0)
			if recap.Opt.LightData and not recap.Opt.LightData.value then
				-- save .Incoming
				recap_set[filen].Combatant[i] = recap_set[filen].Combatant[i].."^"
				for k in recap.Combatant[i].Incoming do
					if recap.Combatant[i].Incoming[k]>0 then
						recap_set[filen].Combatant[i] = recap_set[filen].Combatant[i]..inckey[k]..recap.Combatant[i].Incoming[k]
					end
				end
				recap_set[filen].Combatant[i] = recap_set[filen].Combatant[i].."^"

				for j in recap.Combatant[i].Detail do
					recap_set[filen].Combatant[i] = recap_set[filen].Combatant[i].."["..j..">"
					for k in recap.Combatant[i].Detail[j] do
						recap_set[filen].Combatant[i] = recap_set[filen].Combatant[i]..detkey[k]..recap.Combatant[i].Detail[j][k]
					end
					recap_set[filen].Combatant[i] = recap_set[filen].Combatant[i].."]"
				end

			end				
		end
	end

	fight_count = 0
	for i in recap_set[filen].Combatant do
		fight_count = fight_count + 1
	end
	recap_set[filen].ListSize = fight_count
end

function Recap_MakeKey(arg1)

	local key = 0

	for i in recap_temp.Keys do
		if arg1==recap_temp.Keys[i] then
			key = i
		end
	end
	return key
end

function Recap_GetKey(arg1)

	local key = nil

	if not arg1 or not tonumber(arg1) or arg1==0 then
		key=nil
	else
		key=recap_temp.Keys[tonumber(arg1)]
	end
	return key
end

function Recap_LoadCombatants(filen,overwrite)

	local sd = {} -- temp holding place for fields that may not exist
	local found,set_friend,set_dmgin,set_dmgout,set_maxhit,set_heal,set_kills,set_time,set_faction,set_class,set_level

	if not filen or string.len(filen)<1 or not recap_set[filen] then
		return
	end

	Recap_ResetAllCombatants()
	
	recap[recap_temp.p].TotalDuration = recap[recap_temp.p].TotalDuration + recap_set[filen].TotalDuration
	
	for i in recap_set[filen].Combatant do
		if not recap.Combatant[i] then
			Recap_CreateBlankCombatant(i)
		end
		found,_,set_friend,set_dmgin,set_dmgout,set_maxhit,set_heal,set_kills,set_time = string.find(recap_set[filen].Combatant[i],"(%w+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+%.?%d+)")
		if found then
			if set_friend == "true" then
				recap.Combatant[i].Friend = true
			end
			if tonumber(set_maxhit) > recap.Combatant[i].TotalMaxHit then
				recap.Combatant[i].TotalMaxHit = tonumber(set_maxhit)
			end
			recap.Combatant[i].TotalDmgIn = recap.Combatant[i].TotalDmgIn + tonumber(set_dmgin)
			recap.Combatant[i].TotalDmgOut = recap.Combatant[i].TotalDmgOut + tonumber(set_dmgout)
			recap.Combatant[i].TotalKills = recap.Combatant[i].TotalKills + tonumber(set_kills)
			recap.Combatant[i].TotalHeal = recap.Combatant[i].TotalHeal + tonumber(set_heal)
			recap.Combatant[i].TotalTime = recap.Combatant[i].TotalTime + tonumber(set_time)
			if recap.Combatant[i].TotalTime > recap_temp.MinTime then
				recap.Combatant[i].TotalDPS = recap.Combatant[i].TotalDmgOut/recap.Combatant[i].TotalTime
			else
				recap.Combatant[i].TotalDPS = 0
			end
		end
		found,_,set_faction,set_class,set_level = string.find(recap_set[filen].Combatant[i],"~(%d+) (%d+) (%d+)")
		if not found then
			found,_,set_faction,set_class = string.find(recap_set[filen].Combatant[i],"~(%d+) (%d+)")
		end
		if found then
			recap.Combatant[i].Faction = Recap_GetKey(set_faction)
			recap.Combatant[i].Class = Recap_GetKey(set_class)
			recap.Combatant[i].Level = recap.Combatant[i].Level or set_level or 0
		end
		
		if not recap.Opt.LightData.value then

			-- process incoming: ^a000b000c000^
			for k in sd do sd[k] = nil end -- wipe temp buffer
			found,_,j = string.find(recap_set[filen].Combatant[i],"%^(.-)%^")
			if found then
				for k in inckey do
					_,_,sd[k] = string.find(j,inckey[k].."(%d+)")
				end
			elseif string.find(recap_set[filen].Combatant[i],"%^%d+ %d+ %d+ %d+ %d+ %d+ %d+ %d+ %d+ %d+ %d+ %d+ %d+") then
				found,_,sd.MeleeDamage,sd.MeleeMax,sd.MeleeHits,sd.MeleeCrits,sd.MeleeMissed,sd.MeleeDodged,sd.MeleeParried,sd.MeleeBlocked,
					sd.NonMeleeDamage,sd.NonMeleeMax,sd.NonMeleeHits,sd.NonMeleeCrits,sd.NonMeleeMissed =
						string.find(recap_set[filen].Combatant[i],"%^(%d+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+)")
			end
			if found then
				for k in sd do
					if sd[k] and tonumber(sd[k])>0 then
						recap.Combatant[i].Incoming[k] = tonumber(sd[k])
					end
				end
			end

			-- process details: [name:a000b000c000]
			for j in string.gfind(recap_set[filen].Combatant[i],"%[.-%]") do
				for k in sd do sd[k] = nil end -- wipe temp buffer
				found,_,set_name = string.find(j,"%[(.-)%>")
				if found then -- new saved method
					recap.Combatant[i].Detail[set_name] = {}
					for k in detkey do
						_,_,sd[k] = string.find(j,detkey[k].."(%d+)")
					end
				else
					found,_,set_name = string.find(j,"%[(.-) %d")
					if found then -- old saved method
						recap.Combatant[i].Detail[set_name] = {}
						_,_,sd.HitsDmg,sd.Hits,sd.HitsMax,sd.CritsDmg,sd.Crits,sd.CritsMax,sd.CritsEvents,sd.Missed =
							string.find(j,"%[.+ (%d+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+) (%d+)") -- old method
					end
				end
				if found then 
					for k in sd do
						if sd[k] and tonumber(sd[k])>0 then
							recap.Combatant[i].Detail[set_name][k] = tonumber(sd[k])
						end
					end
				end

			end
		end

	end

end

function Recap_ResetAllCombatants()

    local current_state = recap.Opt.State.value
	recap.Opt.State.value = "Stopped"

	recap.Combatant = {}
	recap_temp.IdleTimer = -1
	recap_temp.Last = {}
	recap[recap_temp.p].LastDuration = 0
	recap[recap_temp.p].TotalDuration = 0
	recap_temp.FightStart = 0
	recap_temp.FightEnd = 0
	recap_temp.ListSize = 0
	
	recap.Opt.State.value = current_state
end

local function Recap_LoadSelf()

	recap.Self = recap.Self or {}
	recap.Self[recap_temp.s] = recap.Self[recap_temp.s] or {}

	recap_temp.SelfList = {}
	recap_temp.SelfListSize = 1
end

function Recap_InitializeData()

	local i,j,k,l,m
	local temp_user = recap_temp.p
	local oldkeys = { ["Krieger"]="WARRIOR", ["Magier"]="MAGE", ["Schurke"]="ROGUE", ["Druide"]="DRUID",
					  ["J\195\164ger"]="HUNTER", ["Schamane"]="SHAMAN", ["Priester"]="PRIEST", ["Hexenmeister"]="WARLOCK",
					  ["Guerrier"]="WARRIOR", ["Voleur"]="ROGUE", ["Chasseur"]="HUNTER", ["Chaman"]="SHAMAN",
					  ["Prêtre"]="PRIEST", ["Démoniste"]="WARLOCK", ["Familier"]="Pet" }


	if not recap.DataVersion then
		-- wipe all pre-2.6 data
		recap = {}
		recap.DataVersion = 3.2
	elseif recap.DataVersion < 3.0 then
		-- convert 2.6 to 3.0, make classes uppercase
		for i in recap.Combatant do

			if recap.Combatant[i].Faction=="Allianz" then
				recap.Combatant[i].Faction = "Alliance"
			end

			if recap.Combatant[i].Class then
				-- convert classes to english, which all locales will use for icon reference
				if oldkeys[recap.Combatant[i].Class] then
					recap.Combatant[i].Class = oldkeys[recap.Combatant[i].Class]
				end
				recap.Combatant[i].Class = string.upper(recap.Combatant[i].Class)
			end
		end
		recap.DataVersion = 3.0
	end

	-- if this user hasn't been made before
	if not recap[recap_temp.p] then
		recap[recap_temp.p] = {}
		recap[recap_temp.p].TotalDuration = 0
		recap[recap_temp.p].LastDuration = 0
	end

	-- if this is a different/first-time user, or if this is first run of a new version,
	-- save the current combatants and options and then load them based on default.
	-- any changes in combatant or option format must come with a version change
	if (recap.User ~= recap_temp.p) or recap.DataVersion<Recap_Version then

		-- if we have data from previous user, save them
		if recap.User then
			recap_temp.p = recap.User
			Recap_SaveCombatants("UserData:"..recap.User,1)
			Recap_SaveOpt(recap.User)
			recap_temp.p = temp_user
		end
		recap.User = recap_temp.p

		-- wipe slate, load defaults and overlay saved data ontop
		Recap_LoadDefaultOpt()
		Recap_ResetAllCombatants()

		-- convert data sets to new format (see readme)
		if recap.DataVersion < 3.2 then
			for i in recap_set do
				if recap_set[i].Combatant then
					j = recap_set[i].TimeStamp
					Recap_LoadCombatants(i,1)
					for k in recap_set[i].Combatant do
						l = (l or 0) + string.len(recap_set[i].Combatant[k])
					end
					Recap_SaveCombatants(i,1)
					for k in recap_set[i].Combatant do
						m = (m or 0) + string.len(recap_set[i].Combatant[k])
					end
					recap_set[i].TimeStamp = j
				end
			end
			recap.DataVersion = 3.2
		end

		Recap_LoadCombatants("UserData:"..recap_temp.p,1)
		Recap_LoadOpt(recap_temp.p)

		recap.DataVersion = Recap_Version

	else
		for i in recap.Combatant do
			recap_temp.Last[i] = { Start = 0, End = 0, DmgIn = 0, DmgOut = 0, MaxHit = 0, Kills = 0, Heal = 0 }
		end
	end

	-- remove current UserData: - it's now "live" in recap.Combatant
	if recap_set and recap_set["UserData:"..recap_temp.p] then
		recap_set["UserData:"..recap_temp.p] = nil
	end

	Recap_LoadSelf()

end

--[[ Context menu functions ]]

function RecapMenu_OnUpdate(arg1)

	local i

	if not recap_temp.MenuTimer or MouseIsOver(RecapMenu) then
		recap_temp.MenuTimer = 0
	end

	recap_temp.MenuTimer = recap_temp.MenuTimer + arg1
	if recap_temp.MenuTimer > .2 then
		recap_temp.MenuTimer = 0
		RecapMenu:Hide()
		if recap_temp.SelectedEffect then
			for i=1,15 do
				getglobal("RecapSelf"..i):UnlockHighlight()
			end
		end
	end
end


--[[ Fight report functions ]]

function Recap_AutoPostGetStatID(arg1)

	if arg1=="Damage" then
		return "DmgOut"
	elseif arg1=="Tanking" then
		return "DmgIn"
	elseif arg1=="Healing" then
		return "Heal"
	end
	return "DPS"
end

function Recap_PostFight()

	local print_chat = SendChatMessage
	local chatchan, chatnum, text

	if recap.Opt.SpamRows.value then
		Recap_PostSpamRows(Recap_AutoPostGetStatID(recap.Opt.AutoPost.Stat),"recap auto")
	else
		chatchan = recap.Opt.AutoPost.Channel
		_,_,chatnum = string.find(chatchan,"(%d+)")
		if chatnum then
			chatchan = "CHANNEL"
		end
		if chatchan=="Self" then
			print_chat = Recap_Print
		end
		text = Recap_PostSpamLine(Recap_AutoPostGetStatID(recap.Opt.AutoPost.Stat))
		if string.find(text,"%d") then
			print_chat(Recap_PostSpamLine(Recap_AutoPostGetStatID(recap.Opt.AutoPost.Stat)),chatchan,nil,chatnum)
		end
	end

end

function Recap_PostLeader()

	local print_chat = SendChatMessage
	local chatchan, chatnum, leader, statid, i

	if recap_temp.ListSize>2 then
		chatchan = recap.Opt.AutoPost.Channel
		_,_,chatnum = string.find(chatchan,"(%d+)")
		if chatnum then
			chatchan = "CHANNEL"
		end
		if chatchan=="Self" then
			print_chat = Recap_Print
		end
		statid = Recap_AutoPostGetStatID(recap.Opt.AutoPost.Stat)

		if not recap.Combatant[recap_temp.Leader] then -- in case leader reset
			recap_temp.Leader = nil
		end

		if not recap_temp.Leader and recap.Combatant[recap_temp.List[1].Name].Friend then
			recap_temp.Leader = recap_temp.List[1].Name
			leader = recap_temp.Leader -- spam initial leader
		end

		if recap_temp.Leader then
			for i in recap.Combatant do
				if (recap.Combatant[i]["Total"..statid] > recap.Combatant[recap_temp.Leader]["Total"..statid]) and recap.Combatant[i].Friend then
					leader = i
				end
			end
			if leader then
				recap_temp.Leader = leader
				print_chat(string.format("New leader for %s: %s with %s",recap.Opt.AutoPost.Stat,leader,Recap_FormatStat(statid,leader)),chatchan,nil,chatnum) 
			end
		end
	end
end


-- returns 'text' to be spammed in autopost or linked in chat
function Recap_PostSpamLine(arg1)

	local text,i

	text = recap_temp.Local.LinkRank[arg1].." for "..recap_temp.Local.LastAll[recap.Opt.View.value]..":"
	for i=1,recap.Opt.MaxRank.value do
		if i<recap_temp.ListSize and recap.Combatant[recap_temp.List[i].Name].Friend then
			if recap_temp.List[i][arg1]>0 then
				text = text.." ["..i.."."..recap_temp.List[i].Name.." "..Recap_FormatStat(arg1,i).."]"
			end
		end
	end
	return text
end

-- arg1 = stat to report by (DPS, DmgOut, etc)
function Recap_PostSpamRows(arg1,chatchan,chatnum)

	local headertext = nil
	local print_chat = SendChatMessage

	if chatchan=="recap auto" then
		chatchan = recap.Opt.AutoPost.Channel
	end

	arg1 = string.gsub(arg1,"P$","") -- strip trailing P from % id's

	if not chatnum then
		_,_,chatnum = string.find(chatchan,"(%d+)")
		if chatnum then
			chatchan = "CHANNEL"
		end
	end
	if chatchan=="Self" then
		print_chat = Recap_Print
	end
	headertext = "__ "..recap_temp.Local.LinkRank[arg1].." for "..recap_temp.Local.LastAll[recap.Opt.View.value].." __"
	for i=1,recap.Opt.MaxRank.value do
		if i<recap_temp.ListSize and recap.Combatant[recap_temp.List[i].Name].Friend then
			text = i..". "..recap_temp.List[i].Name..": "..Recap_FormatStat(arg1,i)
			if recap_temp.List[i][arg1] > 0 then
				if headertext then
					print_chat(headertext,chatchan,nil,chatnum)
					headertext = nil
				end
				print_chat(text,chatchan,nil,chatnum)
			end
		end
	end
end

-- returns a formatted string for arg1 stat ("DPS" "Time" etc) for combatant arg2 (number is .List[], non-number is name)
function Recap_FormatStat(arg1,arg2)

	local i

    arg1 = string.gsub(arg1,"P$","")

	if not tonumber(arg2) then

		for i=1,(recap_temp.ListSize-1) do
			if recap_temp.List[i].Name==arg2 then
				arg2 = i
				i = recap_temp.ListSize
			end
		end
	end

	if tonumber(arg2) then

		if arg1=="DPS" then
			return string.format("%.1f",recap_temp.List[arg2].DPS)
		elseif arg1=="DPSvsAll" then
			return string.format("%.1f",recap_temp.List[arg2].DPSvsAll)
		elseif arg1=="Time" then
			return Recap_FormatTime(recap_temp.List[arg2].Time)
		elseif arg1=="DmgOut" then
			return recap_temp.List[arg2].DmgOut.." ("..recap_temp.List[arg2].DmgOutP.."%)"
		elseif arg1=="DmgIn" then
			return recap_temp.List[arg2].DmgIn.." ("..recap_temp.List[arg2].DmgInP.."%)"
		elseif arg1=="Heal" then
			return recap_temp.List[arg2].Heal.." ("..recap_temp.List[arg2].HealP.."%)"
		elseif arg1=="Over" then
			return recap_temp.List[arg2].Over.."%"
		else
			return recap_temp.List[arg2][arg1]
		end
	end

	return ""
end


function Recap_Print(arg1)
	DEFAULT_CHAT_FRAME:AddMessage(tostring(arg1))
end

--[[ Debug ]]

function Recap_DebugUnusedFilters(filter)

	local i,j,found

	for i in recap_temp[filter] do
		found = false
		for j in recap.debug_Filter do
			if recap_temp[filter][i].pattern==recap.debug_Filter[j].pattern then
				found = true
			end
		end
		if not found then
			DEFAULT_CHAT_FRAME:AddMessage(filter..": "..recap_temp[filter][i].pattern)
		end
	end

end

function Recap_MoveMinimize()

	RecapCloseButton:ClearAllPoints()
	RecapCloseButton:SetPoint("TOPRIGHT","RecapFrame","TOPRIGHT",-6,-6)
	RecapMinStatus:ClearAllPoints()
	RecapMinStatus:SetPoint("TOPLEFT", "RecapFrame", "TOPLEFT", 6,-7)
	RecapResetButton:ClearAllPoints()
	RecapResetButton:SetPoint("BOTTOMLEFT", "RecapFrame", "BOTTOMLEFT", 5,4)
	RecapMinimizeButton:ClearAllPoints()

	if recap.Opt.GrowLeftwards.value and not recap.Opt.GrowUpwards.value then -- topright
		RecapMinimizeButton:SetPoint("TOPRIGHT", "RecapFrame", "TOPRIGHT", -6, -6)
		RecapCloseButton:SetPoint("TOPRIGHT", "RecapMinimizeButton", "TOPLEFT", -2, 0)
	elseif recap.Opt.GrowLeftwards.value and recap.Opt.GrowUpwards.value then -- bottomright
		RecapMinimizeButton:SetPoint("BOTTOMRIGHT", "RecapFrame", "BOTTOMRIGHT", -6, 6)
		if recap.Opt.Minimized.value then
			RecapCloseButton:SetPoint("TOPRIGHT", "RecapMinimizeButton", "TOPLEFT", -2, 0)
		end
	elseif not recap.Opt.GrowLeftwards.value and not recap.Opt.GrowUpwards.value then -- topleft
		RecapMinimizeButton:SetPoint("TOPLEFT", "RecapFrame", "TOPLEFT", 6, -6)
		RecapMinStatus:SetPoint("TOPLEFT", "RecapMinimizeButton", "TOPRIGHT", 2, -1)
	else -- bottomleft
		RecapMinimizeButton:SetPoint("BOTTOMLEFT", "RecapFrame", "BOTTOMLEFT", 6, 6)
		RecapResetButton:SetPoint("BOTTOMLEFT", "RecapFrame", "BOTTOMLEFT", 23, 4)
	end

	if recap.Opt.Minimized.value and not recap.Opt.GrowLeftwards.value then
		RecapMinStatus:SetPoint("TOPLEFT", "RecapMinimizeButton", "TOPRIGHT", 2, -1)
	end

	if recap.Opt.AutoMinimize.value then
		RecapMinimizeButton:SetWidth(1)
		RecapMinimizeButton:Hide()
	else
		RecapMinimizeButton:SetWidth(16)
		RecapMinimizeButton:Show()
	end

end

function RecapMenu_SetCheck(id,arg1)

	if arg1 then
		getglobal("RecapMenu"..id.."_Check"):Show()
	else
		getglobal("RecapMenu"..id.."_Check"):Hide()
	end
end

function RecapMenu_OnClick()

	local id,index = this:GetID()

	if recap_temp.Menu==recap_temp.ColumnMenu or recap_temp.Menu==recap_temp.MinMenu or recap_temp.Menu==recap_temp.EffectOptMenu then
		index = recap_temp.Menu[id].Info -- .Info of menu item selected
		if recap.Opt[index] then
			recap.Opt[index].value = not recap.Opt[index].value
			getglobal("RecapOptCheck_"..recap_temp.Menu[id].Info):SetChecked(0)
			if recap.Opt[index].value then
				getglobal("RecapOptCheck_"..recap_temp.Menu[id].Info):SetChecked(1)
			end
			RecapMenu_SetCheck(id,recap.Opt[index].value)
			if recap.Opt.Minimized.value then
				Recap_Minimize()
			else
				Recap_Maximize()
			end
		elseif index=="Pin" then
			recap.Opt.Pinned.value = not recap.Opt.Pinned.value
			Recap_SetButtons()
			RecapMenu:Hide()
		end
	end


	if recap_temp.Menu==recap_temp.AddMenu or recap_temp.Menu==recap_temp.DropMenu then
		RecapMenu:Hide()
		index = recap_temp.List[recap_temp.Selected].Name -- name selected

		if id==1 and recap_temp.Menu==recap_temp.DropMenu and index~=recap_temp.Player then
			recap.Combatant[index].Friend = false
		elseif id==1 and index~=recap_temp.Player then
			recap.Combatant[index].Friend = not recap.Combatant[index].Friend
		elseif id==2 and recap.Opt.View.value=="All" then -- reset
			recap.Combatant[index] = nil
			Recap_CreateBlankCombatant(index)
		elseif id==2 and recap.Opt.View.value=="Last" then
			if recap.Combatant[index].WasInLast then
				Recap_ResetLastFight(index)
			end
		elseif id==3 and index~=recap_temp.Player then
			recap.Combatant[index] = nil
			Recap_CreateBlankCombatant(index)
			recap.Combatant[index].Ignore = true
		end
	
		Recap_SetView()
	end

	if recap_temp.Menu==recap_temp.EffectMenu then
		RecapMenu:Hide()
		for i=1,15 do
			getglobal("RecapSelf"..i):UnlockHighlight()
		end
		if recap_temp.SelectedEffect and recap.Self[recap_temp.s][recap_temp.SelectedEffect] then
			recap.Self[recap_temp.s][recap_temp.SelectedEffect] = nil
			recap_temp.SelectedEffect = nil
			Recap_SetView()
		end
	end

	if recap_temp.Menu==recap_temp.DetailMenu then
		if recap_temp.DetailsList and recap_temp.DetailsList[1][recap_temp.Menu[id].Info] then
			recap.Opt.PanelDetail.value = recap_temp.Menu[id].Info
			RecapPanel_Populate(recap_temp.List[recap_temp.Selected].Name)
		end
		RecapMenu:Hide()
	end
end

-- offset=1 to move window so it's under pointer
function RecapCreateMenu(menu,offset)

	local i,cy,x,y,lines

	recap_temp.Menu = menu

	if recap_temp.Menu==recap_temp.ColumnMenu or recap_temp.Menu==recap_temp.MinMenu or recap_temp.Menu==recap_temp.EffectOptMenu then
		for i in recap_temp.Menu do
			if recap.Opt[recap_temp.Menu[i].Info] then
				recap_temp.Menu[i].Check = recap.Opt[recap_temp.Menu[i].Info].value
			end
		end
	end

	if recap_temp.Menu==recap_temp.MinMenu then
		recap_temp.Menu[1].Check = recap.Opt.Pinned.value
	end

	if recap_temp.Menu==recap_temp.DetailMenu then
		for i in recap_temp.Menu do
			recap_temp.Menu[i].Check = recap.Opt.PanelDetail.value==recap_temp.Menu[i].Info
		end
	end

	lines = min(table.getn(recap_temp.Menu),16)

	cy = 44 -- 12+32
	for i=1,lines do
		getglobal("RecapMenu"..i.."_Text"):SetText(recap_temp.Menu[i].Text)
		RecapMenu_SetCheck(i,recap_temp.Menu[i].Check)
		getglobal("RecapMenu"..i):Show()
		cy = cy + 14
	end
	for i=(lines+1),16 do
		getglobal("RecapMenu"..i):Hide()
	end

	RecapMenu:SetHeight(cy)
	RecapDropSubFrame:SetHeight(cy-32)
	RecapMenu:ClearAllPoints()
	x,y = GetCursorPosition()
	if recap.Opt.GrowUpwards.value and not offset then
		y = this:GetBottom()*UIParent:GetScale()
		RecapMenu:SetPoint("BOTTOMRIGHT","UIParent","BOTTOMLEFT",(x/UIParent:GetScale())+20,(y/UIParent:GetScale())-4)
	else
		y = this:GetTop()*UIParent:GetScale()
		RecapMenu:SetPoint("TOPRIGHT","UIParent","BOTTOMLEFT",(x/UIParent:GetScale())+20,(y/UIParent:GetScale())+(offset and 16 or 4))
	end
	RecapMenu:Show()

end

function RecapMenu_OnEnter()
	id = this:GetID()
	if recap_temp.Menu[id] and recap_temp.Menu[id].Info then
		if recap_temp.Menu==recap_temp.AddMenu or recap_temp.Menu==recap_temp.DropMenu then
			Recap_OnTooltip(recap_temp.Menu[id].Info,recap_temp.List[recap_temp.Selected].Name)
		elseif recap_temp.Menu==recap_temp.EffectMenu and recap_temp.SelectedEffect then
			Recap_OnTooltip(recap_temp.Menu[id].Info,string.sub(recap_temp.SelectedEffect,2))
		else
			Recap_OnTooltip(recap_temp.Menu[id].Info)
		end
	end
end

function RecapEffectsHeader_OnEnter()
	local s

	_,_,s = string.find(this:GetName(),"RecapHeader_(.+)")
	if s then
		Recap_OnTooltip("Header"..s)
	end
end

function RecapSelf_OnMouseUp(arg1)

	local index,i 
	
	recap_temp.SelectedEffect = nil

	Recap_OnMouseUp(arg1)
	if arg1=="RightButton" then
		_,_,i = string.find(this:GetName(),"(%d+)")
		if i and tonumber(i) then
			i = tonumber(i)
			index = i + FauxScrollFrame_GetOffset(RecapScrollBar)
			if index < recap_temp.SelfListSize then
				this:LockHighlight()
				recap_temp.SelectedEffect = recap_temp.SelfList[index].EName
			end
		end
		
		RecapCreateMenu(recap_temp.EffectMenu,1)
	end
end

function RecapEffectsHeader_OnMouseUp(arg1)

	if arg1=="RightButton" then
		RecapCreateMenu(recap_temp.EffectOptMenu)
	end
end

function RecapEffectsHeader_OnClick()

	local idx,i
	local alltext,text = ""
	local channel,chatnumber = ChatFrameEditBox.chatType
	local hasdata

	if IsShiftKeyDown() and ChatFrameEditBox:IsVisible() then

		if channel=="WHISPER" then
			chatnumber = ChatFrameEditBox.tellTarget
		elseif channel=="CHANNEL" then
			chatnumber = ChatFrameEditBox.channelTarget
		end
		
		_,_,idx = string.find(tostring(this:GetName()),"RecapHeader_(.+)")
		if idx and idx~="EName" then

			alltext = (recap.Opt.SpamRows.value and "__ " or "")..recap_temp.Player.."'s "..Recap_GetTooltip("Header"..idx)..(recap.Opt.SpamRows.value and " __" or ":")
			for i=1,recap_temp.SelfListSize-1 do
				if recap_temp.SelfList[i][idx]~="0.0%" and recap_temp.SelfList[i][idx]~="--" and tostring(recap_temp.SelfList[i][idx])~="0" then
					if not hasdata and recap.Opt.SpamRows.value then
						SendChatMessage(alltext,channel,nil,chatnumber)
					end
					hasdata = true
					text = string.sub(recap_temp.SelfList[i].EName,2)..(string.sub(recap_temp.SelfList[i].EName,1,1)=="3" and "*" or "")..(recap.Opt.SpamRows.value and ": " or " ")..recap_temp.SelfList[i][idx]..(idx=="ETotal" and (" ("..recap_temp.SelfList[i].ETotalP..")") or "")
					if not recap.Opt.SpamRows.value then
						alltext = alltext.." ["..text.."]"
					else
						SendChatMessage(text,channel,nil,chatnumber)
					end
				end
			end
			if not recap.Opt.SpamRows.value and hasdata then
				Recap_InsertChat(alltext)
			end
		end
	end
end


function RecapSelf_OnClick()

	local id = this:GetID()
	local index = id + FauxScrollFrame_GetOffset(RecapScrollBar)
	local linkformat = recap_temp.Local.DamageDetailLink
	local text = ""

	if IsShiftKeyDown() and index<recap_temp.SelfListSize and ChatFrameEditBox:IsVisible() then

		if string.sub(recap_temp.SelfList[index].EName,1,1)=="3" then
			linkformat = recap_temp.Local.HealDetailLink
		end
		text = string.format(linkformat,recap_temp.Player,string.sub(recap_temp.SelfList[index].EName,2),recap_temp.SelfList[index].ETotal,recap_temp.SelfList[index].ETotalP )..", Max: "..recap_temp.SelfList[index].EMaxAll..((recap_temp.SelfList[index].ECritsP~="--") and (", Crit Rate: "..recap_temp.SelfList[index].ECritsP) or "")
		Recap_InsertChat(text," ")
	end
end


function Recap_Combat_OnEvent()

	if event=="CHAT_MSG_COMBAT_FRIENDLY_DEATH" or event=="CHAT_MSG_COMBAT_HOSTILE_DEATH" then
		Recap_DeathEvent(arg1)
	elseif event=="UPDATE_MOUSEOVER_UNIT" then
		Recap_GetUnitInfo("mouseover")
	elseif event=="PLAYER_TARGET_CHANGED" then
		Recap_GetUnitInfo("target")
	elseif event=="SPELLCAST_START" then
		if (not recap.Opt.LightData.value) and recap.Combatant[recap_temp.Player] and recap.Combatant[recap_temp.Player].Detail["1"..arg1] then
			if not recap.Opt.LimitFights.value then
				if recap.Opt.IdleFight.value then
					recap_temp.IdleTimer = 0
				end
				if recap.Opt.State.value=="Idle" then
					Recap_StartFight()
				end
			end
			Recap_CreateCombatant(recap_temp.Player,1) -- if this was a cast that's done damage before, start timer
		end
	elseif string.find(event,"BUFF") then
		Recap_HealEvent(arg1)
	else
		Recap_DamageEvent(arg1)
	end
end

function Recap_Register_CombatEvents()

	if not recap_temp.CombatEventsRegistered then
	-- register damage events
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_HITS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_MISSES")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_PARTY_HITS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_PARTY_MISSES")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_PARTY_MISSES")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_PET_HITS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_PET_MISSES")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_SELF_HITS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_SELF_MISSES")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PET_DAMAGE")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")

		-- register death events
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_FRIENDLY_DEATH")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_COMBAT_HOSTILE_DEATH")

		-- register healing events
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_CREATURE_BUFF")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_PARTY_BUFF")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_SELF_BUFF")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PARTY_BUFF")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_PET_BUFF")
		RecapCombatEvents:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF")

		RecapCombatEvents:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
		RecapCombatEvents:RegisterEvent("PLAYER_TARGET_CHANGED")
		RecapCombatEvents:RegisterEvent("SPELLCAST_START")
		recap_temp.CombatEventsRegistered = 1
	end
end

function Recap_Unregister_CombatEvents()
	if recap_temp.CombatEventsRegistered then
		RecapCombatEvents:UnregisterAllEvents()
		recap_temp.CombatEventsRegistered = nil
	end
end