%% ---------------------------------------------------------------------------
%% @doc mod_chat_cache.erl

%% @author  hjh
%% @email  hejiahua@163.com
%% @since  2016-12-13
%% @deprecated 聊天缓存
%% ---------------------------------------------------------------------------
-module(mod_chat_cache).

-behaviour(gen_server).

-export([
    save_cache/4
    , send_cache/2
    , send_cache/3
    , delete_cache/2
    , delete_guild_cache/1
    , delete_public_cache_with_uid/1
    , simu_chat/2
    ]).

-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-include("common.hrl").
-include("chat.hrl").
-include("def_fun.hrl").
-include("server.hrl").
-include("figure.hrl").

-define(PRIV_CACHE_MAX_LEN, 200).       % 私人聊天缓存的最大长度
-define(PRIV_CACHE_CLEAR_MAX_LEN, 400). % 私人聊天缓存清理的最大长度 ?PRIV_CACHE_CLEAR_MAX_LEN的值要大于?PRIV_CACHE_MAX_LEN 避免达到最大长度之后每次都要去掉之前的聊天记录
-define(PRIV_CACHE_SINGLE_LEN, 20).     % 私人聊天单人最大的长度

-define(MAX_CACNE_LEN, 30).             % 除私聊频道外其他频道缓存最大长度

%% 使用进程字典保存数据
%% 进程字典每个CacheType保存的数据结构： [{RoleId, Unixtime, BinData}, ...]

make_record(msg_record, [Channel, SenderServerId, SenderSerNum, SenderServerName, [{SenderId, SenderFigure}, {ReceiveId, ReceiveFigure}], MsgSend, Args, ArgsResult, Utime]) ->
    #msg_record{
        channel = Channel, sender_id = SenderId, sender_figure = SenderFigure, sender_server_id = SenderServerId,
        sender_server_num = SenderSerNum, sender_server_name = SenderServerName, receive_id = ReceiveId,
        receive_figure = ReceiveFigure, msg_send = MsgSend, args = Args, args_result = ArgsResult, utime = Utime
    };
make_record(msg_record, [Channel, SenderServerId, SenderSerNum, SenderServerName, SenderId, SenderFigure, MsgSend, Args, ArgsResult, Utime]) ->
    #msg_record{
        channel = Channel, sender_id = SenderId, sender_figure = SenderFigure, sender_server_id = SenderServerId,
        sender_server_num = SenderSerNum, sender_server_name = SenderServerName, msg_send = MsgSend, args = Args,
        args_result = ArgsResult, utime = Utime
    };
make_record(msg_record, [Channel, SenderServerId, SenderSerNum, SenderServerName, [{SenderId, SenderFigure}, {ReceiveId, ReceiveFigure}], ClientVer, ClientAutoId, ReceiveId, MsgSend, VoiceMsgTime, Utime]) ->
    #msg_record{
        channel = Channel, sender_id = SenderId, sender_figure = SenderFigure, sender_server_id = SenderServerId,
        sender_server_num = SenderSerNum, sender_server_name = SenderServerName, receive_id = ReceiveId,
        receive_figure = ReceiveFigure, msg_send = MsgSend, utime = Utime, client_ver = ClientVer,
        client_auto_id = ClientAutoId, voice_msg_time = VoiceMsgTime
    };
make_record(msg_record, [Channel, SenderServerId, SenderSerNum, SenderServerName, [{SenderId, SenderFigure}], ClientVer, ClientAutoId, ReceiveId, MsgSend, VoiceMsgTime, Utime]) ->
    #msg_record{
        channel = Channel, sender_id = SenderId, sender_figure = SenderFigure, sender_server_id = SenderServerId,
        sender_server_num = SenderSerNum, sender_server_name = SenderServerName, receive_id = ReceiveId, msg_send = MsgSend,
        utime = Utime, client_ver = ClientVer, client_auto_id = ClientAutoId, voice_msg_time = VoiceMsgTime
    }.

%% 保存聊天的缓存
save_cache(CacheType, SenderId, ReceiveId, PackList) ->
    MesRecord = make_record(msg_record, PackList),
    NotNullMsg = MesRecord#msg_record.msg_send =/= [],
    case is_valid_cache_type(CacheType) andalso NotNullMsg of
        true  -> gen_server:cast(?MODULE, {'save_cache', CacheType, SenderId, ReceiveId, PackList});
        false -> ignore
    end.

