--------------------------------------------------------
-- Ralak's Needy List
-- a UI modification by Ralak of Kel'Thuzad
-- German Localization by Nitram from DE-Azshara
-- French Localization by Olivier Bockstal
--------------------------------------------------------
NL_CURRENT_VERSION = 2.05;

NL_DEBUG_MODE = false;

NL_MAX_TOOLTIP_BUFFS = 16;
NL_MAX_TOOLTIP_DEBUFFS = 16;
NL_MAX_NEEDS = 12;
NL_BLACK_LIST = {};
NL_MINIMIZED = false;
NL_SLOTS = {};
NL_NUM_NEEDY_UNITS = 0;

NL_PLAYERCLASS = "";
NL_ITERATOR = 1;

NeedyListDetails = {
	name = 'Ralak\'s Needy List',
	description = 'Monitor needy units in your party or raid group.',
	version = NL_CURRENT_VERSION,
	releaseDate = 'June 25, 2006',
	author = 'Carson Knittig',
	email = 'needylist@gmail.com',
	category = MYADDONS_CATEGORY_RAID,
	frame = 'NLMainFrame',
	optionsframe = 'NLConfigFrame'
};

NL_CLASS_COLORS = {};
NL_CLASS_COLORS[NL_PRIEST_NAME] = {r = 1.0, g = 1.0, b = 1.0};
NL_CLASS_COLORS[NL_DRUID_NAME] = {r = 0.0, g = 1.0, b = 0.0};
NL_CLASS_COLORS[NL_MAGE_NAME] = {r = 0.0, g = 0.0, b = 1.0};
NL_CLASS_COLORS[NL_PALADIN_NAME] = {r = 1.0, g = 1.0, b = 0.0};
NL_CLASS_COLORS[NL_WARLOCK_NAME] = {r = 0.5, g = 0.25, b = 0.6};
NL_CLASS_COLORS[NL_ROGUE_NAME] = {r = 0.5, g = 0.5, b = 0.5};
NL_CLASS_COLORS[NL_SHAMAN_NAME] = {r = 1.0, g = 1.0, b = 0.0};
NL_CLASS_COLORS[NL_WARRIOR_NAME] = {r = 1.0, g = 0.0, b = 0.0};
NL_CLASS_COLORS[NL_BEAST_NAME] = {r = 0.5, g = 0.4, b = 0.25};
NL_CLASS_COLORS[NL_DEMON_NAME] = {r = 1.0, g = 0.6, b = 0.25};
NL_CLASS_COLORS["Default"] = {r = 0.25, g = 0.25, b = 0.25};

NL_WEAKENED_SOUL = "Interface\\Icons\\Spell_Holy_AshesToAshes";
NL_BANISH = "Interface\\Icons\\Spell_Shadow_Cripple";
NL_PHASE_SHIFT = "Interface\\Icons\\Spell_Shadow_ImpPhaseShift";
NL_MINDCONTROL = "Interface\\Icons\\Spell_Shadow_ShadowWordDominate";
NL_MINDCONTROLCAP = "Interface\\Icons\\Spell_Magic_MageArmor";
NL_FEIGNDEATH = "Interface\\Icons\\Ability_Rogue_FeignDeath";

NL_CLICK_INDICES = {LeftButton = 1, RightButton = 2, MiddleButton = 3, Button4 = 4, Button5 = 5};

NL_ICON_LOCATION = "Interface\\Icons\\";

NLAnchors = { {
AnchorPoint = "TOPLEFT",
ReferenceAnchorPoint = "BOTTOMLEFT",
Offset = -1,
},
{
AnchorPoint = "BOTTOMLEFT",
ReferenceAnchorPoint = "TOPLEFT",
Offset = 1,
}
};

function NL_Msg(msg)
	DEFAULT_CHAT_FRAME:AddMessage(msg);
end

function NL_DebugMsg(msg)
	if( NL_DEBUG_MODE ) then
		DEFAULT_CHAT_FRAME:AddMessage(msg);
	end
end

function NL_PrintTable()
	local test1, test2, test3, test4 = NLMemberplayer:GetPoint("TOPLEFT");
	table.foreach( test2, NL_Msg );
	NL_Msg( test3 .. test4 );
end

function NL_OnLoad()
	-- Register Events
	NLMainFrame:RegisterEvent("UNIT_NAME_UPDATE");
	NLMainFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
	NLMainFrame:RegisterEvent("UNIT_HEALTH");
	NLMainFrame:RegisterEvent("UNIT_MANA");
	NLMainFrame:RegisterEvent("UNIT_RAGE");
	NLMainFrame:RegisterEvent("UNIT_FOCUS");
	NLMainFrame:RegisterEvent("UNIT_ENERGY");
	NLMainFrame:RegisterEvent("UNIT_AURA");
	NLMainFrame:RegisterEvent("PARTY_MEMBERS_CHANGED");
	NLMainFrame:RegisterEvent("RAID_ROSTER_UPDATE");
	NLMainFrame:RegisterEvent("UNIT_PET");
	NLMainFrame:RegisterEvent("VARIABLES_LOADED");
	NLMainFrame:RegisterEvent("PLAYER_TARGET_CHANGED");

	-- Add Slash Commands
	SlashCmdList["NLCONFIG"] = NL_Configure;
	SLASH_NLCONFIG1 = "/nlconfig";

	-- get this player's class so we know which buffs and debuffs to notify him/her of
	NL_PLAYERCLASS = UnitClass("player");

	if( DEFAULT_CHAT_FRAME ) then
		DEFAULT_CHAT_FRAME:AddMessage(NL_STR_INTRO_PREFIX..NL_CURRENT_VERSION..NL_STR_INTRO_SUFFIX);
		DEFAULT_CHAT_FRAME:AddMessage(NL_STR_INTRO_DESC);
	end
	
	-- set the units on each frame so they never have to be set again
	NLMemberplayer.Unit = "player";
	NLMemberpet.Unit = "pet";
	NLMembertarget.Unit = "target";
	for NL_ITERATOR=1, 4 do
		getglobal("NLMemberparty"..NL_ITERATOR).Unit = "party"..NL_ITERATOR;
		getglobal("NLMemberpartypet"..NL_ITERATOR).Unit = "partypet"..NL_ITERATOR;
	end
	for NL_ITERATOR=1, 40 do
		getglobal("NLMemberraid"..NL_ITERATOR).Unit = "raid"..NL_ITERATOR;
		getglobal("NLMemberraidpet"..NL_ITERATOR).Unit = "raidpet"..NL_ITERATOR;
	end
end

function NL_CheckIfEnabled()
	local bRaid = GetNumRaidMembers() > 0;
	local bParty = UnitExists("party1") and not bRaid;
	local bSolo = not ( bParty or bRaid );

	if( ( bRaid and NLConfig.UseInRaid == 1 ) or ( bParty and NLConfig.UseInParty == 1 ) or ( bSolo and NLConfig.UseWhenSolo == 1 ) ) then
		NL_Enable();
	else
		NL_Disable();
	end
end

function NL_Disable()
	NL_ENABLED = false;

	NLHeader:Hide();

	getglobal("NLMemberplayer"):Hide();
	getglobal("NLMemberpet"):Hide();
	for NL_ITERATOR=1, 4 do
		getglobal("NLMemberparty"..NL_ITERATOR):Hide();
		getglobal("NLMemberpartypet"..NL_ITERATOR):Hide();
	end
	for NL_ITERATOR=1, 40 do
		getglobal("NLMemberraid"..NL_ITERATOR):Hide();
		getglobal("NLMemberraidpet"..NL_ITERATOR):Hide();
	end
end

function NL_Enable()
	NL_ENABLED = true;

	NLHeader:Show();

	NLMember_CheckAllUnits();
end

function NL_Configure()
	NLConfigFrame:Show();
end

