--[[
Name: Prat 3.0 (chatsections.lua)
Revision: $Revision: 79217 $
Author(s): Sylvaanar (sylvanaar@mindspring.com)
Inspired By: Prat 2.0, Prat, and idChat2 by Industrial
Website: http://files.wowace.com/Prat/
Documentation: http://www.wowace.com/wiki/Prat
Forum: http://www.wowace.com/forums/index.php?topic=6243.0
SVN: http://svn.wowace.com/wowace/trunk/Prat/
Description: Implements the chat string sectioning service
]]

--[[ BEGIN STANDARD HEADER ]]--

-- Imports
local _G = _G
local LibStub = LibStub

local setmetatable = setmetatable
local pairs, ipairs = pairs, ipairs
local tinsert, tremove, tconcat = table.insert, table.remove, table.concat
local string = string
local strsub = string.sub 
local strsplit = strsplit
local tonumber, tostring = tonumber, tostring
local strlower, strupper = strlower, strupper
local strlen = strlen
local type = type
local next, wipe = next, wipe

--local function RunOldMessageEventFilters(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)
--	local filter = false
--	local chatFilters = _G.ChatFrame_GetMessageEventFilters and _G.ChatFrame_GetMessageEventFilters(event)
--    local newarg1 = arg1
--
--	if chatFilters then
--		for _, filterFunc in next, chatFilters do
--			filter, newarg1 = filterFunc(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)
--			if filter then
--				return true
--			end
--			arg1 = newarg1 or arg1
--		end
--	end
--
--    return filter, arg1
--end


-- arg1, filterthisout = RunMessageEventFilters(event, arg1)
local newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10, newarg11, newarg12, newarg13, newrg14, newarg15
local function RunMessageEventFilters(frame, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15)
	local filter = false
	local chatFilters = _G.ChatFrame_GetMessageEventFilters and _G.ChatFrame_GetMessageEventFilters(event)

	if chatFilters then
		for _, filterFunc in next, chatFilters do
			filter, newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10, newarg11, newarg12, newarg13, newrg14, newarg15 =
                    filterFunc(frame, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15)
			if filter then
				return true
			elseif ( newarg1 ) then
				arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15 =
                    newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10, newarg11, newarg12, newarg13, newrg14, newarg15
			end
		end
	end

    return filter, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15
end



-- Isolate the environment
setfenv(1, SVC_NAMESPACE)

--[[ END STANDARD HEADER ]]--


-- This is the structure of the chat message once it is split
-- section delimiters are uppercase inside and lower case outside
-- ie.   cC CHANNEL Cc =  [ channame ]
SplitMessageSrc = {  -- todo, incidicate which module uses which field, and remove unused fields
    PRE = "",

	nN = "",
	CHANLINK = "",
	NN = "",

    cC = "",
        CHANNELNUM = "",
        CC = "",
        CHANNEL = "",
        zZ = "",
            ZONE = "",
        Zz = "",
    Cc = "",

    TYPEPREFIX = "",

	Nn = "",

    fF = "",
        FLAG = "",
    Ff = "",

    pP = "",
        lL = "",  -- link start
        PLAYERLINK= "",
        PLAYERLINKDATA = "",
        LL = "", --  link text start
        PLAYER = "",

        sS = "",
            SERVER = "",
        Ss = "",
        Ll = "",  -- link text end
    Pp = "",

    TYPEPOSTFIX = "",

    mM = "",
        gG = "",
        LANGUAGE = "",
        Gg = "",
        MOBILE = "",
        MESSAGE = "",
    Mm = "",

    POST = "",
}

SplitMessageIdx = {
    "PRE",
	"nN",
	"CHANLINK",
	"NN",
    "cC",
        "CHANNELNUM",
        "CC",
        "CHANNEL",
-- Zone is not usually included
--      "zZ",
--          "ZONE",
--      "Zz",

    "Cc",
    "TYPEPREFIX",
	"Nn",

    "fF",
        "FLAG",
    "Ff",
    "pP",
        "lL",
            "PLAYERLINK",
            "PLAYERLINKDATA",
        "LL",
        "PLAYER",
        "sS",
            "SERVER",
        "Ss",
        "Ll",
    "Pp",
    "TYPEPOSTFIX",
    "mM",
        "gG",
        "LANGUAGE",
        "Gg",
        "MOBILE",
        "MESSAGE",
    "Mm",
    "POST",
}

