XPerl_MainTanks = {}
MainTankCount = 0
local MainTanks = {}
local Events = {}
local XUnits = {}
local XFreeUnits = {}
local UpdateTime = 0
local XGaps = 0
local XTitle
local pendingTankListChange		-- If in combat when tank list changes, then we'll defer it till next time we're out of combat

-- Dup colours used to show which tanks have the same target
local MTdupColours = {	{r = "0.8", g = "0.2", b = "0.2"},
			{r = "0.2", g = "0.2", b = "0.8"},
			{r = "0.8", g = "0.2", b = "0.8"},
			{r = "0.2", g = "0.8", b = "0.2"},
			{r = "0.2", g = "0.8", b = "0.8"}}

local MAX_UNITS = 50
local MAX_OTHER_ROWS = 10
local GAP_SPACING = 10
XPERL_UNIT_HEIGHT_MIN = 17
XPERL_UNIT_HEIGHT_MAX = 30
XPERL_UNIT_WIDTH_MIN = 50
XPERL_UNIT_WIDTH_MAX = 125

if (not XPerlColourTable) then
	XPerlColourTable = {}
	for class,c in pairs(RAID_CLASS_COLORS) do
		XPerlColourTable[class] = string.format("|c00%02X%02X%02X", 255 * c.r, 255 * c.g, 255 * c.b)
	end
end

-- Although your own "target" is updated immediately, any reference to ourself via our raid id is updated via the server
-- So, to cure the slow response times, we check if anything is 'me'. Only direct targets are updated immedately,
-- so we don't bother with target's target onwards.
local function SpecialCaseUnit(unit)
	if (unit.unit and unit.type == "MTT" and UnitIsUnit("player", unit.unit)) then
		return "target"
	else
		return unit.partyid
	end
end

----------------------------
------- DISPLAY ------------
----------------------------
local function UpdateUnit(unit,forcedUpdate)

	if (forcedUpdate) then
		local time = GetTime()
		if (unit.update and time < unit.update + 0.2) then
			-- Since we catch UNIT_HEALTH changes, we don't need to update always
			return
		end
	end

	unit.update = GetTime()

	local xunit = SpecialCaseUnit(unit)

	local name = UnitName(xunit)
	local text = getglobal(unit:GetName().."_Name")
	local bar = getglobal(unit:GetName().."_HealthBar")
	local percent = getglobal(unit:GetName().."_HealthBarText")
	local barBG = getglobal(unit:GetName().."_HealthBarBG")
	local combat = getglobal(unit:GetName().."_Combat")
	local warning = getglobal(unit:GetName().."_Warning")
	local raidIcon = getglobal(unit:GetName().."_RaidIcon")
	local percBar

	barBG:Show()

	if (name and name ~= UNKNOWNOBJECT) then
		-- Name
		text:SetText(name)
		text:SetPoint("BOTTOMRIGHT", unit, "TOPRIGHT", 0, -12)

		local remCount = 1
		while ((text:GetStringWidth() >= (unit:GetWidth() - 2)) and (string.len(name) > remCount)) do
			name = string.sub(name, 1, string.len(name) - remCount)..".."
			remCount = 3
			text:SetText(name)
		end

		if (UnitPlayerControlled(xunit)) then
			XPerl_ColourFriendlyUnit(text, xunit)
		else
			XPerl_SetUnitNameColor(xunit, text)
		end

		-- Health
		local healthMax = UnitHealthMax(xunit)
		local health = UnitHealth(xunit)
		percBar = health / healthMax
		local perc = percBar * 100

		bar:SetMinMaxValues(0, healthMax)
		bar:SetValue(health)

		if (UnitIsDead(xunit)) then
			percent:SetText(XPERL_LOC_DEAD)
		else
			percent:SetText(math.floor(perc + 0.5).."%")
		end
		if (XPerlConfigHelper.UnitHeight < 23) then
			percent:Hide()
		else
			percent:Show()
		end

		if (unit.type == "MTT") then
			if (BigWigsTargetMonitor) then
				BigWigsTargetMonitor:TargetCheck(xunit)
			end

			local index = GetRaidTargetIndex(xunit)
			if (index) then
				SetRaidTargetIconTexture(raidIcon, index)
				raidIcon:Show()
			else
				raidIcon:Hide()
			end
		else
			raidIcon:Hide()
		end
	else
		text:SetPoint("BOTTOMRIGHT", -2, 1)

		if (unit.type == "MTT") then
			name = UnitName(unit.unit)

			if (name) then
				XPerl_ColourFriendlyUnit(text, unit.unit)
				text:SetText(string.format(XPERL_XS_TARGET, name))
				barBG:Hide()
			end
		else
			text:SetText(XPERL_NO_TARGET)
			text:SetTextColor(0.5, 0.5, 0.5, 1)
		end
		bar:SetMinMaxValues(0, 1)
		bar:SetValue(0)
		percBar = 0
		percent:Hide()
		raidIcon:Hide()
	end

	if (UnitAffectingCombat(xunit)) then
		combat:Show()
	else
		combat:Hide()
	end

	if (UnitIsCharmed(xunit)) then
		warning:Show()
	else
		warning:Hide()
	end

	XPerl_SetSmoothBarColor (bar, percBar)