function NL_OnEvent(event, arg1)
	
	--Player loaded completely
	if ( event == "UNIT_NAME_UPDATE" and arg1 == "player" ) or (event=="PLAYER_ENTERING_WORLD") then
		-- get the configs for this player
		NLConfig = NL_GetConfigForCurrentPlayer( false );

		NL_CheckIfEnabled();
		if( not NL_ENABLED ) then
			return;
		end

		NLHeader:Show();

		NLMember_CheckAllUnits();

		return;
	end

	if( NLConfig == nil ) then
		return;
	end

	-- this check is only for enabling the needy list based on party status
	if( event == "PARTY_MEMBERS_CHANGED" or event == "RAID_ROSTER_UPDATE" ) then
		NL_CheckIfEnabled();
	end

	if( not NL_ENABLED ) then
		return;
	end

	if( event == "PLAYER_TARGET_CHANGED" ) then
		NL_CheckTarget();
		return;
	end
	
	-- if this is a stat or buff change on a unit
	if ( event == "UNIT_HEALTH" or event == "UNIT_MANA" or event == "UNIT_RAGE" or event == "UNIT_FOCUS" or event == "UNIT_ENERGY" or event == "UNIT_AURA" ) then
		-- if the unit is blacklisted or never shown, just return
		if( arg1 ~= "target" ) then
			for NL_ITERATOR = 1, table.getn(NL_BLACK_LIST) do
				if( NL_BLACK_LIST[NL_ITERATOR].Unit == arg1 ) then
					return;
				end
			end
		end

		-- if I'm in a raid group, only use units with raid in their name
		local criteria;
		if( GetNumRaidMembers() > 0 ) then
			criteria = string.find( arg1, "raid" ) ~= nil;
		elseif( GetNumPartyMembers() > 0 ) then
			criteria = string.find( arg1, "party" ) ~= nil or arg1 == "player" or arg1 == "pet";
		else
			criteria = arg1 == "player" or arg1 == "pet";
		end

		-- if this player is in the raid or party, or is the default player
		if( criteria or (arg1 == "target" and UnitIsFriend("target", "player") and NLConfig.ShowTargetFrame == 1)) then
			if( getglobal("NLMember"..arg1).TopNeed and getglobal("NLMember"..arg1).TopNeed == 0 ) then
				return;
			end
		
			if( not NL_CheckForResurrectionNeed( arg1 ) ) then
				if( NLConfig.ResurrectionNeedIndex ) then
					if( getglobal("NLMember"..arg1).Needs[NLConfig.ResurrectionNeedIndex] ) then
						getglobal("NLMember"..arg1).Needs[NLConfig.ResurrectionNeedIndex] = false;
						NL_CheckAllNeeds(arg1);
						return;
					end
				end
				
				if( event == "UNIT_HEALTH" ) then
					if( NL_CheckHealth( arg1 ) ) then
						NL_AddToList( arg1 );
					else
						NL_RemoveFromList( arg1 );
					end
				elseif( event == "UNIT_AURA" ) then
					if( NL_CheckAura( arg1 ) ) then
						NL_AddToList( arg1 );
						if( getglobal("NLMember"..arg1):IsShown() ) then
							NL_UpdateAura( arg1 );
						end
					else
						NL_RemoveFromList( arg1 );
					end
				else
					if( NL_CheckMana( arg1 ) ) then
						NL_AddToList( arg1 );
					else
						NL_RemoveFromList( arg1 );
					end
				end
				if( getglobal("NLMember"..arg1):IsShown() ) then
					NL_UpdateUnit( arg1 );
				end
			end
		end

		return;
	end

	if( event == "UNIT_PET" ) then
		local criteria;
		local prefix;
		local suffix;
		if( GetNumRaidMembers() > 0 ) then
			criteria = string.find( arg1, "raid" ) ~= nil;
			prefix = "raid";
			suffix = string.sub( arg1, 5 );
		else
			-- the reason we only check the player here is because in a raid, the player is treated as a raid member as well as the player
			-- no sense updating twice!
			if( arg1 == "player" ) then
				NL_CheckAllNeeds( "pet" );
				return;
			else
				criteria = string.find( arg1, "party" ) ~= nil;
				prefix = "party";
				suffix = string.sub( arg1, 6 );
			end
		end

		-- if we're in a raid, check raid members
		-- if not, check party members, or do nothing if we already found the player
		if( criteria ) then
			local petUnit = prefix .. "pet" .. suffix;
			NL_DebugMsg( "pet event for "..petUnit .. " was caught");
			NL_CheckAllNeeds( petUnit );
		end
		return;
	end

	if( event == "PARTY_MEMBERS_CHANGED" or event == "RAID_ROSTER_UPDATE" ) then
		NLMember_CheckAllUnits();
		return;
	end

	if( event == "VARIABLES_LOADED" ) then
		-- Register the addon in myAddOns
		if(myAddOnsFrame_Register) then
			myAddOnsFrame_Register(NeedyListDetails);
		end

		return;
	end
end

function NL_UnitPassesFilter( unit, filter )
	if( filter == nil or filter.Type == "Everyone" ) then
		return true;
	end

	if( filter.Type == "Units" ) then
		if( string.find( filter.Names, UnitName( unit ) ) ) then
			local foundName = string.sub( filter.Names, string.find( filter.Names, UnitName( unit ) ) );
			if( string.find( foundName, "," ) ) then
				foundName = string.sub( foundName, string.find( foundName, "," ) - 1 );
			end
			if( foundName == UnitName( unit ) ) then
				return true;
			end
		end
	end

	-- if the filter is by party, use the unit name to determine which party and check if that party is being monitored
	-- party filter is only valid in raids, so if you're not in a raid, pretend filter is everyone
	if( filter.Type == "Multi" ) then
		local bPassedPartyCheck = false;
		if( unit == "target" ) then
			bPassedPartyCheck = true;
		else
			if( ( UnitInParty(unit) or string.find( unit, "partypet" ) or unit == "pet" ) and filter.Names["My Party"] ) then
				bPassedPartyCheck = true;
			elseif( string.find( unit, "raid" ) ~= nil ) then
				local raidIndex;
				if( string.find( unit, "raidpet" ) ~= nil ) then
					raidIndex = string.sub( unit, 8 );
				else
					raidIndex = string.sub( unit, 5 );
				end
	
				local name, rank, subgroup = GetRaidRosterInfo(raidIndex);
	
				if( filter.Names["Party " .. subgroup] ) then
					bPassedPartyCheck = true;
				end
			end
		end

		if( bPassedPartyCheck ) then
			if( string.find( unit, "pet" ) ~= nil ) then
				if( filter.Names[UnitCreatureType( unit )] ) then
					return true;
				end
			else
				if( filter.Names[UnitClass( unit )] ) then
					return true;
				end
			end
		end
	end

	return false;
end

function NL_ClearNeedDetails(member)
	-- clear the need buttons in the needdetails frame
	local frameName = "NLMember" .. member;
	for NL_ITERATOR=1, NL_MAX_NEEDS do
		getglobal(frameName .. "NeedsDetailsNeed" .. NL_ITERATOR):Hide();
	end
	getglobal(frameName .. "NeedsDetails"):Hide();
end

function NL_GetUnitBuffsAndDebuffs( member )
	local buffList = {};
	local debuffList = {};
	local debuffTypeList = {};

	NL_ITERATOR = 1;
	local buff = UnitBuff(member, NL_ITERATOR);
	while( buff ~= nil ) do
		buffList[buff] = true;
		NL_ITERATOR = NL_ITERATOR + 1;
		buff = UnitBuff(member, NL_ITERATOR);
	end

	NL_ITERATOR = 1;
	local debuff = UnitDebuff(member, NL_ITERATOR);

	while( debuff ~= nil ) do
		-- do not add the ignored debuffs to the debuff lists
		if( debuff ~= NL_ICON_LOCATION.."Spell_Holy_MindVision" ) then
			debuffList[debuff] = true;

			NL_BuffTooltipTextRight1:SetText(nil);
			NL_BuffTooltip:SetUnitDebuff( member, NL_ITERATOR );
			local debuffType = NL_BuffTooltipTextRight1:GetText();

			if( debuffType ) then
				debuffTypeList[debuffType] = true;
			end
		end

		NL_ITERATOR = NL_ITERATOR + 1;
		debuff = UnitDebuff(member, NL_ITERATOR);
	end

	if( string.find( member, "pet" ) and ((buffList and buffList[NL_PHASE_SHIFT]) or (debuffList and debuffList[NL_BANISH] )) ) then
		NL_RemoveFromList( member );
		return nil;
	end
	if( debuffList and (debuffList[NL_MINDCONTROL] or debuffList[NL_MINDCONTROLCAP]) and UnitIsFriend("player", member) ) then
		NL_RemoveFromList( member );
		return nil;
	end
	
	return buffList, debuffList, debuffTypeList;
end

