%% --------------------------------
%% desc:唤神副本积分排行
%% module:god_dun_rank_mgr.erl
%% author:hh
%% time:20/08/06
%% --------------------------------

-module(god_dun_rank_mgr).

-include("god_dun.hrl").
-include("common.hrl").
-include("def_module.hrl").


-behaviour(gen_server).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

%% API
-export([start_link/0]).

-export([
    upload_power_rank/1         %% 上传战力排行数据
    ,batch_upload_power_rank/1  %% 大批量上传战力排行数据
    ,timer_update/0             %% 周期更新
    ,daily_reset/0              %% 日常更新
    ,add_svrs/1                 %% 分区新增节点
    ,recalc_zone/1              %% 重新分区
    ,handle_merge/3             %% 合服处理
    ,get_power_rank/4           %% 获取战力排行人物属性数据
    ,upload_role_wave_score/3   %% 上传人物波数积分
    ,upload_role_total_score/3  %% 上传人物副本总积分
    ,update_god_live_role/4     %% 更新人物活跃数据
    ]).

%% @doc
%% 启动服务器初始化逻辑
%% 启动 -> 延迟db数据初始化 -> 通知各游戏服上传战力排行人物属性
%%  -> 分区信息初始化 -> 根据分区信息校正战力排行&积分排行&波数积分排行

-define(SERVER, ?MODULE).
-define(DELAY_TIME,               5000).        %% X毫秒后初始化
-define(NOTICE_POWER_RANK_DELAY,  5000).        %% X毫秒后通知游戏服获取分区唤神排行数据

-ifdef(DEV_SERVER).
    -define(NOTICE_RANK_TIME,        40000).    %% X毫秒后通知游戏服上传战力排行人物的属性数据
    -define(ZONE_INIT_DELAY_TIME,    10000).    %% X毫秒后分区信息初始化(预留时间等待游戏服战力排行人物属性上传完成)
    -define(RANK_ZONE_INIT_DELEY,    10000).    %% X毫秒后分区排行数据校正(预留时间等待分区信息初始化)
    -define(RESET_POWER_RANK_TIME,   30000).    %% X毫秒后日常通知更新战力排行人物属性数据
-else.
    -define(NOTICE_RANK_TIME,       900000).
    -define(ZONE_INIT_DELAY_TIME,   300000).
    -define(RANK_ZONE_INIT_DELEY,    60000).
    -define(RESET_POWER_RANK_TIME,  300000).
-endif.

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

%% 上传战力排行数据
upload_power_rank({ZoneId, PowerRankL}) ->
    gen_server:cast(?MODULE, {update_power_rank, ZoneId, PowerRankL}).

%% 大批量上传战力排行数据
batch_upload_power_rank(ZipPowerRankData) ->
    gen_server:cast(?MODULE, {batch_upload_power_rank, ZipPowerRankData}).

timer_update() ->
    gen_server:cast(?MODULE, {timer_update}).

daily_reset() ->
    gen_server:cast(?MODULE, {daily_reset}).

%% Map :: #{cz_state => CZState, add_servers => AddServers}
add_svrs(Map) ->
    gen_server:cast(?MODULE, {add_svrs, Map}).

recalc_zone(Map) ->
    gen_server:cast(?MODULE, {recalc_zone, Map}).

handle_merge(MasterServId, MasterServNum, MergeServers) ->
    gen_server:cast(?MODULE, {handle_merge, MasterServId, MasterServNum, MergeServers}).


get_power_rank(ServId, ZoneId, From, CallbackMsg) ->
    gen_server:cast(?MODULE, {get_power_rank, ServId, ZoneId, From, CallbackMsg}).

upload_role_wave_score(ZoneId, Wave, WaveRole) ->
    gen_server:cast(?MODULE, {upload_role_wave_score, ZoneId, Wave, WaveRole}).

upload_role_total_score(ZoneId, Wave, Role) ->
    gen_server:cast(?MODULE, {upload_role_total_score, ZoneId, Wave, Role}).

