-module(mod_pottymouth).

-behaviour(gen_mod).

-include("logger.hrl").
-include("xmpp.hrl").

-export([
  start/2,
  stop/1,
  on_filter_packet/1,
  mod_opt_type/1,
  depends/2,
  reload/3,
  mod_options/1
]).

-import(bloom_gen_server, [start/0, stop/0, member/1]).
-import(nomalize_leet_gen_server, [normalize/1]).

getMessageLang(Msg) ->
  LangAttr = xmpp:get_lang(Msg),
  if
    (LangAttr /= <<>>) ->
      Lang = list_to_atom(binary_to_list(LangAttr));
    true ->
      Lang = default
  end,
  Lang.

censorWord({Lang, Word} = _MessageTerm) ->
  % we need unicode characters to normlize the word
  NormalizedWord = normalize_leet_gen_server:normalize({Lang, unicode:characters_to_list(list_to_binary(Word))}),
  % we need bytewise format for bloom lookup
  IsBadWord = bloom_gen_server:member({Lang, binary_to_list(unicode:characters_to_binary(NormalizedWord))}),
  if
    IsBadWord ->
      "****";
    true ->
      Word
  end.

filterWords(L) ->
  lists:map(fun censorWord/1, L).

filterMessageText(Lang, MessageText) ->
    try filterMessageText2(Lang, MessageText) of
        R ->
            R
    catch exit:{noproc,{gen_server,call,[_,_]}} ->
	?DEBUG("Blacklist of language '~p' not found, using 'default' list.", [Lang]),
	filterMessageText2(default, MessageText)
    end.

filterMessageText2(Lang, MessageText) ->
  % we want to token-ize utf8 'words'
  MessageWords = string:tokens(unicode:characters_to_list(MessageText, utf8), " "),
  MessageTerms = [{Lang, Word} || Word <- MessageWords],
  % we get back bytewise format terms (rather than utf8)
  string:join(filterWords(MessageTerms), " ").

start(_Host, Opts) ->
  Blacklists = gen_mod:get_opt(blacklists, Opts, fun(A) -> A end, []),
  lists:map(fun bloom_gen_server:start/1, Blacklists),
  CharMaps = gen_mod:get_opt(charmaps, Opts, fun(A) -> A end, []),
  lists:map(fun normalize_leet_gen_server:start/1, CharMaps),
  ejabberd_hooks:add(filter_packet, global, ?MODULE, on_filter_packet, 0),
  ok.

stop(Host) ->
  Blacklists = gen_mod:get_module_opt(Host, ?MODULE, blacklists),
  lists:map(fun bloom_gen_server:stop/1, Blacklists),
  CharMaps = gen_mod:get_module_opt(Host, ?MODULE, charmaps),
  lists:map(fun normalize_leet_gen_server:stop/1, CharMaps),
  ejabberd_hooks:delete(filter_packet, global, ?MODULE, on_filter_packet, 0),
  ok.

on_filter_packet(drop) ->
  drop;

on_filter_packet(Msg) ->
  Type = xmpp:get_type(Msg),
  if 
    (Type == chat) orelse (Type == groupchat)  ->
      BodyText = xmpp:get_text(Msg#message.body),
      if
        (BodyText /= <<>>) ->
          Lang = getMessageLang(Msg),
          FilteredMessageWords = binary:list_to_bin(filterMessageText(Lang, binary:bin_to_list(BodyText))),
          [BodyObject|_] = Msg#message.body,
          NewBodyObject = setelement(3, BodyObject, FilteredMessageWords),
          NewMsg = Msg#message{body = [NewBodyObject]},
          NewMsg;
        true ->
          Msg
      end;
    true -> 
      Msg
  end.

mod_opt_type(blacklists) -> fun (A) when is_list(A) -> A end;
mod_opt_type(charmaps) -> fun (A) when is_list(A) -> A end;
mod_opt_type(_) -> [blacklists, charmaps].
depends(_Host, _Opts) -> [].
reload(_Host, _NewOpts, _OldOpts) -> ok.
mod_options(_) ->
  [{blacklists, []},{charmaps, []}].