function NL_CheckAura( member )
	local buffList, debuffList, debuffTypeList = NL_GetUnitBuffsAndDebuffs( member );
	
	if( buffList == nil ) then
		return false;
	end

	local foundNewTopNeed = false;

	local unitFrame = getglobal("NLMember"..member);
	
	-- 0 and 1 are never show and sticky, so don't need to go thru them here
	for NL_ITERATOR = 2, NLConfig.NumNeeds - 1 do
		local CurrentNeed = NLConfig.Needs[NL_ITERATOR];
		if( CurrentNeed.Toggle == 1 and NL_UnitPassesFilter( member, CurrentNeed.Filter ) ) then
			local FoundNeed = false;
			if( CurrentNeed.Name == "WellFed" ) then
				if( not buffList[NL_ICON_LOCATION .. NL_OTHER.WellFed.Icon] ) then
					FoundNeed = true;
				end
			elseif( CurrentNeed.Type == "BUFF" and debuffList ) then
				if( not NL_FindInBuffList( CurrentNeed.Name, buffList ) ) then
					-- if the buff is power word shield, need to look for the weakened soul debuff before finding need
					if( CurrentNeed.Name == "PWShield" ) then
						if( not debuffList[NL_WEAKENED_SOUL] ) then
							FoundNeed = true;
						end
					else
						-- didn't find buff on unit, need it
						FoundNeed = true;
					end
				end
			elseif( CurrentNeed.Type == "ENCHANT" ) then
				local hasMainHandEnchant, mainHandExpiration, mainHandCharges,
					hasOffHandEnchant, offHandExpiration, offHandCharges = GetWeaponEnchantInfo()
				if( NL_PLAYERCLASS == NL_SHAMAN_NAME ) then
					if( hasMainHandEnchant ~= 1 ) then
						FoundNeed = true;
					end
				elseif( NL_PLAYERCLASS == NL_ROGUE_NAME ) then
					if( ( CurrentNeed.Name == "MainhandPoison" and hasMainHandEnchant ~= 1 ) or
						( CurrentNeed.Name == "OffhandPoison" and hasOffHandEnchant ~= 1 ) ) then
						FoundNeed = true;
					end
				end
			elseif( CurrentNeed.Type == "DEBUFF" and debuffTypeList ) then
				if( debuffTypeList[CurrentNeed.Name] ) then
					-- found debuff on unit, this is the highest priority need
					FoundNeed = true;
				end
			end

			if( FoundNeed ) then
				unitFrame.Needs[NL_ITERATOR] = true;
				if( not foundNewTopNeed ) then
					NL_SetNewTopNeed( unitFrame, NL_ITERATOR );
					foundNewTopNeed = true;
				end
			elseif( NL_ITERATOR ~= NLConfig.HealthNeedIndex and NL_ITERATOR ~= NLConfig.ManaNeedIndex and NL_ITERATOR ~= NLConfig.ResurrectionNeedIndex ) then
				unitFrame.Needs[NL_ITERATOR] = false;
				if( unitFrame.TopNeed == NL_ITERATOR ) then
					NL_SetNewTopNeed( unitFrame, NL_ITERATOR );
				end
			end
		end
	end

	if( unitFrame.TopNeed and unitFrame.TopNeed > 0 ) then
		return true;
	end
	
	return false;
end

function NL_CheckHealth( member )
	if( not NL_GetUnitBuffsAndDebuffs( member ) ) then
		return false;
	end
	
	local unitFrame = getglobal("NLMember"..member);
	unitFrame.CurrentHealth = UnitHealth( member ) / UnitHealthMax( member ) * 100;

	if( not NLConfig.HealthNeedIndex ) then
		if( unitFrame.TopNeed and unitFrame.TopNeed > 0 ) then
			return true;
		end
		return false;
	end
	
	local HealthNeed = NLConfig.Needs[NLConfig.HealthNeedIndex];
	if( HealthNeed.Toggle == 1 and NL_UnitPassesFilter( member, HealthNeed.Filter ) ) then
		if(unitFrame.CurrentHealth < HealthNeed.Threshold or 
			(unitFrame.CurrentHealth < min(HealthNeed.Threshold + 5,99) and unitFrame.SlotIndex and
			unitFrame.TopNeed == NLConfig.HealthNeedIndex ) ) then
			-- needs health
			unitFrame.Needs[NLConfig.HealthNeedIndex] = true;
		else
			unitFrame.Needs[NLConfig.HealthNeedIndex] = false;
		end
	else
		unitFrame.Needs[NLConfig.HealthNeedIndex] = false;
	end
	
	return NL_SetNewTopNeed( unitFrame, NLConfig.HealthNeedIndex );
end

function NL_CheckMana( member )
	if( not NL_GetUnitBuffsAndDebuffs( member ) ) then
		return false;
	end

	local unitFrame = getglobal("NLMember"..member);
	unitFrame.CurrentMana = UnitMana( member ) / UnitManaMax( member ) * 100;

	if( not NLConfig.ManaNeedIndex ) then
		if( unitFrame.TopNeed and unitFrame.TopNeed > 0 ) then
			return true;
		end
		return false;
	end
	
	local ManaNeed = NLConfig.Needs[NLConfig.ManaNeedIndex];
	if( ManaNeed.Toggle == 1 and NL_UnitPassesFilter( member, ManaNeed.Filter ) ) then
		if( UnitPowerType(member) == 0 and (unitFrame.CurrentMana < ManaNeed.Threshold or 
			(unitFrame.CurrentMana < min(ManaNeed.Threshold + 5,99) and unitFrame:IsShown() and
			NLConfig.ManaNeedIndex and unitFrame.TopNeed == NLConfig.ManaNeedIndex ) ) ) then
			-- needs mana
			unitFrame.Needs[NLConfig.ManaNeedIndex] = true;
		else
			unitFrame.Needs[NLConfig.ManaNeedIndex] = false;
		end
	else
		unitFrame.Needs[NLConfig.ManaNeedIndex] = false;
	end

	return NL_SetNewTopNeed( unitFrame, NLConfig.ManaNeedIndex );
end

function NL_SetNewTopNeed( unitFrame, index )
	if( unitFrame.Needs[index] == true and (not unitFrame.TopNeed or unitFrame.TopNeed > index) ) then
		unitFrame.TopNeed = index;
	elseif( unitFrame.Needs[index] == false and unitFrame.TopNeed == index ) then
		-- the next need is the new top need because health is no longer a need
		-- find the first true need in the unit's needs
		for NL_ITERATOR = 1, NLConfig.NumNeeds - 1 do
			if( unitFrame.Needs[NL_ITERATOR] ) then
				unitFrame.TopNeed = NL_ITERATOR;
				return true;
			end
		end
		
		-- if we got here, there is no longer a top need, so this unit can be removed
		unitFrame.TopNeed = nil;
		return false;
	end

	return true;
end

function NL_CheckAllNeeds( member )
	-- first, make sure the unit exists
	if( not UnitExists( member ) or UnitName( member ) == "" or UnitName( member ) == nil or not UnitIsConnected( member ) ) then
		NL_RemoveFromList( member );
		return;
	end

	-- check if never show
	if( NLConfig.Needs[0].Toggle == 1 and NL_UnitPassesFilter( getglobal("NLMember"..member).Unit, NLConfig.Needs[0].Filter ) ) then
		getglobal("NLMember"..member).TopNeed = 0;
		return;
	end
	
	-- check if stickied
	if( NLConfig.Needs[1].Toggle == 1 and NL_UnitPassesFilter( getglobal("NLMember"..member).Unit, NLConfig.Needs[1].Filter ) ) then
		getglobal("NLMember"..member).TopNeed = 1;
		getglobal("NLMember"..member).Needs[1] = true;
	end

	if( NL_CheckForResurrectionNeed( member ) ) then
		return;
	end

	-- make sure to set the resurrection need to false if we got here
	if( NLConfig.ResurrectionNeedIndex ) then
		if( getglobal("NLMember"..member).TopNeed == NLConfig.ResurrectionNeedIndex ) then
			getglobal("NLMember"..member).TopNeed = nil;
		end
		getglobal("NLMember"..member).Needs[NLConfig.ResurrectionNeedIndex] = false;
	end

	NL_CheckHealth( member );
	NL_CheckMana( member );
	NL_CheckAura( member );
	if( getglobal("NLMember"..member).TopNeed and getglobal("NLMember"..member).TopNeed > 0 ) then
		NL_AddToList( member );
	else
		NL_RemoveFromList( member );
	end
end

function NL_CheckForResurrectionNeed( member )
	-- if the unit is dead or a ghost, they either have
	if( UnitIsDead(member) or UnitIsGhost(member) or UnitHealth(member) <= 0 ) then
		if( UnitClass(member) == "Hunter" ) then
			local buffList = NL_GetUnitBuffsAndDebuffs( member );
		
			if( buffList and buffList[NL_FEIGNDEATH] ) then
				return false;
			end
		end
		-- if unit is the player, then do not monitor resurrection need, because a) can't rez yourself  b) it's obvious when you need a rez
		if( NLConfig.ResurrectionNeedIndex and NLConfig.Needs[NLConfig.ResurrectionNeedIndex].Toggle == 1 ) then
			if( not UnitIsUnit( member, "player" ) ) then
				if( getglobal("NLMember"..member).TopNeed ~= 1 ) then
					getglobal("NLMember"..member).TopNeed = NLConfig.ResurrectionNeedIndex;
				end
				getglobal("NLMember"..member).Needs[NLConfig.ResurrectionNeedIndex] = true;
				NL_AddToList( member );
				if( getglobal("NLMember"..member):IsShown() ) then
					NL_UpdateAura( member );
					NL_UpdateUnit( member );
				end
				return true;
			end
		end
		if( getglobal("NLMember"..member).TopNeed ~= 1 ) then
			NL_RemoveFromList( member );
		end
		return true;
	end

	return false;
end

function NL_GetMaxRankPlayerCanCast( spellName )
	NL_ITERATOR = 1;
	local foundSpellName, foundSpellRank;
	local highestMatch = nil;

	repeat
		foundSpellName, foundSpellRank = GetSpellName(NL_ITERATOR, BOOKTYPE_SPELL);
		if( spellName == foundSpellName ) then
			if( foundSpellRank == nil ) then
				return nil;
			end
			highestMatch = tonumber(string.sub( foundSpellRank, 6 ));
		end
		NL_ITERATOR = NL_ITERATOR + 1;
	until not foundSpellName

	return highestMatch;
end

