%% -------------------------------------------
%% desc         :守卫信标支援榜&历史最佳记录展示
%% module       :guard_dun_assist_local.erl
%% author       :hh
%% time         :20/12/08
%% -------------------------------------------

-module(guard_dun_assist_local).

-behaviour (gen_server).

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

-export ([
    init/1
    ,update_guard_assist/3          %% 更新支援值
    ,weekly_reset/0                 %% 周更新
    ,get_assist_rank/2              %% 获取支援榜单

    ,get_invite_assist_rank_role/4  %% 获取支援榜上可邀请的人
    % ,invite_assist_role/6           %% 邀请支援榜上的人
    ]).

-define (DELAY_INIT_TIME,           5000).
-define (ASSIST_RANK_LEN,           25).

-include ("dungeon.hrl").
-include ("figure.hrl").
-include ("dun_guard.hrl").
-include ("predefine.hrl").
-include ("def_module.hrl").
-include ("errcode.hrl").
-include ("server.hrl").
-include ("common.hrl").
-include ("role.hrl").

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

init([]) ->
    erlang:send_after(?DELAY_INIT_TIME, self(), {init}),
    {ok, #guard_assist_data{}}.

%% 更新支援值
update_guard_assist(RoleId, Power, Val) ->
    gen_server:cast(?MODULE, {update_guard_assist, RoleId, Power, Val}).

%% 周更新
weekly_reset() ->
    gen_server:cast(?MODULE, {weekly_reset}).

%% 获取支援榜
get_assist_rank(Node, Sid) ->
    gen_server:cast(?MODULE, {get_assist_rank, Node, Sid}).

%% 获取可邀请的支援榜的人
get_invite_assist_rank_role(Node, RoleId, DunId, TeamId) ->
    gen_server:cast(?MODULE, {get_invite_assist_rank_role, Node, RoleId, DunId, TeamId}).

%% 邀请支援榜上的人
% invite_assist_role(DunId, TeamId, RoleId, Node, Figure, ToIdList) ->
%     gen_server:cast(?MODULE, {invite_assist_role, DunId, TeamId, RoleId, Node, Figure, ToIdList}).

handle_call(_Msg, _From, State) ->
    {reply, ok, State}.

handle_cast(Msg, State) ->
    try
        do_handle_cast(Msg, State)
    catch _:_Error ->
        io:format("handle_cast Msg:~p, Error:~p, ~p~n", [Msg, _Error, erlang:get_stacktrace()]),
        {noreply, State}
    end.

handle_info(Msg, State) ->
    try
        do_handle_info(Msg, State)
    catch _:_Error ->
        io:format("[~p]~nhandle_info Msg:~p~n Error:~p~n ~p~n", [erlang:localtime(), Msg, _Error, erlang:get_stacktrace()]),
        {noreply, State}
    end.

do_handle_cast({update_guard_assist, RoleId, Power, Val}, State) ->
    #guard_assist_data{assist_rank = RankData, map = Map} = State,
    % RankLimit = maps:get(rank_limit, Map, 0),
    % ?ERR("~p ~p ~p", [RoleId, Val, RankLimit]),
    NewState =
    case lists:keyfind(RoleId, #guard_assist_role.role_id, RankData) of
        GuardRole = #guard_assist_role{val = OldVal} ->
            NewVal = Val + OldVal,
            NewGuardRole = GuardRole#guard_assist_role{val = NewVal, power = Power},
            NewGuardRole =/= GuardRole andalso db_update_guard_assist_rank_role(NewGuardRole),
            RankData0 = lists:keyreplace(RoleId, #guard_assist_role.role_id, RankData, NewGuardRole),
            {NewRankData, NewMap} = update_assist_rank(RankData0, Map, false),
            State#guard_assist_data{assist_rank = NewRankData, map = NewMap};
        _ ->
            GuardRole = #guard_assist_role{
                role_id = RoleId, val = Val, power = Power
            },
            db_update_guard_assist_rank_role(GuardRole),
            RankData0 = lists:keystore(RoleId, #guard_assist_role.role_id, RankData, GuardRole),
            {NewRankData, NewMap} = update_assist_rank(RankData0, Map, false),
            State#guard_assist_data{assist_rank = NewRankData, map = NewMap}
    end,
    {noreply, NewState};

%% 周结算
do_handle_cast({weekly_reset}, State) ->
    #guard_assist_data{assist_rank = RankData, map = Map} = State,
    %% 执行结算等操作
    do_weekly_account(RankData),
    db:execute(?SQL_BATCH_DELETE_GUARD_ASSIST_RANK, []),
    NewMap = maps:put(rank_limit, 0, Map),
    NewState = State#guard_assist_data{assist_rank = [], map = NewMap},
    {noreply, NewState};

%% 获取支援榜
do_handle_cast({get_assist_rank, Node, Sid}, State) ->
    #guard_assist_data{assist_rank = RankData} = State,
    PackRankInfo = pack_assist_rank_data(RankData),
    {ok, BinData} = pt_611:write(61104, [PackRankInfo]),
    lib_server_send:send_to_sid(Node, Sid, BinData),
    {noreply, State};

%% 获取支援榜可邀请的人
do_handle_cast({get_invite_assist_rank_role, Node, RoleId, DunId, _TeamId}, State) ->
    #guard_assist_data{assist_rank = RankData} = State,
    InviteRoles =
    lists:foldl(fun(#guard_assist_role{role_id = RoleIdX, rank = Rank, val = Val}, Acc) ->
        #ets_role_show{online_flag = Online, figure = FigureX} = lib_role:get_role_show(RoleIdX), 
        if Online == ?ONLINE_ON andalso RoleId =/= RoleIdX ->
                [{RoleIdX, FigureX, Rank, Val}|Acc];
            true ->
                Acc
        end
    end, [], RankData),
    {ok, BinData} = pt_215:write(21526, [?SUCCESS, DunId, 2, InviteRoles]),
    lib_server_send:send_to_uid(Node, RoleId, BinData),
    {noreply, State};

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

do_handle_info({init}, State) ->
    #guard_assist_data{map = Map} = State,
    RankData =
    case db:get_all(?SQL_SELECT_GUARD_ASSIST_RANK, []) of
        Data1 = [_|_] ->
            change_data_format_2(Data1);
        _ ->
            []
    end,
    {NewRankData, NewMap} = update_assist_rank(RankData, Map, true),
    NewState = State#guard_assist_data{map = NewMap, assist_rank = NewRankData},
    {noreply, NewState};

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

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, _State, _Extra) ->
    ok.

change_data_format_2(Data) ->
    lists:foldl(fun([RoleId, _ServerId, _ServerNum, Val, Power], Acc) -> 
        Role = #guard_assist_role{role_id = RoleId, val = Val, power = Power},
        [Role|Acc]
    end, [], Data).

%% 更新副本协助玩家数据
db_update_guard_assist_rank_role(GuardAssistRole) ->
    #guard_assist_role{
        role_id = RoleId,
        val = Val,
        power = Power
    } = GuardAssistRole,
    db:execute(?SQL_REPLACE_GUARD_ASSIST_RANK, [RoleId, 0, 0, Val, Power]),
    ok.

%% 更新支援榜
update_assist_rank(RankData, Map, CleanDb) ->
    SortedRankData  = lists:sort(fun sort_fun/2, RankData),
    Len = length(SortedRankData),
    if Len > ?ASSIST_RANK_LEN ->
            {SortedRankData1, _DbCleanRank} = ulists:sublist(SortedRankData, ?ASSIST_RANK_LEN),
            [#guard_assist_role{val = MinVal}|_] = lists:reverse(SortedRankData1),
            SortedRankData2 = make_index(SortedRankData1),
            %% 删除多余数据
            CleanDb andalso db:execute(?SQL_DELETE_GUARD_ASSIST_RANK, [MinVal]),
            NewMap = maps:put(rank_limit, MinVal, Map),
            {SortedRankData2, NewMap};
        true ->
            SortedRankData1 = lists:reverse(SortedRankData),            
            case SortedRankData1 of
                [#guard_assist_role{val = MinVal}|_] -> ok;
                _   -> MinVal = 0
            end,
            SortedRankData2 = make_index(SortedRankData),
            NewMap = maps:put(rank_limit, MinVal, Map),
            {SortedRankData2, NewMap}
    end.

make_index(RankData) ->
    {_, NewRankData} = lists:foldl(fun(R, {TempIndex, TempRankData}) -> 
        {TempIndex+1, [R#guard_assist_role{rank = TempIndex}|TempRankData]}
    end, {1, []}, RankData),
    NewRankData.

%% 排序规则:从大到小排序,支援值相等则比较战力
sort_fun(R1, R2) ->
    #guard_assist_role{power = Power1, val = Val1} = R1,
    #guard_assist_role{power = Power2, val = Val2} = R2,
    if Val1 == Val2 -> Power1 >= Power2;
        true -> Val1 > Val2
    end.


%% 结算奖励
do_weekly_account(RankData) ->
    lists:map(fun(#guard_assist_role{rank = Rank, val = Val, role_id = RoleId}) -> 
        case data_dun_guard:get_assist_reward(Rank) of
            Rewards = [_|_] ->
                lib_dungeon_guard:send_weekly_account_mail(RoleId, Rank, Val, Rewards);
            _ ->
                skip
        end
    end, RankData).


%% 打包61104协议数据
pack_assist_rank_data(RankData) ->
    ServerNum = config:get_server_num(),
    lists:foldl(fun(Role, Acc) -> 
    #guard_assist_role{
        role_id = RoleId, val = Val, rank = Rank
    } = Role,
    NewFigure1 = lib_role:get_role_figure(RoleId),
    [{Rank, RoleId, NewFigure1, ServerNum, Val}|Acc]
    end, [], RankData).