%%%--------------------------------------
%%% @Module  : pp_chat
%%% @Author  : xyao
%%% @Email   : jiexiaowen@gmail.com
%%% @Created : 2010.05.06
%%% @Description:  聊天功能
%%%                内容驱动，内容可以推送到任意频道
%%%--------------------------------------
-module(pp_chat).
-export([handle/3, check_auto_banned/3]).

-include("server.hrl").
-include("chat.hrl").
-include("figure.hrl").
-include("common.hrl").
-include("def_module.hrl").
-include("role.hrl").
-include("def_vip.hrl").
-include("def_event.hrl").
-include("errcode.hrl").
-include("guild.hrl").
-include("game_vsn.hrl").

-define(VOICE_MAX_SIZE, 95030).       %% 语音信息最大数 65535*1.5
-define(PICTURE_MAX_SIZE, 204800).    %% 图片最大大小   200K

%% 发送文字信息
handle(11001, Status, [Channel, ReceiveId, Msg, ArgsStr, TkTime, Ticket, IsSystemMsg]) when is_list(Msg), is_list(ArgsStr) ->
    lib_chat:chat(Status, Channel, ReceiveId, Msg, ArgsStr, TkTime, Ticket, IsSystemMsg);

%% 发送语音聊天
handle(11003, Status, [Channel, ReceiveId, VoiceMsgTime, TkTime, Ticket, DataSend, VoiceText, ClientAutoId, IsEnd]) ->
    #player_status{
        server_id = SerId,
        server_num = SerNum,
        server_name = ServerName,
        id    = Id,
        figure = Figure,
        sid = Sid,
        client_ver = ClientVer,
        talk_lim_type = TalkLimType
      } = Status,
    IsCanSend
        = case IsEnd == 1 of
              true ->
                  case lib_chat:check_talk_condition(Status, Channel, TkTime, Ticket) of
                      {fail, ErrorCode} -> lib_chat:send_error_code(Sid, ErrorCode);
                      true ->
                          case lib_chat:check_send_msg(Status, Channel) of
                              {false, ErrorCode} -> lib_chat:send_error_code(Sid, ErrorCode);
                              _ -> true
                          end
                  end;
              false -> true
          end,
    case IsEnd of
        0 -> %% 语音还没有传送完毕, 先存储一部分到玩家进程，等待下一部分
            {NewDataSend, NewVoiceText}
                = case get(process_dict_voice_data) of
                      undefined ->
                          {DataSend, VoiceText};
                      {LastSendData, LastVoiceText} ->
                          {list_to_binary([LastSendData, DataSend]), list_to_binary([LastVoiceText, VoiceText])}
                  end,
            case byte_size(NewDataSend) > ?VOICE_MAX_SIZE orelse byte_size(NewVoiceText) > ?VOICE_MAX_SIZE of
                true -> %% 语音已经超过限制
                    erase(process_dict_voice_data),
                    %% ?INFO("process_dict_voice_data 0 NewDataSend:~p ~n", [byte_size(NewDataSend)]),
                    ok;
                false ->
                    put(process_dict_voice_data, {NewDataSend, NewVoiceText}),
                    %% ?INFO("process_dict_voice_data 0 NewDataSend:~p ~n", [byte_size(NewDataSend)]),
                    %% 告诉客户端语音分部传送成功,可以传下一分部了
                    {ok, BinData} = pt_110:write(11012, [1]),
                    lib_server_send:send_to_sid(Sid, BinData)
            end;
        1 when IsCanSend==true ->  %% 语音传送完毕
            case get(process_dict_voice_data) of
                undefined ->
                    FinDataSend     = DataSend,
                    FinVoiceText    = VoiceText;
                {LastSendData, LastVoiceText} ->
                    FinDataSend     = list_to_binary([LastSendData,  DataSend]),
                    FinVoiceText    = list_to_binary([LastVoiceText, VoiceText])
            end,

            erase(process_dict_voice_data),
            %% ?INFO("process_dict_voice_data 1 FinDataSend:~p ~n", [byte_size(FinDataSend)]),
            %% [Channel, [{Id, Figure}, {ReceiveId, ReceiveFigure}], MsgSend, util:term_to_string(Args), ArgsResult, utime:unixtime()],
            mod_chat_voice:send_voice(Id, ClientAutoId, FinDataSend, FinVoiceText),
            lib_player_event:async_dispatch(Id, ?EVENT_CHAT_VOICE),
            case Channel == ?CHAT_CHANNEL_PRIVATE andalso ReceiveId > 0 of
                true ->
                    case lib_role:get_role_show(ReceiveId) of
                        [] -> ReceiveFigure = #figure{};
                        #ets_role_show{figure = ReceiveFigure} -> ok
                    end,
                    % PackData = [Channel, SerId, SerNum, ServerName, [{Id, Figure}, {ReceiveId, ReceiveFigure}], FinVoiceText, util:term_to_string([]), ?CHAT_ARGS_RESULT_NO, utime:unixtime()],
                    PlayerList = [{Id, Figure}, {ReceiveId, ReceiveFigure}];
                false ->
                    % PackData = [Channel, SerId, SerNum, ServerName, Id, Figure, FinVoiceText, util:term_to_string([]), ?CHAT_ARGS_RESULT_NO, utime:unixtime()],
                    PlayerList = [{Id, Figure}]
            end,
            List = [Channel, SerId, SerNum, ServerName, PlayerList, ClientVer, ClientAutoId, ReceiveId, FinVoiceText, VoiceMsgTime, utime:unixtime(), 0, 0],
            PackData = [Channel, SerId, SerNum, ServerName, PlayerList, ClientVer, ClientAutoId, ReceiveId, FinVoiceText, VoiceMsgTime, utime:unixtime()],
            {ok, BinData} = pt_110:write(11003, List),
            case lib_chat:check_talk_lim(Status, Channel, TkTime, Ticket) of
                true when Channel == ?CHAT_CHANNEL_WORLD_KF orelse Channel == ?CHAT_CHANNEL_COUNTRY -> mod_chat_agent:send_msg_to_kf(self, Id, PackData);
                true when TalkLimType /= ?TALK_LIMIT_TYPE_6 -> lib_server_send:send_to_sid(Sid, BinData);
                _ ->
                    %% 发送语音
                    {ok, NewStatus} = lib_chat:send_msg(?CHAT_CONTENT_VOICE, Channel, Status, ReceiveId, PackData, BinData),
                    %% 私聊增加聊天监控
                    ChatTo =
                    if Channel =:= ?CHAT_CHANNEL_PRIVATE ->
                            case lib_role:get_role_show(ReceiveId) of
                                #ets_role_show{figure = ReceiveFigure1} ->
                                    #figure{
                                        name = ChatToName
                                        ,accname = ChatToAccName
                                        ,lv = ChatToLevel
                                        ,vip = ChatToVipLevel
                                        ,power = ChatToPower
                                        ,career = ChatToCareer
                                    } = ReceiveFigure1,
                                    #chat_to{
                                        id = ReceiveId
                                        ,name = ChatToName
                                        ,level = ChatToLevel
                                        ,vip_level = ChatToVipLevel
                                        ,accname = ChatToAccName
                                        ,power = ChatToPower
                                        ,career = ChatToCareer
                                    };
                                _ ->
                                    #chat_to{id = ReceiveId}
                            end;
                        true ->
                            #chat_to{}
                    end,
                    chat_monitor_agent:role_chat(NewStatus, Channel, unicode:characters_to_binary("语音消息"), utime:unixtime(), ChatTo),
                    {ok, NewStatus}
            end;
        _ ->
            erase(process_dict_voice_data),
            ok
    end;