function NL_GetMaxRankUnitCanReceive( spellRanks, unitLevel )
	NL_ITERATOR = 1;
	while spellRanks[NL_ITERATOR] and unitLevel + 10 >= spellRanks[NL_ITERATOR] do
		NL_ITERATOR = NL_ITERATOR + 1;
	end

	return NL_ITERATOR - 1;
end

function NL_CureNeedOnUnit( needName, unit, mousebutton )
	-- once in a while the interface can miss an event, leaving you with a unit whose needs have already been fixed
	-- do a check needs before casting, to make sure this is still a need on this unit
	NL_CheckAllNeeds(unit);
	NL_ITERATOR = 1;
	local needFrame = getglobal("NLMember".. unit .. "NeedsDetailsNeed"..NL_ITERATOR);
	while( needFrame and needFrame.NeedName and needFrame.NeedName ~= needName ) do
		NL_ITERATOR = NL_ITERATOR + 1;
		needFrame = getglobal("NLMember".. unit .. "NeedsDetailsNeed"..NL_ITERATOR);
	end

	-- if it wasn't found as a need, it's been cured, so just return
	if( not needFrame or needFrame.NeedName == nil ) then
		return;
	end

	local spellName = nil;
	local spellRanks = nil;
	local spellCanTargetEnemy = false;
	local spellNeedsNoTarget = false;
	local isBuff = false;

	local clickIndex = NL_CLICK_INDICES[mousebutton];

	-- find a cure spell that I can cast for this problem based on the player's level
	if( NL_BUFF_SPELLS[NL_PLAYERCLASS] and NL_BUFF_SPELLS[NL_PLAYERCLASS][needName] ) then
		spellName = NL_BUFF_SPELLS[NL_PLAYERCLASS][needName][clickIndex];
		spellRanks = NL_BUFF_SPELLS[NL_PLAYERCLASS][needName].Ranks;
		spellCanTargetEnemy = NL_BUFF_SPELLS[NL_PLAYERCLASS][needName].CanTargetEnemy;
		spellNeedsNoTarget = NL_BUFF_SPELLS[NL_PLAYERCLASS][needName].NoTarget;
		isBuff = true;
	elseif( NL_CURE_SPELLS[NL_PLAYERCLASS] and NL_CURE_SPELLS[NL_PLAYERCLASS][needName] ) then
		-- it's a debuff, find the cure spell for it
		spellName = NL_CURE_SPELLS[NL_PLAYERCLASS][needName][clickIndex];
		spellRanks = NL_CURE_SPELLS[NL_PLAYERCLASS][needName].Ranks;
		spellCanTargetEnemy = NL_CURE_SPELLS[NL_PLAYERCLASS][needName].CanTargetEnemy;
	elseif( NL_ENCHANT_SPELLS[NL_PLAYERCLASS] and NL_ENCHANT_SPELLS[NL_PLAYERCLASS][needName] ) then
		if( NL_PLAYERCLASS == "Rogue" ) then
			return;
		end

		spellName = NL_ENCHANT_SPELLS[NL_PLAYERCLASS][needName][clickIndex];
		spellRanks = NL_ENCHANT_SPELLS[NL_PLAYERCLASS][needName].Ranks;
		spellCanTargetEnemy = NL_ENCHANT_SPELLS[NL_PLAYERCLASS][needName].CanTargetEnemy;
	elseif( needName == "Health" ) then
		if( NLConfig.HealSpells ) then
			spellName = NLConfig.HealSpells[clickIndex];
		end
	elseif( needName == "Mana" ) then
		spellName = "Innervate";
	end

	if( spellName ) then
		local hadTarget = false;

		-- cast pet spells here
		-- fire shield seems unique in that it requires your target to actually change
		-- most other spells don't require a target to start casting
		-- therefore Fire Shield is the only spell that will not add units to the blacklist
		if( spellName == "Fire Shield" ) then
			local switchedTarget = false;
			local targetWasEnemy = false;
			local friendlyTargetName = "";
			if( UnitExists( "target" ) ) then
				hadTarget = true;
			end

			if( UnitIsEnemy( "player", "target" ) ) then
				targetWasEnemy = true;
			else
				friendlyTargetName = UnitName("target");
			end
			TargetUnit( unit );
			switchedTarget = true;
			-- find the action on the pet bar that matches this spell name
			NL_ITERATOR = 1;
			while( spellName ~= GetPetActionInfo(NL_ITERATOR) and NL_ITERATOR < 12 ) do
				NL_ITERATOR = NL_ITERATOR + 1;
			end

			if( spellName == GetPetActionInfo(NL_ITERATOR) ) then
				CastPetAction(NL_ITERATOR);
			end

			if( hadTarget == false ) then
				ClearTarget();
			elseif( switchedTarget ) then
				if( targetWasEnemy ) then
					TargetLastEnemy();
				else
					TargetByName(friendlyTargetName);
				end
			end
		-- cast the highest ranked spell based on player level
		elseif( string.sub( spellName, 0, 1 ) ~= "[" ) then
			-- if the unit is not already the target
			-- AND
			-- if the spell needs a target
			-- and the player has a target
			-- and
				-- the spell can target enemies OR (the spell can't target the enemy AND the target is friendly)
			-- Clear the target
			if( unit ~= "target" and (not spellNeedsNoTarget and UnitExists( "target" ) and (spellCanTargetEnemy or (not spellCanTargetEnemy and UnitIsFriend("player","target"))))) then
				hadTarget = true;
				ClearTarget();
			end
			
			-- figure out the rank of the spell to cast based on level
			if( not spellNeedsNoTarget ) then
				if( spellRanks ) then
					local validRank = NL_GetMaxRankPlayerCanCast( spellName );
					if( validRank ) then
						local allowedRank = validRank;
						if( isBuff ) then
							allowedRank = NL_GetMaxRankUnitCanReceive( spellRanks, UnitLevel(unit) );
						end
						if( validRank > 0 and allowedRank > 0 ) then
							-- cast the maximum rank of this spell
							CastSpellByName( spellName .. "("..NL_LANG_RANK.." " .. min(validRank,allowedRank) .. ")" );
						end
					else
						CastSpellByName( spellName );
					end
				else
					CastSpellByName( spellName );
				end
			else
				-- the spell needs no target, so just cast it.  The game will find the correct rank to cast.
				CastSpellByName( spellName );
			end
		else
			if( unit ~= "target" and UnitExists( "target" ) and UnitIsFriend("player","target")) then
				hadTarget = true;
				ClearTarget();
			end

			local bagNum, slotNum;
			local done = false;
			for bagNum = 0, NUM_BAG_FRAMES do
				local numSlots = GetContainerNumSlots(bagNum);
				for slotNum = 1, numSlots do
					local itemLink = GetContainerItemLink(bagNum, slotNum);
					local itemName = string.sub(spellName, 2, string.len(spellName) - 1);
					if( itemLink and string.find(itemLink, itemName) ) then
						UseContainerItem(bagNum, slotNum);
						done = true;
						break;
					end
				end
				if( done ) then
					break;
				end
			end
		end

		if( not spellNeedsNoTarget ) then
			if ( SpellIsTargeting() ) then
				if( SpellCanTargetUnit(unit) ) then
					-- message the person we're casting on, if notifications are enabled
					if( NLConfig.SpellNotify == 1 ) then
						SendChatMessage( "I'm casting "..spellName.." on you.", "WHISPER", this.language, UnitName(unit) );
					end
					SpellTargetUnit(unit);
				else
					-- blacklist the unit, so they drop to the end of the list for however long the 'out of range' timer is set for
					-- for now, remove them from the list and set a timer on them
					SpellStopTargeting();
					-- ONLY if the unit isn't stickied and the blacklist delay is above 0
					if( getglobal("NLMember"..unit).TopNeed ~= 1 and NLConfig.BlackListDelay > 0 ) then
						NLBlacklistUnit( unit );
					end
				end
			end

			if( hadTarget == true ) then
				TargetLastTarget();
			end
		end
	end
end

function NL_PulloutIfInvisible( unit )
	-- previous frame won't be nil on a frame that has been removed already but not pulled out
	if( getglobal( "NLMember"..unit ).PendingRemoval ) then
		NL_RemoveFromList( unit );
		getglobal( "NLMember"..unit ).PendingRemoval = false;
	end
end

function NL_PulloutAllInvisibleFrames()
	if( GetNumRaidMembers() > 0 ) then
		-- iterate through all frames
		for NL_ITERATOR = 1, 40 do
			NL_PulloutIfInvisible( "raid" .. NL_ITERATOR );
			NL_PulloutIfInvisible( "raidpet" .. NL_ITERATOR );
		end
	else
		-- iterate through all frames
		NL_PulloutIfInvisible( "player" );
		NL_PulloutIfInvisible( "pet" );

		for NL_ITERATOR = 1, 4 do
			NL_PulloutIfInvisible( "party" .. NL_ITERATOR );
			NL_PulloutIfInvisible( "partypet" .. NL_ITERATOR );
		end
	end
end

function NL_ResortSingleUnit( unit )
	if( not UnitExists(unit) ) then
		return;
	end

	-- if the frame has a slot index it has a need
	if( getglobal("NLMember"..unit).SlotIndex ) then
		getglobal("NLMember"..unit).SlotIndex = nil;
		if( getglobal("NLMember"..unit).TopNeed ) then
			NL_AddToList(unit);
		end
	end
end

function NL_SortAllVisibleFrames()
	NL_NUM_NEEDY_UNITS = 0;
	NL_SLOTS = {};
	
	if( GetNumRaidMembers() > 0 ) then
		-- iterate through all frames
		for NL_ITERATOR = 1, 40 do
			NL_ResortSingleUnit( "raid" .. NL_ITERATOR );
			NL_ResortSingleUnit( "raidpet" .. NL_ITERATOR );
		end
	else
		-- iterate through all frames
		NL_ResortSingleUnit( "player" );
		NL_ResortSingleUnit( "pet" );

		for NL_ITERATOR = 1, 4 do
			NL_ResortSingleUnit( "party" .. NL_ITERATOR );
			NL_ResortSingleUnit( "partypet" .. NL_ITERATOR );
		end
	end
end

function NL_OnUpdate( elapsed )
	if( not NL_MOUSE_IN_FRAME and not NL_LIST_IS_SORTED ) then
		-- do a generic sort of all visible units
		if( NLConfig.AutoSort == 1 ) then
			NL_PulloutAllInvisibleFrames();
			NL_SortAllVisibleFrames();
		else
			NL_PulloutAllInvisibleFrames();
		end

		NL_LIST_IS_SORTED = true;
	end

	NL_ITERATOR = 1;
	while( NL_BLACK_LIST[NL_ITERATOR] ) do
		NL_BLACK_LIST[NL_ITERATOR].Time = NL_BLACK_LIST[NL_ITERATOR].Time - elapsed;

		if( NL_BLACK_LIST[NL_ITERATOR].Time <= 0 ) then
			-- check needs and remove this unit from the blacklist
			local member = NL_BLACK_LIST[NL_ITERATOR].Unit;
			table.remove(NL_BLACK_LIST, NL_ITERATOR);
			NL_CheckAllNeeds( member );
		else
			NL_ITERATOR = NL_ITERATOR + 1;
		end
	end
end

function NL_FindInBuffList( needName, buffList )
	if( NL_BUFF_SPELLS[ NL_PLAYERCLASS ] ) then
		if( NL_BUFF_SPELLS[ NL_PLAYERCLASS ][needName] ) then
			if( NL_BUFF_SPELLS[ NL_PLAYERCLASS ][needName].Icons[1] and buffList[NL_ICON_LOCATION..NL_BUFF_SPELLS[ NL_PLAYERCLASS ][needName].Icons[1]] ) then
				return true;
			elseif( NL_BUFF_SPELLS[ NL_PLAYERCLASS ][needName].Icons[2] and buffList[NL_ICON_LOCATION..NL_BUFF_SPELLS[ NL_PLAYERCLASS ][needName].Icons[2]] ) then
				return true;
			end
		end
	end

	return false;
end

function NL_AddToList(member)
	if( not UnitExists(member) or UnitName(member) == nil or UnitName(member) == "" or UnitName(member) == "Unknown Entity" ) then
		NL_RemoveFromList(member);
		return nil;
	end
	
	getglobal("NLMember"..member.."Name"):SetText( string.sub(UnitName(member),1,NLConfig.FrameWidth/8) );
	local frame = getglobal("NLMember"..member);

	if( frame.TopNeed == nil ) then
		return;
	end

	if( frame.PendingRemoval ) then
		frame.PendingRemoval = false;
		frame:Show();
	end

	if( NLConfig.ColorByClass == 1 ) then
		local class = "";
		if( string.find( member, "pet" ) ~= nil ) then
			class = UnitCreatureType(member);
		else
			class = UnitClass(member);
		end
		
		if( NL_CLASS_COLORS[class] ) then
			frame:SetBackdropColor(NL_CLASS_COLORS[class].r, NL_CLASS_COLORS[class].g, NL_CLASS_COLORS[class].b, 1);
		else
			frame:SetBackdropColor(NL_CLASS_COLORS["Default"].r, NL_CLASS_COLORS["Default"].g, NL_CLASS_COLORS["Default"].b, 1);
		end
	elseif( NLConfig.Needs[frame.TopNeed] ~= nil ) then
		frame:SetBackdropColor(NLConfig.Needs[frame.TopNeed].BGColor.r, NLConfig.Needs[frame.TopNeed].BGColor.g, NLConfig.Needs[frame.TopNeed].BGColor.b, 0.8);
	end

	-- target has special treatment, as it should not be in the list
	if( member == "target" ) then
		frame:Show();
		return frame;
	end
	
	if( not frame.SlotIndex ) then
		frame.SlotIndex = NL_NUM_NEEDY_UNITS;
		NL_SLOTS[NL_NUM_NEEDY_UNITS] = frame;
		NL_NUM_NEEDY_UNITS = NL_NUM_NEEDY_UNITS + 1;
		NLAnchorFrame( frame );
	end

	if( NLConfig.AutoSort == 1 and not NL_MOUSE_IN_FRAME ) then
		NLSortFrameIntoList(frame);
	end

	if( NL_MINIMIZED ) then
		NLHeaderMinimized:Show();
	else
		NLHeader:Show();
	end

	return frame;
end

function NLAnchorFrame( frame )
	if( frame ~= NL_SLOTS[frame.SlotIndex] ) then
		NLBlacklistUnit(frame.Unit);
	end
	
	if( frame.SlotIndex < NLConfig.MaxUnits ) then
		if( not frame:IsShown() ) then
			-- draw the details of the frame, because it's now being made visible
			frame:Show();
		end
		frame:ClearAllPoints();
		frame:SetPoint( NLAnchors[NLConfig.InvertList+1].AnchorPoint, NLHeader, NLAnchors[NLConfig.InvertList+1].ReferenceAnchorPoint, 0, NLAnchors[NLConfig.InvertList+1].Offset * (frame:GetHeight() - 5) * frame.SlotIndex - (5 * NLAnchors[NLConfig.InvertList+1].Offset) );
	else
		frame:Hide();
	end
end

function NLBlacklistUnit( unit )
	NL_RemoveFromList(unit);
	local newBlackListItem = {};
	newBlackListItem.Unit = unit;
	newBlackListItem.Time = NLConfig.BlackListDelay;
	table.insert(NL_BLACK_LIST, newBlackListItem);
end

function NLSortFrameDown( frame )
	local nextFrame = nil;
	while( frame.SlotIndex < NL_NUM_NEEDY_UNITS - 1 and NL_SLOTS[frame.SlotIndex+1] and NL_SLOTS[frame.SlotIndex+1].Unit and not NL_SLOTS[frame.SlotIndex+1].TopNeed ) do
		NL_DebugMsg("Removing " .. NL_SLOTS[frame.SlotIndex+1].Unit .. " from list because its TopNeed is nil");
		NL_RemoveFromList(NL_SLOTS[frame.SlotIndex+1].Unit);
		-- at this point, the removed frame should shift the rest up, and this frame can still be sorted downward
	end
	while( frame.SlotIndex < NL_NUM_NEEDY_UNITS - 1 and ( NL_SLOTS[frame.SlotIndex].TopNeed > NL_SLOTS[frame.SlotIndex+1].TopNeed 
		or (NL_SLOTS[frame.SlotIndex].TopNeed == NL_SLOTS[frame.SlotIndex+1].TopNeed and 
		( ( NL_SLOTS[frame.SlotIndex].TopNeed == NLConfig.HealthNeedIndex and NL_SLOTS[frame.SlotIndex].CurrentHealth > NL_SLOTS[frame.SlotIndex+1].CurrentHealth ) or
		( ( NLConfig.ManaNeedIndex and NL_SLOTS[frame.SlotIndex].TopNeed == NLConfig.ManaNeedIndex and NL_SLOTS[frame.SlotIndex].CurrentMana > NL_SLOTS[frame.SlotIndex+1].CurrentMana ) ) ) ) ) ) do
		nextFrame = NL_SLOTS[frame.SlotIndex+1];
		nextFrame.SlotIndex = frame.SlotIndex;
		frame.SlotIndex = frame.SlotIndex + 1;
		NL_SLOTS[frame.SlotIndex] = frame;
		NL_SLOTS[nextFrame.SlotIndex] = nextFrame;

		NLAnchorFrame( frame );
		NLAnchorFrame( nextFrame );

		if( frame.SlotIndex < NL_NUM_NEEDY_UNITS - 1 and NL_SLOTS[frame.SlotIndex+1] and NL_SLOTS[frame.SlotIndex+1].Unit and not NL_SLOTS[frame.SlotIndex+1].TopNeed ) then
			NL_DebugMsg("Removing " .. NL_SLOTS[frame.SlotIndex+1].Unit .. " from list because its TopNeed is nil");
			NL_RemoveFromList(NL_SLOTS[frame.SlotIndex+1].Unit);
			-- at this point, the removed frame should shift the rest up, and this frame can still be sorted downward
		end
	end
end

function NLSortFrameUp( frame )
	local previousFrame = nil;
	while( frame.SlotIndex > 0 and NL_SLOTS[frame.SlotIndex-1] and NL_SLOTS[frame.SlotIndex-1].Unit and not NL_SLOTS[frame.SlotIndex-1].TopNeed ) do
		NL_DebugMsg("Removing " .. NL_SLOTS[frame.SlotIndex-1].Unit .. " from list because its TopNeed is nil");
		NL_RemoveFromList(NL_SLOTS[frame.SlotIndex-1].Unit);
		-- at this point, the removed frame should shift the rest up, and this frame can still be sorted upward
	end
	while( frame.SlotIndex > 0 and ( NL_SLOTS[frame.SlotIndex].TopNeed < NL_SLOTS[frame.SlotIndex-1].TopNeed 
		or (NL_SLOTS[frame.SlotIndex].TopNeed == NL_SLOTS[frame.SlotIndex-1].TopNeed and 
		( ( NL_SLOTS[frame.SlotIndex].TopNeed == NLConfig.HealthNeedIndex and NL_SLOTS[frame.SlotIndex].CurrentHealth < NL_SLOTS[frame.SlotIndex-1].CurrentHealth ) or
		( NLConfig.ManaNeedIndex and NL_SLOTS[frame.SlotIndex].TopNeed == NLConfig.ManaNeedIndex and NL_SLOTS[frame.SlotIndex].CurrentMana < NL_SLOTS[frame.SlotIndex-1].CurrentMana ) ) ) ) ) do
		previousFrame = NL_SLOTS[frame.SlotIndex-1];
		previousFrame.SlotIndex = frame.SlotIndex;
		frame.SlotIndex = frame.SlotIndex - 1;
		NL_SLOTS[frame.SlotIndex] = frame;
		NL_SLOTS[previousFrame.SlotIndex] = previousFrame;

		NLAnchorFrame( frame );
		NLAnchorFrame( previousFrame );
		
		if( frame.SlotIndex > 0 and NL_SLOTS[frame.SlotIndex-1] and NL_SLOTS[frame.SlotIndex-1].Unit and not NL_SLOTS[frame.SlotIndex-1].TopNeed ) then
			NL_DebugMsg("Removing " .. NL_SLOTS[frame.SlotIndex-1].Unit .. " from list because its TopNeed is nil");
			NL_RemoveFromList(NL_SLOTS[frame.SlotIndex-1].Unit);
			-- at this point, the removed frame should shift the rest up, and this frame can still be sorted upward
		end
	end
end

function NLSortFrameIntoList(frame)
	NLSortFrameUp( frame );
	NLSortFrameDown( frame );
end

function NLShiftRemainingUp( index )
	while( index < NL_NUM_NEEDY_UNITS - 1 ) do
		NL_SLOTS[index] = NL_SLOTS[index+1];
		NL_SLOTS[index].SlotIndex = index;

		NLAnchorFrame( NL_SLOTS[index] );
		
		index = index + 1;
	end
end

function NL_RemoveFromList(member)
	-- sort to bottom of list (so all other units shift up)
	if( not member ) then
		return;
	end
	
	local frame = getglobal("NLMember"..member);

	-- target has special treatment, as it should not be in the list
	if( member == "target" ) then
		frame:Hide();
		return;
	end
	
	if( NL_MOUSE_IN_FRAME ) then
		frame.PendingRemoval = true;
		frame:Hide();
		return;
	end

	if( frame.SlotIndex ) then
		NL_SLOTS[frame.SlotIndex] = nil;
		frame:Hide();
		NLShiftRemainingUp( frame.SlotIndex );
		NL_NUM_NEEDY_UNITS = NL_NUM_NEEDY_UNITS - 1;
		frame.SlotIndex = nil;
		frame.TopNeed = nil;
	end

	if( NLConfig.HideHeader == 1 and NL_NUM_NEEDY_UNITS == 0 ) then
		NLHeader:Hide();
		NLHeaderMinimized:Hide();
	end
end

function NL_MouseEnteredFrame()
	NL_MOUSE_IN_FRAME = true;
	NL_LIST_IS_SORTED = false;
end

function NL_MouseLeftFrame()
	NL_MOUSE_IN_FRAME = false;
end

function NL_UpdateUnit(id)
	NL_UpdateNeeds( id );
	local frame = "NLMember" .. id;
	if( NLConfig.ShowHealthNum == 1 ) then
		if( NLConfig.ShowHealthLost == 1 ) then
			getglobal(frame .. "HealthNum"):SetText(UnitHealth(id)-UnitHealthMax(id));
		else
			getglobal(frame .. "HealthNum"):SetText(UnitHealth(id) .. "/" .. UnitHealthMax(id));
		end
		getglobal(frame .. "HealthNum"):Show();
	elseif( getglobal(frame .. "HealthNum"):IsShown() ) then
		getglobal(frame .. "HealthNum"):Hide();
	end

	if( UnitIsDead(id) or UnitIsGhost(id) or UnitHealth(id) == 0 ) then
		getglobal(frame .. "HPBar"):Hide();
		getglobal(frame .. "MPBar"):Hide();
		if( NLConfig.ShowHealth == 1 or NLConfig.ShowMana == 1 ) then
			if( UnitIsGhost(id) ) then
				getglobal(frame .. "Status"):SetText("Ghost");
			elseif( UnitIsDead(id) or UnitHealth(id) == 0 ) then
				getglobal(frame .. "Status"):SetText("Dead");
			end
			getglobal(frame .. "Status"):Show();
		end
		return;
	else
		getglobal(frame .. "Status"):Hide();
		if( NLConfig.ShowHealth == 1 ) then
			getglobal(frame .. "HPBar"):Show();
		else
			getglobal(frame .. "HPBar"):Hide();
		end
		-- determine the color for the power bar
		local info = ManaBarColor[UnitPowerType(id)];
		getglobal(frame .. "MPBar"):SetStatusBarColor(info.r, info.g, info.b);
		if( NLConfig.ShowMana == 1 ) then
			getglobal(frame .. "MPBar"):Show();
		else
			getglobal(frame .. "MPBar"):Hide();
		end
	end

	-- set the name in the frame
	if( getglobal(frame).CurrentHealth ) then
		local percent = floor(getglobal(frame).CurrentHealth);
		if ( percent and percent > 0 ) then
			if ( percent > 100 ) then
				percent = 100;
			end
			getglobal(frame .. "HPBar"):SetValue(percent);
			local hppercent = percent/100;
			local r, g;
			if ( hppercent > 0.5 and hppercent <= 1) then
				g = 1;
				r = (1.0 - hppercent) * 2;
			elseif ( hppercent >= 0 and hppercent <= 0.5 ) then
				r = 1.0;
				g = hppercent * 2;
			else
				r = 0;
				g = 1;
			end
			getglobal(frame .. "HPBar"):SetStatusBarColor(r, g, 0);
		end
	end

	-- get the class of the player and update the mana bar appropriately
	if( UnitManaMax(id) == 0 ) then
		getglobal(frame .. "MPBar"):SetValue(0);
	else
		if( getglobal(frame).CurrentMana ) then
			local percent = floor(getglobal(frame).CurrentMana);
			getglobal(frame .. "MPBar"):SetValue(percent);
		end
	end
end

function NL_UpdateAura( unit )
	if( not UnitExists(unit) ) then
		return;
	end

	local frame = "NLMember" .. unit;

	if( NLConfig.ShowBuffs == 0 and NLConfig.ShowHealBuffs == 0 ) then
		getglobal(frame .. "BuffsDetails"):SetWidth( 5 );
		getglobal(frame .. "BuffsDetails"):Hide();
	else
		local buff;
		local numBuffs = 0;
		local index = 1;
		for NL_ITERATOR=1, NL_MAX_TOOLTIP_BUFFS do
			buff = UnitBuff(unit, NL_ITERATOR);
			if ( buff and (NLConfig.ShowBuffs == 1 or (NLConfig.ShowHealBuffs == 1 and 
				(buff == NL_ICON_LOCATION .. "Spell_Nature_Rejuvenation" or
				buff == NL_ICON_LOCATION .. "Spell_Nature_ResistNature" or
				buff == NL_ICON_LOCATION .. "Spell_Holy_Renew" )
				) ) ) then
				getglobal(frame .. "BuffsDetailsBuff"..index):SetNormalTexture(buff);
				getglobal(frame .. "BuffsDetailsBuff"..index):Show();
				getglobal(frame .. "BuffsDetailsBuff"..index):SetID(index);
				if( getglobal(frame .. "BuffsDetailsBuff"..index) == NL_MOUSE_OVER_FRAME ) then
					GameTooltip:SetUnitBuff( getglobal(frame).Unit, NL_ITERATOR );
				end
				index = index + 1;
				numBuffs = numBuffs + 1;
			end
		end
		for NL_ITERATOR=index, NL_MAX_TOOLTIP_BUFFS do
			getglobal(frame .. "BuffsDetailsBuff"..NL_ITERATOR):Hide();
		end
	
		getglobal(frame .. "BuffsDetailsBuff1"):ClearAllPoints();
		getglobal(frame .. "BuffsDetailsBuff1"):SetPoint( "TOPLEFT", frame .. "BuffsDetails", "TOPLEFT", 5, -5 );
		getglobal(frame .. "BuffsDetails"):SetHeight( 26 );
		-- Size the tooltip
		if ( numBuffs > 0 ) then
			getglobal(frame .. "BuffsDetails"):SetWidth( (numBuffs * 17) + 9 );
			getglobal(frame .. "BuffsDetails"):Show();
		else
			getglobal(frame .. "BuffsDetails"):SetWidth( 5 );
			getglobal(frame .. "BuffsDetails"):Hide();
		end
	end

	if( NLConfig.ShowDebuffs == 0 ) then
		getglobal(frame .. "DebuffsDetails"):SetWidth( 5 );
		getglobal(frame .. "DebuffsDetails"):Hide();
	else
		local buff;
		local numDebuffs = 0;
		local index = 1;
		for NL_ITERATOR=1, NL_MAX_TOOLTIP_DEBUFFS do
			buff = UnitDebuff(unit, NL_ITERATOR);
			
			if ( buff ) then
				getglobal(frame .. "DebuffsDetailsDebuff"..index):SetNormalTexture(buff);
				getglobal(frame .. "DebuffsDetailsDebuff"..index):Show();
				getglobal(frame .. "DebuffsDetailsDebuff"..index):SetID(index);
				if( getglobal(frame .. "DebuffsDetailsDebuff"..index) == NL_MOUSE_OVER_FRAME ) then
					GameTooltip:SetUnitDebuff( getglobal(frame).Unit, NL_ITERATOR );
				end
				index = index + 1;
				numDebuffs = numDebuffs + 1;
			end
		end
		for NL_ITERATOR=index, NL_MAX_TOOLTIP_DEBUFFS do
			getglobal(frame .. "DebuffsDetailsDebuff"..NL_ITERATOR):Hide();
		end
	
		getglobal(frame .. "DebuffsDetailsDebuff1"):ClearAllPoints();
		getglobal(frame .. "DebuffsDetailsDebuff1"):SetPoint( "TOPLEFT", frame .. "DebuffsDetails", "TOPLEFT", 5, -5 );
		getglobal(frame .. "DebuffsDetails"):SetHeight( 26 );
		-- Size the tooltip
		if ( numDebuffs > 0 ) then
			getglobal(frame .. "DebuffsDetails"):SetWidth( (numDebuffs * 17) + 9 );
			getglobal(frame .. "DebuffsDetails"):Show();
		else
			getglobal(frame .. "DebuffsDetails"):SetWidth( 5 );
			getglobal(frame .. "DebuffsDetails"):Hide();
		end
	end
end

function NL_UpdateNeeds( member )
	local frame = getglobal( "NLMember"..member );
	if( not frame or not frame.TopNeed or frame.TopNeed < 1 or NLConfig.ShowNeeds == 0 ) then
		NL_ClearNeedDetails( member );
		return;
	end

	local needFrameIndex = 1;
	
	-- if resurrection is a need of this unit
	if( frame.Needs[NLConfig.ResurrectionNeedIndex] ) then
		if( NL_CURE_SPELLS[NL_PLAYERCLASS] and NL_CURE_SPELLS[NL_PLAYERCLASS].Resurrection ) then
			iconName = NL_CURE_SPELLS[NL_PLAYERCLASS].Resurrection.Icon;
			if( iconName ~= "" ) then
				local needFrame = getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex);
				if( NLConfig.LargeNeedIcons == 1 ) then
					getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex):SetWidth( 22 );
					getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex):SetHeight( 22 );
				else
					getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex):SetWidth( 16 );
					getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex):SetHeight( 16 );
				end
				needFrame:SetNormalTexture(NL_ICON_LOCATION .. iconName);
				needFrame.NeedName = "Resurrection";
				needFrame:Show();
				if( needFrame == NL_MOUSE_OVER_FRAME ) then
					NL_MouseOverNeedButton(needFrame);
				end
				needFrameIndex = needFrameIndex + 1;
			end
		end
	else
		for NL_ITERATOR=2, NLConfig.NumNeeds - 1 do
			local iconName = "";
			if( frame.Needs[NL_ITERATOR] ) then
				if( NL_BUFF_SPELLS[NL_PLAYERCLASS] and NL_BUFF_SPELLS[NL_PLAYERCLASS][NLConfig.Needs[NL_ITERATOR].Name] ) then
					iconName = NL_BUFF_SPELLS[NL_PLAYERCLASS][NLConfig.Needs[NL_ITERATOR].Name].Icons[1];
				elseif( NL_CURE_SPELLS[NL_PLAYERCLASS] and NL_CURE_SPELLS[NL_PLAYERCLASS][NLConfig.Needs[NL_ITERATOR].Name] ) then
					iconName = NL_CURE_SPELLS[NL_PLAYERCLASS][NLConfig.Needs[NL_ITERATOR].Name].Icon;
				elseif( NL_ENCHANT_SPELLS[NL_PLAYERCLASS] and NL_ENCHANT_SPELLS[NL_PLAYERCLASS][NLConfig.Needs[NL_ITERATOR].Name] ) then
					iconName = NL_ENCHANT_SPELLS[NL_PLAYERCLASS][NLConfig.Needs[NL_ITERATOR].Name].Icon;
				elseif( NL_OTHER[NLConfig.Needs[NL_ITERATOR].Name] ) then
					iconName = NL_OTHER[NLConfig.Needs[NL_ITERATOR].Name].Icon;
				end
	
				if( iconName ~= "" ) then
					local needFrame = getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex);
					if( NLConfig.LargeNeedIcons == 1 ) then
						getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex):SetWidth( 22 );
						getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex):SetHeight( 22 );
					else
						getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex):SetWidth( 16 );
						getglobal(frame:GetName() .. "NeedsDetailsNeed"..needFrameIndex):SetHeight( 16 );
					end
					needFrame:SetNormalTexture(NL_ICON_LOCATION .. iconName);
					needFrame.NeedName = NLConfig.Needs[NL_ITERATOR].Name;
					needFrame:Show();
					if( needFrame == NL_MOUSE_OVER_FRAME ) then
						NL_MouseOverNeedButton(needFrame);
					end
					needFrameIndex = needFrameIndex + 1;
					if( needFrameIndex > NL_MAX_NEEDS ) then
						-- this is a break condition, we don't need to see more needs
						NL_ITERATOR = NLConfig.NumNeeds;
					end
				end
			end
		end
	end

	local needCount = needFrameIndex - 1;
	
	for NL_ITERATOR=needFrameIndex, NL_MAX_NEEDS - 1 do
		getglobal(frame:GetName() .. "NeedsDetailsNeed"..NL_ITERATOR):Hide();
	end

	if( NLConfig.LargeNeedIcons == 1 ) then
		getglobal(frame:GetName() .. "NeedsDetails"):SetWidth( needCount * 23 + 9 );
		getglobal(frame:GetName() .. "NeedsDetails"):SetHeight( 32 );
	else
		getglobal(frame:GetName() .. "NeedsDetails"):SetWidth( needCount * 17 + 9 );
		getglobal(frame:GetName() .. "NeedsDetails"):SetHeight( 26 );
	end
	getglobal(frame:GetName() .. "NeedsDetailsNeed1"):ClearAllPoints();
	getglobal(frame:GetName() .. "NeedsDetailsNeed1"):SetPoint( "TOPLEFT", frame:GetName() .. "NeedsDetails", "TOPLEFT", 5, -5 );
	if( needCount > 0 ) then
		getglobal(frame:GetName() .. "NeedsDetails"):Show();
	else
		getglobal(frame:GetName() .. "NeedsDetails"):Hide();
	end
