local _G = getfenv(0);

----------------------------------------------------------------------------------------
--- Constants
----------------------------------------------------------------------------------------

LHCP_IGNORE_SPAM = 2^0;			-- (DEPRECATED) ignore repeated emotes
LHCP_IGNORE_ENEMY = 2^1;		-- (DEPRECATED) ignore opposing faction
LHCP_IGNORE_SOUNDS = 2^2;		-- ignore sounds
LHCP_IGNORE_EMOTES = 2^3;		-- ignore character emotes
LHCP_IGNORE_NSFW = 2^4;			-- ignore NSFW sounds
LHCP_IGNORE_RAID = 2^5;			-- (DEPRECATED) ignore in raid zones

----------------------------------------------------------------------------------------
--- Variables
----------------------------------------------------------------------------------------

LHCP_timeTilNext = 0;			-- throttle sound spamming
LHCP_emoteTime = 0;				-- throttle emote spamming
LHCP_ignoreMode = 0;			-- bitfield for ignore flags
LHCP_factionText = nil;			-- player's faction name
LHCP_isEnabled = false;			-- disable parsing when not in world
LHCP_debug = false;				-- print debug statements
LHCP_phrases = nil;				-- LHCP phrase index
LHCP_lastSound = nil;			-- last emote, for stopping 
LHCP_lastEmote = nil;			-- last emote, for emote antispam
LHCP_actionList = nil;			-- list of LHCP actions for /lhcp list

----------------------------------------------------------------------------------------
--- Init
----------------------------------------------------------------------------------------

function LHCP_MakeSlashCmd(name,cmd,i,slashList)
	cmd = "/"..cmd;
	
	if slashList and slashList[cmd] then
		return false;
	end
	
	_G["SLASH_"..name..i] = cmd;
	
	return true;
end

function LHCP_GetSlashList()
	local timeout = time() + 10;
	local slashList = {};
	for key,_ in pairs(_G.SlashCmdList) do
		local i = 1;
		local name = "SLASH_"..key..i;
		while _G[name] do
			slashList[_G[name]] = key;
			i = i+1;
			name = "SLASH_"..key..i;
		end
	end

	return slashList;
end

function LHCP_Enable()
	LHCP_isEnabled = true;
end

function LHCP_Disable()
	LHCP_isEnabled = false;
end

function LHCP_LoadDataText(key,value,abortTime)
	if time() > abortTime then error("LHCP load stalled on "..key); end
	
	if value["text"] ~= nil and value["file"] ~= nil then
		local txts = {
			value["text"],
			value[LHCP_factionText],
			value["alttext"]
		};
		
		for i,t in ipairs(txts) do
			if t ~= nil and string.len(t) > 0 then
				LHCP_phrases[t] = key;
			end
		end
	end
end

function LHCP_LoadDataCmd(key,value,abortTime)
	if time() > abortTime then error("LHCP load stalled on cmd "..key); end
	
	local name = "LHCP"..key;
	-- need to use a dynamic script here to avoid variable scoping
	-- how do you pre-evaluate "key" ???
	_G.SlashCmdList[name] = loadstring("LHCP_SayOutgoingEmote(\""..key.."\");");
	
	local i = 1;
	_= LHCP_MakeSlashCmd(name,key,i,slashList)
	  or LHCP_Print("LHCP register /"..key.." failed",1,.3,.3);
	
	i = i+1;
	
	-- add slash aliases
	local j = 1;
	local cmd = value["cmd"..j];
	while cmd ~= nil do
		_= LHCP_MakeSlashCmd(name,cmd,i)
		  or LHCP_Print("LHCP register /"..cmd.." failed",1,.3,.3);
		
		i = i+1;
		j = j+1;
		cmd = value["cmd"..j];
	end
end

function LHCP_Load()
	local t0 = time();
	local abortTime = t0 + 10;
	
	local slashList = LHCP_GetSlashList();

	LHCP_phrases = {};
	
	-- generate all the slash commands!
	if LeeroyHillCatsPower_data then
		for key,value in pairs(LeeroyHillCatsPower_data) do
			LHCP_LoadDataCmd(key,value,abortTime);
		end
	end
	
	-- TODO get player faction
	LHCP_factionText = UnitFactionGroup("player").."EnemyText";
	
	-- index the phrases
	if LeeroyHillCatsPower_data then
		for key,value in pairs(LeeroyHillCatsPower_data) do
			LHCP_LoadDataText(key,value,abortTime);
		end
	end
	
	local dt = time() - t0;
	
	LHCP_Print("Leeroy Hill Cats Power loaded. "
	  .."Type /lhcp for more info.",0,1,0);
	LHCP_Debug("LHCP load took ",t0," seconds.");
	
	LHCP_ShowStatus();
end

----------------------------------------------------------------------------------------
--- OnEvent functionality
----------------------------------------------------------------------------------------