%% 获取语音信息
handle(11004, Status, [ClientAutoId, PlayerId, TkTime, Ticket]) ->
    #player_status{sid=Sid} = Status,
    case util:check_char_encrypt(PlayerId, TkTime, Ticket) of
        false -> skip;
        true -> mod_chat_voice:get_voice_data(PlayerId, ClientAutoId, Sid)
    end;

%% 上传语音翻译文字信息
handle(11005, Status, [Channel, ReceiveId, ClientAutoId, VoiceTextData]) ->
    #player_status{id=Id, sid=Sid} = Status,
    mod_chat_voice:send_voice_text(Id, ClientAutoId, Sid, VoiceTextData),
    lib_chat:send_voice_text(Status, Channel, ReceiveId, ClientAutoId, VoiceTextData),
    ok;

%% 获取语音文字内容
handle(11006, Status, [ClientAutoId, PlayerId]) ->
    #player_status{id=_Id, sid=Sid} = Status,
    mod_chat_voice:get_voice_text_data(PlayerId, ClientAutoId, Sid),
    ok;

%% 查询聊天缓存
handle(11010, Status, [Channel]) ->
    #player_status{id = RoleId, guild = GuildStatus} = Status,
    #status_guild{id = GuildId} = GuildStatus,
    if
        Channel == ?CHAT_CHANNEL_WORLD ->
            mod_chat_cache:send_cache(Channel, RoleId);
        Channel == ?CHAT_CHANNEL_GUILD andalso GuildId > 0 ->
            mod_chat_cache:send_cache({Channel, GuildId}, RoleId);
        Channel == ?CHAT_CHANNEL_PRIVATE ->
            mod_chat_cache:send_cache(Channel, RoleId);