%% 发送聊天的缓存
send_cache(CacheType, RoleId) ->
    case is_valid_cache_type(CacheType) of
        true  -> gen_server:cast(?MODULE, {'send_cache', CacheType, RoleId});
        false -> ignore
    end.

%% 请求跨服发送聊天的缓存
send_cache(CacheType, Node, RoleId) ->
    case is_valid_cache_type(CacheType) of
        true  -> gen_server:cast(?MODULE, {'send_cache', CacheType, Node, RoleId});
        false -> ignore
    end.

%% 成功触发模拟聊天
simu_chat(RoleId, Classify) ->
    ?MODULE ! {simu_chat, RoleId, Classify, 1}.

%%--------------------------------------------------
%% 删除缓存
%% @param  CacheType  ?CHAT_CHANNEL_WORLD|?CHAT_CHANNEL_PRIVATE|{?CHAT_CHANNEL_GUILD, GuildId}
%% @param  SpecifyIds 指定要删除相关内容的玩家id列表,[]表示直接删除这个聊天频道的聊天缓存
%% @return            description
%%--------------------------------------------------
delete_cache(CacheType, SpecifyIds) ->
    case is_valid_cache_type(CacheType) of
        true  -> gen_server:cast(?MODULE, {'delete_cache', CacheType, SpecifyIds});
        false -> ignore
    end.

delete_guild_cache(GuildIds) ->
    [delete_cache({?CHAT_CHANNEL_GUILD, GuildId}, []) || GuildId <- GuildIds, GuildId > 0].

delete_priv_cache_by_sender_id(SenderIds) ->
    gen_server:cast(?MODULE, {'delete_priv_cache_by_sender_id', SenderIds}).

%% 删除聊天缓存里面指定玩家的数据
delete_public_cache_with_uid([]) -> ignore;
delete_public_cache_with_uid(RoleIdList) when is_list(RoleIdList) ->
    delete_cache(?CHAT_CHANNEL_WORLD, RoleIdList),
    IdListString = util:link_list(RoleIdList),
    SqlGetGuildInfoList = io_lib:format("SELECT p.id,IFNULL(g.guild_id,0) FROM player_login p LEFT JOIN guild_member g ON p.id = g.id WHERE p.id IN (~s)", [IdListString]),
    InfoList = db:get_all(SqlGetGuildInfoList),
    [
        delete_cache({?CHAT_CHANNEL_GUILD, GuildId}, [Id || [Id, GuildIdX] <- InfoList, GuildIdX =:= GuildId])
        || [_, GuildId] <- InfoList, GuildId > 0
    ],
    delete_priv_cache_by_sender_id(RoleIdList),
    ok;
delete_public_cache_with_uid(_) -> error.

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) ->
    % process_flag(trap_exit, true),
    {ok, #cache_state{}}.

handle_call(Request, _From, State) ->
    case catch do_handle_call(Request, _From, State) of
        {'EXIT', _R} ->
            ?ERR("Error:~p~n", [_R]),
            {reply, ok, State};
        Res ->
            Res
    end.

handle_cast(Msg, State) ->
    case catch do_handle_cast(Msg, State) of
        {'EXIT', _R} ->
            ?ERR("Error:~p~n", [_R]),
            {noreply, State};
        Res ->
            Res
    end.

handle_info(Info, State) ->
    case catch do_handle_info(Info, State) of
        {'EXIT', _R} ->
            ?ERR("Error:~p~n", [_R]),
            {noreply, State};
        Res ->
            Res
    end.

do_handle_call(_Request, _From, State)->
    {reply, ok, State}.

%% 模拟聊天
do_handle_info({simu_chat, RoleId, Classify, Order}, State) ->
    Res =
        case data_chat_simulation:get_chat_content(Classify, Order) of
            #base_chat_simulation_content{id = RobotId, content = Content, space_time = SpaceTime} ->
                case data_chat_simulation:get_chat_robot(RobotId) of
                    #base_chat_simulation_robot{
                        name = Name,
                        vip = Vip,
                        sex = Sex,
                        picture = Picture,
                        lv = Lv
                    } ->
                        Figure = #figure{name = Name, vip = Vip, sex = Sex, profile_photo_id = Picture, lv = Lv},
                        Now = utime:unixtime(),
                        PackData = [?CHAT_CHANNEL_WORLD, config:get_server_id(), 0, "", 0, Figure, Content, util:term_to_string([]), ?CHAT_ARGS_RESULT_NO, Now, 0, 0],
                        BinData = lib_chat:make_data(PackData),
                        Log = #chat_simulation_log{name = Name,
                            sex = Sex,
                            vip = Vip,
                            picture = Picture, msg = Content, time = Now, lv = Lv},
                        {BinData, Log, SpaceTime};
                    _ ->
                        error
                end;
            _ ->
                error
        end,
    case Res of
        {Bin, AddLog,  NextTime} ->
            lib_server_send:send_to_uid(RoleId, Bin),
            add_simu_chat_log(RoleId, AddLog),
            NextTime > 0 andalso
                erlang:send_after(NextTime*1000, self(), {simu_chat, RoleId, Classify, Order+1});
        _ ->
            ok
    end,
    {noreply, State};

