--[[ 
Minetest 0.4.16+ mod: chat2
chat2 is mod for Minetest, created by Andrey. It's purpose is to improve or replace game built-in chat:

  + Higlight some messages in different colors - nearby talk, PM messages, messages with your name in it, shouts(!)
  + Also can show all regular chat messages (use "/chat2 *" command)
  + Switch on/off (use "/chat2" command)
  + Old messages dissapear after some time
  - Problems with unicode symbols on some clients

This mod is Free and Open Source Software, released under the GNU LGPLv2.1 or later.
I used this mod:https://github.com/vegasd/minetest-mods/blob/master/kmchat/init.lua as example for how to use hud api.
--]]

chat2 = {}
chat2.speedlimit = {}
chat2.lastmessagetimes = {} --minetest.get_gametime()
chat2.users = {}
chat2.additionalfilters = {}    --additional words set by player, which need to be higligted
chat2.firsthud = {} --to be able to use previously set huds
chat2.messages_on_screen = 11
chat2.chat_width = 100  --symbols
chat2.position_x = 0.01
chat2.position_y = 0.92
chat2.line_distance = 15  --it depends on client, 15 is ok for where i have tested.

chat2.add_message = function(player, new_text, new_color)
    local temp_text
    local temp_color
    local hud
    local name = player:get_player_name()
    local firsthud = chat2.firsthud[name]
    if not player then
        minetest.log("action", "Player "..name.." - chat2 player error")
    end
    if (not chat2.firsthud[name]) or (not player:hud_get(chat2.firsthud[name]) )  then
        minetest.log("action", "Player "..name.." - chat2 no hud yet error")
        return
    end

    for id = firsthud, (firsthud + chat2.messages_on_screen - 1) do
        hud = player:hud_get(id)
        if hud and hud.name == "chat2" then
            temp_text = hud.text
            temp_color = hud.number
            player:hud_change(id, "number", new_color)
            player:hud_change(id, "text", new_text)
            new_text = temp_text
            new_color = temp_color
        end
    end
end

chat2.send_message = function(player, message, color)
    local line1 = nil   --allow message to span at most to three lines. more is not ok for public chat.
    local line2 = nil
    local line3 = nil
    local symbols = ''
    for i = 1, #message do
        if
            string.byte(message, i) == 32 and    --is space symbol
            string.len(symbols) > (chat2.chat_width - 8) and    --space have priority for breaking lines
            (
                not line1 or
                not line2 or
                not line3
            )
        then
            if not line1 then
                line1 = symbols
                symbols = ''
            elseif not line2 then
                line2 = symbols
                symbols = ''
            elseif not line3 then
                line3 = symbols
                symbols = ''
            end
        elseif
            (string.byte(message, i) < 128 or string.byte(message, i) >= 192) and  --is ascii or first byte of unicode
            string.len(symbols) > (chat2.chat_width - 1) and
            (
                not line1 or
                not line2 or
                not line3
            )
        then
            if not line1 then
                line1 = symbols
                symbols = ''
            elseif not line2 then
                line2 = symbols
                symbols = ''
            elseif not line3 then
                line3 = symbols
                symbols = ''
            end
        elseif line1 and line2 and line3 then   --stop when all three lines filled
            break
        end
        symbols = symbols..message:sub(i,i)
    end

    if not line1 and symbols then   --when message is shorten than line
        line1 = symbols
    elseif not line2 and symbols then   --when message is shorten than line
        line2 = symbols
    elseif not line3 and symbols then   --when message is shorten than line
        line3 = symbols
    end

    if line1 then
        chat2.add_message(player, line1, color)
    end
    if line2 then
        chat2.add_message(player, line2, color)
    end
    if line3 then
        chat2.add_message(player, line3, color)
    end

    if message ~= '' then
        chat2.lastmessagetimes[player:get_player_name()] = minetest.get_gametime()
    end
end

minetest.register_on_joinplayer(function(player)
    minetest.after(2, function(player)
        local name = player:get_player_name()
        for i = 1, chat2.messages_on_screen do
            local hud_id = player:hud_add({
                hud_elem_type = "text",
                text = "",
                position = {x = chat2.position_x, y = chat2.position_y},
                name = "chat2",
                scale = {x=500, y=50},
                number = 0xCCCCCC,
                alignment = {x=1, y=0},
                offset = {x=0, y= -i * chat2.line_distance}
            })
            if not chat2.firsthud[name] then
                chat2.firsthud[name] = hud_id
            end
        end
        chat2.users[name] = 1
        chat2.send_message(player, 'chat2 activated! Use /chat2 command to switch it off/on. Use /chat2 * to see all chat here.', 0xDDAA55)
        chat2.lastmessagetimes[name] = minetest.get_gametime()
        end, player)
end)

minetest.register_on_leaveplayer(function(player)
    local name = player:get_player_name()
    chat2.lastmessagetimes[name] = nil
    chat2.users[name] = nil
    chat2.firsthud[name] = nil
end)