function LHCP_HandleTextEmote(event,arg1,arg2)
	if not LHCP_isEnabled then
		return nil;
	end
	
	LHCP_Debug("HandleTextEmote ",event," ",arg1);
	
	
	local now = time();
	if LHCP_lastEmote and now >= LHCP_emoteTime then
		LHCP_lastEmote = nil;
	end
	
	LHCP_emoteTime = now + 5;
	
	if LHCP_lastEmote and string.find(arg1,LHCP_lastEmote) then
		-- filter this text from the screen
		LHCP_Debug("Skip ",event," ",arg1);
	else
		for _,emote in ipairs(LeeroyHillCatsPower_emotes) do
			if string.find(arg1,emote) then
				LHCP_lastEmote = emote;
				LHCP_Debug("Last emote ",emote);
				return;
			end
		end
	end
end

function LHCP_PlaySoundFile(file)
	if not LHCP_isEnabled then
		return nil;
	end
	
	if not file then
		return nil;
	end

	local willPlay, soundHandle = PlaySoundFile(file);
	if willPlay then
		LHCP_Debug("Playing ",file);
	end

	-- Stop last sound if it exists
	if LHCP_lastSound then
		StopSound(LHCP_lastSound);
	end
	
	LHCP_lastSound = soundHandle;
end

function LHCP_SayOutgoingEmote(key)
	local emote = LeeroyHillCatsPower_data[key];

	if emote == nil then return; end
	
	SendChatMessage(emote["text"],"SAY");
end

function LHCP_PlayIncomingText(event,arg1,arg2)
	if not LHCP_isEnabled then
		return nil;
	end
	
	LHCP_Debug("PlayIncomingText ",event," ",arg1);
	
	local key = LHCP_phrases[arg1];
	
	LHCP_PlayIncomingEmote(key,event,arg1,arg2);
end

function LHCP_PlayIncomingEmote(key,event,arg1,arg2)
	local emote = LeeroyHillCatsPower_data[key];
	local selfplay = arg1 == nil; -- indicates self-play
	
	if emote == nil then return; end
	
	local emoteinfo = ChatTypeInfo["EMOTE"];

	if not selfplay then
		LHCP_Debug("Skipping ",event," ",arg1);
	end
	
	local msg_str = emote["msg"];
	if msg_str then
		if selfplay or LHCP_GetEmotesAllowed() then
			LHCP_Print(arg2..msg_str,
			  emoteinfo.r,emoteinfo.g,emoteinfo.b);
		end
	end
	
	local emote_str = emote["emote"];
	if emote_str then
		if selfplay or LHCP_GetEmotesAllowed() then
			DoEmote(emote_str);
		end
	end
	
	local file_str = emote["file"];
	if file_str then
		if LHCP_GetMuted() then
			LHCP_Print("(Sound muted. Unmute with /lhcp sound)");
			return;
		end

		if time() >= LHCP_timeTilNext then
			if emote["nsfw"] and LHCP_GetNSFWMuted() then
				LHCP_Print("(NSFW sound muted. Unmute with /lhcp nsfw)");
				return;
			end
			
		  	LHCP_PlaySoundFile(file_str);
			LHCP_timeTilNext = time() + 2;
			-- minimum 2 seconds between all emotes
		end
	end
end

----------------------------------------------------------------------------------------
--- Event handling
----------------------------------------------------------------------------------------

function LHCP_OnEvent(self,event,...)
	if not LHCP_phrases then
		LHCP_Load();
	end

	LHCP_HandleEvent(self,event,...);
end

function LHCP_HandleEvent(self,event,...)
	local arg1,arg2 = ...;
	if (event == "CHAT_MSG_SAY") then
		LHCP_PlayIncomingText(event,arg1,arg2);
	elseif (event == "CHAT_MSG_TEXT_EMOTE") then
		LHCP_HandleTextEmote(event,arg1,arg2);
	elseif (event == "PLAYER_LEAVING_WORLD") then
		LHCP_Disable();
	elseif (event == "PLAYER_ENTERING_WORLD") then
		LHCP_Enable();
	else
		DEFAULT_CHAT_FRAME:AddMessage(event);
	end
end

----------------------------------------------------------------------------------------
--- Slash Commands
----------------------------------------------------------------------------------------

function LHCP_Debug(...)
	LHCP_Print(arg,nil,nil,nil,true,true);
end

function LHCP_Print(text,r,g,b,oneline,dodebug)
	if dodebug and not LHCP_debug then
		return;
	end
	
	local out = "";
	if type(text) == "table" then
		for _,msg in ipairs(text) do
			if oneline then
				out = out..msg;
			else
				DEFAULT_CHAT_FRAME:AddMessage(msg, r, g, b);
				--SendChatMessage(msg,"SAY","COMMON");
			end
		end
		
		if oneline then
			DEFAULT_CHAT_FRAME:AddMessage(out, r, g, b);
			--SendChatMessage(out,"SAY","COMMON");
		end
	else
		if (text == nil) then text = ""; end
		DEFAULT_CHAT_FRAME:AddMessage(text, r, g, b);
		--SendChatMessage(text,"EMOTE","COMMON");
	end
end

