

--[[ 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 select = select

--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, newarg16, newarg17
local function RunMessageEventFilters(frame, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17)
  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, newarg16, newarg17 =
      filterFunc(frame, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17)
      if filter then
        return true
      elseif (newarg1) then
        arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17 =
        newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10, newarg11, newarg12, newarg13, newrg14, newarg15, newarg16, newarg17
      end
    end
  end

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



-- Isolate the environment
setfenv(1, select(2, ...))

--[[ 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 = "",
  NONPLAYER = "",
  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",
  "NONPLAYER",
  "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, arg16, arg17 = ...

  ClearChatSections(SplitMessageOrg)
  ClearChatSections(SplitMessage)

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

    if (arg16) then
      -- hiding sender in letterbox: do NOT even show in chat window (only shows in cinematic frame)
      return true;
    end

    local s = SplitMessageOrg

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

    local channelLength = strlen(safestr(arg4));

    s.GUID = arg12

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

    if CHAT_PLAYER_GUIDS then
      if s.GUID and s.GUID:len() > 0 and s.GUID ~= "0000000000000000" 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 =
    RunMessageEventFilters(frame, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14)
    if kill then
      return true
    end
    if newarg1 ~= nil then
      arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14 =
      newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10, newarg11, newarg12, newarg13, newarg14
    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


    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_GetBNetIDAccount(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(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
        s.NONPLAYER = arg2
      else
         s.PLAYERLINK = arg2

          --ambiguate guild chat names
          if (type == "GUILD") then
              arg2 = _G.Ambiguate(arg2, "guild")
          else
              arg2 = _G.Ambiguate(arg2, "none")
          end

        local plr, svr = arg2:match("([^%-]+)%-?(.*)")

        s.pP = "["
        s.lL = "|Hplayer:"

        s.LL = "|h"
        s.PLAYER = plr

        if svr and strlen(svr) > 0 then
          s.sS = "-"
          s.SERVER = svr
        end
        s.Ll = "|h"
        s.Pp = "]"

        local playerLinkDisplayText = s.pP .. s.PLAYER .. s.Pp
        local isCommunityType = type == "COMMUNITIES_CHANNEL";
        local playerName, lineID, bnetIDAccount = arg2, arg11, arg13;
        if ( isCommunityType ) then
--          local isBattleNetCommunity = bnetIDAccount ~= nil and bnetIDAccount ~= 0;
--          local messageInfo, clubId, streamId, clubType = _G.C_Club.GetInfoFromLastCommunityChatLine();
--          if (messageInfo ~= nil) then
--            if ( isBattleNetCommunity ) then
--              playerLink = _G.GetBNPlayerCommunityLink(playerName, playerLinkDisplayText, bnetIDAccount, clubId, streamId, messageInfo.messageId.epoch, messageInfo.messageId.position);
--            else
--              playerLink = _G.GetPlayerCommunityLink(playerName, playerLinkDisplayText, clubId, streamId, messageInfo.messageId.epoch, messageInfo.messageId.position);
--            end
--          else
--            playerLink = playerLinkDisplayText;
--          end
        else
          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
        end


      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)
        if _G.GetLocale() == "koKR" then
          s.MESSAGE = chatnotice:format("", "", arg2, arg5)
        else
          s.MESSAGE = chatnotice:format(arg2, arg5)
        end
      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 = _G.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 = ""
      elseif chatGroup == "COMMUNITIES_CHANNEL" then
          s.cC = "["
          s.Cc = "] "
          s.CHANNEL = _G.ChatFrame_ResolvePrefixedChannelName(arg4):match("%d%.%s+(.+)")
      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.
    local term;

    for tag in string.gmatch(arg1, "%b{}") do
      term = strlower(string.gsub(tag, "[{}]", ""));
      if (not arg17 and _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, "");
      elseif (_G.GROUP_TAG_LIST[term]) then
        local groupIndex = _G.GROUP_TAG_LIST[term];
        local groupList = "[";
        for i = 1, _G.GetNumGroupMembers() do
          local name, rank, subgroup, level, class, classFileName = _G.GetRaidRosterInfo(i);
          if (name and subgroup == groupIndex) then
            local t = _G.RAID_CLASS_COLORS[classFileName];
            name = string.format("\124cff%.2x%.2x%.2x%s\124r", t.r * 255, t.g * 255, t.b * 255, name);
            groupList = groupList .. (groupList == "[" and "" or _G.PLAYER_LIST_DELIMITER) .. name;
          end
        end
        groupList = groupList .. "]";
        s.MESSAGE = string.gsub(s.MESSAGE, tag, groupList);
      end
    end



    if type == "SYSTEM" or strsub(type, 1, 11) == "ACHIEVEMENT" or strsub(type, 1, 11) == "TARGETICONS" 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

      if (strsub(type, 1, 18) == "GUILD_ACHIEVEMENT") then
        if (_G.C_Social.IsSocialEnabled()) then
          local achieveID = _G.GetAchievementInfoFromHyperlink(arg1);
          if (achieveID) then
            local isGuildAchievement = select(12, _G.GetAchievementInfo(achieveID));
            if (isGuildAchievement) then
              s.MESSAGE = s.MESSAGE .. " " .. _G.Social_GetShareAchievementLink(achieveID, true);
            end
          end
        end
      end

      if (strsub(type, 1, 11) == "ACHIEVEMENT") then
        if (arg12 == _G.UnitGUID("player") and _G.C_Social.IsSocialEnabled()) then
          local achieveID = _G.GetAchievementInfoFromHyperlink(arg1);
          if (achieveID) then
            s.MESSAGE = s.MESSAGE .. " " .. _G.Social_GetShareAchievementLink(achieveID, true);
          end
        end
      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, arg12 or arg13);

    s.ORG = SplitMessageOrg

    return SplitMessage, info
  end
end

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



