--imports
local WIM = WIM;
local _G = _G;
local table = table;
local unpack = unpack;
local string = string;
local pairs = pairs;
local ipairs = ipairs;
local select = select;

--set namespace
setfenv(1, WIM);

local Emote = WIM.CreateModule("Emoticons", true);

-----------------------------------------------------
--                Emoticon Functions               --
-----------------------------------------------------

local LinkRepository = {}; -- used for emotes and link parsing.
local tmpList = {};

-- Dri: keeping the special table as a helper for authors poking into WIM code to more easily add chars for parsing, 
-- we could just as well construct a static specialstr for the replacement function manually and save us some string garbage.
local special = {"%", ":", "-", "^", "$", ")", "(", "]", "]", "~", "@", "#", "&", "*", "_", "+", "=", ",", ".", "?", "/", "\\", "{", "}", "|", "`", ";", "\"", "'"};
local specialrepl = "["
for i,token in ipairs(special) do
	specialrepl = specialrepl.."%"..token
end
specialrepl = specialrepl.."]"

local function convertEmoteToPattern(theEmote)
    theEmote = string.gsub(theEmote,specialrepl,"%%%0");
    return theEmote;
--[[    local i; -- Dri: ticket 379 this loop hits the in combat script execution time limit since 5.1, looped gsub is costly, do it all-in-one instead
    for i=1, #special do
        theEmote = string.gsub(theEmote, "%"..special[i], "%%"..special[i]);
    end
    return theEmote;]]
end

local function getEmoteFilePath(theEmote)
    local emoteTable = GetSelectedSkin().emoticons;

    local tmp = emoteTable.definitions[theEmote];
    -- if emote not found or if mal formed/linked emote, prevent infinate loop.
    if(not tmp or tmp == theEmote) then
        return "";
    else
        if(emoteTable.definitions[tmp]) then
            return getEmoteFilePath(tmp);
        else
            return tmp;
        end
    end
end


local function encodeColors(theMsg)
    theMsg = string.gsub(theMsg, "|c", "\001\002");
    theMsg = string.gsub(theMsg, "|r", "\001\003");
    return theMsg;
end

local function decodeColors(theMsg)
    theMsg = string.gsub(theMsg, "\001\002", "|c");
    theMsg = string.gsub(theMsg, "\001\003", "|r");
    return theMsg;
end

local function filterEmoticons(theMsg, smf)

    --safety check...
    if(not theMsg or theMsg == "") then
        return "";
    end
    
    --check if special formatting is not wanted
    if(smf and (smf.noEscapedStrings or smf.noEmoticons)) then
        return theMsg;
    end
	
	--remove player hyperlink from message to circumvent misbehavior as of 8.1
	local s,e = string.find(theMsg, ": ")
	local playerLink
	
	if e then
		playerLink = string.sub(theMsg, 1, e)
		theMsg = string.sub(theMsg, e+1)
	elseif string.find(theMsg, "^|K") then
		return theMsg
	end
	
	--safety check again...
    if(not theMsg or theMsg == "") then
        return playerLink or "";
    end
	
    --accomodate WoW's built in symbols and inherrit WoW's options whether to display them or not.
    if ( 1 ) then
	for tag in string.gmatch(theMsg, "%b{}") do
	    local term = string.lower(string.gsub(tag, "[{}]", ""));
	    if ( _G.ICON_TAG_LIST[term] and _G.ICON_LIST[_G.ICON_TAG_LIST[term]] ) then
		theMsg = string.gsub(theMsg, tag, _G.ICON_LIST[_G.ICON_TAG_LIST[term]] .. "0|t");
	    end
	end
    end

    local emoteTable = GetSelectedSkin().emoticons;
        
    -- first as to not disrupt any links, lets remove them and put them back later.
    local results, orig;
    orig = theMsg;
    -- clean out colors and wait to put back.
    local results;
    theMsg = encodeColors(theMsg);
    repeat
        theMsg, results = string.gsub(theMsg, "(|K[^|]+|k[^|]+|k)", function(theLink)
            table.insert(LinkRepository, theLink);
            return "\001\004"..#LinkRepository;
        end, 1);
    until results == 0;
    repeat
        theMsg, results = string.gsub(theMsg, "(|H[^|]+|h[^|]+|h)", function(theLink)
            table.insert(LinkRepository, theLink);
            return "\001\004"..#LinkRepository;
        end, 1);
    until results == 0;
    --restore color
    
    -- lets exchange emotes...
    local emote, img;
    for emote,_ in pairs(emoteTable.definitions) do
        img = getEmoteFilePath(emote);
        if(img and img ~= "") then
            theMsg = string.gsub(theMsg, convertEmoteToPattern(emote), "|T"..img..":"..emoteTable.width..":"..emoteTable.height..":"..emoteTable.offset[1]..":"..emoteTable.offset[2].."|t");
        end
    end
        
    -- put all the links back into the string...
    for i=#LinkRepository, 1, -1 do
        theMsg = string.gsub(theMsg, "\001\004"..i.."", LinkRepository[i]);
    end
    
    -- clear table to be recycled by next process
    for key, _ in pairs(LinkRepository) do
        LinkRepository[key] = nil;
    end
    
    return decodeColors((playerLink or "")..theMsg);