minetest.register_on_chat_message(function(name, message)

    local fmt = nil
    local color = nil
    local submes = nil
    local player = minetest.get_player_by_name(name)
    local players = minetest.get_connected_players()

    if chat2.speedlimit[name]==nil then
        chat2.speedlimit[name] = true
    else
        return
    end

    submes = string.match(message, "^/(.+)")
    if submes then
        return
    end

    submes = string.match(message, "^!(.+)")
    if submes then
        fmt = "<%s> %s"
        color = 0xFF0000
        minetest.log("action", "chat2 !:"..string.format(fmt, name, submes))
    end

    local senderpos = player:get_pos()
    for i = 1, #players do
        local fmt_p = fmt
        local color_p = color
        local submes_p = submes
        local name_p = players[i]:get_player_name()

        --if not submes_p and chat2.users[name_p] and string.find(message, name_p, 1, true) ~= nil then
        if not submes_p and chat2.users[name_p] and string.find( string.lower(message), string.lower(name_p), 1, true) ~= nil then
            fmt_p = "<%s> %s"
            color_p = 0x00FF00
            submes_p = message
        end

        if not submes_p and chat2.users[name_p] and chat2.additionalfilters[name_p] then
            local additionalfound = false
            for n = 1, #chat2.additionalfilters[name_p] do
                --if additionalfound or name == chat2.additionalfilters[name_p][n] or string.find(message, chat2.additionalfilters[name_p][n], 1, true) ~= nil then
                if additionalfound or name == chat2.additionalfilters[name_p][n] or string.find( string.lower(message), string.lower(chat2.additionalfilters[name_p][n]), 1, true) ~= nil then
                    additionalfound = true
                end
            end
            if additionalfound then
                fmt_p = "<%s> %s"
                color_p = 0x55FF55
                submes_p = message
            end
        end

        if not submes_p and chat2.users[name_p] and name_p ~= name then
            recieverpos = players[i]:get_pos()
            if vector.distance(senderpos, recieverpos) < 12 then
                fmt_p = "<%s> %s"
                color_p = 0x88FFFF
                submes_p = message
            end
        end

        if not submes_p and chat2.users[name_p] == 2 then
            fmt_p = "<%s> %s"
            color_p = 0xFFFFFF
            submes_p = message
        end

        if submes_p and chat2.users[name_p] then
            chat2.send_message(players[i], string.format(fmt_p, name, submes_p), color_p)
        end
    end

    return
end)

if minetest.chatcommands["msg"] then
    local old_command = minetest.chatcommands["msg"].func
    minetest.chatcommands["msg"].func = function(name, param)
        local sendto, message = param:match("^(%S+)%s(.+)$")

        -- Check if old /msg was succeful
        local result, msg = old_command(name, param)

        if sendto and message and chat2.users[sendto] then
            local player = minetest.get_player_by_name(sendto)
            local sender = minetest.get_player_by_name(name)
            if result and player and sender then
                chat2.send_message(player, string.format("<%s> %s", name, message), 0xFF00FF)
                chat2.send_message(sender, string.format("<%s> %s", name, message), 0xF000F0)
                minetest.log("action", "chat2 msg:"..string.format("<%s> %s", name, message))
            end

        end
        return result, msg
    end
end

minetest.register_chatcommand("chat2", {
    params = "",
	description = "Turn chat2 off/on or add words to monitor or use /chat2 * to see all chat here",
	privs = {shout = true},
	func = function(name, param)
        local player = minetest.get_player_by_name(name)
        if not player then
            return false, "chat2: Player not found."
        end

        if chat2.users[name] == nil and param and #param > 0 then
            minetest.chat_send_player(name, 'First, please turn chat2 on.')
            return
        end

        if param == "*" then
            if chat2.users[name] == 1 then
                chat2.users[name] = 2
                minetest.chat_send_player(name, 'chat2 - receiving all')
                minetest.log("action", "chat2: receiving all "..name)
            else
                chat2.users[name] = 1
                minetest.chat_send_player(name, 'chat2 - receiving only special messages')
                minetest.log("action", "chat2: receiving only special messages "..name)
            end
            return
        end

        --user add additional search strings
        if param and #param > 0 then
            local parameters = {}
            if chat2.additionalfilters[name] and #chat2.additionalfilters[name] > 0 and #chat2.additionalfilters[name] < 14 then
                parameters = chat2.additionalfilters[name]
            end
            for s in string.gmatch(param, "%g+") do
                parameters[#parameters+1] = s:sub(0, 20)
            end
            if #parameters > 0 and #parameters < 15 then
                chat2.additionalfilters[name] = parameters
                minetest.chat_send_player(name, 'chat2 parameters set')
            else
                chat2.additionalfilters[name] = nil
                minetest.chat_send_player(name, 'chat2 parameters removed(too mutch)')
            end
            return
        end

        if chat2.users[name] ~= nil then
            for i = 1, chat2.messages_on_screen do
                chat2.send_message(player, '', 0x000000)
            end
            chat2.lastmessagetimes[name] = nil
            chat2.users[name] = nil
        else
            minetest.chat_send_player(name, 'chat2 activated! Use /chat2 command if you want to switch it off.')
            chat2.send_message(player, 'chat2 activated!', 0xDDAA55)
            chat2.lastmessagetimes[name] = minetest.get_gametime()
            chat2.users[name] = 1
        end
	end,
})

-- every 3 seconds clean speedlimit
local timer = 0
minetest.register_globalstep(function(dtime)
	timer = timer + dtime;
	if timer >= 3 then
        chat2.speedlimit = {}
		timer = 0

        --clean chat
        local players = minetest.get_connected_players()
        for i = 1, #players do
            local name = players[i]:get_player_name()
            if
                chat2.lastmessagetimes[name] and
                (minetest.get_gametime() - chat2.lastmessagetimes[name]) > 90
            then
                chat2.send_message(players[i], '', 0x000000)
                if (minetest.get_gametime() - chat2.lastmessagetimes[name]) > (90 + chat2.messages_on_screen * 3) then
                    chat2.lastmessagetimes[name] = nil
                end
            end
        end
	end
end)
