

GHI_AddOns = {
	classType="GHI_AddOns",
	timeout=10,
}; -- class is a singleton

--	======================== Player Finding ========================
local existingPlayers = {};
local playerAddOns = {};

local comm = GHI_Comm();

-- register events for finding exsisting players
-- including channel lists (see area sound)
local function OnFindEvent(event,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)
	--print("event: ",event,"arg1",arg1,"arg2",arg2,"arg3",arg3,"arg4",arg4,"arg5",arg5,"arg6",arg6,"arg7",arg7)
	-- when joining or leaving a channel
	if event == "CHAT_MSG_CHANNEL_NOTICE" then 
		
		-- not needed, assuming all others with GHI will send a message to the player when he joins the channel anyway. By this he will be aware of them
	end
	
	-- when others joining ("CHAT_MSG_CHANNEL_JOIN")
	if event == "CHAT_MSG_CHANNEL_JOIN" then	
		GHI_AddOns:NoticePlayer(arg2);
	elseif event == "CHAT_MSG_CHANNEL_LEAVE" then
	 --return;
	end
	
	-- on chat message
	local e = {
		CHAT_MSG_CHANNEL=true, CHAT_MSG_EMOTE=true, CHAT_MSG_GUILD=true, CHAT_MSG_OFFICER=true,
		CHAT_MSG_PARTY=true, CHAT_MSG_PARTY_LEADER=true, CHAT_MSG_RAID=true, CHAT_MSG_SAY=true,
		CHAT_MSG_TEXT_EMOTE=true, CHAT_MSG_WHISPER=true, CHAT_MSG_YELL=true, 
	};
	if e[event] and not(arg6 == "GM") then
		GHI_AddOns:NoticePlayer(arg2);
	end
	
	if event == "CHAT_MSG_ADDON" then
		GHI_AddOns:NoticePlayer(arg4);
	end
	
	if event=="CHAT_MSG_SYSTEM" then
	local player = gsub(string.match(arg1,"'.*'") or "","'","");
	if player and format(ERR_CHAT_PLAYER_NOT_FOUND_S,player) == mesg and ((GHI_MessageErrorsExpected_A[player] or GHI_MessageErrorsExpected_B[player])) then
			GHI_AddOns:RemovePlayer(player)
			return;
		end
	end
	
	-- friendslist Update
	if event == "FRIENDLIST_UPDATE" then
		for i=1,GetNumFriends() do
			local name, _, _, _, connected = GetFriendInfo(i);
			if connected then
				GHI_AddOns:NoticePlayer(name);
			end
		end
	end
	
	-- on trade Frame
	if event == "TRADE_REQUEST" then
		GHI_AddOns:NoticePlayer(arg1);
	end
	
end



function GHI_AddOns:SetUp()
	GHI_RegEvent("CHAT_MSG_CHANNEL_NOTICE",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_CHANNEL_JOIN",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_CHANNEL_LEAVE",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_SYSTEM",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_CHANNEL",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_EMOTE",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_GUILD",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_OFFICER",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_PARTY",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_PARTY_LEADER",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_RAID",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_ADDON",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_TEXT_EMOTE",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_WHISPER",OnFindEvent);
	GHI_RegEvent("CHAT_MSG_YELL",OnFindEvent);
	GHI_RegEvent("FRIENDLIST_UPDATE",OnFindEvent);
	GHI_RegEvent("TRADE_REQUEST",OnFindEvent);
	
	GHI_AddOns:NoticePlayer(UnitName("player"));
end

--	======================== Addon List ========================

local addOnList;
local notifyFuncs = {};

local function GetAddOnList()
	if not(addOnList) then
		-- generate the list
		addOnList = {};
		for i = 1, GetNumAddOns() do
			local addOnName, addOnTitle, addOnNotes, addOnEnabled, addOnLoadable, addOnReason, addOnSecurity = GetAddOnInfo(i);
			
			if string.find(addOnTitle,"Gryphonheart") and addOnEnabled then
				-- check if X-Lib is not true
				if not(GetAddOnMetadata(addOnName,"X-Lib") == "true") then
					table.insert(addOnList,{
						short = addOnName,
						title = addOnTitle,
						version = GetAddOnMetadata(addOnName,"Version"),
					});
				end
			end
		end
	end
	
	return addOnList;
end

-- Get all players with a given addon. Version is optional
function GHI_AddOns:GetAllPlayers(addOnShort,version)
	addOnShort = addOnShort or "GHI"; -- default value for addOn is GHI
	
	local players = {};
	
	for player,addons in pairs(playerAddOns) do
		if self:PlayerGotAddOn(player,addOnShort,version) then
			table.insert(players,player);
		end
	end
	
	return players
end

-- return a list of addons the player has
function GHI_AddOns:GetPlayerAddOns(player)
	local result = {};
	local t = playerAddOns[player] or {};
	
	for _,addon in pairs(t) do
		table.insert(result,addon.short);
	end
	
	return result;
end

function GHI_AddOns:GetPlayerAddOnVer(player,addOnShort)
	addOnShort = addOnShort or "GHI"; -- default value for addOn is GHI
	local t = playerAddOns[player] or {};
	-- look for the addon
	for _,addon in pairs(t) do
		if type(addon) == "table" then
			if addon.short == addOnShort then
				return addon.version;
			end
		end
	end
end

function GHI_AddOns:PlayerGotAddOn(player,addOnShort,version)
	addOnShort = addOnShort or "GHI"; -- default value for addOn is GHI
	local t = playerAddOns[player] or {};
	
	-- look for the addon
	for _,addon in pairs(t) do
		if type(addon) == "table" then
			if addon.short == addOnShort then
				
				-- if a version is given, make sure the addon is newer than the given version
				if version then
					return GHI_CompareVersions(addon.version or 0,version);
				else
					return true;
				end		
			end
		end
	end
	return false;
end

-- Notice a player and update the player if it was unknown or outdated
function GHI_AddOns:NoticePlayer(player)
	--print("notice ",player)
	if not(player) or string.len(player or "") == 0 then
		return;
	end 
	
	
	-- check if the player is already known and recently updated
	if (existingPlayers[player] and (GetTime() - existingPlayers[player]) < self.timeout*60 ) then
		-- no new info, so return
		return; 
	end
		
	-- save noticed time
	existingPlayers[player] = GetTime();

	-- request information about the players addons
	comm.Send("ALERT",player,"ReqAddOns");
end

-- Set timeout for refreshing
function GHI_AddOns:SetTimeout(timeout)
	assert(type(timeout)=="number","Timeout must be a number");
	self.timeout = timeout;
end

function GHI_AddOns:RemovePlayer(player)
	existingPlayers[player] = 0;
end

-- send message to all registered notifiers when the player is updated
function GHI_AddOns:NotifyAll(player)
	for _,notifier in pairs(notifyFuncs) do
		if self:PlayerGotAddOn(player,notifier.addOnShort,notifier.version) then
			notifier.func(player)
		end
	end
end

-- register functions for notification
function GHI_AddOns:RegNotifyFunc(func,addOnShort,version)
	table.insert(notifyFuncs,{
		func = func,
		addOnShort = addOnShort,
		version = version,
	});
end

-- Answer with table of addon info
local function RecieveAddOnRequest(player,...)
	comm.Send("ALERT",player,"AddOns",GetAddOnList());
end
comm.AddRecieveFunc("ReqAddOns",RecieveAddOnRequest)

local function RecieveAddOnAnswer(player,addons,...)
	playerAddOns[player] = addons;
	GHI_AddOns:NotifyAll(player)
end
comm.AddRecieveFunc("AddOns",RecieveAddOnAnswer)