%%        Channel == ?CHAT_CHANNEL_WORLD_KF ->
%%            clusters_node:apply_cast(mod_chat_cache, send_cache, [Channel, node(), RoleId]);
        true ->
            {ok, Bin} = pt_110:write(11010, [[]]),
            lib_server_send:send_to_uid(RoleId, Bin)
    end;

%% 世界/跨服聊天的次数
handle(11011, Status, [Channel]) ->
    lib_chat:send_channel_left_count(Channel, Status);

%% 小红点
handle(11016, Status, [ModuleId, Type]) ->
    lib_chat:send_red_point(Status, ModuleId, Type);

%% 帮派求助 update 191106
%handle(11022, Status, [_BossType, _KillId, _KillName]) ->
    %#player_status{id = RoleId, server_id = _ServerId, scene = Scene, x = X, y = Y, figure = Figure} = Status,
    %#figure{guild_id = GuildId} = Figure,
    %InBoss = lib_boss:is_in_all_boss(Scene),
    %if
    %    Figure#figure.guild_id == 0 -> skip;
    %    InBoss ->
    %        {ok,Bin} = pt_110:write(11022,[RoleId, Figure, Scene, util:ceil(X/60), util:ceil(Y/30)]),
    %        lib_server_send:send_to_guild(GuildId, Bin);
    %    true ->
    %        skip
    %end;