end

-- XPerl_RaidHelp_Show
function XPerl_RaidHelp_Show()
	XPerlConfigHelper.RaidHelper = 1
	XPerl_EnableDisable()
	return true
end

-- XPerl_MTListUnit_OnEnter
function XPerl_MTListUnit_OnEnter(self)

	local x = XPerl_Frame:GetCenter()
	local a1, a2 = "ANCHOR_LEFT", "ANCHOR_TOPLEFT"
	if ( x < (GetScreenWidth() / 2)) then
		a1, a2 = "ANCHOR_RIGHT", "ANCHOR_TOPRIGHT"
	end

	if (XPerlConfigHelper.Tooltips == 1) then
		if (self.partyid and UnitName(self.partyid) and XPerlConfigHelper.TooltipsWhich == 2) then
			GameTooltip:SetOwner(self, a1)
			GameTooltip:SetUnit(self.partyid)

			if (self.unit and (self.type == "MTT") and XPerlConfigHelper.TooltipsWhich == 2) then
				XPerl_BottomTip:SetOwner(GameTooltip, a2, 0, 10)
				XPerl_BottomTip:SetUnit(self.unit)
				XPerl_BottomTip:SetBackdropColor(0.1, 0.4, 0.1, 0.75)
			end
		else
			if (XPerlConfigHelper.TooltipsWhich == 0) then
				GameTooltip:SetOwner(self, a1)
				GameTooltip:SetUnit(self.partyid)
			else
				if (self.type == "MTT") then
					GameTooltip:SetOwner(self, a1)
					GameTooltip:SetUnit(self.unit)
					GameTooltip:SetBackdropColor(0.1, 0.4, 0.1, 0.75)
				end
			end
		end
	end
end

-- XPerl_SetupFrameSimple
function XPerl_SetupFrameSimple(argFrame, alpha)
	argFrame:SetBackdropBorderColor(0.5, 0.5, 0.5, alpha or 0.8)
	argFrame:SetBackdropColor(0, 0, 0, alpha or 0.8)
end

-- AllocateUnit
local AllocateUnitNum = 0
local LastUnitWarning
local function AllocateUnit(list, parent)

	local unit = XFreeUnits[1]
	if (unit) then
		tremove(XFreeUnits, 1)
		tinsert(list, unit)
		unit:SetParent(parent)
	else
		if (AllocateUnitNum < MAX_UNITS) then
			AllocateUnitNum = AllocateUnitNum + 1
			unit = CreateFrame("Button", "XPerl_Unit"..AllocateUnitNum, parent, "XPerl_UnitTemplate")

			if (unit) then
				XPerl_SetupFrameSimple(unit, XPerlConfigHelper.Background_Transparency)
				unit:SetAlpha(XPerlConfigHelper.Targets_Transparency)
				tinsert(list, unit)

                                if (XPerl_StatusBarSetValue) then
                                	if (not XPerl_OldStatusBarSetValue) then
                                        	XPerl_OldStatusBarSetValue = unit.SetValue
                                        end
                                	unit.SetValue = XPerl_StatusBarSetValue
                                end

				if (XPerl_RegisterHighlight) then
					XPerl_RegisterHighlight(unit, 3)
				else
					local tex = unit:GetHighlightTexture()
					tex:SetVertexColor(0.86, 0.82, 0.41)
				end
			else
				XPerl_Notice("Error creating frame called 'XPerl_Unit"..AllocateUnitNum.."' from template 'XPerl_UnitTemplate'")
			end
		else
			if (not LastUnitWarning or GetTime() > LastUnitWarning + 600) then
				LastUnitWarning = GetTime()
		   		XPerl_Notice("Maximum number of units reached ("..MAX_UNITS..")")
			end
		end
	end

	if (unit) then
		if (XPerl_GetBarTexture) then
			local tex = XPerl_GetBarTexture()
			getglobal(unit:GetName().."_HealthBarTex"):SetTexture(tex)

			if (XPerlConfig.BackgroundTextures == 1) then
				getglobal(unit:GetName().."_HealthBarBG"):SetTexture(tex)
			else
				getglobal(unit:GetName().."_HealthBarBG"):SetTexture("Interface\\TargetingFrame\\UI-StatusBar")
			end
		end

		local t = getglobal(unit:GetName().."_HealthBarText");		t:Hide()
		t = getglobal(unit:GetName().."_Label");	t:SetText("");	t:Hide()
		t = getglobal(unit:GetName().."_Name");				t:Show()

		XPerl_SetupFrameSimple(unit, XPerlConfigHelper.Background_Transparency)

		unit:SetScale(1)
		unit:SetWidth(XPerlConfigHelper.UnitWidth)
		unit:SetHeight(XPerlConfigHelper.UnitHeight)

		local bar = getglobal(unit:GetName().."_HealthBar")
		bar:ClearAllPoints()
		if (XPerlConfigHelper.UnitHeight > 16) then
			bar:SetPoint("TOPLEFT", 3, -14)
			bar:SetPoint("BOTTOMRIGHT", -3, 2)
		else
			bar:SetPoint("TOPLEFT", 4, -4)
			bar:SetPoint("BOTTOMRIGHT", -4, 4)
		end

		unit:Show()
	end

	return unit