SplitMessage = {}
SplitMessageOrg = {}

SplitMessageOut = {
    MESSAGE = "",
    TYPE = "",
    TARGET = "",
    CHANNEL = "",
    LANGUAGE = "",
}

setmetatable(SplitMessageOrg, { __index=SplitMessageSrc }) 


setmetatable(SplitMessage, { __index=SplitMessageOrg })


do 
	local t = {}
	function BuildChatText(message, index)
	    local index = index or SplitMessageIdx  --todo
	    local s = message
	
	    for k in pairs(t) do
	        t[k] = nil
	    end
	
	    for i,v in ipairs(index) do
            tinsert(t, s[v])
	    end
	
	    return tconcat(t, "")
	end
end

function RegisterMessageItem(itemname, anchorvar, relativepos)
	--[[ RegisterMessageItem:

		API to allow other modules to inject new items into the components
		making up a chat message. Primarily intended to help resolve
		conflicts between modules.

		 - itemname  = name of the variable to be injected

		 - aftervar  = the position in the chat message after which the item
		               will be displayed

		 - relativepos = "before" or "after"
		Leave aftervar blank to position the item at the beginning of the list.

		If you would like to change the item's position in the chat message,
		call :RegisterMessageItem() again with a different value for aftervar.

		Example:
		--------

		The mod Prat_ExampleMod counts the number of times people
		say the word "Example" and you would like to display the count
		for a player before their name in a chat message. Default chat
		message structure contains:

			... cC CHANNEL Cc .. pP PLAYER Pp ...

		This means that the module should use the following:

			RegisterMessageItem('NUMEXAMPLES', 'Cc')

		Which would then alter the structure of chat messages to be:

			.. CHANNEL Cc .. NUMEXAMPLES .. pP PLAYER ...

]]

	local pos = 1

	if SplitMessageSrc[itemname] then
--		ResetSeparators(itemname)

		local oldpos = GetMessageItemIdx(itemname)

		if oldpos ~= 0 then
			tremove(SplitMessageIdx, oldpos)
		end
	end

	if anchorvar then 
		pos = GetMessageItemIdx(anchorvar) + (relativepos == "before" and 0 or 1)
	end

	tinsert(SplitMessageIdx, pos, itemname)
	SplitMessageSrc[itemname] = ""
end


function GetMessageItemIdx(itemname)
	for i, v in ipairs(SplitMessageIdx) do
		if v == itemname then
			return i
		end
	end

	return 0
end


function ClearChatSections(message)
    if message then wipe(message) end
--    for k,v in pairs(message) do
--        message[k] = SplitMessageSrc[k] and nil -- WTF?
--    end
end

local function safestr(s) return s or "" end

function SplitChatMessage(frame, event, ...)
	local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15 = ...

	ClearChatSections(SplitMessageOrg)
	ClearChatSections(SplitMessage)

	if ( strsub((event or ""), 1, 8) == "CHAT_MSG" ) then
        local type = strsub(event, 10)
        local info = _G.ChatTypeInfo[type]

        local s = SplitMessageOrg

        -- blizzard bug, arg2 (player name) can have an extra space
        if arg2 then
            arg2=arg2:trim()
        end

	    s.GUID = arg12

--[===[@debug@ 
        s.ARGS = { ... }

        if CHAT_PLAYER_GUIDS then    
    		if s.GUID and s.GUID:len() > 0 then
    			s.GUIDINFO = { _G.GetPlayerInfoByGUID(s.GUID) }
    		end        
        end
--@end-debug@]===]