%% 禁言
handle(11040, Status, [Uid, LimitTime]) ->
    case Status#player_status.figure#figure.gm of
        1-> %是GM
            Result = lib_chat:forbid_chat([Uid], LimitTime, 1);
        _-> %非GM
            Result = 0
    end,
    {ok, BinData} = pt_110:write(11040, [Result]),
    lib_server_send:send_to_sid(Status#player_status.sid, BinData);

%% 解除禁言
handle(11041, Status, [Uid]) ->
    case Status#player_status.figure#figure.gm of
        1-> %是GM
            Result = lib_chat:release_chat([Uid], 1);
        _-> %非GM
            Result = 0
    end,
    {ok, BinData} = pt_110:write(11041, [Result]),
    lib_server_send:send_to_sid(Status#player_status.sid, BinData);

%% 获取禁言信息
handle(11043, Status, [Uid]) ->
    case mod_chat_agent:lookup(Status#player_status.id) of
        [] ->
            ok;
        [_Player] ->
            [Talk_lim, _Talk_lim_time, _Talk_lim_right] = lib_chat:get_talk_lim(Uid),
            {ok, BinData} = pt_110:write(11043, [Talk_lim]),
            lib_server_send:send_to_sid(Status#player_status.sid, BinData)
    end;

%% 聊天举报(客户端没处理)
handle(11044, Status, [TargetId]) ->
    Now = utime:unixtime(),
    Time = lib_chat:get_time(?CHAT_CHANNEL_INFORM),
    case Now - Time < ?CHAT_GAP_INFORM of
        true ->
            skip;
        false ->
            case mod_chat_agent:lookup(Status#player_status.id) of
                [] ->
                    ok;
                [_Player] ->
                    lib_chat:put_time(?CHAT_CHANNEL_INFORM, Time),
                    %% 是否为好友，是的话返回true
                    Is_friends = lib_relationship:is_friend_on_dict(Status#player_status.id, TargetId),
                    case Is_friends of
                        true -> ReturnCode = 2;
                        false ->
                            case lib_chat:is_chat_forbid(TargetId) of
                                true ->
                                    ReturnCode = 1;
                                false ->
                                    Count = mod_chat_forbid:inform_chat(Status#player_status.id, TargetId),
                                    case Count>?ALLOW_INFORM_NUM andalso lib_chat:is_chat_forbid(TargetId) =/=true of
                                        true  -> lib_chat:forbid_chat([TargetId],2,4);
                                        false -> skip
                                    end,
                                    ReturnCode = 1
                            end
                    end,
                    {ok, BinData} = pt_110:write(11044, [ReturnCode]),
                    lib_server_send:send_to_sid(Status#player_status.sid, BinData)
            end
    end;

%% 聊天举报(客户端没处理)
%% mod_server设置每2秒才能处理一次该协议
handle(11045, Status, [TargetId, TargetData]) when is_list(TargetData)->
    ESC_CHARS = ["'" , "|", "\""],    %% 影响SQL语句的非法字符
    NewTargetData1 = re:replace(TargetData, ESC_CHARS, "*", [global, {return, list}]),
    NewTargetData2 = case length(NewTargetData1) =< 300 of
        true -> NewTargetData1;
        false -> []
    end,
    %% 防止在线多次举报同一个玩家
    case get(talk_report_list) of
        undefined -> TalkReportList = [];
        TalkReportList -> ok
    end,
    case mod_chat_agent:lookup(TargetId) of
        [] -> Res = 0;
        [_Player] ->
            IsReported = lists:member(TargetId, TalkReportList),
            case Status#player_status.id /= TargetId andalso IsReported == false of
                true ->
                    TargetData_Bin = list_to_binary(NewTargetData2),
                    SQL1 = io_lib:format(?SQL_TALK_REPORT, [Status#player_status.id, TargetId, TargetData_Bin]),
                    db:execute(SQL1),
                    SQL2 = io_lib:format(<<"update player_login set talk_reported_count = talk_reported_count + 1 where `id` = ~p">>,
                        [TargetId]),
                    db:execute(SQL2),
                    NewTalkReportList = lists:sublist([TargetId|TalkReportList], 50),   %% 最多保留五十个id
                    put(talk_report_list, NewTalkReportList),
                    Res = 1;
                false -> Res = 0
            end
    end,
    {ok, BinData} = pt_110:write(11045, [Res]),
    lib_server_send:send_to_sid(Status#player_status.sid, BinData);

%% 发送公告
handle(11050, Status, []) ->
    lib_chat:chat_11050_help(Status#player_status.sid);

%%%% 获取屏蔽列表
handle(11065, Status, []) ->
   lib_chat_limit:get_role_shield(Status);

handle(11066, Status, [Type, RoleId]) ->
   lib_chat_limit:shield_role(Type, RoleId, Status);

%% 发送图片
handle(11082, Status, [Channel, ReceiveId, IsEnd, TkTime, Ticket, TinyPicture, RealPicture]) ->
    #player_status{
       id          = Id,
       figure      = Figure,
       sid         = Sid
      } = Status,

    case IsEnd of
        0 -> %% 图片还没有传送完毕, 先存储一部分到玩家进程，等待下一部分
            {NewTinyPicture, NewRealPicture}
                = case get(process_dict_picture_data) of
                      undefined ->
                          {TinyPicture, RealPicture};
                      {PreTinyPicture, PreRealPicture} ->
                          {list_to_binary([PreTinyPicture, TinyPicture]), list_to_binary([PreRealPicture, RealPicture])}
                  end,

            case byte_size(NewTinyPicture) > ?PICTURE_MAX_SIZE orelse byte_size(NewRealPicture) > ?PICTURE_MAX_SIZE of
                true -> %% 图片已经超过限制
                    erase(process_dict_picture_data),
                    ok;
                false ->
                    put(process_dict_picture_data, {NewTinyPicture, NewRealPicture}),
                    %% 告诉客户端图片分部传送成功,可以传下一分部了
                    {ok, BinData} = pt_110:write(11083, 1),
                    lib_server_send:send_to_sid(Sid, BinData)
            end;

        1 ->  %% 图片传送完毕
            case get(process_dict_picture_data) of
                undefined ->
                    FinTinyPicture = TinyPicture,
                    FinRealPicture = RealPicture;
                {PreTinyPicture, PreRealPicture} ->
                    FinTinyPicture = list_to_binary([PreTinyPicture, TinyPicture]),
                    FinRealPicture = list_to_binary([PreRealPicture, RealPicture])
            end,

            erase(process_dict_picture_data),

            case lib_chat:check_talk_condition(Status, Channel, TkTime, Ticket) of
                false -> skip;
                true  ->
                    TmpAutoId = urand:rand(1, 1),  % 需求需要再进行改动
                    NewAutoId = case TmpAutoId >= 99 of
                                    true  -> 1;
                                    false -> TmpAutoId + 1
                                end,
                    SaveAutoId = Id*100 + NewAutoId,
                    put(process_dict_auto_picture_id, NewAutoId),
                    Data  = [Channel, Id, Figure, SaveAutoId, ReceiveId, FinTinyPicture],
                    mod_chat_voice:send_picture(SaveAutoId, FinRealPicture),
                    {ok, BinData} = pt_110:write(11082, Data),
                  %  mod_daily:set_count(Status#player_status.dailypid, Id, 7000202, NewAutoId),
                    case lib_chat:check_talk_lim(Status, Channel, TkTime, Ticket) of
                        true  -> lib_server_send:send_to_sid(Sid, BinData);
                        false -> lib_chat:send_msg(?CHAT_CONTENT_PICTURE, Channel, Status, ReceiveId, Data, BinData)
                    end
            end;
        _ -> %% 容错,无视错误的标志位
            skip
    end;

%% 获取图片内容
handle(11083, Status, [AutoId, TkTime, Ticket]) ->
    #player_status{id=Id, sid=Sid} = Status,
    case util:check_char_encrypt(Id, TkTime, Ticket) of
        false ->
            skip;
        true ->
            mod_chat_voice:get_picture_data(AutoId, Sid)
    end;

handle(_Cmd, _Status, _Data) ->
    {error, "pp_chat no match"}.


%% 检查自动禁言，并确定是否可以发送消息
%% -> true | false. true表示可以发送
check_auto_banned(Id, Data, _DataSend) ->
    ContainBannedWord = mod_word:word_is_sensitive_banned(Data),
    case ContainBannedWord of
        true  ->
            OldBannedWordCount =
                case erlang:get(banned_word_count) of
                    Num when is_integer(Num) -> Num;
                    _Undefined -> 0
                end,
            NewBannedWordCount = OldBannedWordCount + 1,
            erlang:put(banned_word_count, NewBannedWordCount),
            IsLimitVersion = check_lim_game_version(),
            case NewBannedWordCount >= 2 andalso IsLimitVersion of
                true  ->
                    % 是否要修改为同一设备下所有角色都禁言呢？（另外，是否要把这个计数保存到数据库呢？看策划大爷的意见）
                    lib_chat:forbid_chat([Id], 7, ?TALK_LIMIT_TYPE_2),
                    false;
                false -> true
            end;
        false -> true
    end.

%% 检测是否是限制版本
check_lim_game_version() ->
    GameVersion = data_vsn:get_game_vsn(),
    GameVersion =:= ?VER_TEST orelse GameVersion =:= ?VER_MAIN.