do_handle_info(_Info, State) ->
    {noreply, State}.

%% 保存离线私聊的缓存
do_handle_cast({'save_cache', ?CHAT_CHANNEL_PRIVATE, SenderId, ReceiveId, PackList}, State) ->
    #cache_state{priv_cache_map = CacheMap} = State,
    PrivMsg = make_record(msg_record, PackList),
    Map = maps:get(ReceiveId, CacheMap, #{}),
    #priv_cache{len = Len, msg = MsgList} = maps:get(SenderId, Map, #priv_cache{}),
    NewMsgList = [PrivMsg|MsgList],
    NewLen = Len + 1,
    case NewLen > ?PRIV_CACHE_SINGLE_LEN of
        true ->
            LastLen = ?PRIV_CACHE_SINGLE_LEN,
            LastMsgList = lists:droplast(NewMsgList);
        false ->
            LastLen = NewLen,
            LastMsgList = NewMsgList
    end,
    NewMap = maps:put(SenderId, #priv_cache{len = LastLen, msg = LastMsgList}, Map),
    % 清理过多的缓存
    F = fun(_, TCache, {Sum, Acc}) ->
        #priv_cache{len = TLen, msg = TMsgL} = TCache,
        {Sum + TLen, [TMsgL|Acc]}
    end,
    {TotalLen, AllMsgL} = maps:fold(F, {0, []}, NewMap),
    case TotalLen > ?PRIV_CACHE_CLEAR_MAX_LEN of
        true ->
            AllMsgListSort = lists:reverse(lists:keysort(#msg_record.utime, lists:flatten(AllMsgL))),
            AllMsgListSub = lists:sublist(AllMsgListSort, ?PRIV_CACHE_MAX_LEN),
            F2 = fun(#msg_record{sender_id = TmpSenderId} = TmpPrivMsg, TmpMap) ->
                #priv_cache{len = TmpLen, msg = TmpMsgList} = maps:get(TmpSenderId, TmpMap, #priv_cache{}),
                maps:put(TmpSenderId, #priv_cache{len = TmpLen + 1, msg = [TmpPrivMsg|TmpMsgList]}, TmpMap)
            end,
            LastMap = lists:foldr(F2, #{}, AllMsgListSub);
        false ->
            LastMap = NewMap
    end,
    NewCacheMap = CacheMap#{ReceiveId => LastMap},
    NewState = State#cache_state{priv_cache_map = NewCacheMap},
    {noreply, NewState};
do_handle_cast({'save_cache', CacheType, SenderId, _ReceiveId, PackList}, State) ->
    Unixtime = utime:unixtime(),
    case get(CacheType) of
        CacheList when is_list(CacheList) ->
            NewCacheList = lists:sublist([{SenderId, Unixtime, make_record(msg_record, PackList)} | CacheList], ?MAX_CACNE_LEN);
        _ ->
            NewCacheList = [{SenderId, Unixtime, make_record(msg_record, PackList)}]
    end,
    put(CacheType, NewCacheList),
    {noreply, State};

%% 获得聊天的缓存
do_handle_cast({'send_cache', ?CHAT_CHANNEL_PRIVATE, RoleId}, State) ->
    #cache_state{priv_cache_map = CacheMap} = State,
    Map = maps:get(RoleId, CacheMap, #{}),
    F = fun(_, TCache, Acc) ->
        #priv_cache{msg = TMsgL} = TCache,
        [TMsgL|Acc]
    end,
    AllMsgL = maps:fold(F, [], Map),
    MsgListFlatten = lists:flatten(AllMsgL),
    F1 = fun(#msg_record{channel = Channel, sender_server_id = SenderSerId, sender_server_num = SenderSerNum, sender_server_name = SenderServerName, sender_id = SenderId, sender_figure = SenderFigure,
                           receive_id = ReceiveId, receive_figure = ReceiveFigure, msg_send = MsgSend, client_ver = ClientVer, client_auto_id = ClientAutoId, voice_msg_time = VoiceMsgTime,
                           args = Args, args_result = ArgsResult, utime = Utime}) ->
                {Channel, [{SenderSerId, SenderSerNum, SenderServerName, SenderId, SenderFigure}, {SenderSerId, SenderSerNum, SenderServerName, ReceiveId, ReceiveFigure}], MsgSend, Args, ArgsResult, Utime, ClientVer, ClientAutoId, VoiceMsgTime}
                % {Channel, [{SenderSerId, SenderSerNum, SenderServerName, SenderId, SenderFigure}, {SenderSerId, SenderSerNum, SenderServerName, ReceiveId, ReceiveFigure}], MsgSend, Args, ArgsResult, Utime}
        end,
    List = lists:map(F1, MsgListFlatten),
    {ok, BinData} = pt_110:write(11010, [List]),
    lib_server_send:send_to_uid(RoleId, BinData),
    NewCacheMap = maps:remove(RoleId, CacheMap),
    NewState = State#cache_state{priv_cache_map = NewCacheMap},
    {noreply, NewState};
do_handle_cast({'send_cache', CacheType, RoleId}, State) ->
    case get(CacheType) of
        CacheList when is_list(CacheList) -> skip;
        _ -> CacheList = []
    end,
    % NewCacheList = lists:keysort(2, CacheList),
    F = fun({_RoleId, _Time, MsgRecord}) ->
        #msg_record{
            channel = Channel, sender_id = SenderId, sender_figure = SenderFigure, sender_server_id = SenderServerId,
            sender_server_num = SenderSerNum, sender_server_name = SenderServerName, msg_send = MsgSend, args = Args,
            args_result = ArgsResult, utime = Utime, client_ver = ClientVer, client_auto_id = ClientAutoId,
            voice_msg_time = VoiceMsgTime
        } = MsgRecord,
        {Channel, [{SenderServerId, SenderSerNum, SenderServerName, SenderId, SenderFigure}], MsgSend, Args, ArgsResult, Utime, ClientVer, ClientAutoId, VoiceMsgTime}
        % {Channel, [{SenderServerId, SenderSerNum, SenderServerName, SenderId, SenderFigure}], MsgSend, Args, ArgsResult, Utime}
    end,
    send_simu_chat_cache(CacheType, RoleId, CacheList),
    SendList = [F(T) || T <- CacheList],
    {ok, BinData} = pt_110:write(11010, [SendList]),
    lib_server_send:send_to_uid(RoleId, BinData),
    {noreply, State};
do_handle_cast({'send_cache', CacheType, Node, RoleId}, State) ->
    case get(CacheType) of
        CacheList when is_list(CacheList) -> skip;
        _ -> CacheList = []
    end,
    % NewCacheList = lists:keysort(2, CacheList),
    F = fun({_RoleId, _Time, MsgRecord}) ->
        #msg_record{
            channel = Channel, sender_id = SenderId, sender_figure = SenderFigure, sender_server_id = SenderServerId,
            sender_server_num = SenderSerNum, sender_server_name = SenderServerName, msg_send = MsgSend, args = Args,
            args_result = ArgsResult, utime = Utime, client_ver = ClientVer, client_auto_id = ClientAutoId,
            voice_msg_time = VoiceMsgTime
        } = MsgRecord,
        {Channel, [{SenderServerId, SenderSerNum, SenderServerName, SenderId, SenderFigure}], MsgSend, Args, ArgsResult, Utime, ClientVer, ClientAutoId, VoiceMsgTime}
    end,
    SendList = [F(T) || T <- CacheList],
    {ok, BinData} = pt_110:write(11010, [SendList]),
    clusters_center:apply_cast(Node, lib_server_send, send_to_uid, [RoleId, BinData]),
    {noreply, State};

%% 删除聊天的缓存
do_handle_cast({'delete_cache', ?CHAT_CHANNEL_PRIVATE, RoleIdList}, #cache_state{priv_cache_map = CacheMap} = State) ->
    NewCacheMap = maps:without(RoleIdList, CacheMap),
    NewState = State#cache_state{priv_cache_map = NewCacheMap},
    {noreply, NewState};
do_handle_cast({'delete_cache', CacheType, SpecifyIds}, State) ->
    case is_list(SpecifyIds) andalso SpecifyIds =/= [] of
        true ->
            case get(CacheType) of
                CacheList when is_list(CacheList) ->
                    NewCacheList = [{SenderId, Time, MsgRecord} || {SenderId, Time, MsgRecord} <- CacheList, not lists:member(SenderId, SpecifyIds)],
                    put(CacheType, NewCacheList);
                _ ->
                    skip
            end;
        false -> erase(CacheType)
    end,
    {noreply, State};
do_handle_cast({'delete_priv_cache_by_sender_id', SenderIds}, #cache_state{priv_cache_map = CacheMap} = State) ->
    F = fun(_RoleId, OneCacheMap) ->
        maps:without(SenderIds, OneCacheMap)
    end,
    NewCacheMap = maps:map(F, CacheMap),
    NewState = State#cache_state{priv_cache_map = NewCacheMap},
    {noreply, NewState};

do_handle_cast(_Msg, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.


%% -----------------------------------------------
%% 内部函数
%% -----------------------------------------------

is_valid_cache_type(CacheType) ->
    case CacheType of
        ?CHAT_CHANNEL_WORLD -> true;
        ?CHAT_CHANNEL_PRIVATE -> true;
        {?CHAT_CHANNEL_GUILD, GuildId} when GuildId > 0 -> true;
        ?CHAT_CHANNEL_WORLD_KF -> true;
        _ -> false
    end.

add_simu_chat_log(RoleId, Log) ->
    case get({chat_simulation, RoleId}) of
        Logs when is_list(Logs) ->
            put({chat_simulation, RoleId}, lists:sublist([Log|Logs], ?MAX_CACNE_LEN));
        _ ->
            put({chat_simulation, RoleId}, [Log])
    end.

get_simu_chat_log(RoleId) ->
    case get({chat_simulation, RoleId}) of
        Logs when is_list(Logs) ->
            Logs;
        _ ->
            []
    end.

del_simu_chat_log(RoleId) ->
    erase({chat_simulation, RoleId}).

%% 发送模拟聊天缓存
send_simu_chat_cache(?CHAT_CHANNEL_WORLD, RoleId, CacheList) ->
    Logs = get_simu_chat_log(RoleId),
    case Logs of
        [#chat_simulation_log{time = SimuTime}|_] ->
            %% 世界聊天的最早一条数据
            WorldChatTime = case length(CacheList) >= ?MAX_CACNE_LEN of
                                true  ->
                                    {_, Time, _} = lists:last(CacheList),
                                    Time;
                                false ->
                                    0
                            end,
            %% 模拟聊天的最新一条时间还早于世界聊天，则模拟聊天可以删除了
            case SimuTime < WorldChatTime of
                true  ->
                    del_simu_chat_log(RoleId);
                false ->
                    FormatLogs =
                    [{Name, Sex, Vip, Picture, Msg, Time, Lv} ||
                        #chat_simulation_log{
                            name = Name,
                            sex = Sex,
                            vip = Vip,
                            picture = Picture,
                            msg = Msg,
                            time = Time,
                            lv = Lv
                    } <- Logs],
                    lib_server_send:send_to_uid(RoleId, pt_110, 11023, [FormatLogs])
            end;
        _ ->
            ok
    end;
send_simu_chat_cache(_, _, _) ->
    ok.