--        if NEW_CHATFILTERS then
            local kill, newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10, newarg11, newarg12, newarg13, newarg14, newarg15 =
                    RunMessageEventFilters(frame, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15)
            if kill then
                return true
            end
            if newarg1 ~= nil then
                arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15 =
                    newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10, newarg11, newarg12, newarg13, newarg14, newarg15
            end
--        else
--            local kill, newarg1 = RunOldMessageEventFilters(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)
--            if kill then
--                return true
--            end
--
--            arg1 = newarg1 or arg1
--        end


		if ((type == "PARTY_LEADER") and (_G.HasLFGRestrictions())) then
			type = "PARTY_GUIDE"
            event = "CHAT_MSG_PARTY_GUIDE"
		end

        s.CHATTYPE = type
        s.EVENT = event
        local chatGroup = _G.Chat_GetChatCategory(type)
        s.CHATGROUP = chatGroup
        
        
        
		local chatTarget;
        local presenceID
        if ( chatGroup == "CHANNEL" or chatGroup == "BN_CONVERSATION" ) then
            chatTarget = tostring(arg8);
        elseif ( chatGroup == "WHISPER" or chatGroup == "BN_WHISPER" ) then
            if(not(strsub(arg2, 1, 2) == "|K")) then
                chatTarget = strupper(arg2);
                s.presenceID = _G.BNet_GetPresenceID(arg2)
                --s.presenceID = presenceID and _G.BNIsSelf(presenceID)
            else
                chatTarget = arg2;
            end
        end
		

        s.CHATTARGET = chatTarget
        s.MESSAGE = safestr(arg1)
     
     
     	if ( _G.FCFManager_ShouldSuppressMessage(frame, s.CHATGROUP, s.CHATTARGET) ) then
			s.DONOTPROCESS = true
		end
     
		if ( chatGroup == "WHISPER" or chatGroup == "BN_WHISPER" ) then
			if ( frame.privateMessageList and not frame.privateMessageList[strlower(arg2)] ) then
				s.DONOTPROCESS = true
			elseif ( frame.excludePrivateMessageList and frame.excludePrivateMessageList[strlower(arg2)] ) then
				s.DONOTPROCESS = true
			end
		elseif ( chatGroup == "BN_CONVERSATION" ) then
			if ( frame.bnConversationList and not frame.bnConversationList[arg8] ) then
				s.DONOTPROCESS = true
			elseif ( frame.excludeBNConversationList and frame.excludeBNConversationList[arg8] ) then
				s.DONOTPROCESS = true
			end
		end     
     

        local chatget = _G["CHAT_"..type.."_GET"]


        if chatget then
            local chatlink = chatget:match("|H(channel:[^|]-)|h[^|]-|h")

            if chatlink then
                s.CHANLINK = chatlink
    			s.nN = "|H"
    			s.NN = "|h"
    			s.Nn = "|h"

                chatget = chatget:gsub("|Hchannel:[^|]-|h([^|]-)|h", "%1")
            end
 
            s.TYPEPREFIX, s.TYPEPOSTFIX = string.match(_G.TEXT(chatget), "(.*)%%s(.*)")
        end

        s.TYPEPOSTFIX = safestr(s.TYPEPOSTFIX)
        s.TYPEPREFIX = safestr(s.TYPEPREFIX)



        local arg2 = safestr(arg2)
        if strlen(arg2) > 0 then

        	if ( strsub(type, 1, 7) == "MONSTER" or type == "RAID_BOSS_EMOTE" or 
                    type == "CHANNEL_NOTICE" or type == "CHANNEL_NOTICE_USER") then
        		-- no link
        	else
               local plr, svr = arg2:match("([^%-]+)%-?(.*)")

                s.pP = "["
                s.lL = "|Hplayer:"
                
                s.PLAYERLINK = arg2
                
                s.LL = "|h"
                s.PLAYER = plr

                if svr and strlen(svr) > 0 then
                    s.sS = "-"
                    s.SERVER = svr
                end


            	if ( type ~= "BN_WHISPER" and type ~= "BN_WHISPER_INFORM" and type ~= "BN_CONVERSATION") or arg2 == _G.UnitName("player") --[[or presenceID]] then
                    s.PLAYERLINKDATA = ":"..safestr(arg11)..":"..chatGroup..(chatTarget and ":"..chatTarget or "")
    			else
    			    s.lL = "|HBNplayer:"
    				s.PLAYERLINKDATA = ":"..safestr(arg13)..":"..safestr(arg11)..":"..chatGroup..(chatTarget and ":"..chatTarget or "")
                    s.PRESENCE_ID = arg13
    			end

                s.Ll = "|h"
                s.Pp = "]"
            end
        end

        -- If we are handling notices, format them like bliz
        if (type == "CHANNEL_NOTICE_USER") then
			local globalstring = _G["CHAT_"..arg1.."_NOTICE_BN"];
            local chatnotice
			if  globalstring then
                chatnotice = globalstring:gsub("|Hchannel:CHANNEL[^|]-|h[^|]-|h", ""):trim();
            else
                globalstring = _G["CHAT_"..arg1.."_NOTICE"];
                chatnotice = globalstring:gsub("|Hchannel:[^|]-|h[^|]-|h", ""):trim()
			end


			if strlen(arg5) > 0 then
				-- TWO users in this notice (E.G. x kicked y)
				s.MESSAGE = chatnotice:format(arg2, arg5)
			elseif ( arg1 == "INVITE" ) then
				s.MESSAGE = chatnotice:format(arg4, arg2)
			else
				s.MESSAGE = chatnotice:format(arg2)
			end
		elseif type == "CHANNEL_NOTICE" then
			local globalstring = _G["CHAT_"..arg1.."_NOTICE_BN"];
			if ( not globalstring ) then
				globalstring = _G["CHAT_"..arg1.."_NOTICE"];
			end
            
			if ( arg10 > 0 ) then
				arg4 = arg4.." "..arg10;
			end

            if _G["CHAT_"..arg1.."_NOTICE"] then
                if arg1 == "YOU_JOINED" or arg1 == "YOU_LEFT"  or arg1 == "YOU_CHANGED" or arg1 == "SUSPENDED" or arg1 == "NOT_IN_LFG" then
        			s.MESSAGE =  _G["CHAT_"..arg1.."_NOTICE"]:format(arg8, arg4):trim()                
                else
        			s.MESSAGE =  _G["CHAT_"..arg1.."_NOTICE"]:gsub("|Hchannel:[^|]-|h[^|]-|h", ""):trim()
                end
            end
		end

        local arg6 = safestr(arg6)
        if strlen(arg6) > 0 then
            s.fF = ""

			-- 2.4 Change
			if arg6 == "GM" then 
				s.FLAG = "|TInterface\\ChatFrame\\UI-ChatIcon-Blizz.blp:0:2:0:-3|t "
			elseif ( arg6 == "DEV" ) then
				--Add Blizzard Icon, this was sent by a Dev
				s.FLAG = "|TInterface\\ChatFrame\\UI-ChatIcon-Blizz.blp:0:2:0:-3|t ";
            else
	            s.FLAG = _G["CHAT_FLAG_"..arg6]
			end

            s.Ff = ""
        end

        if arg15 then
            s.MOBILE = ChatFrame_GetMobileEmbeddedTexture(info.r, info.g, info.b)
        end

        local arg3 = safestr(arg3)
        if ( (strlen(arg3) > 0) and (arg3 ~= "Universal") and (arg3 ~= frame.defaultLanguage) ) then
            s.gG = "["
            s.LANGUAGE = arg3
            s.Gg = "] "
        else
            s.LANGUAGE_NOSHOW = arg3
        end

        local arg9 = safestr(arg9)
        if strlen(arg9) > 0 or chatGroup == "BN_CONVERSATION" then