function LHCP_List()
	-- list all events
	if not LHCP_actionList then
		LHCP_actionList = {};
		
		local allCmds = {};
		for key,value in pairs(LeeroyHillCatsPower_data) do
			
			table.insert(allCmds,key);
			
			local i = 1;
			local cmd = "cmd"..i;
			while value[cmd] do
				table.insert(allCmds,value[cmd]);
				i = i+1;
				cmd = "cmd"..i;
			end
		end
		
		table.sort(allCmds);
		
		LHCP_actionList = {};
		local str = "";
		for i,value in ipairs(allCmds) do
			if string.len(str) + string.len(value) > 50 then
				table.insert(LHCP_actionList,str);
				str = "";
			end

			str = str .." ".. value;
		end
		table.insert(LHCP_actionList,str);
	end
	
	LHCP_Print("Listing all LHCP emotes:",0,1,0);
	LHCP_Print(LHCP_actionList);
end


function LHCP_main()
	LHCP_Print("Leeroy Hill Cats Power",0,1,0);
	LHCP_Print(LeeroyHillCatsPower_about);
end

function LHCP_GetMuted()
	return bit.band(LHCP_ignoreMode,LHCP_IGNORE_SOUNDS) ~= 0;
end

function LHCP_GetNSFWMuted()
	return bit.band(LHCP_ignoreMode,LHCP_IGNORE_NSFW) ~= 0;
end

function LHCP_GetEmotesAllowed()
	return bit.band(LHCP_ignoreMode,LHCP_IGNORE_EMOTES) == 0;
end

function LHCP_ToggleSound()
	if LHCP_GetMuted() then
		LHCP_ignoreMode = bit.band(LHCP_ignoreMode,bit.bnot(LHCP_IGNORE_SOUNDS));
		LHCP_Print("LHCP sounds unmuted.",0,1,0);
	else
		LHCP_ignoreMode = bit.bor(LHCP_ignoreMode,LHCP_IGNORE_SOUNDS);
		LHCP_Print("LHCP sounds muted.",0,1,0);
	end
end

function LHCP_ToggleNSFW()
	if LHCP_GetNSFWMuted() then
		LHCP_ignoreMode = bit.band(LHCP_ignoreMode,bit.bnot(LHCP_IGNORE_NSFW));
		LHCP_Print("LHCP NSFW sounds unmuted.",0,1,0);
	else
		LHCP_ignoreMode = bit.bor(LHCP_ignoreMode,LHCP_IGNORE_NSFW);
		LHCP_Print("LHCP NSFW sounds muted.",0,1,0);
	end
end

function LHCP_ToggleEmotes()
	if not LHCP_GetEmotesAllowed() then
		LHCP_ignoreMode = bit.band(LHCP_ignoreMode,bit.bnot(LHCP_IGNORE_EMOTES));
		LHCP_Print("LHCP character emotes unmuted.",0,1,0);
	else
		LHCP_ignoreMode = bit.bor(LHCP_ignoreMode,LHCP_IGNORE_EMOTES);
		LHCP_Print("LHCP character emotes muted.",0,1,0);
	end
end

function LHCP_ToggleAll()
	if LHCP_ignoreMode ~= 0 then
		LHCP_ignoreMode = 0;
		LHCP_Print("LHCP all unmuted.",0,1,0);
	else
		LHCP_ignoreMode = bit.bor(LHCP_ignoreMode,LHCP_IGNORE_SPAM,LHCP_IGNORE_ENEMY,LHCP_IGNORE_SOUNDS,LHCP_IGNORE_EMOTES,LHCP_IGNORE_NSFW,LHCP_IGNORE_RAID);
		LHCP_Print("LHCP all muted.",0,1,0);
	end
end

function LHCP_ShowStatus()
	if LHCP_GetMuted() then
		LHCP_Print("- Sounds muted.",0,1,0);
	end

	if LHCP_GetNSFWMuted() then
		LHCP_Print("- NSFW sounds muted.",0,1,0);
	end
	
	if not LHCP_GetEmotesAllowed() then
		LHCP_Print("- Character emotes muted.",0,1,0);
	end
end

function LHCP_EnableDebug()
	if(LHCP_debug) then
		LHCP_debug = false;
		LHCP_Print("Debug disabled.");
	else
		LHCP_debug = true;
		LHCP_Print("Debug enabled.");
	end
end

LHCP_cmds = {
	["list"] = LHCP_List,
	[""] = LHCP_main,
	["sound"] = LHCP_ToggleSound,
	["nsfw"] = LHCP_ToggleNSFW,
	["emote"] = LHCP_ToggleEmotes,
	["mute"] = LHCP_ToggleAll,
	["debug"] = LHCP_EnableDebug,
};

function lhcp_command(msg)
	local argv = {}
	for s in string.gmatch(msg,"%a+") do
		table.insert(argv,s);
	end
	
	local cmd = table.remove(argv,1);
	
	if not cmd then
		cmd = "";
	end
	
	local func = LHCP_cmds[cmd];
	
	if func ~= nil then
		func(unpack(argv));
	else
		LHCP_SayOutgoingEmote(cmd);
	end
end

-- our main slash command
SlashCmdList["LHCP"] = lhcp_command;
SLASH_LHCP1 = "/lhcp";