end

function NL_SetTooltipOwner( frame, anchor )
	if( NLConfig.ShowTooltips == 1 ) then
		GameTooltip:SetOwner(frame, anchor);
	else
		GameTooltip:Hide();
	end
end

function NLMember_OnEnter(frame)
	NL_SetTooltipOwner( frame, "ANCHOR_TOPLEFT" );
	GameTooltip:SetUnit( frame.Unit );
end

function NLMember_OnLeave(frame)
	GameTooltip:Hide();
end

function NLMember_OnClick(button, frame)
	if( NLConfig.UseCastParty == 1 and CastParty_OnClickByUnit ~= nil ) then
		CastParty_OnClickByUnit( button, frame.Unit );
	else
		if ( button == "LeftButton" ) then
			-- determine which unit was clicked
			if ( SpellIsTargeting() ) then
				SpellTargetUnit(frame.Unit);
			elseif ( CursorHasItem() ) then
				DropItemOnUnit(frame.Unit);
			else
				TargetUnit(frame.Unit);
			end
		else
			if( SpellIsTargeting() ) then
				SpellStopTargeting();
				return;
			end
		end
	end
end

function NLMember_CheckAllUnits()
	NL_NUM_NEEDY_UNITS = 0;
	if( NLConfig.HideHeader == 1 ) then
		NLHeader:Hide();
	end
	
	NL_SLOTS = {};

	-- hide all frames
	NLMemberplayer:Hide();
	NLMemberplayer.SlotIndex = nil;
	NLMemberplayer.TopNeed = nil;
	NLMemberplayer.Needs = {};
	NLMemberpet:Hide();
	NLMemberpet.SlotIndex = nil;
	NLMemberpet.TopNeed = nil;
	NLMemberpet.Needs = {};

	for NL_ITERATOR = 1, 4 do
		getglobal("NLMemberparty"..NL_ITERATOR):Hide();
		getglobal("NLMemberparty"..NL_ITERATOR).SlotIndex = nil;
		getglobal("NLMemberparty"..NL_ITERATOR).TopNeed = nil;
		getglobal("NLMemberparty"..NL_ITERATOR).Needs = {};
		getglobal("NLMemberpartypet"..NL_ITERATOR):Hide();
		getglobal("NLMemberpartypet"..NL_ITERATOR).SlotIndex = nil;
		getglobal("NLMemberpartypet"..NL_ITERATOR).TopNeed = nil;
		getglobal("NLMemberpartypet"..NL_ITERATOR).Needs = {};
	end
	for NL_ITERATOR = 1, 40 do
		getglobal("NLMemberraid"..NL_ITERATOR):Hide();
		getglobal("NLMemberraid"..NL_ITERATOR).SlotIndex = nil;
		getglobal("NLMemberraid"..NL_ITERATOR).TopNeed = nil;
		getglobal("NLMemberraid"..NL_ITERATOR).Needs = {};
		getglobal("NLMemberraidpet"..NL_ITERATOR):Hide();
		getglobal("NLMemberraidpet"..NL_ITERATOR).SlotIndex = nil;
		getglobal("NLMemberraidpet"..NL_ITERATOR).TopNeed = nil;
		getglobal("NLMemberraidpet"..NL_ITERATOR).Needs = {};
	end
	
	-- if player in a raid, check all 40 units, otherwise just check player and party
	if( GetNumRaidMembers() > 0 ) then
		for NL_ITERATOR = 1, 40 do
			if( UnitExists( "raid" .. NL_ITERATOR ) ) then
				NL_CheckAllNeeds( "raid" .. NL_ITERATOR );
				if( UnitExists( "raidpet" .. NL_ITERATOR ) ) then
					NL_CheckAllNeeds( "raidpet" .. NL_ITERATOR );
				end
			end
		end
	else
		if( UnitExists( "player" ) ) then
			NL_CheckAllNeeds( "player" );
			if( UnitExists( "pet" ) ) then
				NL_CheckAllNeeds( "pet" );
			end
		end

		for NL_ITERATOR = 1, 4 do
			if( UnitExists( "party" .. NL_ITERATOR ) ) then
				NL_CheckAllNeeds( "party" .. NL_ITERATOR );
				if( UnitExists( "partypet" .. NL_ITERATOR ) ) then
					NL_CheckAllNeeds( "partypet" .. NL_ITERATOR );
				end
			end
		end
	end

	NL_CheckTarget();