update_god_live_role(RoleId, ZoneId, ServerId, Time) ->
    gen_server:cast(?MODULE, {update_god_live_role, RoleId, ZoneId, ServerId, Time}).

%%%-------------------------------------------------------------------
%%% Callback Functions
%%%-------------------------------------------------------------------
init(_Args) ->
    InitRef = erlang:send_after(?DELAY_TIME, self(), {proc_init}),
    NoticeRef = erlang:send_after(?NOTICE_RANK_TIME, self(), {notice_rank_upload}),
    Map = maps:put(?INIT_REF, InitRef, #{}),
    NewMap = maps:put(?NOTICE_REF, NoticeRef, Map),
    {ok, #god_dun_rank_state{extra_map = NewMap}}.

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

-spec handle_cast(Msg, State) -> 
    {noreply, State}
    | {stop, Reason, State} when
    Msg      :: term(),
    Reason   :: term(),
    State    :: #god_dun_rank_state{}.
handle_cast(Msg, State) ->
    case catch do_handle_cast(Msg, State) of
        NewState = #god_dun_rank_state{} ->
            {noreply, NewState};
        {noreply, NewState} ->
            {noreply, NewState};
        ok ->
            {noreply, State};
        skip ->
            {noreply, State};
        {stop, _Reason, NewState} ->
            _Reason =/= normal andalso ?ERR("god_dun_rank_mgr stop reason ~n ~p", [_Reason]),
            {stop, _Reason, NewState};
        Reason ->
            ?ERR("god_dun_rank_mgr handle cast msg ~n ~p ~n error reason ~n ~p", [Msg, Reason]),
            {noreply, State}
    end.

-spec handle_info(Msg, State) ->
    {noreply, State}
    | {stop, Reason, State} when
    Msg :: term(),
    Reason :: term(),
    State :: #god_dun_rank_state{}.
handle_info(Msg, State) ->
    case catch do_handle_info(Msg, State) of
        NewState = #god_dun_rank_state{} ->
            {noreply, NewState};
        {noreply, NewState} ->
            {noreply, NewState};
        ok ->
            {noreply, State};
        skip ->
            {noreply, State};
        {stop, _Reason, NewState} ->
            _Reason =/= normal andalso ?ERR("god_dun_rank_mgr process stop reason:~n ~p", [_Reason]),
            {stop, _Reason, NewState};
        _Reason ->
            ?ERR("god_dun_rank_mgr process msg: ~n ~p ~n reason: ~n ~p", [Msg, _Reason]),
            {noreply, State}
    end.


terminate(_Reason, _State) ->
    ok.

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

%%%-------------------------------------------------------------------
%%% Internal Functions
%%%-------------------------------------------------------------------

do_handle_cast({update_power_rank, ZoneId, ZonePowerRankL}, State) ->
   NewState = god_dun_rank_api:update_zone_power_rank(State, ZoneId, ZonePowerRankL),
   {noreply, NewState};

do_handle_cast({batch_upload_power_rank, ZipPowerRankData}, State) ->
    ZipPowerRankBin = zlib:unzip(ZipPowerRankData),
    {ZoneId, ZonePowerRankL} = util:bitstring_to_term(ZipPowerRankBin),
    NewState = god_dun_rank_api:update_zone_power_rank(State, ZoneId, ZonePowerRankL),
    {noreply, NewState};

do_handle_cast({timer_update}, State) ->
    #god_dun_rank_state{extra_map = Map} = State,
    case maps:get(?RANK_INIT_SIGN, Map, 0) of
        1 ->
            NewState = god_dun_rank_api:timer_update(State);
        _ ->
            NewState = State
    end,
    {noreply, NewState};

do_handle_cast({daily_reset}, State) ->
    #god_dun_rank_state{extra_map = Map} = NewState = god_dun_rank_api:daily_reset(State),
    Ref = erlang:send_after(?RESET_POWER_RANK_TIME, self(), {daily_update_power_rank}),
    NewMap = maps:put(?RESET_POWER_RANK_REF, Ref, Map),
    {noreply, NewState#god_dun_rank_state{extra_map = NewMap}};

do_handle_cast({recalc_zone, Map}, State) ->
    NewState = god_dun_rank_api:recalc_zone(Map, State),
    {noreply, NewState};

do_handle_cast({get_power_rank, ServId, ZoneId, From, CallbackMsg}, State) ->
    #god_dun_rank_state{power_rank = PowerRankL} = State,
    ZonePowerRankL = 
    case lists:keyfind(ZoneId, 1, PowerRankL) of
        {_, RankL} -> RankL;
        _   -> []
    end,
    % ?ERR("~p", [ZonePowerRankL]),
    clusters_center:apply_cast(ServId, gen_server, cast, [From, {CallbackMsg, ZonePowerRankL}]),
    {noreply, State};

do_handle_cast({upload_role_wave_score, ZoneId, WaveId, WaveRole}, State) ->
    #god_dun_rank_state{dun_wave_rank = WaveScoreRank, extra_map = Map} = State,
    #god_dun_wave_score_role{role_id = RoleId, score = NewScore} = WaveRole,
    case maps:get(?RANK_INIT_SIGN, Map, 0) of
        1 ->
            NewState = 
            case lists:keyfind({ZoneId, WaveId}, 1, WaveScoreRank) of
                {_, ZoneWaveScoreRank} ->
                    case lists:keyfind(RoleId, #god_dun_wave_score_role.role_id, ZoneWaveScoreRank) of
                        #god_dun_wave_score_role{score = OldScore} when NewScore > OldScore ->
                            NewZoneWaveScoreRank = lists:keystore(RoleId, #god_dun_wave_score_role.role_id, ZoneWaveScoreRank, WaveRole),
                            NewWaveScoreRank = lists:keystore({ZoneId, WaveId}, 1, WaveScoreRank, {{ZoneId, WaveId}, NewZoneWaveScoreRank}),
                            god_dun_rank_api:db_update_role_wave_score(WaveRole),
                            State#god_dun_rank_state{dun_wave_rank = NewWaveScoreRank};
                        #god_dun_wave_score_role{} ->
                            State;
                        _ ->
                            NewRankList = [WaveRole|ZoneWaveScoreRank],
                            god_dun_rank_api:db_update_role_wave_score(WaveRole),
                            NewWaveScoreRank = lists:keystore({ZoneId, WaveId}, 1, WaveScoreRank, {{ZoneId, WaveId}, NewRankList}),
                            State#god_dun_rank_state{dun_wave_rank = NewWaveScoreRank}
                    end;
                _ ->
                    NewRankList = [WaveRole],
                    god_dun_rank_api:db_update_role_wave_score(WaveRole),
                    NewWaveScoreRank = lists:keystore({ZoneId, WaveId}, 1, WaveScoreRank, {{ZoneId, WaveId}, NewRankList}),
                    State#god_dun_rank_state{dun_wave_rank = NewWaveScoreRank}
            end;
        _ ->
            NewState = State
    end,
    % ?ERR("~p", [NewState#god_dun_rank_state.dun_wave_rank]),
    {noreply, NewState};

do_handle_cast({upload_role_total_score, ZoneId, _Wave, Role}, State) ->
    #god_dun_rank_state{score_rank = ScoreRank, extra_map = Map} = State,
    #god_dun_score_role{role_id = RoleId, score = Score} = Role,
    case maps:get(?RANK_INIT_SIGN, Map, 0) of
        1 ->
            NewState = 
            case lists:keyfind(ZoneId, 1, ScoreRank) of
                {_, ZoneScoreRank} ->
                    case lists:keyfind(RoleId, #god_dun_score_role.role_id, ZoneScoreRank) of
                        #god_dun_score_role{score = OldScore}->
                            NewRole = Role#god_dun_score_role{score = round(OldScore+Score)},
                            NewZoneScoreRank = lists:keystore(RoleId, #god_dun_score_role.role_id, ZoneScoreRank, NewRole),
                            NewScoreRank = lists:keystore(ZoneId, 1, ScoreRank, {ZoneId, NewZoneScoreRank}),
                            god_dun_rank_api:db_update_role_score(NewRole),
                            State#god_dun_rank_state{score_rank = NewScoreRank};
                        _A1 ->
                            NewZoneScoreRank = [Role|ZoneScoreRank],
                            NewScoreRank = lists:keystore(ZoneId, 1, ScoreRank, {ZoneId, NewZoneScoreRank}),
                            god_dun_rank_api:db_update_role_score(Role),
                            State#god_dun_rank_state{score_rank = NewScoreRank}
                    end;
                _A ->
                    NewZoneScoreRank = [Role],
                    NewScoreRank = lists:keystore(ZoneId, 1, ScoreRank, {ZoneId, NewZoneScoreRank}),
                    god_dun_rank_api:db_update_role_score(Role),
                    State#god_dun_rank_state{score_rank = NewScoreRank}
            end;
        _ ->
            NewState = State
    end,
    {noreply, NewState};

do_handle_cast({update_god_live_role, RoleId, ZoneId, ServerId, Time}, State) ->
    #god_dun_rank_state{today_roles = TodayRoles, extra_map = Map} = State,
    InitSign = maps:get(?RANK_INIT_SIGN, Map, 0),
    if InitSign =/= 0 ->
            NewTodayRoles = 
            case lists:keyfind(ZoneId, 1, TodayRoles) of
                {_, ZoneRoles} ->
                    case lists:keyfind(RoleId, 1, ZoneRoles) of
                        {_, _OldServerId, _OldTime} ->
                            TodayRoles;
                        _ ->
                            god_dun_rank_api:db_update_role_god_live(RoleId, ServerId, Time),
                            NewZoneRoles = lists:keystore(RoleId, 1, ZoneRoles, {RoleId, ServerId, Time}),
                            lists:keystore(ZoneId, 1, TodayRoles, {ZoneId, NewZoneRoles})
                    end;
                _ ->
                    god_dun_rank_api:db_update_role_god_live(RoleId, ServerId, Time),
                    NewZoneRoles = [{RoleId, ServerId, Time}],
                    lists:keystore(ZoneId, 1, TodayRoles, {ZoneId, NewZoneRoles})
            end;
        true ->
            NewTodayRoles = TodayRoles
    end,
    NewState = State#god_dun_rank_state{today_roles = NewTodayRoles},
    {noreply, NewState};

do_handle_cast({get_role_god_dun_score, RoleId, ServerId, ServerNum, Name, PhotoId, ZoneId, Wave, SumScore}, State) ->
    #god_dun_rank_state{dun_wave_rank = WaveRank} = State,
    % ?ERR("~p ~p", [ZoneId, Wave]),
    case lists:keyfind({ZoneId, Wave}, 1, WaveRank) of
        {_, ZoneWaveRank} ->
            % RankL1 = lists:filter(fun(R) -> is_record(R, god_dun_wave_score_role) andalso R#god_dun_wave_score_role.role_id =/= RoleId end, ZoneWaveRank),
            Data = get_role_newest_rank(RoleId, ServerNum, Wave, Name, PhotoId, SumScore, ZoneWaveRank),
            % ?ERR("~p ~p ~p", [ZoneId, Wave, Data]),
            clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [RoleId, pt_610, 61093, [Data]]);
        _ -> %% 找不到数据忽略
            Data = [{0, 0, 1, 0, "", 0},{0, 0, 2, 0, "", 0},{0, 0, 3, 0, "", 0}],
            clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [RoleId, pt_610, 61093, [Data]])
    end,
    {noreply, State};

%% 结算时获取最新数据
do_handle_cast({get_god_dun_score_rank, RoleId, RoleName, ServerId, ZoneId, RoleScore}, State) ->
    #god_dun_rank_state{score_rank = ScoreRank, today_roles = TodayRoles} = State,
    case lists:keyfind(ZoneId, 1, ScoreRank) of
        {_, ZoneScoreRank} ->
            case lists:keyfind(RoleId, #god_dun_score_role.role_id, ZoneScoreRank) of
                #god_dun_score_role{rank = OldRank} -> OldRank;
                _ -> OldRank = 0
            end,
            SortRankL  = god_dun_rank_api:sort_rank_list(score_rank, ZoneScoreRank),
            PackSortRank = god_dun_rank_api:pack_score_rank(SortRankL),
            case lists:keyfind(ZoneId, 1, TodayRoles) of
                {_, ZoneRoleNum} -> ok;
                _  -> ZoneRoleNum = []
            end,
            case lists:keyfind(RoleId, #god_dun_score_role.role_id, SortRankL) of
                #god_dun_score_role{rank = Rank, score = Score} -> ok;
                _  -> Rank = 0, Score = RoleScore
            end,
            [{_, RankNumL}|_] = god_dun_rank_api:calc_zone_rank([{ZoneId, length(ZoneRoleNum)}]),
            PercentRankMax = data_god_dun:get_max_rank_by_type(1),
            if Rank > PercentRankMax ->
                    RankType = 2,
                    {NewRankMin, NewRankMax} = god_dun_rank_api:calc_role_percent_rank_do(RankNumL, Rank);
                true ->
                    check_broad_cast(Rank, OldRank, RoleId, RoleName, ZoneId),
                    RankType = 1, NewRankMin = Rank, NewRankMax = 0
            end,            
            % RoleId == 4294968471 andalso ?ERR("~p ~p ~p ~p score ~p", [RankType, Rank, NewRankMin, NewRankMax, Score]),
            clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [RoleId, pt_610, 61088, [1, PackSortRank, RankType, NewRankMin, NewRankMax, Score]]);
        _ ->
            clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [RoleId, pt_610, 61088, [1, [], 1, 0, 0, 0]])
    end,
    {noreply, State};

do_handle_cast({add_svrs, Map}, State) ->
    NewState = god_dun_rank_api:add_svrs(Map, State),
    {noreply, NewState};

do_handle_cast({apply_cast, {M, F, A}}, State) ->
    NewArgs = [State|A],
    NewState1 = 
    case catch apply(M, F, NewArgs) of
        {noreply, NewState} ->
            NewState;
        NewState = #god_dun_rank_state{} ->
            NewState;
        Res when Res == ok orelse Res == skip
         orelse Res == ignore ->
            State;
        _A ->
            ?ERR("error happen ~p", [_A]),
            State
    end,
    {noreply, NewState1};

%% 合服处理
do_handle_cast({handle_merge, MasterServId, MasterServNum, MergeServers}, State) ->
    NewState = god_dun_rank_api:handle_merge(MasterServId, MasterServNum, MergeServers, State),
    {noreply, NewState};

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

do_handle_info({proc_init}, State) ->
    #god_dun_rank_state{extra_map = Map} = State,
    %% 积分排行
    case catch db:get_all(?SQL_GOD_DUN_SELECT, []) of
        RankData = [_|_] ->
            RankList = god_dun_rank_api:init_rank_data(RankData),
            NewState = State#god_dun_rank_state{score_rank = RankList};
        [] ->
            NewState = State;
        _A ->
            ?ERR("god rank db select score rank error ~p", [_A]),   
            NewState = State
    end,
    %% 波数积分排行
    case catch db:get_all(?SQL_GOD_DUN_WAVE_SCORE_SELECT, []) of
        WaveRanks = [_|_] ->
            WaveRankList = god_dun_rank_api:init_wave_rank_data(WaveRanks),
            NewState1 = NewState#god_dun_rank_state{dun_wave_rank = WaveRankList};
        [] ->
            NewState1 = NewState;
        _A1 ->
            ?ERR("god rank db select wave rank error ~p", [_A1]),
            NewState1 = NewState
    end,
    %% 今日参与人数
    case catch db:get_all(?SQL_GOD_LIVE_ROLE_SELECT, []) of
        LiveRoles = [_|_] ->
            LiveRoles1 = [{RoleId, ServId, Time} || [RoleId, ServId, Time] <- LiveRoles],
            NewState2 = NewState1#god_dun_rank_state{today_roles = LiveRoles1};
        [] ->
            NewState2 = NewState1;
        _A2 ->
            ?ERR("god rank db select live role error ~p", [_A2]),
            NewState2 = NewState1
    end,
    NewMap = cancel_timer(?INIT_REF, Map),
    NewState3 = NewState2#god_dun_rank_state{extra_map = NewMap},
    {noreply, NewState3};

%% 通知战力排行上传
do_handle_info({notice_rank_upload}, State) ->
    #god_dun_rank_state{extra_map = Map} = State,
    NewMap = cancel_timer(?NOTICE_REF, Map),
    clusters_center:apply_to_all_node(god_dun_rank_api, collect_power_rank, []),
    %% 5分钟后更新分区数据
    Ref1 = erlang:send_after(?ZONE_INIT_DELAY_TIME, self(), {zone_update}),
    NewMap1 = maps:put(?ZONE_INIT_REF, Ref1, NewMap),
    NewState = State#god_dun_rank_state{extra_map = NewMap1},
    {noreply, NewState};

%% 更新分区数据
do_handle_info({zone_update}, State) ->
    #god_dun_rank_state{extra_map = Map} = State,
    NewMap = cancel_timer(?ZONE_INIT_REF, Map),
    case maps:get(?ZONE_CALC_SIGN, NewMap, 0) of
        0 ->
            SrvZoneMap = czone:get_svr_zone_map(),
            NewMap0 = maps:put(?ZONE_CALC_SIGN, 1, NewMap),
            ZoneSrvL = god_dun_rank_api:update_zone_serv_info(SrvZoneMap),
            NewState = State#god_dun_rank_state{
                srvzones = SrvZoneMap, 
                zonesrvs = ZoneSrvL, 
                extra_map = NewMap0};
        _ ->
            NewState = State
    end,
    #god_dun_rank_state{extra_map = NewMap1} = NewState,
    %% 1分钟后校正排行分区数据
    RankRef = erlang:send_after(?RANK_ZONE_INIT_DELEY, self(), {zone_rank_init}),
    NewMap3 = maps:put(?RANK_ZONE_UPDATE_REF, RankRef, NewMap1),
    NewState1 = NewState#god_dun_rank_state{extra_map = NewMap3},
    {noreply, NewState1};

%% 更新排行数据
do_handle_info({zone_rank_init}, State) ->
    #god_dun_rank_state{extra_map = Map} = State,
    NewMap = cancel_timer(?RANK_ZONE_UPDATE_REF, Map),
    case maps:get(?RANK_INIT_SIGN, NewMap, 0) of
        0 ->
            State1 = State#god_dun_rank_state{extra_map = NewMap},
            #god_dun_rank_state{extra_map = Map2} = State2 = god_dun_rank_api:zone_rank_init(State1),
            NewMap2 = maps:put(?RANK_INIT_SIGN, 1, Map2),
            NewState = State2#god_dun_rank_state{extra_map = NewMap2};
        _ ->
            NewState = State#god_dun_rank_state{extra_map = NewMap}
    end,
    %% 通知游戏服战力排行获取
    PowerRankRef = erlang:send_after(?NOTICE_POWER_RANK_DELAY, self(), {notice_power_rank_download}),
    #god_dun_rank_state{extra_map = NewMap3} = NewState,
    NewMap4 = maps:put(?NOTICE_POWER_RANK_REF, PowerRankRef, NewMap3),
    NewState1 = NewState#god_dun_rank_state{extra_map = NewMap4},
    {noreply, NewState1};

do_handle_info({daily_update_power_rank}, State) ->
    #god_dun_rank_state{power_rank = PowerRank, extra_map = Map} = State,
    NewPowerRank = god_dun_rank_api:update_power_rank(PowerRank),
    ?ERR("daily update power rank", []),
    NewState = State#god_dun_rank_state{power_rank = NewPowerRank},
    NewMap = cancel_timer(?RESET_POWER_RANK_REF, Map),
    %% 通知游戏服战力排行获取
    PowerRankRef = erlang:send_after(?NOTICE_POWER_RANK_DELAY, self(), {notice_power_rank_download}),
    NewMap1 = maps:put(?NOTICE_POWER_RANK_REF, PowerRankRef, NewMap),
    {noreply, NewState#god_dun_rank_state{extra_map = NewMap1}};

do_handle_info({notice_power_rank_download}, State) ->
    #god_dun_rank_state{extra_map = Map} = State,
    god_dun_rank_api:notice_power_rank_download(),
    NewMap = cancel_timer(?NOTICE_POWER_RANK_REF, Map),
    NewState = State#god_dun_rank_state{extra_map = NewMap},
    {noreply, NewState};

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


get_role_newest_rank(RoleId, RoleServNum, Wave, Name, PhotoId, Score, RankL) ->
    % ?ERR("~p ~p", [RoleId, Score]),
    Score1 = case lists:keyfind(RoleId, #god_dun_wave_score_role.role_id, RankL) of
        #god_dun_wave_score_role{score = OldScore} -> max(OldScore, Score);
        _ -> Score
    end,
    GodScoreRole = #god_dun_wave_score_role{role_id = RoleId, wave = Wave, score = Score1},
    RankL1 = lists:keystore(RoleId, #god_dun_wave_score_role.role_id, RankL, GodScoreRole),
    SortRankL = lists:reverse(lists:keysort(#god_dun_wave_score_role.score, RankL1)),
    {_, SortRankL1} = lists:foldl(fun(Role, {Index, Acc}) -> 
        {Index+1, [Role#god_dun_wave_score_role{rank = Index}|Acc]}
    end, {1, []}, SortRankL),
    Len = length(SortRankL),
    #god_dun_wave_score_role{rank = RoleRank} = lists:keyfind(RoleId, #god_dun_wave_score_role.role_id, SortRankL1),
    {Other1, Other2} =
    if
        RoleRank =< 1 ->
            {RoleRank+1, RoleRank+2};
        Len == 2 ->
            [R|_] = [1,2] -- [RoleRank],
            {R, 3};
        RoleRank >= 15 ->
            {RoleRank-1, RoleRank-2};
        true ->
            {RoleRank-1, RoleRank+1}
    end,
    % ?ERR("~p ~p ~p ~p score ~p", [Len, RoleRank, Other1, Other2, Score1]),
    OtherRole1 = case lists:keyfind(Other1, #god_dun_wave_score_role.rank, SortRankL1) of
        R1 = #god_dun_wave_score_role{} -> R1;
        _  -> #god_dun_wave_score_role{rank = Other1, show = #wave_key_figure{}}
    end,
    OtherRole2 = case lists:keyfind(Other2, #god_dun_wave_score_role.rank, SortRankL1) of
        R2 = #god_dun_wave_score_role{} -> R2;
        _  -> #god_dun_wave_score_role{rank = Other2, show = #wave_key_figure{}}
    end,
    NewGodScoreRole = GodScoreRole#god_dun_wave_score_role{rank = RoleRank, server_num = RoleServNum, show = #wave_key_figure{name = Name, profile_photo_id = PhotoId}},
    lists:foldl(fun(RoleX, Acc) -> 
        #god_dun_wave_score_role{
            server_num = ServerNumX, role_id = RoleIdX, rank = RankX, score = ScoreX, show = #wave_key_figure{name = NameX, profile_photo_id = PhotoIdX}
        } = RoleX,
        [{ServerNumX, RoleIdX, RankX, ScoreX, NameX, PhotoIdX}|Acc]
    end, [], [NewGodScoreRole, OtherRole1, OtherRole2]).

check_broad_cast(NewRankMin, OldRank, RoleId, RoleName, ZoneId) ->
    if NewRankMin =< 20 andalso (NewRankMin < OldRank orelse (OldRank == 0 andalso NewRankMin =/= 0)) ->
            czone:zone_apply_cast(ZoneId, {lib_chat, send_TV, [{all}, ?MOD_DUNGEON, 8, [RoleName, RoleId, NewRankMin]]});
        true ->
            skip
    end.


cancel_timer(RefName, Map) ->
    Ref = maps:get(RefName, Map, undefined),
    util:cancel_timer(Ref),
    maps:put(RefName, undefined, Map).