--            local bracket, post_bracket = string.match(s.TYPEPREFIX, "%[(.*)%](.*)")
--            bracket = safestr(bracket)
--            if strlen(bracket) > 0 then
--                s.cC = "["
--                s.Cc = "]"
--                s.CHANNEL = bracket
--                s.TYPEPREFIX = safestr(post_bracket)
--            end



            if strlen(safestr(arg8)) > 0 and arg8 > 0 then
                s.CC = ". "

    			s.nN = "|H"
    			s.NN = "|h"
    			s.Nn = "|h"
                
                
     			if chatGroup  == "BN_CONVERSATION" then
     			    s.CHANLINK = "channel:BN_CONVERSATION:"..arg8
    			else
                    s.CHANNELNUM = tostring(arg8)
                    s.CHANLINK = "channel:channel:"..tostring(arg8)	
    			end                
            end

            if chatGroup  == "BN_CONVERSATION" then                
                s.cC = "["
                s.Cc = "] "
 			    s.CHANNELNUM = tostring(_G.MAX_WOW_CHAT_CHANNELS + arg8)
                s.CHANNEL = _G.CHAT_BN_CONVERSATION_SEND:match("%[%%d%. (.*)%]")
            elseif arg7 > 0 then
                s.cC = "["
                s.Cc = "] "
                s.CHANNEL, s.zZ, s.ZONE = string.match(arg9, "(.*)(%s%-%s)(.*)")

                if s.CHANNEL:len() == 0 then
                    s.CHANNEL = arg9
                end

                s.CHANNEL = safestr(s.CHANNEL)
                s.zZ = safestr(s.zZ)
                s.ZONE = safestr(s.ZONE)
                s.Zz = ""
            else
                if strlen(arg9) > 0 then
                    s.CHANNEL = arg9
                    s.cC = "["
                    s.Cc = "] "
                end
            end
        end


