%% ---------------------------------------------------------------------------
%% @doc 跨服竞榜排行榜进程
%% @author zhaoyu
%% @since  2018-11-29
%% ---------------------------------------------------------------------------
-module(race_rank_mgr).


-behaviour(gen_server).

%% API
-export([
        start_link/0
        ,call/1                     %% call本进程
        ,cast/1                     %% cast本进程
        ,info/1                     %% 给进程发送消息
        ,recalc_zone/1
        ,add_svrs/1
    ]).

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

-include("common.hrl").
-include("race_rank.hrl").
-include("clusters.hrl").


%% ---------------------------------------------------------------------------
%% @doc Starts the server
-spec start_link() -> {ok, Pid} | ignore | {error, Error} when
    Pid :: pid(),
    Error :: term().
%% ---------------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).


%% ---------------------------------------------------------------------------
%% @doc call进程
-spec call(Msg) -> term() | {error, Reason} when
    Msg     :: term(),
    Reason  :: any().
%% ---------------------------------------------------------------------------
call(Msg) ->
    try gen_server:call(?MODULE, Msg) of
    Reply -> Reply
    catch Class:Reason ->
        Error = {Class, Reason, erlang:get_stacktrace()},
        ?ERR("~w:handle_cast error:~p~n", [?MODULE, Error]),
        {error, Reason}
    end.


%% ---------------------------------------------------------------------------
%% @doc cast进程
-spec cast(Msg) -> ok when
    Msg     :: term().
%% ---------------------------------------------------------------------------
cast(Msg) ->
    gen_server:cast(?MODULE, Msg).


%% ---------------------------------------------------------------------------
%% @doc 给进程发送消息
-spec info(Msg) -> ok when
    Msg     :: term().
%% ---------------------------------------------------------------------------
info(Msg) ->
    erlang:send(?MODULE, Msg).

recalc_zone(CBMap)->
    cast({recalc_zone,CBMap}),
    ok.

add_svrs(CBMap)->
    cast({add_svrs,CBMap}),
    ok.

%% ---------------------------------------------------------------------------
%% @doc Initializes the server
-spec init(Args) ->
    {ok, State}
    | {ok, State, Timeout}
    | ignore
    | {stop, Reason} when
    Args    :: term() ,
    State   :: #rr_rank_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason  :: term().
