


--===================================================
--									
--										GHU Links
--									ghu_links.lua
--
--				Dynamic handler of links in chat
--	
-- 						(c)2011 The Gryphonheart Team
--								All rights reservated
--===================================================	


function GHU_Links(prefix,syncDataFunc,linkClickFunc)
	local class = GHClass("GHU_Links");
	local handler = GHU_LinksHandler();
	
	class.GenerateLink = function(...)
		return handler.GenerateLink(prefix,...);
	end
	
	handler.RegisterSyncDataFunc(prefix,syncDataFunc);
	handler.RegisterLinkClickFunc(prefix,linkClickFunc);
	
	return class;
end

local class;
function GHU_LinksHandler()
	if class then
		return class;
	end	
	
	class = GHClass("GHU_LinksHandler");
	local syncDataFuncs = {};
	local linkClickFuncs = {};
	local sendLinks = {};
	local supportedEvents = {"CHAT_MSG_CHANNEL"};
	local registeredFrames = {};
	local comm = GHI_Comm();
	local comm = GHI_Comm();
	local incommingEvents = {};
	local timer;
	
	local orig = {};
	local SendChatMessage,GetCleanTextAndLinksFromText,OnEvent,ParseEventToFrames,ConvertEventNameToChatType,SubstractSuspectedLinkNames,SendSuspectedLinkName,StoreEvent;
	local IdentifyAndSendLinkInfo,RecieveLinkConfirm,RecieveLinkDeconfirm,HexColorToColorArray,InsertLinkInStoredEvent,TableContainsValue,RemoveValueFromTable,ParseEventsTimedOut;
	local SetItemRef;
	
	class.RegisterSyncDataFunc = function(prefix,func)
		syncDataFuncs[prefix] = func;
	end
	class.RegisterLinkClickFunc = function(prefix,func)
		linkClickFuncs[prefix] = func;
	end
	class.GenerateLink = function(prefix,text,ID,color)
		GHCheck("GHU_Links.GenerateLink",{"String","String","String","Table"},{prefix,text,ID,color})
		return string.format("|CFF%s%s%s|HGH_%s:%s:0:0:0:0:0:0:0|h[%s]|h|r",string.format("%.2x",color.r*255),string.format("%.2x",color.g*255),string.format("%.2x",color.b*255),prefix,ID,text);
	end
	
	SendChatMessage = function(text,chatType,...)
		local cleanText = GetCleanTextAndLinksFromText(text,chatType);
		return orig.SendChatMessage(cleanText,chatType,...);
	end
	
	GetCleanTextAndLinksFromText = function(text,chatType)
		local clean = string.gsub(text,"|CFF(%x%x)(%x%x)(%x%x)|HGH_(.-):(.-):0:0:0:0:0:0:0|h%[(.-)%]|h|r",function(r,g,b,prefix,ID,name) 
			table.insert(sendLinks,{
				prefix = prefix,
				ID = ID,
				name = name,
				color = HexColorToColorArray(r,g,b);
				time = time(),
				chatType = chatType,
			});
			return string.format("[%s]",name); 
		end);
		return clean;
	end
	HexColorToColorArray = function(hexR,hexG,hexB) 
		return {
			r = tonumber(hexR,16)/255,
			g = tonumber(hexG,16)/255,
			b = tonumber(hexB,16)/255,
		}
	end

	OnEvent = function(self,event,msg,author,...)
		local linksInText = SubstractSuspectedLinkNames(msg);
		if #(linksInText) > 0 then
			StoreEvent(event,msg,author,linksInText,...);
			SendSuspectedLinkName(author,linksInText,event);
		else
			ParseEventToFrames(event,msg,author,...)
		end
	end
	
	ParseEventToFrames = function(event,...)
		for _,frame in pairs(registeredFrames[event]) do
			if not(frame:IsEventRegistered(event)) then
				local s = frame:GetScript("OnEvent")
				s(frame,event,...);
			end
		end
	end
	
	SubstractSuspectedLinkNames = function(text)
		local links = {};
		string.gsub(text.." ","%[(.-)%][^|]",function(link)
			table.insert(links,link);
		end);
		return links;
	end
	
	StoreEvent = function(event,msg,author,linkNames,...)
		table.insert(incommingEvents,{
			event = event,
			msg = msg,
			author = author,
			linkNames = linkNames,
			time = time(),
			args = {...},
		});
	end
	
	SendSuspectedLinkName = function(author,linkNames,event)
		assert(type(linkNames)=="table","");
		local chatType = ConvertEventNameToChatType(event);
		comm.Send("ALERT",author,"SuspectedLinkName",chatType,linkNames);
	end
	
	ConvertEventNameToChatType = function(event)
		if event == "CHAT_MSG_CHANNEL" then
			return "CHANNEL";	
		end
		return "";
	end
	
	RecieveSuspectedLinkName = function(sender,chatType,linkNames)
		for _,linkName in pairs(linkNames) do
			IdentifyAndSendLinkInfo(sender,chatType,linkName);
		end
	end 
	
	IdentifyAndSendLinkInfo = function(sender,chatType,linkName)
		for _,link in pairs(sendLinks) do
			if time() - link.time > 60*5 then
				break;
			end
			if link.name == linkName and link.chatType == chatType then
				comm.Send("ALERT",sender,"LinkConfirm",link.name,link.prefix,link.ID,link.chatType,link.color);
				if syncDataFuncs[link.prefix] then
					syncDataFuncs[link.prefix](sender,link.ID);
				end
				return
			end					
		end
		comm.Send("ALERT",sender,"LinkDeconfirm",linkName,chatType);
	end
	
	RecieveLinkConfirm = function(sender,name,prefix,ID,chatType,color)
		local link = class.GenerateLink(prefix,name,ID,color);
		InsertLinkInStoredEvent(sender,chatType,name,link);
	end
	
	RecieveLinkDeconfirm = function(sender,name,chatType)
		InsertLinkInStoredEvent(sender,chatType,name,"["..name.."]");
	end
	
	InsertLinkInStoredEvent = function(author,chatType,linkText,newLink)
		for i,event in pairs(incommingEvents) do
			if event.author == author and chatType == ConvertEventNameToChatType(event.event) then
				if TableContainsValue(event.linkNames,linkText) then
					event.msg = gsub(event.msg,"%["..linkText.."%]([^|]?)",function(followingChar)
						return newLink..followingChar;
					end)
					RemoveValueFromTable(event.linkNames,linkText);
					
					if #(event.linkNames) == 0 then
						ParseEventToFrames(event.event,event.msg,event.author,unpack(event.args));
						table.remove(incommingEvents,i);
					end
				end
			end
		end
	end
	
	TableContainsValue = function(t,value)
		for _,v in pairs(t) do
			if v == value then
				return true;					
			end
		end
		return false;
	end
	
	RemoveValueFromTable = function(t,value)
		for i,v in pairs(t) do
			if v == value then
				t[i] = nil;
			end
		end
	end
	
	MSG_TIMEOUT = 20; 
	
	ParseEventsTimedOut = function()
		for i,event in pairs(incommingEvents) do
			if time() - event.time > MSG_TIMEOUT then
				ParseEventToFrames(event.event,event.msg,event.author,unpack(event.args));
				table.remove(incommingEvents,i);
			end
		end
	end
	timer = GHI_Timer(ParseEventsTimedOut,1);
	
	SetItemRef = function(link, text, button)
		local prefix,ID = string.match(link,"GH_(.-):(.-):");
		if prefix and ID then
			if IsShiftKeyDown() == 1 then
				ChatEdit_InsertLink(text)
			else
				if linkClickFuncs[prefix] then
					linkClickFuncs[prefix](ID);
				end
			end
		else 		
			orig.SetItemRef(link,text,button);
		end
	end
	
	comm.AddRecieveFunc("SuspectedLinkName",RecieveSuspectedLinkName);
	comm.AddRecieveFunc("LinkConfirm",RecieveLinkConfirm);
	comm.AddRecieveFunc("LinkDeconfirm",RecieveLinkDeconfirm);
	
	class:SetScript("OnEvent",OnEvent);
	
	orig.SendChatMessage = _G.SendChatMessage;
	_G.SendChatMessage = SendChatMessage;
	orig.SetItemRef = _G.SetItemRef;
	_G.SetItemRef = SetItemRef;
	
	for _,event in pairs(supportedEvents) do
		registeredFrames[event] = {GetFramesRegisteredForEvent(event)};
		for _,frame in pairs(registeredFrames[event]) do
			frame:UnregisterEvent(event);
		end
		
		class:RegisterEvent(event);
  end
      
	
	return class;
end