--		local _, fontHeight = _G.GetChatWindowInfo(frame:GetID());
--		
--		if ( fontHeight == 0 ) then
--			--fontHeight will be 0 if it's still at the default (14)
--			fontHeight = 14;
--		end



		local arg7 = tonumber(arg7)
 		-- 2.4
		-- Search for icon links and replace them with texture links.
		if arg7 and ( arg7 < 1 or ( arg7 >= 1 and _G.CHAT_SHOW_ICONS ~= "0" ) ) then
			local term;
			for tag in string.gmatch(arg1, "%b{}") do
				term = strlower(string.gsub(tag, "[{}]", ""));
				if ( _G.ICON_TAG_LIST[term] and _G.ICON_LIST[_G.ICON_TAG_LIST[term]] ) then
					s.MESSAGE  = string.gsub(s.MESSAGE , tag, _G.ICON_LIST[_G.ICON_TAG_LIST[term]] .. "0|t");
-- 
-- This would allow for ignoring unknown icon tags
--
--				else
--					s.MESSAGE = string.gsub(s.MESSAGE, tag, "");
				end
			end
		end

        if type == "SYSTEM" or strsub(type,1,11) == "ACHIEVEMENT" or strsub(type,1,18) == "GUILD_ACHIEVEMENT" then
			if strsub(type,1,11) == "ACHIEVEMENT" or strsub(type,1,18) == "GUILD_ACHIEVEMENT" then
				s.MESSAGE = s.MESSAGE:format("")
			end
            local pl, p, rest = string.match(s.MESSAGE, "|Hplayer:(.-)|h%[(.-)%]|h(.+)")
            if pl and p then
                local plr, svr = pl:match("([^%-]+)%-?(.*)")
                s.pP = "["
                s.lL = "|Hplayer:"
                s.PLAYERLINK = pl
                s.LL = "|h"
                s.PLAYER = plr
                s.Ll = "|h"
                s.Pp = "]"
                s.MESSAGE = rest

                if svr and strlen(svr) > 0 then
                    s.sS = "-"
                    s.SERVER = svr
                end

                if arg11 then
                    s.PLAYERLINKDATA = ":"..safestr(arg11)
                end
            end
        end

        s.ACCESSID = _G.ChatHistory_GetAccessID(chatGroup, chatTarget);
        s.TYPEID = _G.ChatHistory_GetAccessID(type, chatTarget);
        
        s.ORG = SplitMessageOrg

        return SplitMessage, info
    end
end

local NULL_INFO = {r = 1.0, g = 1.0, b = 1.0, id = 0 }