%% ---------------------------------------------------------------------------
init(_) ->
    process_flag(trap_exit, true),
    erlang:send_after(0, self(), {init_rank}),
    {ok, #rr_rank_state{}}.


%% ---------------------------------------------------------------------------
%% @doc Handling call messages
-spec handle_call(Request, From, State) ->
    {reply, Reply, State}
    | {reply, Reply, State, Timeout}
    | {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, Reply, State}
    | {stop, Reason, State} when
    Request :: term(),
    From :: pid(),
    State :: #rr_rank_state{},
    Reply :: term(),
    State :: #rr_rank_state{},
    Timeout :: non_neg_integer() | infinity.
%% ---------------------------------------------------------------------------
handle_call(Request, From, State) ->
    try handle_call2(Request, From, State) of
        Result ->
            Result
    catch Class:Reason ->
        Error = {Class, Reason, erlang:get_stacktrace()},
        ?ERR("~w:handle_cast error:~p~n", [?MODULE, Error]),
        {reply, error, State}
    end.


%% ---------------------------------------------------------------------------
%% @doc Handling cast messages
-spec handle_cast(Msg, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Msg :: term(),
    State :: #rr_rank_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
handle_cast(Msg, State) ->
    try handle_cast2(Msg, State) of
        Result ->
            Result
    catch Class:Reason ->
        Error = {Class, Reason, erlang:get_stacktrace()},
        ?ERR("~w:handle_cast error:~p~n", [?MODULE, Error]),
        {noreply, State}
    end.


%% ---------------------------------------------------------------------------
%% @doc Handling all non call/cast messages
-spec handle_info(Info, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Info :: term(),
    State :: #rr_rank_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
handle_info(Info, State) ->
    try handle_info2(Info, State) of
        Result ->
            Result
    catch Class:Reason ->
        Error = {Class, Reason, erlang:get_stacktrace()},
        ?ERR("~w:handle_cast error:~p~n", [?MODULE, Error]),
        {noreply, State}
    end.


%% ---------------------------------------------------------------------------
%% @doc Handling call messages
-spec handle_call2(Request, From, State) ->
    {reply, Reply, State}
    | {reply, Reply, State, Timeout}
    | {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, Reply, State}
    | {stop, Reason, State} when
    Request :: term(),
    From :: pid(),
    State :: #rr_rank_state{},
    Reply :: term(),
    State :: #rr_rank_state{},
    Timeout :: non_neg_integer() | infinity.
%% ---------------------------------------------------------------------------
handle_call2(_Request, _From, State) ->
    {reply, ok, State}.


%% ---------------------------------------------------------------------------
%% @doc Handling cast messages
-spec handle_cast2(Msg, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Msg :: term(),
    State :: #rr_rank_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
%% 玩家上榜
handle_cast2({hit_rank, RankRole}, State) ->
    #rr_rank_role{server_id = ServerId} = RankRole,
    #rr_rank_state{server2zone_dict = IndexDict} = State,
    NewState = case maps:get(ServerId, IndexDict, false) of
        ZoneId when is_integer(ZoneId)->
            RankRole2 = RankRole#rr_rank_role{zone_id = ZoneId},
            update_rank(RankRole2, State, true, true);
        _ ->
            State
    end,
    {noreply, NewState};
%% 查询竞榜活动信息
handle_cast2({race_rank_act_info,Cmd,Sid, ServerId, Data}, State) ->
    #rr_rank_state{server2zone_dict = IndexDict} = State,
    [ActId | _] = Data,
   case maps:get(ServerId, IndexDict, false) of
        ZoneId when is_integer(ZoneId)->
            #rr_rank_state{acts_rank = ActsRank} = State,
            %% 每个活动的排行榜数据
            ActIdPos = #rr_act_rank.act_id,
            #rr_act_rank{
                ranks = Ranks
            } = case lists:keyfind(ActId, ActIdPos, ActsRank) of
                ARank = #rr_act_rank{} -> ARank;
                _ -> #rr_act_rank{}
            end,
            %% 每个活动各个分区的排行榜数据
            ZoneIdPos = #rr_rank.zone_id,
            #rr_rank{
                roles = Roles
            } = case lists:keyfind(ZoneId, ZoneIdPos, Ranks) of
                RRank = #rr_rank{} -> RRank;
                _ -> #rr_rank{}
            end,
            DataList = pack_rank_data(Roles, []),
            ProData = Data ++ [DataList],
            {ok, BinData} = pt_333:write(Cmd, ProData),
            lib_server_send:send_to_sid(Sid, BinData);
        _ ->
            ok
    end,
    {noreply, State};

handle_cast2({fetch_rank_data,Cmd,Sid, ServerId,ActId},State)->
    #rr_rank_state{server2zone_dict = IndexDict} = State,
    case maps:get(ServerId, IndexDict, false) of
        ZoneId when is_integer(ZoneId)->
            #rr_rank_state{acts_rank = ActsRank} = State,
            %% 每个活动的排行榜数据
            ActIdPos = #rr_act_rank.act_id,
            #rr_act_rank{
                ranks = Ranks
            } = case lists:keyfind(ActId, ActIdPos, ActsRank) of
                ARank = #rr_act_rank{} -> ARank;
                _ -> #rr_act_rank{}
            end,
            %% 每个活动各个分区的排行榜数据
            ZoneIdPos = #rr_rank.zone_id,
            #rr_rank{
                roles = Roles
            } = case lists:keyfind(ZoneId, ZoneIdPos, Ranks) of
                RRank = #rr_rank{} -> RRank;
                _ -> #rr_rank{}
            end,
            DataList = pack_rank_data(Roles, []),
            {ok, BinData} = pt_333:write(Cmd, [ActId,DataList]),
            lib_server_send:send_to_sid(Sid, BinData);
        _ ->
            ok
    end,
    {noreply, State};