end

-- Remove units from list and place back into available unit pool
local function UnallocateUnits(list)

	while (#list > 0) do
		local unit = list[1]

		unit:ClearAllPoints()
		unit:Hide()

		if (unit:GetAttribute("unit")) then
			XPerl_UnregisterClickCastFrame(unit)
			unit:SetAttribute("unit", nil)
		end

		unit.partyid = nil
		unit.unit = nil
		unit.OtherTarget = nil
		unit.collapseable = nil
		unit.type = nil

		tremove(list, 1)
		tinsert(XFreeUnits, unit)
	end
end

-- XPerl_HelperBarTextures
if (XPerl_GetBarTexture) then
function XPerl_HelperBarTextures(tex)
	local function UnitTex(v, tex)
		local f = getglobal(v:GetName().."_HealthBarTex")
		f:SetTexture(tex)
		f = getglobal(v:GetName().."_HealthBarBG")
		if (XPerlConfig and XPerlConfig.BackgroundTextures == 1) then
			f:SetTexture(tex)
		else
			f:SetTexture("Interface\\TargetingFrame\\UI-StatusBar")
		end
	end

	XPerl_MyThreatTex:SetTexture(tex)
	XPerl_MyThreatBG:SetTexture(tex)

	for k,v in pairs(XUnits) do
		UnitTex(v, tex)
	end
	for k,v in pairs(XFreeUnits) do
		UnitTex(v, tex)
	end
end
end

-- RemoveDupMT
local function RemoveDupMT(name)
	for k, v in pairs(XPerl_MainTanks) do
		if (v[2] == name) then
			XPerl_MainTanks[k] = nil
			return true
		end
	end
end

-- GetRaidIDByName
local function GetRaidIDByName(name)
	for i = 1, GetNumRaidMembers() do
		if (UnitName("raid"..i) == name) then
			return i
		end
	end
end

-- GetUnitRank
local function GetUnitRank(name)
	local index = GetRaidIDByName(name)
	if (index) then
		local _, rank = GetRaidRosterInfo(index)
		return rank
	end
	return 0
end

-- ValidateTankList
-- Check the roster for any tanks that have left the raid
local function ValidateTankList()
	for index,entry in pairs(XPerl_MainTanks) do
		local found
		for i = 1,GetNumRaidMembers() do
			if (UnitName("raid"..i) == entry[2]) then
				found = true
				break
			end
		end

		if (not found) then
			XPerl_MainTanks[index] = nil
		end
	end
end

-- XPerl_MTRosterChanged()
function XPerl_MTRosterChanged()
	ValidateTankList()

	MainTankCount = 0
	for i in pairs(XPerl_MainTanks) do
		MainTankCount = MainTankCount + 1
	end

	if (MainTankCount == 0 or XPerlConfigHelper.UseCTRATargets == 0) then
		-- If no defined tanks, then make a list from the warriors in raid
		MainTanks = {}
		MainTankCount = 0

		if (XPerlConfigHelper.NoAutoList == 0) then
			for i = 1,GetNumRaidMembers() do
				local _, class = UnitClass("raid"..i)
				if (class == "WARRIOR") then
					tinsert(MainTanks, {i, UnitName("raid"..i)})
					MainTankCount = MainTankCount + 1
				end
			end
		end
	else
		MainTankCount = 0
		for index,entry in pairs(XPerl_MainTanks) do
			local raidid = GetRaidIDByName(entry[2])
			if (raidid) then
				MainTankCount = MainTankCount + 1
				entry[1] = raidid
			end
		end

		MainTanks = XPerl_MainTanks
	end

	XPerl_MakeTankList()
end

-- CT_RA_IsSendingWithVersion override
-- Override this so that we get the MT list if there's only us in the raid with a CTRA version registered
local old_CT_RA_IsSendingWithVersion
if (CT_RA_IsSendingWithVersion) then
	if (CT_RA_IsSendingWithVersion ~= XPerl_IsSendingWithVersion) then
		old_CT_RA_IsSendingWithVersion = CT_RA_IsSendingWithVersion
		CT_RA_IsSendingWithVersion = function(version)
			if (version == 1.08) then
				return 1
			end
			return old_CT_RA_IsSendingWithVersion(version)
		end
	end
end

-- ProcessCTRAMessage
local function ProcessCTRAMessage(unitName, msg)

	local mtListUpdate

	if (strsub(msg, 1, 4) == "SET ") then
		local rankSender = GetUnitRank(unitName)
		if (rankSender < 1) then
			return
		end

		local num, name = strmatch(msg, "^SET (%d+) (.+)$")
		if (num and name) then
			num = tonumber(num)
			local mtID = 0
			for i = 1, GetNumRaidMembers() do
				if (UnitName("raid"..i) == name) then
					mtID = i
					break
				end
			end

			if (XPerl_MainTanks[num]) then
				if (XPerl_MainTanks[num][1] == mtID and XPerl_MainTanks[num][2] == name) then
					return			-- No Change
				end
			end

			RemoveDupMT(name)

			XPerl_MainTanks[tonumber(num)] = {mtID, name}

			mtListUpdate = true
		end

	elseif (strsub(msg, 1, 2) == "R ") then
		local rankSender = GetUnitRank(unitName)
		if (rankSender < 1) then
			return
		end

		local name = strmatch(msg, "^R (.+)$")
		if (name) then
			mtListUpdate = RemoveDupMT(name)
		end
	end

	if (mtListUpdate) then
		XPerl_MTRosterChanged()
	end
end

-- XPerl_SplitCTRAMessage
function XPerl_SplitCTRAMessage(msg, char)
	local arr = { }
	while (strfind(msg, char) ) do
		local iStart, iEnd = strfind(msg, char)
		tinsert(arr, strsub(msg, 1, iStart-1))
		msg = strsub(msg, iEnd+1, strlen(msg))
	end
	if ( strlen(msg) > 0 ) then
		tinsert(arr, msg)
	end
	return arr
end

-- XPerl_ParseCTRA
function XPerl_ParseCTRA(nick, msg, func)
	if (strfind(msg, "#")) then
		local arr = XPerl_SplitCTRAMessage(msg, "#")
		for i,subMsg in pairs(arr) do
			func(nick, subMsg)
		end
	else
		func(nick, msg)
	end
end

-- CHAT_MSG_ADDON
function Events:CHAT_MSG_ADDON()
	if (arg1 == "CTRA" and arg3 == "RAID") then
		XPerl_ParseCTRA(arg4, arg2, ProcessCTRAMessage)
	end
end

----------------------------
---------- EVENTS ----------
----------------------------

-- OnEvent
function XPerl_OnEvent()
	local Event = Events[event]
	if (Event) then
		Event()
	else
XPerl_ShowMessage("EXTRA EVENT")
	end
end

-- XPerl_GetUnit
local function XPerl_GetUnit(unit)
	for i,search in pairs(XUnits) do
		if (search.unit == unit) then
			return search
		end
	end
end

-- ScanForMTDups
-- Check MTs all have unique targets
local function ScanForMTDups()
	local dup = 1
	local dups = {}

	for i,unit in pairs(XUnits) do
		XPerl_SetupFrameSimple(unit, XPerlConfigHelper.Background_Transparency)
	end

	for i,t1 in pairs(MainTanks) do
		local any = false

		if (UnitName("raid"..t1[1].."target") and UnitName("raid"..t1[1].."target") ~= UNKNOWNOBJECT and not UnitIsDeadOrGhost("raid"..t1[1])) then
			for j,t2 in pairs(MainTanks) do
				if (j > i and not dups["raid"..t2[1]]) then
					if (UnitIsUnit("raid"..t1[1].."target", "raid"..t2[1].."target")) then
						dups["raid"..t2[1]] = 1

						local unit1 = XPerl_GetUnit("raid"..t1[1])
						local unit2 = XPerl_GetUnit("raid"..t2[1])

						if (unit1) then
							unit1:SetBackdropBorderColor(MTdupColours[dup].r, MTdupColours[dup].g, MTdupColours[dup].b)
							unit1:SetBackdropColor(MTdupColours[dup].r, MTdupColours[dup].g, MTdupColours[dup].b)
						end
						if (unit2) then
							unit2:SetBackdropBorderColor(MTdupColours[dup].r, MTdupColours[dup].g, MTdupColours[dup].b)
							unit2:SetBackdropColor(MTdupColours[dup].r, MTdupColours[dup].g, MTdupColours[dup].b)
						end
						any = true
					end
				end
			end

			if (any and dup < 5) then
				dup = dup + 1
			end
		end
	end
end

-- MoveArrow
local function MoveArrow(unit)
	XPerl_MyTarget:SetParent(unit)
	XPerl_MyTarget:SetPoint("RIGHT", unit, "LEFT", 2 - (XPerlConfigHelper.MTLabels * 10), 0)
	XPerl_MyTarget:Show()
end

-- OnUpdate
local function OnUpdate(self, elapsed)
	UpdateTime = UpdateTime + elapsed
	if (UpdateTime > 0.5) then
		for k,unit in pairs(XUnits) do
			if (unit.type == "MTT" or unit.type == "MTTT") then
				UpdateUnit(unit, true)
			end
		end
	end
end

-- VARIABLES_LOADED
function Events:VARIABLES_LOADED()
	this:UnregisterEvent(event)

	if (type(XPerl_MainTanks) ~= "table") then
		XPerl_MainTanks = {}
	end

	XPerl_Startup()
	XPerl_EnableDisable()
end

-- Registration
local function Registration()
	local list = {	"UNIT_HEALTH",
			"UNIT_MAXHEALTH",
			"UNIT_TARGET",
			"PLAYER_TARGET_CHANGED",
			"PLAYER_REGEN_ENABLED",
			"CHAT_MSG_ADDON"}

	for i,a in pairs(list) do
		if (GetNumRaidMembers() > 0 and XPerlConfigHelper.RaidHelper == 1) then
			XPerl_Frame:RegisterEvent(a)
		else
			XPerl_Frame:UnregisterEvent(a)
		end
	end
end

local XPerl_Old_KLHTM_Redraw = nil
function XPerl_KLHTM_Redraw(forceRedraw)
	XPerl_Old_KLHTM_Redraw(forceRedraw)
	if (XPerlConfigHelper.ThreatBar == 1 and XPerl_Frame:IsShown()) then
		XPerl_UpdateThreat()
	end
end

-- XPerl_EnableDisable
function XPerl_EnableDisable()
	if (XPerlConfigHelper and XPerlConfigHelper.RaidHelper == 1 and GetNumRaidMembers() > 0) then
		if (not XPerl_Frame:IsShown()) then
			XPerl_Frame:Show()
			XPerl_Frame:SetScript("OnUpdate", OnUpdate)
		end

		if (KLHTM_Redraw and KLHTM_GetRaidData and XPerlConfigHelper.ThreatBar == 1) then
			if (not XPerl_Old_KLHTM_Redraw) then
				XPerl_Old_KLHTM_Redraw = KLHTM_Redraw
				KLHTM_Redraw = XPerl_KLHTM_Redraw
			end
		else
			XPerl_KLHTM_Redraw = nil
			XPerl_UpdateThreat = nil
		end

		if (CT_RAMenu_Options and CT_RA_UpdateVisibility) then
			XPerlConfigHelper.OldCTMTListHide = CT_RAMenu_Options["temp"]["HideMTs"]
			CT_RAMenu_Options["temp"]["HideMTs"] = true
			CT_RA_UpdateVisibility(1)
		end
	else
		if (XPerl_Frame:IsShown()) then
			XPerl_Frame:Hide()
			XPerl_Frame:SetScript("OnUpdate", nil)
		end

		if (XPerl_Old_KLHTM_Redraw) then
			KLHTM_Redraw = XPerl_Old_KLHTM_Redraw
			XPerl_Old_KLHTM_Redraw = nil
		end

		if (XPerlConfigHelper and XPerlConfigHelper.RaidHelper == 0 and CT_RAMenu_Options and CT_RA_UpdateVisibility) then
			if (XPerlConfigHelper.OldCTMTListHide ~= true) then
				XPerlConfigHelper.OldCTMTListHide = nil
				CT_RAMenu_Options["temp"]["HideMTs"] = false
				CT_RA_UpdateVisibility(1)
			end
		end
	end

	Registration()
	XPerl_MTRosterChanged()
end

-- RAID_ROSTER_UPDATE
function Events:RAID_ROSTER_UPDATE()
	if (GetNumRaidMembers() == 0) then
		MainTanks = {}
	end

	XPerl_EnableDisable()
end

-- UNIT_HEALTH
function Events:UNIT_HEALTH()
	if (strmatch(arg1, "^raid") and not strmatch(arg1, "pet")) then
		for k,unit in pairs(XUnits) do
			if (UnitIsUnit(unit.partyid, arg1)) then
				UpdateUnit(unit)
			end
		end
	end
end

Events.UNIT_MAXHEALTH = Events.UNIT_HEALTH

-- CheckArrowPosition
local function CheckArrowPosition()
	local arrowDone
	local arrowType = "MTT"
	local arrowExtra = ""
	if (XPerlConfigHelper.ShowMT == 1) then
		arrowType = "MT"
		arrowExtra = "target"
	end

	XPerl_MyTarget:Hide()
	XPerl_MainAssist:Hide()

	local maAssignment
	if (GetPartyAssignment) then		-- TBC only (NOT generic to WoW 2.0.1 6180)
		maAssignment = GetPartyAssignment("mainassist")
	else
		maAssignment = ""
	end

	local maDone = false
	for i,unit in pairs(XUnits) do
		local xunit = SpecialCaseUnit(unit)
		if (not arrowDone and unit.type == arrowType and UnitIsUnit("target", xunit..arrowExtra) and UnitExists("target")) then
			MoveArrow(unit)
			arrowDone = true
		end

		local name = UnitName(unit.unit)
		if (maAssignment and unit.type == arrowType and not maDone and name == maAssignment) then
			maDone = true
			XPerl_MainAssist:SetParent(getglobal(unit:GetName().."_HealthBar"))
			XPerl_MainAssist:ClearAllPoints()

			if (XPerlConfigHelper.MTLabels == 1) then
				XPerl_MainAssist:SetPoint("BOTTOMRIGHT", unit, "BOTTOMLEFT", 1, 3)
			else
				XPerl_MainAssist:SetPoint("BOTTOMRIGHT", -1, 1)
			end
			XPerl_MainAssist:Show()
		end
	end
end

-- PLAYER_TARGET_CHANGED
function Events:PLAYER_TARGET_CHANGED()
	local any

	for i,unit in pairs(XUnits) do
		if (UnitIsUnit(unit.unit, "player")) then
			UpdateUnit(unit)
			any = true
		end
	end

	if (any) then
		ScanForMTDups()
		CheckArrowPosition()
	end
end

-- UNIT_TARGET
function Events:UNIT_TARGET()
	if (XPerl_Frame:IsShown() and strmatch(arg1, "^raid")) then
		local arrowType = "MTT"
		local arrowExtra = ""
		if (XPerlConfigHelper.ShowMT == 1) then
			arrowType = "MT"
			arrowExtra = "target"
		end

		local any
		for i,unit in pairs(XUnits) do
			if (UnitIsUnit(unit.unit, arg1)) then
				UpdateUnit(unit)
				any = true
			end
		end

		if (any) then
			ScanForMTDups()
			CheckArrowPosition()
		end
	end
end

-- PLAYER_REGEN_ENABLED
function Events:PLAYER_REGEN_ENABLED()
	if (pendingTankListChange) then
		XPerl_MakeTankList()
	end
end

-- The number of 'Other' colums that can be shown
local function DisplayableColumns()
	return 1 + XPerlConfigHelper.ShowMT + XPerlConfigHelper.MTTargetTargets
end

-- XPerl_SetTitle
function XPerl_SetTitle()
	if (DisplayableColumns() > 1 and XPerlConfigHelper.UnitWidth >= 70) then
		if (XPerl_MainTanks == MainTanks) then
			XPerl_Frame_Title:SetText(XPERL_TITLE_MT_LONG)
		else
			XPerl_Frame_Title:SetText(XPERL_TITLE_WARRIOR_LONG)
		end
	else
		if (XPerl_MainTanks == MainTanks) then
			XPerl_Frame_Title:SetText(XPERL_TITLE_MT_SHORT)
		else
			XPerl_Frame_Title:SetText(XPERL_TITLE_WARRIOR_SHORT)
		end
	end
end

-- XPerl_MakeTankList
function XPerl_MakeTankList()

	if (InCombatLockdown()) then
		pendingTankListChange = true
		return
	else
		pendingTankListChange = nil
	end

	if (not XPerlConfigHelper or XPerlConfigHelper.RaidHelper == 0 or GetNumRaidMembers() == 0) then
		XPerl_SetFrameSizes()
		return
	end

	if (XPerl_NoFadeBars) then XPerl_NoFadeBars(true) end

	XPerl_SetTitle()
	XPerl_MyTarget:Hide()
	UnallocateUnits(XUnits)
	XGaps = 0

	local previousUnit
	local previousID = 0
	local first = true
	local foundMA = false
	local mainTanksDisplayed = 0
	for i,tank in pairs(MainTanks) do
		local unit = AllocateUnit(XUnits, XPerl_MTTargets)
		if (not unit) then
			break
		end

		unit:SetAttribute("unit", "raid"..tank[1].."target")
		unit:SetAttribute("type1", "target")
		XPerl_RegisterClickCastFrame(unit)

		unit.partyid = "raid"..tank[1].."target"
		unit.unit = "raid"..tank[1];			-- 1 == id, 2 == name
		unit.type = "MTT"

		mainTanksDisplayed = mainTanksDisplayed + 1

		local rowFrames = {}
		rowFrames[2] = unit

		-- Correct the Main Assist number in case people leave the raid
		if (XPerlConfigHelper.ShowMT == 1) then
			local unitTank = AllocateUnit(XUnits, XPerl_MTTargets)
			if (unitTank) then
				unitTank:SetAttribute("unit", "raid"..tank[1])
				unitTank:SetAttribute("type1", "target")
				XPerl_RegisterClickCastFrame(unitTank)

				unitTank.partyid = "raid"..tank[1]
				unitTank.unit = "raid"..tank[1]
				unitTank.type = "MT"

				rowFrames[1] = unitTank
			else
				break
			end
		end

		if (XPerlConfigHelper.MTTargetTargets == 1) then
			local unitTarget = AllocateUnit(XUnits, XPerl_MTTargets)
			if (unitTarget) then
				unitTarget:SetAttribute("unit", "raid"..tank[1].."targettarget")
				unitTarget:SetAttribute("type1", "target")
				XPerl_RegisterClickCastFrame(unitTarget)

				unitTarget.partyid = "raid"..tank[1].."targettarget"
				unitTarget.unit = "raid"..tank[1]
				unitTarget.type = "MTTT"

				rowFrames[3] = unitTarget
			else
				break
			end
		end

		local previousColumn
		for j,frame in pairs(rowFrames) do
			UpdateUnit(frame)

			if (not previousColumn) then
				if (first) then
					first = false
					frame:SetPoint("TOPLEFT", XPerl_MTTargets, "TOPLEFT", XPerlConfigHelper.MTLabels * 10, 0)
				else
					local gap = 0;		-- Maintain a gap if not sequential numbers
					if (i > previousID + 1) then
						XGaps = XGaps + 1
						gap = -GAP_SPACING
					end

					frame:SetPoint("TOPLEFT", previousUnit, "BOTTOMLEFT", 0, gap)
				end
			else
				frame:SetPoint("TOPLEFT", previousColumn, "TOPRIGHT", 0, 0)
			end

			if (not previousColumn) then
				local label = getglobal(frame:GetName().."_Label")
				label:SetText(i)
				label:SetTextColor(1, 1, 1)
				if (XPerlConfigHelper.MTLabels == 1) then
					label:Show()
				end

				previousUnit = frame
			end
			previousColumn = frame
		end

		if (mainTanksDisplayed >= XPerlConfigHelper.MaxMainTanks) then
			break
		end

		previousID = i
	end

	if (previousUnit) then
		XPerl_Frame:Show()
	else
		XPerl_Frame:Hide()
	end

	if (XPerl_NoFadeBars) then XPerl_NoFadeBars() end

	XPerl_SetFrameSizes()
	CheckArrowPosition()
end

-- XPerl_SetFrameSizes
function XPerl_SetFrameSizes()

	local tanks = MainTankCount
	local unitHeight = 20

	if (tanks > XPerlConfigHelper.MaxMainTanks) then
		tanks = XPerlConfigHelper.MaxMainTanks
	end

	if (XPerl_Unit1) then
		unitHeight = XPerl_Unit1:GetHeight()
	end

	XPerl_MTTargets:SetHeight((unitHeight * tanks) + (XGaps * GAP_SPACING))

	local ExtraWidth = (XPerlConfigHelper.MTLabels * 10);	-- 0 for off, 10 for on
	XLastColumCount = DisplayableColumns()
	XPerl_Frame:SetWidth(XLastColumCount * XPerlConfigHelper.UnitWidth + 6 + ExtraWidth)

	XPerl_Frame:SetHeight((unitHeight * tanks) + (XGaps * GAP_SPACING) + 6 + XPerl_Frame_TitleBar:GetHeight())
end

-- XPerl_Toggle_UseCTRATargets
function XPerl_Toggle_UseCTRATargets()
	if (XPerlConfigHelper.UseCTRATargets == 1) then
		XPerlConfigHelper.UseCTRATargets = 0
	else
		XPerlConfigHelper.UseCTRATargets = 1
	end

	XPerl_MTRosterChanged()
	return true
end

function XPerl_Toggle_MTTargets(noChange)
	if (not noChange) then
		if (XPerlConfigHelper.MTTargetTargets == 1) then
			XPerlConfigHelper.MTTargetTargets = 0
		else
			XPerlConfigHelper.MTTargetTargets = 1
		end
	end

	XPerl_MakeTankList()

	XPerl_Frame_ToggleMTTargets:SetButtonTex()
	return true
end

-- XPerl_Toggle_ToggleLabels
function XPerl_Toggle_ToggleLabels(noChange)

	if (not noChange) then
		if (XPerlConfigHelper.MTLabels == 1) then
			XPerlConfigHelper.MTLabels = 0
		else
			XPerlConfigHelper.MTLabels = 1
		end
	end

	XPerl_MakeTankList()

	XPerl_Frame_ToggleLabels:SetButtonTex()

	return true
end

function XPerl_Toggle_ShowMT(noChange)
	if (not noChange) then
		if (XPerlConfigHelper.ShowMT == 1) then
			XPerlConfigHelper.ShowMT = 0
		else
			XPerlConfigHelper.ShowMT = 1
		end
	end

	XPerl_MakeTankList()

	XPerl_Frame_ToggleShowMT:SetButtonTex()

	return true
end

-- SmoothBarColor
local function SmoothBarColor(bar, barBG)
	local barmin, barmax = bar:GetMinMaxValues()
	local percentage = (bar:GetValue()/(barmax-barmin))

	local r, g
	if (percentage < 0.5) then
		r = 2*percentage
		g = 1
	else
		r = 1
		g = 2*(1 - percentage)
	end

	if ((r>=0) and (g>=0) and (r<=1) and (g<=1)) then
		bar:SetStatusBarColor(r, g, 0)
		barBG:SetVertexColor(r, g, 0, 0.25)
	end
end

-- Redo some of this stuff. too much duplication and work on each frame
local function SetBarToTitle(bar)
	local offset = XPerl_Frame_Title:GetStringWidth() + 25
	bar:SetScale(0.75);		-- Scale it down for the font size
	bar:SetParent(XPerl_Frame_TitleBar)
	bar:Show()
	bar:ClearAllPoints()
	bar:SetPoint("TOPLEFT", XPerl_Frame_TitleBar, "TOPLEFT", offset / 0.75, -3)
	bar:SetPoint("BOTTOMRIGHT", XPerl_Frame_ToggleShowMT, "BOTTOMLEFT", -5, 2)
end

local function SetBarToFrame(bar)
	bar:SetScale(0.75)
	bar:SetParent(XPerl_Threat)
	bar:Show()
	bar:ClearAllPoints()
	bar:SetPoint("TOPLEFT", 3, -3)
	bar:SetPoint("BOTTOMRIGHT", -3, 3)
end

-- XPerl_UpdateThreat
local lastThreat = -1
local lastThreat100
local lastRedraw = 0
function XPerl_UpdateThreat()

	if (lastRedraw + 0.5 > GetTime()) then
		return
	end
	lastRedraw = GetTime()

	if (UnitAffectingCombat("player")) then
		local data, playerCount, threat100 = KLHTM_GetRaidData()
		if (threat100 > 0) then
			local userThreat = klhtm.table.raiddata[UnitName("player")] or 0

			if (userThreat == lastThreat and threat100 == lastThreat100) then
				return
			else
				lastThreat = userThreat
				lastThreat100 = threat100

				if (data[1].name == "Aggro Gain") then
					playerCount = playerCount - 1
				end

				if (playerCount > 1) then
					local titleSpace = (XPerl_Frame_ToggleShowMT:GetLeft() - XPerl_Frame_Title:GetLeft()) - (XPerl_Frame_Title:GetStringWidth() + 25)

					XPerl_MyThreatText:SetText(userThreat - threat100)		-- Show deficit threat value

					XPerl_MyThreat:SetMinMaxValues(0, threat100)
					XPerl_MyThreat:SetValue(userThreat)

					if (titleSpace >= 10) then
						SetBarToTitle(XPerl_MyThreat)
						XPerl_Threat:Hide()
						XPerl_Threat:SetHeight(2)
					else
						SetBarToFrame(XPerl_MyThreat)
						XPerl_Threat:Show()
						XPerl_Threat:SetHeight(12)
					end

					SmoothBarColor(XPerl_MyThreat, XPerl_MyThreatBG)
					return
				end
			end
		end
	end

	if (lastThreat) then
		XPerl_Threat:Hide()
		XPerl_Threat:SetHeight(2)
		XPerl_MyThreat:Hide()
		lastThreat = nil
		lastThreat100 = nil
	end
end