end

function NL_CheckTarget()
	NLMembertarget.Needs = {};
	NLMembertarget.TopNeed = nil;
	NLMembertarget:Hide();

	if( NLConfig.ShowTargetFrame == 1 ) then
		if( UnitExists( "target" ) and UnitIsFriend( "target", "player" ) ) then
			NL_CheckAllNeeds( "target" );
		else
			NLMembertarget:Hide();
		end
	end
end

function NL_MouseOverNeedButton(frame)
	NL_SetTooltipOwner(frame:GetParent():GetParent(), "ANCHOR_TOPLEFT");
	local tooltipText = "";
	if( NL_BUFF_SPELLS[NL_PLAYERCLASS] and NL_BUFF_SPELLS[NL_PLAYERCLASS][frame.NeedName] and NL_BUFF_SPELLS[NL_PLAYERCLASS][frame.NeedName][1] ) then
		if( NL_BUFF_SPELLS[NL_PLAYERCLASS] and NL_BUFF_SPELLS[NL_PLAYERCLASS][frame.NeedName] ) then
			if( NL_BUFF_SPELLS[NL_PLAYERCLASS][frame.NeedName][1] ) then
				tooltipText = "Left-click to cast " .. NL_BUFF_SPELLS[NL_PLAYERCLASS][frame.NeedName][1] .. ".";
			end
			if( NL_BUFF_SPELLS[NL_PLAYERCLASS][frame.NeedName][2] ) then
				tooltipText = tooltipText .. "\nRight-click to cast " .. NL_BUFF_SPELLS[NL_PLAYERCLASS][frame.NeedName][2] .. ".";
			end
		end
	elseif( NL_ENCHANT_SPELLS[NL_PLAYERCLASS] and NL_ENCHANT_SPELLS[NL_PLAYERCLASS][frame.NeedName] and NL_ENCHANT_SPELLS[NL_PLAYERCLASS][frame.NeedName][1] ) then
		if( NL_PLAYERCLASS == "Rogue" ) then
			tooltipText =  "Needs " .. NL_ENCHANT_SPELLS[NL_PLAYERCLASS][frame.NeedName][1] .. ".";
		else
			tooltipText = "Needs " .. NL_ENCHANT_SPELLS[NL_PLAYERCLASS][frame.NeedName][1] .. ".\nClick here to fix.";
		end
	elseif( frame.NeedName == "Resurrection" ) then
		if( NL_CURE_SPELLS[NL_PLAYERCLASS] and NL_CURE_SPELLS[NL_PLAYERCLASS].Resurrection ) then
			tooltipText = "Left-click to cast " .. NL_CURE_SPELLS[NL_PLAYERCLASS].Resurrection[1] .. ".";
		end
	elseif( frame.NeedName == "WellFed" ) then
		tooltipText = "Needs to be well fed.";
	elseif( frame.NeedName == "Health" ) then
		tooltipText = "Needs healing.";
		if( NLConfig.HealSpells ) then
			if( NLConfig.HealSpells[1] ) then
				tooltipText = tooltipText .. "\nLeft-click to cast " .. NLConfig.HealSpells[1] .. ".";
			end
			if( NLConfig.HealSpells[2] ) then
				tooltipText = tooltipText .. "\nRight-click to cast " .. NLConfig.HealSpells[2] .. ".";
			end
			if( NLConfig.HealSpells[3] ) then
				tooltipText = tooltipText .. "\nMiddle-click to cast " .. NLConfig.HealSpells[3] .. ".";
			end
			if( NLConfig.HealSpells[4] ) then
				tooltipText = tooltipText .. "\nClick button 4 to cast " .. NLConfig.HealSpells[4] .. ".";
			end
			if( NLConfig.HealSpells[5] ) then
				tooltipText = tooltipText .. "\nClick button 5 to cast " .. NLConfig.HealSpells[5] .. ".";
			end
		end
		if( tooltipText == "Needs healing." ) then
			tooltipText = tooltipText .. "\nUse 'Configure Spells' from the configuration window to add heal spells.";
		end
	elseif( frame.NeedName == "Mana" ) then
		tooltipText = "Needs mana.\nLeft-click to cast Innervate.";
	else
		tooltipText = "Needs cure for " .. frame.NeedName .. ".";
		if( NL_CURE_SPELLS[NL_PLAYERCLASS] and NL_CURE_SPELLS[NL_PLAYERCLASS][frame.NeedName] ) then
			if( NL_CURE_SPELLS[NL_PLAYERCLASS][frame.NeedName][1] ) then
				tooltipText = tooltipText .. "\nLeft-click to cast " .. NL_CURE_SPELLS[NL_PLAYERCLASS][frame.NeedName][1] .. ".";
			end
			if( NL_CURE_SPELLS[NL_PLAYERCLASS][frame.NeedName][2] ) then
				tooltipText = tooltipText .. "\nRight-click to cast " .. NL_CURE_SPELLS[NL_PLAYERCLASS][frame.NeedName][2] .. ".";
			end
		end
	end
	GameTooltip:SetText( tooltipText );
end

function NL_CureTopNeed(button)
	for NL_ITERATOR = 0, NL_NUM_NEEDY_UNITS - 1 do
		if( NL_SLOTS[NL_ITERATOR] and NL_SLOTS[NL_ITERATOR].TopNeed and NL_SLOTS[NL_ITERATOR].TopNeed > 1 and NL_SLOTS[NL_ITERATOR].Unit ) then
			NL_CureNeedOnUnit( NLConfig.Needs[NL_SLOTS[NL_ITERATOR].TopNeed].Name, NL_SLOTS[NL_ITERATOR].Unit, button );
			return;
		end
	end
end

function NL_SortList()
	if( NL_MOUSE_IN_FRAME ) then
		NL_MOUSE_IN_FRAME = false;
		NL_SortAllVisibleFrames();
		NL_MOUSE_IN_FRAME = true;
	else
		NL_SortAllVisibleFrames();
	end
end