%% 排行榜展示时间也结束了
handle_cast2({act_show_end, ActId}, State) ->
    #rr_rank_state{acts_rank = ActsRank} = State,
    %% 删除排行榜数据
    ActsRank2 = lists:keydelete(ActId, #rr_act_rank.act_id, ActsRank),
    NewState = State#rr_rank_state{acts_rank = ActsRank2},
    del_act_rank(ActId),
    {noreply, NewState};
%% 合服处理
handle_cast2({handle_merge, ToServerId, ToServerNum, ServerIds}, State) ->
    ?INFO("~w:handle_merge:~w", [?MODULE, {ToServerId, ServerIds}]),
    NewState = handle_merge(State, ToServerId, ToServerNum, ServerIds),
    {noreply, NewState};
%% 结算排行榜
handle_cast2({settlement_rank, ActId,StartTime,EndTime}, State) ->
    NewState = settlement_rank(State, ActId,StartTime,EndTime),
    {noreply, NewState};
%% 更新ServerId对应的ZoneId的索引数据
%% Todo 分区变化 需要矫正数据
handle_cast2({recalc_zone,CBMap},State)->
    #{svr_zone_map := IndexDict, zone_map := ZoneMap} = CBMap,
    NewState = do_recalc_zone(maps:to_list(ZoneMap),State),
    {noreply, NewState#rr_rank_state{server2zone_dict = IndexDict}};
handle_cast2({add_svrs,CBMap},State)->
    #rr_rank_state{server2zone_dict=ZoneMap}=State,
    #{add_servers := AddServers}=CBMap,
    NZoneMap = do_add_svrs(AddServers,ZoneMap),
    NewState = State#rr_rank_state{server2zone_dict = NZoneMap},
    {noreply, NewState};
handle_cast2(_Msg, State) ->
    {noreply, State}.


%% ---------------------------------------------------------------------------
%% @doc Handling all non call/cast messages
-spec handle_info2(Info, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Info :: term(),
    State :: #rr_rank_state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
%% 加载排行榜数据
handle_info2({init_rank}, State) ->
    NewState0 = load_ranks(State),
    IndexDict = cross_zone:get_server_zone_map(czone),
    NewState  = NewState0#rr_rank_state{server2zone_dict = IndexDict},
    {noreply, NewState};
handle_info2(_Info, State) ->
    {noreply, State}.


%% ---------------------------------------------------------------------------
%% @doc called by a gen_server when it is about to terminate
-spec terminate(Reason, State) -> ok when
    Reason :: term(),
    State  :: #rr_rank_state{}.
%% ---------------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.


%% ---------------------------------------------------------------------------
%% @doc Convert process state when code is changed
-spec code_change(OldVsn, State, Extra) -> {ok, NewState} when
    OldVsn :: term(),
    State :: #rr_rank_state{},
    Extra :: term(),
    NewState :: #rr_rank_state{}.
%% ---------------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.


%% ---------------------------------------------------------------------------
%% @doc 加载排行榜数据
-spec load_ranks(State)-> NewState when
    State    :: #rr_rank_state{},
    NewState :: #rr_rank_state{}.
%% ---------------------------------------------------------------------------
load_ranks(State)->
    RankRoles = case db:get_all(?RR_RANK_SELECT) of
        DbList when is_list(DbList) ->
            format_rank(DbList, []);
        _ ->
            []
    end,
    init_rank(State, RankRoles).

format_rank([], RankRoles)->
    RankRoles;
format_rank([RankRole | T], RankRoles)->
    [
        ActId, ZoneId, RoleId, Name, ServerId, ServerNum, Score, Time
    ] = RankRole,
    RRole = #rr_rank_role{
        act_id  = ActId, zone_id = ZoneId, role_id = RoleId
        ,role_name = Name, server_id = ServerId
        ,server_num = ServerNum, score = Score, time = Time
    },
    NewRankRoles = [RRole | RankRoles],
    format_rank(T, NewRankRoles).

init_rank(State, []) ->
    State;
init_rank(State, [RankRole | T]) ->
    NewState = update_rank(RankRole, State, false, false),
    init_rank(NewState, T).


%% ---------------------------------------------------------------------------
%% @doc 合服处理
-spec handle_merge(State, ToServerId, ToServerNum, ServerIds) -> NewState when
    State       :: #rr_rank_state{},
    ToServerId  :: integer(),   %% 合服后的ServerId
    ToServerNum :: integer(),   %% 合服后的ServerNum
    ServerIds   :: [ServerId :: integer()],
    NewState    :: #rr_rank_state{}.
%% ---------------------------------------------------------------------------
handle_merge(State, ToServerId, ToServerNum, ServerIds) ->
    #rr_rank_state{server2zone_dict = IndexDict} = State,
    case maps:get(ToServerId, IndexDict, false) of
        ToZoneId when is_integer(ToZoneId)->
            handle_merge(State, ToServerId, ToServerNum, ToZoneId, ServerIds);
        _ ->
            State
    end.
handle_merge(State, _ToServerId, _ToServerNum, _ToZoneId, []) ->
    State;
handle_merge(State, ToServerId, ToServerNum, ToZoneId, [FromServerId | T]) ->
    #rr_rank_state{
        server2zone_dict = IndexDict, acts_rank = ActsRank
    } = State,
    NewState =  case maps:get(FromServerId, IndexDict, false) of
        FromZoneId when is_integer(FromZoneId),FromZoneId =/= ToZoneId -> %% 游戏服合到其他分区了
            ActsRank2 = [
                act_rank_handle_merge(
                    ToServerId, ToServerNum, ToZoneId, FromServerId, FromZoneId, ActRank
                ) || ActRank <- ActsRank
            ],
            State#rr_rank_state{acts_rank = ActsRank2};
        _ ->
            State
    end,
    handle_merge(NewState, ToServerId, ToServerNum, ToZoneId, T).

%% 某个竞榜活动各个分区的排行榜
act_rank_handle_merge(ToServerId, ToServerNum, ToZoneId, FromServerId, FromZoneId, ActRank) ->
    #rr_act_rank{act_id = ActId, ranks = Ranks} = ActRank,
    {
        Ranks2, OtherRankAddRoles
    } = case lists:keytake(FromZoneId, #rr_rank.zone_id, Ranks) of
        {value, Rank = #rr_rank{roles = Roles}, LessRanks} ->
            {RankRoles, OtherRankRoles} = split_roles(
                FromServerId, ToServerId, ToServerNum, ToZoneId, Roles, [], []
            ),
            %% 最大上榜人数
            RankMax = get_rank_max(),
            StartRanking = 1,
            AscRankRoles = set_ranking(RankRoles, RankMax, StartRanking, []),
            Rank2 = Rank#rr_rank{roles = AscRankRoles},
            {[Rank2 | LessRanks], OtherRankRoles};
        _ -> %% 找不到原分区
            {Ranks, []}
    end,
    Ranks3 = case OtherRankAddRoles of
        [] -> %% 没有玩家需要移动到其他分区
            Ranks2;
        _ -> %% 需要移动到其他分区的玩家重新上榜
            roles_hit_rank(OtherRankAddRoles, Ranks2, ActId, ToZoneId)
    end,
    ActRank#rr_act_rank{ranks = Ranks3}.

%% 排行榜里面server_id服的玩家和其他服的玩家分开
split_roles(_ServerId, _ToServerId, _ToServerNum, _ToZoneId, [], RankRoles, OtherRankRoles) ->
    {RankRoles, OtherRankRoles};
split_roles(ServerId, ToServerId, ToServerNum, ToZoneId, [Role | T], RankRoles, OtherRankRoles) ->
    {RankRoles2, OtherRankRoles2} = case Role of
        #rr_rank_role{server_id = ServerId} -> %% 是被合服的玩家
            Role2 = Role#rr_rank_role{
                zone_id = ToZoneId, server_id = ToServerId
                ,server_num = ToServerNum
            },
            {RankRoles, [Role2 | OtherRankRoles]};
        _ ->
            {[Role | RankRoles], OtherRankRoles}
    end,
    split_roles(ServerId, ToServerId, ToServerNum, ToZoneId, T, RankRoles2, OtherRankRoles2).

%% 需要移动到其他分区的玩家重新上榜
roles_hit_rank(OtherRankAddRoles, Ranks, ActId, ToZoneId) ->
    {
        Rank = #rr_rank{roles = Roles}, LessRanks
    } = case lists:keytake(ToZoneId, #rr_rank.zone_id, Ranks) of
        {value, RRank = #rr_rank{}, LRank} -> {RRank, LRank};
        _ -> {#rr_rank{act_id  = ActId, zone_id = ToZoneId}, Ranks}
    end,
    %% 最大上榜人数
    MaxLen = get_rank_max(),
    NewRoles = roles_hit_rank_do(OtherRankAddRoles, Roles, MaxLen),
    NewRank = Rank#rr_rank{roles = NewRoles},
    save_rank_roles(NewRoles),
    DelRoles = [
        Role || Role = #rr_rank_role{role_id = Id} <- OtherRankAddRoles
        ,lists:keymember(Id, #rr_rank_role.role_id, NewRoles) =:= false
    ],
    DelRoles2 = [
        Role || Role = #rr_rank_role{role_id = Id} <- Roles
        ,lists:keymember(Id, #rr_rank_role.role_id, NewRoles) =:= false
    ],
    batch_del_rank_role(ActId, DelRoles ++ DelRoles2),
    [NewRank | LessRanks].

roles_hit_rank_do([], Roles, _MaxLen) ->
    Roles;
roles_hit_rank_do([RankRole | T], Roles, MaxLen) ->
    #rr_rank_role{score = RoleScore} = RankRole,
    case Roles of
        [
            _LastRole = #rr_rank_role{ranking = Ranking, score = Score} | _
        ] when Ranking >= MaxLen andalso Score >= RoleScore ->
            %% 上不了榜
            roles_hit_rank_do(T, Roles, MaxLen);
        _ ->
            %% 更新排行榜
            NewRoles = update_rank_core(
                MaxLen, RankRole, Roles, false, false
            ),
            roles_hit_rank_do(T, NewRoles, MaxLen)
    end.


%% ---------------------------------------------------------------------------
%% @doc 更新排行榜
-spec update_rank(RankRole, State, IsBroadcast, IsSave) -> NewState when
    RankRole    :: #rr_rank_role{},
    State       :: #rr_rank_state{},
    IsBroadcast :: true | false,
    IsSave      :: true | false,        %% 是否保存到硬盘
    NewState    :: #rr_rank_state{}.
%% ---------------------------------------------------------------------------
update_rank(RankRole, State, IsBroadcast, IsSave) ->
    #rr_rank_role{
        act_id  = ActId, zone_id = ZoneId, score = RoleScore
    } = RankRole,
    #rr_rank_state{acts_rank = ActsRank} = State,
    %% 每个活动的排行榜数据
    ActIdPos = #rr_act_rank.act_id,
    {
        ActRank = #rr_act_rank{ranks = Ranks}, LessActsRank
    } = case lists:keytake(ActId, ActIdPos, ActsRank) of
        {value, ARank = #rr_act_rank{}, LActsRank} -> {ARank, LActsRank};
        _ -> {#rr_act_rank{act_id  = ActId}, ActsRank}
    end,
    %% 每个活动各个分区的排行榜数据
    ZoneIdPos = #rr_rank.zone_id,
    {
        Rank = #rr_rank{roles = Roles}, LessRanks
    } = case lists:keytake(ZoneId, ZoneIdPos, Ranks) of
        {value, RRank = #rr_rank{}, LRank} -> {RRank, LRank};
        _ -> {#rr_rank{act_id  = ActId, zone_id = ZoneId}, Ranks}
    end,

    %% 最大上榜人数
    MaxLen = get_rank_max(),
    case Roles of
        [
            _LastRole = #rr_rank_role{ranking = Ranking, score = Score} | _
        ] when Ranking >= MaxLen andalso Score >= RoleScore ->
            %% 上不了榜
            State;
        _ ->
            %% 更新排行榜
            NewRoles = update_rank_core(
                MaxLen, RankRole, Roles, IsSave, IsBroadcast
            ),
            NewRank = Rank#rr_rank{roles = NewRoles},
            NewRanks = [NewRank | LessRanks],
            NewActRank = ActRank#rr_act_rank{ranks = NewRanks},
            NewActsRank = [NewActRank | LessActsRank],
            State#rr_rank_state{acts_rank = NewActsRank}
    end.

update_rank_core(RankMax, RankRole, AscRankList, IsSave, IsBroadcast) ->
    {Len, DescRankList} = insert_into_sorted_list(RankRole, AscRankList),
    StartRanking = 1,
    AscRankList2 = set_ranking(DescRankList, RankMax, StartRanking, []),
    IsSave andalso spawn(fun() -> save_rank_role(RankRole) end),
    NewAscRankList = case Len > RankMax of
        true -> %% RankRole替换了排行榜上的某个人
            [DelRole | AscRankList3] = AscRankList2,
            IsSave andalso spawn(fun() -> del_rank_role(DelRole) end),
            AscRankList3;
        _ -> %% 排行榜上增加了一个玩家
            AscRankList2
    end,
    case IsBroadcast of
        true -> broadcast_rank(DescRankList, NewAscRankList);
        false -> ok
    end,
    NewAscRankList.

%% 根据排名名次需要的积分要求对排行榜进行排名。
set_ranking([], _RankMax, _Ranking, RankList) ->
    RankList;
set_ranking([RankRole | T], RankMax, Ranking, RankList) ->
    case Ranking > RankMax of
        true -> %% 已经排到最大排名之后了，剩下的排名都设置为0
            RankRole2 = RankRole#rr_rank_role{ranking  = 0},
            RankList2 = [RankRole2 | RankList],
            set_ranking(T, RankMax, Ranking + 1, RankList2);
        false ->
            #rr_rank_role{act_id  = ActId, score = Score} = RankRole,
            ScoreRanking = data_race_rank:get_score_top_ranking(ActId, Score),
            if
                ScoreRanking > RankMax ->
                    %% 已经排到最大排名之后了，剩下的排名都设置为0
                    RankRole2 = RankRole#rr_rank_role{ranking  = 0},
                    RankList2 = [RankRole2 | RankList],
                    set_ranking(T, RankMax, RankMax + 1, RankList2);
                true ->
                    RoleRanking = max(Ranking, ScoreRanking),
                    RankRole2 = RankRole#rr_rank_role{ranking  = RoleRanking},
                    RankList2 = [RankRole2 | RankList],
                    set_ranking(T, RankMax, RoleRanking + 1, RankList2)
            end
    end.

%% ---------------------------------------------------------------------------
%% @doc 把元素合并到有序升序列表中。返回降序列表。
-spec insert_into_sorted_list(Element, List) -> {Len, NewList} when
    Element     :: #rr_rank_role{},
    List        :: [#rr_rank_role{}],
    Len         :: integer(),
    NewList     :: [#rr_rank_role{}].
%% ---------------------------------------------------------------------------
insert_into_sorted_list(Element, List) ->
    insert_into_sorted_list(Element, List, 0, []).

insert_into_sorted_list(false, [], Len, List) ->
    {Len, List};
insert_into_sorted_list(false, [H | T], Len, List) ->
    insert_into_sorted_list(false, T, Len + 1, [H | List]);

insert_into_sorted_list(Element, [], Len, List) ->
    insert_into_sorted_list(false, [], Len + 1, [Element | List]);
insert_into_sorted_list(Element, [H | T], Len, List) ->
    if
        Element#rr_rank_role.role_id =:= H#rr_rank_role.role_id ->
            %% 已经在列表中，保留新的数据
            insert_into_sorted_list(Element, T, Len, List);
        Element#rr_rank_role.score > H#rr_rank_role.score ->
            insert_into_sorted_list(Element, T, Len + 1, [H | List]);
        (
            Element#rr_rank_role.score =:= H#rr_rank_role.score andalso
            Element#rr_rank_role.time < H#rr_rank_role.time
        ) ->
            insert_into_sorted_list(Element, T, Len + 1, [H | List]);
        true -> %% 新加入的元素加入到列表中
            insert_into_sorted_list(false, [H | T], Len + 1, [Element | List])
    end.


%% ---------------------------------------------------------------------------
%% @doc 把排行榜数据更新给榜单上的所有人
-spec broadcast_rank(RecieverList, RankList) -> ok when
    RecieverList    :: [#rr_rank_role{}],
    RankList        :: [#rr_rank_role{}].
%% ---------------------------------------------------------------------------
broadcast_rank(_RecieverList, []) ->
    ok;
broadcast_rank(RecieverList, RankList) ->
    ServerRList = classify_roles_by_server_id(RecieverList, []),
    BinData = pack_rank_data(RankList),
    broadcast_rank_do(ServerRList, BinData).

broadcast_rank_do([], _BinData) ->
    ok;
broadcast_rank_do([{ServerId, RoleList} | T], BinData) ->
    RoleIdList = [RoleId || #rr_rank_role{role_id = RoleId} <- RoleList],
    clusters_center:apply_cast(
        ServerId, {lib_server_send, send_to_uid, [RoleIdList, BinData]}
    ),
    broadcast_rank_do(T, BinData).

%% 打包排行榜数据
pack_rank_data(RankList) ->
    [#rr_rank_role{act_id = ActId} | _T] = RankList,
    DataList = pack_rank_data(RankList, []),
    {ok, BinData} = pt_333:write(33304, [ActId, DataList]),
    BinData.

pack_rank_data([], DataList) ->
    DataList;
pack_rank_data([RankRole | T], DataList) ->
    #rr_rank_role{
        role_id = RoleId, role_name = Name, server_num = ServerNum
        ,score = Score, ranking = Ranking
    } = RankRole,
    %% 排名大于0才是真正上榜
    DataList2 = case Ranking > 0 of
        true -> [{RoleId, Name, ServerNum, Ranking, Score} | DataList];
        false -> DataList
    end,
    pack_rank_data(T, DataList2).

%% 广播排行榜
broadcast_rank(Rank = #rr_rank{}) ->
    #rr_rank{roles = Ranks} = Rank,
    broadcast_rank(Ranks, Ranks).
%% 广播排行榜
broadcast_act_rank(ActRank = #rr_act_rank{}) ->
    #rr_act_rank{ranks = Ranks} = ActRank,
    [broadcast_rank(Rank) || Rank <- Ranks],
    ok.


%% ---------------------------------------------------------------------------
%% @doc 结算排行榜
settlement_rank(State, ActId, StartTime, EndTime) ->
    #rr_rank_state{acts_rank = ActsRank} = State,
    case lists:keyfind(ActId, #rr_act_rank.act_id, ActsRank) of
        ActRank = #rr_act_rank{} ->
            broadcast_act_rank(ActRank),
            %% 1.> 先写日志保存排行榜数据
            log_race_rank(StartTime,EndTime,ActRank),
            %% 2.> 发奖励
            settlement_rank_award(StartTime,EndTime,ActRank);
        _ ->
            ok
    end,
    State.

%% 结算排行榜，发奖励。
settlement_rank_award(StartTime,EndTime,ActRank) ->
    #rr_act_rank{ranks = Ranks} = ActRank,
    [settlement_rank_award_do(StartTime,EndTime,Rank) || Rank <- Ranks],
    ok.

settlement_rank_award_do(StartTime,EndTime,Rank) ->
    #rr_rank{roles = Roles} = Rank,
    ServerRList = classify_roles_by_server_id(Roles, []),
    [
        clusters_center:apply_cast(
            ServerId, {race_rank, race_rank_award, [StartTime,EndTime,RoleList]}
        ) || {ServerId, RoleList} <- ServerRList
    ],
    ok.

classify_roles_by_server_id([], ServerList) ->
    ServerList;
classify_roles_by_server_id([RankRole | T], ServerList) ->
    #rr_rank_role{server_id = ServerId} = RankRole,
    ServerList2 = case lists:keytake(ServerId, 1, ServerList) of
        {value, {ServerId, RoleList}, LessServerList} ->
            RoleList2 = [RankRole | RoleList],
            [{ServerId, RoleList2} | LessServerList];
        false ->
            [{ServerId, [RankRole]} | ServerList]
    end,
    classify_roles_by_server_id(T, ServerList2).

%% 最大上榜人数
get_rank_max() ->
    case data_race_rank:get_race_rank_config(max_rank) of
        Max when is_integer(Max) -> min(Max, ?RR_RANK_MAX);
        _ -> ?RR_RANK_MAX
    end.


%% ---------------------------------------------------------------------------
%% @doc 保存上榜角色数据
-spec save_rank_role(RankRole) -> ok when
    RankRole  :: #rr_rank_role{}.
%% ---------------------------------------------------------------------------
save_rank_role(RankRole) ->
    #rr_rank_role{
        act_id  = ActId, zone_id = ZoneId, role_id = RoleId
        ,role_name = Name, server_id = ServerId
        ,server_num = ServerNum, score = Score, time = Time
    } = RankRole,
    Data = [
        ActId, ZoneId, RoleId, Name, ServerId, ServerNum, Score, Time
    ],
    db:execute(io_lib:format(?RR_RANK_REPLACE, Data)),
    ok.


%% ---------------------------------------------------------------------------
%% @doc 批量保存排行榜上的玩家。
-spec save_rank_roles(RankRoles) -> ok when
    RankRoles     :: [#rr_rank_role{}].
%% ---------------------------------------------------------------------------
save_rank_roles(RankRoles) ->
    DbRoleList = format_save_rank_roles([], RankRoles),
    batch_save(DbRoleList),
    ok.

batch_save([]) ->
    ok;
batch_save(DbRoleList) ->
    ArgsString = string:join(DbRoleList, ","),
    Sql = io_lib:format(?RR_RANK_BATCH, [ArgsString]),
    db:execute(Sql),
    ok.

format_save_rank_roles(DbRoleList, []) ->
    DbRoleList;
format_save_rank_roles(DbRoleList, [Role | T]) ->
    DbRole = format_save_rank_role(Role),
    DbRoleList2 = [DbRole | DbRoleList],
    format_save_rank_roles(DbRoleList2, T).

format_save_rank_role(RankRole)->
    #rr_rank_role{
        act_id  = ActId, zone_id = ZoneId, role_id = RoleId
        ,role_name = Name, server_id = ServerId
        ,server_num = ServerNum, score = Score, time = Time
    } = RankRole,
    Data = [
        ActId, ZoneId, RoleId, Name, ServerId, ServerNum, Score, Time
    ],
    io_lib:format("(~p,~p,~p,'~ts',~p,'~ts',~p,~p)", Data).


%% ---------------------------------------------------------------------------
%% @doc 删除排行榜上的角色数据
-spec del_rank_role(RankRole) -> ok when
    RankRole  :: #rr_rank_role{}.
%% ---------------------------------------------------------------------------
del_rank_role(RankRole) ->
    #rr_rank_role{act_id  = ActId, role_id = RoleId} = RankRole,
    Sql = io_lib:format(?RR_RANK_DEL, [ActId, RoleId]),
    db:execute(Sql),
    ok.


%% ---------------------------------------------------------------------------
%% @doc 删除排行榜上的角色数据
-spec batch_del_rank_role(ActId, RankRoles) -> ok when
    ActId     :: integer(),
    RankRoles :: [#rr_rank_role{}].
%% ---------------------------------------------------------------------------
batch_del_rank_role(_ActId, []) ->
    ok;
batch_del_rank_role(ActId, RankRoles) ->
    RoleIdList = [
        erlang:integer_to_list(Id) || #rr_rank_role{role_id = Id} <- RankRoles
    ],
    RoleIdsStr = string:join(RoleIdList, ","),
    Sql = io_lib:format(?RR_RANK_BATCH_DEL, [ActId, RoleIdsStr]),
    db:execute(Sql),
    ok.


%% ---------------------------------------------------------------------------
%% @doc 删除ActId活动的排行榜数据
-spec del_act_rank(ActId) -> ok when
    ActId       :: integer().
%% ---------------------------------------------------------------------------
del_act_rank(ActId) ->
    db:execute(io_lib:format(?RR_RANK_DEL_ACT, [ActId])),
    ok.


%% ---------------------------------------------------------------------------
%% @doc 排行榜数据日志。
log_race_rank(StartTime,EndTime,ActRank) ->
    #rr_act_rank{ranks = Ranks} = ActRank,
    log_race_rank_do(StartTime,EndTime,Ranks).

log_race_rank_do(_StartTime,_EndTime,[]) ->
    ok;
log_race_rank_do(StartTime,EndTime,Ranks) ->
    Now = utime:unixtime(),
    log_do(Ranks, [], Now, 0,StartTime,EndTime).

log_do([], DbRoleList, _Now, _Num,_StartTime,_EndTime) ->
    batch_log(DbRoleList);
log_do([Rank | T], DbRoleList, Now, Num,StartTime,EndTime) ->
    #rr_rank{roles = RoleList} = Rank,
    DbRoleList2 = format_log_rank_roles(DbRoleList, Now, RoleList,StartTime,EndTime),
    case (Num + 1) rem 2 of
        true ->
            batch_log(DbRoleList2),
            log_do(T, [], Now, 0,StartTime,EndTime);
        _->
            log_do(T, DbRoleList2, Now, Num + 1,StartTime,EndTime)
    end.

batch_log([]) ->
    ok;
batch_log(DbRoleList) ->
    ArgsString = string:join(DbRoleList, ","),
    Sql = io_lib:format(?RR_LOG_RANK_BATCH, [ArgsString]),
    db:execute(Sql),
    ok.

format_log_rank_roles(DbRoleList, _Now, [],_StartTime,_EndTime) ->
    DbRoleList;
format_log_rank_roles(DbRoleList, Now, [Role | T],StartTime,EndTime) ->
    DbRole = format_log_rank_role(Role, Now,StartTime,EndTime),
    DbRoleList2 = [DbRole | DbRoleList],
    format_log_rank_roles(DbRoleList2, Now, T,StartTime,EndTime).

format_log_rank_role(RankRole, Now,StartTime,EndTime)->
    #rr_rank_role{
        act_id  = ActId, zone_id = ZoneId, role_id = RoleId
        ,role_name = Name, server_id = ServerId
        ,server_num = ServerNum, score = Score, ranking = Rank, time = Time
    } = RankRole,
    Data = [
        ActId, ZoneId, RoleId, Name, ServerId, ServerNum
        ,StartTime,EndTime,Score, Rank, Time, Now
    ],
    io_lib:format("(~p,~p,~p,'~ts',~p,~p,~p,~p,~p,~p,~p,~p)", Data).


do_add_svrs([],ZoneMap)->
    ZoneMap;
do_add_svrs([{SerId,ZoneId,_}|T],ZoneMap)->
    NZoneMap = maps:put(SerId,ZoneId,ZoneMap),
    do_add_svrs(T,NZoneMap).


%$%重新分区处理
do_recalc_zone([],State)->
    State;
do_recalc_zone([{OZone, NZone}|T],State)->
    NState = handle_rezone(State,NZone,OZone),
    do_recalc_zone(T,NState).

%% @doc 重新分区处理
handle_rezone(State, NZone, OZone) ->
    #rr_rank_state{
        acts_rank = ActsRank
    } = State,
    ActsRank2 = [
        act_rank_handle_rezone(NZone, OZone, ActRank) || ActRank <- ActsRank
    ],
    State#rr_rank_state{acts_rank = ActsRank2}.

%% 某个竞榜活动各个分区的排行榜
act_rank_handle_rezone(NZone, OZone, ActRank) ->
    #rr_act_rank{act_id = ActId, ranks = Ranks} = ActRank,
    {
        Ranks2, OtherRankAddRoles
    } = case lists:keytake(OZone, #rr_rank.zone_id, Ranks) of
        {value, #rr_rank{roles = Roles}, LessRanks} ->
            {LessRanks, Roles};
        _ -> %% 找不到原分区
            {Ranks, []}
    end,
    Ranks3 = case OtherRankAddRoles of
        [] -> %% 没有玩家需要移动到其他分区
            Ranks2;
        _ -> %% 需要移动到其他分区的玩家重新上榜
            roles_hit_rank(OtherRankAddRoles, Ranks2, ActId, NZone)
    end,
    ActRank#rr_act_rank{ranks = Ranks3}.