end




function Emote:OnEnable()
    RegisterStringModifier(filterEmoticons, true);
end

function Emote:OnDisable()
    UnregisterStringModifier(filterEmoticons);
end


local function clearTempTable()
    for key, _ in pairs(tmpList) do
        tmpList[key] = nil;
    end
end


local function loadTable(tbl, ...)
    clearTempTable();
    for i=1, select("#", ...) do
        local item = select(i, ...);
        table.insert(tbl, item);
    end
end


-- Extended Global
function GetEmotes()
    clearTempTable();
    local emotes = GetSelectedSkin().emoticons.definitions;
    for k, v in pairs(emotes) do
       if(emotes[v] == nil) then
            table.insert(tmpList, k);
       end
    end
    return unpack(tmpList);
end

function GetEmoteAlias(emote)
    clearTempTable();
    local emotes = GetSelectedSkin().emoticons.definitions;
    for k, v in pairs(emotes) do
       if(string.lower(v) == string.lower(emote)) then
            table.insert(tmpList, k);
       end
    end
    return unpack(tmpList);
end

function GetEmoteTexture(emote)
    return getEmoteFilePath(emote);
end



-- define context menu
local emotesPerMenu = 14;

local info = _G.UIDropDownMenu_CreateInfo();
info.text = "MENU_MSGBOX";
local msgBoxMenu = AddContextMenu(info.text, info);
    info = _G.UIDropDownMenu_CreateInfo();
    info.text = WIM.L["Emoticons"];
    info.notCheckable = true;
    local emoticonsMenu = msgBoxMenu:AddSubItem(AddContextMenu("EMOTICON_LIST", info), 2);

local function emoteMenuClicked(self)
    _G.CloseDropDownMenus();
    if(MSG_CONTEXT_MENU_EDITBOX) then
        MSG_CONTEXT_MENU_EDITBOX:Insert(self.value);
    end
end

local emoteTmpList = {};
local function generateEmoticonList(self, button)
    if(button ~= "RightButton") then
        return;
    end
    local emoticonsMenu = GetContextMenu("EMOTICON_LIST");
    local tbl = emoticonsMenu.menuTable;
    --clear initial table
    if(tbl) then
        for k, _ in pairs(tbl) do
            tbl[k] = nil;
        end
    end
    
    loadTable(emoteTmpList, GetEmotes());
    local EMOTICON_MORE = 1;
    local info;
    for i=1, #emoteTmpList do
        if(i % emotesPerMenu == 0) then
            local more = GetContextMenu("EMOTICON_MORE"..EMOTICON_MORE) or _G.UIDropDownMenu_CreateInfo();
            more.text = "|cff69ccf0"..WIM.L["More"].."|r";
            more.notCheckable = true;
            if(more.menuTable) then
                for k, _ in pairs(more.menuTable) do
                    more.menuTable[k] = nil;
                end
            end
            emoticonsMenu:AddSubItem(GetContextMenu("MENU_SPACE"), 1);
            emoticonsMenu:AddSubItem(AddContextMenu("EMOTICON_MORE"..EMOTICON_MORE, more), 1);
            emoticonsMenu = GetContextMenu("EMOTICON_MORE"..EMOTICON_MORE);
            EMOTICON_MORE = EMOTICON_MORE + 1;
        end

        info = GetContextMenu("EMOTICON_"..emoteTmpList[i]) or _G.UIDropDownMenu_CreateInfo();
        info.text = "|T"..getEmoteFilePath(emoteTmpList[i])..":16:16:0:0|t   "..emoteTmpList[i];
        info.tooltipTitle = info.text
        loadTable(tmpList, GetEmoteAlias(emoteTmpList[i]))
        info.tooltipText = table.concat(tmpList, "\n");
        if(info.tooltipText and info.tooltipText ~= "") then
            info.tooltipText = "|cff69ccf0"..L["Also"]..":|r\n"..info.tooltipText
        end
        info.notCheckable = true;
        info.value = emoteTmpList[i];
        info.func = emoteMenuClicked;
        emoticonsMenu:AddSubItem(AddContextMenu("EMOTICON_"..emoteTmpList[i], info));
    end
    for key, _ in pairs(emoteTmpList) do
        emoteTmpList[key] = nil;
    end
    clearTempTable();
end

RegisterWidgetTrigger("msg_box", "whisper,chat,w2w", "OnMouseDown", generateEmoticonList);
    
    
