%% -----------------------------------
%% module   :god_dun_rank_api.erl
%% desc     :唤神副本排行相关逻辑
%% author   :hh
%% time     :20/08/07
%% -----------------------------------

-module(god_dun_rank_api).


-export([
    init_rank_data/1
    ,init_wave_rank_data/1
    ,zone_rank_init/1
    ,update_zone_power_rank/3
    ,update_zone_serv_info/1
    ,timer_update/1
    ,daily_reset/1
    ,recalc_zone/2
    ,add_svrs/2
    ,handle_merge/4
    ,notice_power_rank_download/0
    ,db_update_role_wave_score/1
    ,db_update_role_score/1
    ,db_update_role_god_live/3
    ,send_role_mail_rewards/6
    ,update_power_rank/1
    ,sort_rank_list/2
    ,pack_score_rank/1
    ,calc_role_percent_rank_do/2
    ]).

-export([
    collect_power_rank/0
    ,upload_local_power_rank/2
    ,upload_role_power_rank/5
    ,local_notice_power_rank_download/0
    ]).

-export([
    test/1
    ,unzip_test/2
    ,gm_notice_power_rank/0
    ,calc_zone_rank/1
    ]).

-include("common_rank.hrl").
-include("god_dun.hrl").
-include("server.hrl").
-include("predefine.hrl").
-include("common.hrl").
-include("language.hrl").
-include("def_module.hrl").
-include("figure.hrl").
-include("role.hrl").

-define(POWER_RANK_LEN, 100).
-define(WAVE_SCORE_RANK_LEN, 50).

init_rank_data(RankData) ->
    RankList = [make_record(god_dun_score_role, Data) || Data <- RankData],
    RankList.

init_wave_rank_data(RankData) ->
    RankList = [make_record(god_dun_wave_score_role, Data) || Data <- RankData],
    RankList.


zone_rank_init(State) ->
    #god_dun_rank_state{
        score_rank = ScoreRank,
        dun_wave_rank = DunWaveRank,
        power_rank = PowerRank,
        today_roles = TodayRoles,
        srvzones = SrvZones
    } = State,
    NewScoreRank    = update_score_rank_zone(SrvZones, ScoreRank),
    NewDunWaveRank  = update_wave_score_rank_zone(SrvZones, DunWaveRank),
    NewPowerRank    = update_power_rank(PowerRank),
    NewTodayRoles   = update_zone_live_roles(SrvZones, TodayRoles),
    State#god_dun_rank_state{
        score_rank = NewScoreRank,
        dun_wave_rank = NewDunWaveRank,
        power_rank = NewPowerRank,
        today_roles = NewTodayRoles
    }.

%% 初始化更新分区积分排行列表
update_score_rank_zone(SrvZones, RankList) ->
    UpdateFun = fun(Role = #god_dun_score_role{role_id = RoleId, server_id = ServerId}, Acc) ->
        NowSrv = clusters_center:find_now_server_id(ServerId),
        ZoneId = maps:get(NowSrv, SrvZones, 0),
        if
            ZoneId == 0 -> Acc;
            true ->
                case lists:keyfind(ZoneId, 1, Acc) of
                    {_, ZoneRank} ->
                        case lists:keyfind(RoleId, #god_dun_score_role.role_id, ZoneRank) of
                            #god_dun_score_role{} ->
                                Acc;
                            _ ->
                                NewZoneRank = lists:keystore(RoleId, #god_dun_score_role.role_id, ZoneRank, Role),
                                lists:keystore(ZoneId, 1, Acc, {ZoneId, NewZoneRank})
                        end;
                    _ ->
                        NewZoneRank = [Role],
                        lists:keystore(ZoneId, 1, Acc, {ZoneId, NewZoneRank})
                end
        end
    end,
    NewRankList = lists:foldl(UpdateFun, [], RankList),
    update_score_rank(NewRankList).

%% 初始化更新分区波数积分排行
update_wave_score_rank_zone(SrvZones, DunWaveRank) ->
    F = fun(Role = #god_dun_wave_score_role{role_id = RoleId, server_id = ServerId, wave = Wave}, Acc) ->
        NowSrv = clusters_center:find_now_server_id(ServerId),
        ZoneId = maps:get(NowSrv, SrvZones, 0),
        if
            ZoneId == 0 -> Acc;
            true ->
                case lists:keyfind({ZoneId, Wave}, 1, Acc) of
                    {_, WaveRank} ->
                        case lists:keyfind(RoleId, #god_dun_wave_score_role.role_id, WaveRank) of
                            #god_dun_wave_score_role{} ->
                                NewWaveRank = WaveRank;
                            _ ->
                                NewWaveRank = lists:keystore(RoleId, #god_dun_wave_score_role.role_id, WaveRank, Role)
                        end,
                        lists:keystore({ZoneId, Wave}, 1, Acc, {{ZoneId, Wave}, NewWaveRank});
                    _ ->
                        NewWaveRank = [Role],
                        lists:keystore({ZoneId, Wave}, 1, Acc, {{ZoneId, Wave}, NewWaveRank})
                end    
        end
    end,
    NewDunWaveRankList = lists:foldl(F, [], DunWaveRank),
    NewDunWaveRankList1 = update_wave_score_rank(NewDunWaveRankList, db_update),
    %% TODO 删除数据库数据,只保留每波前三名
    NewDunWaveRankList1.

%% 更新游戏服上传的战力排行数据
update_zone_power_rank(State, ZoneId, ZonePowerRankL) ->
    #god_dun_rank_state{power_rank = PowerRankL} = State,
    NewPowerRankL =
    case lists:keyfind(ZoneId, 1, PowerRankL) of
        {_, OldZoneRankL} ->
            NewZoneRankL = update_zone_role_power_rank(OldZoneRankL, ZonePowerRankL),
            lists:keystore(ZoneId, 1, PowerRankL, {ZoneId, NewZoneRankL});
        _ ->
            lists:keystore(ZoneId, 1, PowerRankL, {ZoneId, ZonePowerRankL})
    end,
    State#god_dun_rank_state{power_rank = NewPowerRankL}.

update_zone_role_power_rank(ZonePowerRankL, []) -> ZonePowerRankL;
update_zone_role_power_rank(OldZoneRankL, [PowerRole = #god_dun_power_role{role_id = RoleId}|Rest]) ->
    case lists:keyfind(RoleId, #god_dun_power_role.role_id, OldZoneRankL) of
        #god_dun_power_role{} ->
            NewZoneRankL = lists:keyreplace(RoleId, #god_dun_power_role.role_id, OldZoneRankL, PowerRole),
            update_zone_role_power_rank(NewZoneRankL, Rest);
        _ ->
            NewZoneRankL = lists:keystore(RoleId, #god_dun_power_role.role_id, OldZoneRankL, PowerRole),
            update_zone_role_power_rank(NewZoneRankL, Rest)
    end.

timer_update(State) ->
    #god_dun_rank_state{
        score_rank = ScoreRankL,
        dun_wave_rank = WaveScoreRankL,
        today_roles = TodayRoles,
        zonesrvs = ZoneSrvs
    } = State,
    NewScoreRankL       = update_score_rank(ScoreRankL),
    NewWaveScoreRankL   = update_wave_score_rank(WaveScoreRankL, ignore),
    NewTodayRoles       = calc_zone_live_roles(TodayRoles),
    NewState = State#god_dun_rank_state{
        score_rank    = NewScoreRankL,
        dun_wave_rank = NewWaveScoreRankL
    },
    broad_cast_rank(ZoneSrvs, NewScoreRankL, NewTodayRoles),
    NewState.

%% 跨天更新
daily_reset(State) ->
    #god_dun_rank_state{score_rank = ScoreRank, today_roles = TodayRoles} = State,
    role_rank_settlement(ScoreRank, TodayRoles),
    db:execute(?SQL_DELETE_DUN_RANK_SCORE, []),
    db:execute(?SQL_DELETE_DUN_RANK_WAVE_SCORE, []),
    db:execute(?SQL_DELETE_LIVE_ROLE, []),
    clusters_center:apply_to_all_node(?MODULE, collect_power_rank, []),
    State#god_dun_rank_state{
        score_rank = [],
        dun_wave_rank = [],
        power_rank = [],
        today_roles = []
    }.

%% 结算
role_rank_settlement(ScoreRank, TodayRoles) ->
    PercentRankMax = data_god_dun:get_max_rank_by_type(1),
    TodayRoles1 = calc_zone_live_roles(TodayRoles),
    ZoneRankNumL = calc_zone_rank(TodayRoles1),
    lists:map(fun({ZoneId, ZoneScoreRankL}) -> 
        [begin
            Rewards = 
            if Rank > PercentRankMax ->
                    {RewardRank, RewardRank1} = calc_role_percent_rank(ZoneRankNumL, ZoneId, Rank),
                    data_god_dun:get_rank_reward(2, RewardRank);
                true ->
                    RewardRank = Rank, RewardRank1 = 0,
                    data_god_dun:get_rank_reward(1, Rank)
            end,
            case Rewards of
                [_|_] ->
                    clusters_center:apply_cast(ServerId, ?MODULE, send_role_mail_rewards, [RoleId, PercentRankMax, Rank, RewardRank, RewardRank1, Rewards]);
            _ -> ?ERR("rank ~p", [Rank]),
                skip
            end
        end || #god_dun_score_role{rank = Rank, role_id = RoleId, server_id = ServerId} <- ZoneScoreRankL]
    end, ScoreRank),
    ok.

calc_zone_rank(TodayRoles) ->
    PercentRankMax = data_god_dun:get_max_rank_by_type(1),
    AllRankRanges = data_god_dun:get_all_rank_range(),
    lists:foldl(fun({ZoneId, ZoneRoleNum}, Acc) -> 
        ZoneRankRange = lists:foldl(fun({RankMin, RankMax, Percent}, Acc1) -> 
            Num = util:ceil(ZoneRoleNum*Percent/100),
            case calc_pre_rank_num(Acc1) of
                TotalNum = 0 ->
                    [{{RankMin, RankMax}, PercentRankMax+TotalNum+1, PercentRankMax+TotalNum+Num}|Acc1];
                TotalNum ->
                    [{{RankMin, RankMax}, TotalNum+1, TotalNum+Num}|Acc1]
            end
        end, [], AllRankRanges),
        [{ZoneId, ZoneRankRange}|Acc]
    end, [], TodayRoles).

calc_pre_rank_num(RankNumL) ->
    case lists:reverse(lists:keysort(3, RankNumL)) of
        [{{_, _}, _, MaxNum}|_] ->
            MaxNum;
        _ ->
            0
    end.

calc_role_percent_rank(ZoneRankNumL, ZoneId, Rank) ->
    case lists:keyfind(ZoneId, 1, ZoneRankNumL) of
        {_, ZoneNumL} ->
            calc_role_percent_rank_do(ZoneNumL, Rank);
        _ ->
            {0, 0}
    end.

calc_role_percent_rank_do([], _Rank) -> {0, 0};
calc_role_percent_rank_do([{{RankMinPer, RankMaxPer}, RankMin, RankMax}|Rest], Rank) ->
    if
        Rank >= RankMin andalso Rank =< RankMax ->
            {RankMinPer, RankMaxPer};
        true ->
            calc_role_percent_rank_do(Rest, Rank)
    end.

send_role_mail_rewards(RoleId, PercentRankMax, Rank, Rank1, Rank2, Rewards) ->
    if Rank =< PercentRankMax ->
            Title = ?LAN_MSG(6100009),
            Content = uio:format(?LAN_MSG(6100010), [Rank]);
        true ->
            Title = ?LAN_MSG(6100009),
            if Rank1 == Rank2 ->
                    Rank3 = PercentRankMax+1, Rank4 = integer_to_list(Rank1) ++ "%";
                true ->
                    Rank3 = integer_to_list(Rank1) ++ "%",
                    Rank4 = integer_to_list(Rank2) ++ "%"
            end,
            Content = uio:format(?LAN_MSG(6100011), [Rank3, Rank4])
    end,
    lib_mail_api:send_sys_mail([RoleId], ?MOD_DUNGEON, 10, Title, Content, Rewards),
    ok.

%% ZoneMap       #{OldZoneId  => NewZoneId}
%% FactionMaps   #{OldZoneId  => FactionMap :: #{OldFaction => NewFaction}}
%% （ZoneMap中，OldZoneId 等于 NewZoneId 说明该分区为主分区，未发生zone_id变化）
    % CBMap = #{
    %     cz_state => CZState
    %     ,svr_zone_map => NewSvrZoneMap
    %     ,zone_map => ZoneMap
    %     ,faction_maps => FactionMaps
    % },
recalc_zone(Map, State) ->
    #god_dun_rank_state{
        score_rank = ScoreRankL,
        dun_wave_rank = WaveScoreRankL,
        power_rank = PowerRankL,
        today_roles = TodayRoles,
        srvzones = SrvZones,
        zonesrvs = ZoneSrvs
    } = State,
    #{zone_map := ZoneMap, svr_zone_map := NewSvrZoneMap} = Map,
    % ?ERR("~p ~p", [ZoneMap, NewSvrZoneMap]),
    ZoneList = maps:to_list(ZoneMap),
    SvrZoneChangeL = maps:to_list(NewSvrZoneMap),
    NewScoreRankL = recalc_score_rank_list(ZoneList, ScoreRankL),
    % ?ERR("old ~p new ~p", [ScoreRankL, NewScoreRankL]),
    NewWaveScoreRankL = recalc_wave_score_rank_list(ZoneList, WaveScoreRankL),
    NewPowerRankL = recalc_power_rank(ZoneList, PowerRankL),
    % ?ERR("~p ~p", [PowerRankL, NewPowerRankL]),
    NewTodayRoles = recalc_today_role(ZoneList, TodayRoles),
    NewSrvZones   = recalc_srv_zone(SvrZoneChangeL, SrvZones),
    NewZoneSrvs   = recalc_zone_srv_info(SvrZoneChangeL, SrvZones, ZoneSrvs),
    NewState = State#god_dun_rank_state{
        score_rank = NewScoreRankL,
        dun_wave_rank = NewWaveScoreRankL,
        power_rank = NewPowerRankL,
        today_roles = NewTodayRoles,
        srvzones = NewSrvZones,
        zonesrvs = NewZoneSrvs
    },
    timer_update(NewState).

%% Map :: #{cz_state => CZState, add_servers => AddServers}
%% AddServers :: [{ServerId, ZoneId, Faction}]
add_svrs(Map, State) ->
    #god_dun_rank_state{zonesrvs = ZoneSrvs, srvzones = SrvZones} = State,
    #{add_servers := AddServers} = Map,
    {NewSrvZones, NewZoneSrvs} = lists:foldl(fun({ServId, ZoneId, _FacId}, {TempSrvZones, TempZoneSrvs}) -> 
        NewTempZoneSrvs = 
        case lists:keyfind(ZoneId, 1, TempZoneSrvs) of
            {_, SrvL} ->
                case lists:member(ServId, SrvL) of
                    true ->
                        TempZoneSrvs;
                    _ ->
                        lists:keystore(ZoneId, 1, TempZoneSrvs, {ZoneId, [ServId|SrvL]})
                end;
            _ ->
                lists:keystore(ZoneId, 1, TempZoneSrvs, {ZoneId, [ServId]})
        end,
        NewTempSrvZones = maps:put(ServId, ZoneId, TempSrvZones),
        {NewTempSrvZones, NewTempZoneSrvs}
    end, {SrvZones, ZoneSrvs}, AddServers),
    State#god_dun_rank_state{
        zonesrvs = NewZoneSrvs, 
        srvzones = NewSrvZones
    }.

%% 合服处理
handle_merge(MasterServId, MasterServNum, MergeServers, State) ->
    #god_dun_rank_state{
        score_rank = ScoreRank, dun_wave_rank = DunWaveRank, 
        power_rank = PowerRank, srvzones = SrvZones, zonesrvs = ZoneSrvs
    } = State,
    {NewSrvZones, MergedZoneSrvs} = 
    maps:fold(fun(ServId, ZoneId, {TempSrvZones, TempMergedZoneSrvs}) -> 
        case lists:member(ServId, MergeServers) of
            true -> {maps:remove(ServId, TempSrvZones), [{ServId, ZoneId}|TempMergedZoneSrvs]};
            _    -> {TempSrvZones, TempMergedZoneSrvs}
        end
    end, {SrvZones, []}, SrvZones),
    NewZoneSrvs = 
    lists:foldl(fun({ZoneId, SrvL}, TempZoneSrvs) -> 
        NewSrvL = SrvL -- MergeServers,
        [{ZoneId, NewSrvL}|TempZoneSrvs]
    end, [], ZoneSrvs),
    NewScoreRank    = merge_score_rank({MasterServId, MasterServNum}, MergedZoneSrvs, MergedZoneSrvs, ScoreRank, []),
    NewDunWaveRank  = merge_wave_rank({MasterServId, MasterServNum}, MergedZoneSrvs, MergedZoneSrvs, DunWaveRank, []),
    NewPowerRank    = merge_power_rank({MasterServId, MasterServNum}, MergedZoneSrvs, MergedZoneSrvs, PowerRank),
    State#god_dun_rank_state{
        score_rank = NewScoreRank,
        dun_wave_rank = NewDunWaveRank,
        power_rank = NewPowerRank,
        srvzones = NewSrvZones,
        zonesrvs = NewZoneSrvs
    }.

merge_score_rank(_, [], _, ScoreRank, DBList) ->
    SQL = io_lib:format(?SQL_BATCH_REPLACE_GOD_DUN_RANK, [string:join(DBList, ",")]),
    DBList =/= [] andalso db:execute(SQL),
    ScoreRank;
merge_score_rank({MasterServId, MasterServNum}, [{_, ZoneId}|Rest], MergedZoneSrvs, ScoreRank, DBList) ->
    case lists:keyfind(ZoneId, 1, ScoreRank) of
        {_, ZoneScoreRank} ->
            {NewZoneScoreRank, NewDBList} = lists:foldl(fun(Role, {TempZoneScoreRank, TempDBList}) -> 
                #god_dun_score_role{server_id = SrvId} = Role,
                NewRole = 
                case lists:keyfind(SrvId, 1, MergedZoneSrvs) of
                    {_, ZoneId} -> Role#god_dun_score_role{server_id = MasterServId, server_num = MasterServNum};
                    _           -> Role
                end,
                if NewRole =/= Role ->
                        #god_dun_score_role{
                            role_id = RoleId, server_id = ServId, server_num = SerNum, 
                            score = Score, show = Show, pass = Pass} = NewRole,
                        ShowBin = util:term_to_bitstring(Show),
                        SQL = io_lib:format(?SQL_BATCH_GOD_DUN_RANK_VAL, [RoleId, ServId, SerNum, Score, ShowBin, Pass]),
                        NewTempDBList = [SQL|TempDBList];
                    true -> NewTempDBList = TempDBList
                end,
                {[NewRole|TempZoneScoreRank], NewTempDBList}
            end, {[], DBList}, ZoneScoreRank),
            NewScoreRank = lists:keystore(ZoneId, 1, ScoreRank, {ZoneId, NewZoneScoreRank}),
            merge_score_rank({MasterServId, MasterServNum}, Rest, MergedZoneSrvs, NewScoreRank, NewDBList);
        _ ->
            merge_score_rank({MasterServId, MasterServNum}, Rest, MergedZoneSrvs, ScoreRank, DBList)
    end.

merge_wave_rank(_, [], _, DunWaveRank, DBList) ->
    SQL = io_lib:format(?SQL_BATCH_GOD_DUN_RANK_WAVE_SCORE_REPLACE, [string:join(DBList, ",")]),
    DBList =/= [] andalso db:execute(SQL),
    DunWaveRank;
merge_wave_rank({MasterServId, MasterServNum}, [{_, ZoneId}|Rest], MergedZoneSrvs, DunWaveRank, DBList) ->
    DunWaveRank1 = calc_change_dun_wave_rank(ZoneId, DunWaveRank),
    {NewDunWaveRank, NewDBList} = merge_wave_rank_1(MasterServId, MasterServNum, ZoneId, MergedZoneSrvs, DunWaveRank1, DunWaveRank, DBList),
    merge_wave_rank({MasterServId, MasterServNum}, Rest, MergedZoneSrvs, NewDunWaveRank, NewDBList).

merge_wave_rank_1(_, _, _, _, [], DunWaveRank, DBList) ->
    {DunWaveRank, DBList};
merge_wave_rank_1(MasterSvrId, MasterSvrNum, ZoneId, MergedZoneSrvs, [{{ZoneId, Wave}, ZoneDunWaveRank}|Rest], DunWaveRank, DBList) ->
    {NewZoneDunWaveRank, NewDBList} = 
    lists:foldl(fun(Role, {TempZoneDunWaveRank, TempDBList}) -> 
        #god_dun_wave_score_role{server_id = SrvId} = Role,
        case lists:keyfind(SrvId, 1, MergedZoneSrvs) of
            {_, ZoneId} -> 
                NewRole = Role#god_dun_wave_score_role{server_id = MasterSvrId, server_num = MasterSvrNum},
                #god_dun_wave_score_role{
                    dun_id = DunId, wave = Wave, role_id = RoleId, score = Score, show = Show} = NewRole,
                ShowBin = util:term_to_bitstring(Show),
                SQL = io_lib:format(?SQL_BATCH_GOD_DUN_RANK_WAVE_SCORE_VAL, [DunId, Wave, RoleId, MasterSvrId, MasterSvrNum, Score, ShowBin]),
                NewTempZoneDunWaveRank = [NewRole|TempZoneDunWaveRank],
                NewTempDBList = [SQL|TempDBList];
            _ -> 
                NewTempZoneDunWaveRank = [Role|TempZoneDunWaveRank],
                NewTempDBList = TempDBList
        end,
        {NewTempZoneDunWaveRank, NewTempDBList}
    end, {[], DBList}, ZoneDunWaveRank),
    NewDunWaveRank = lists:keystore({ZoneId, Wave}, 1, DunWaveRank, {{ZoneId, Wave}, NewZoneDunWaveRank}),
    merge_wave_rank_1(MasterSvrId, MasterSvrNum, ZoneId, MergedZoneSrvs, Rest, NewDunWaveRank, NewDBList).

calc_change_dun_wave_rank(ZoneId, DunWaveRank) ->
    calc_change_dun_wave_rank_1(ZoneId, DunWaveRank, []).

calc_change_dun_wave_rank_1(_ZoneId, [], ChangeDunWaveRank) ->
    ChangeDunWaveRank;
calc_change_dun_wave_rank_1(ZoneId, [{{ZoneId, Wave}, ZoneDunWaveRank}|Rest], ChangeDunWaveRank) ->
    calc_change_dun_wave_rank_1(ZoneId, Rest, [{{ZoneId, Wave}, ZoneDunWaveRank}|ChangeDunWaveRank]);
calc_change_dun_wave_rank_1(ZoneId, [_|Rest], ChangeDunWaveRank) ->
    calc_change_dun_wave_rank_1(ZoneId, Rest, ChangeDunWaveRank).

merge_power_rank(_, [], _, PowerRank) ->
    PowerRank;
merge_power_rank({MasterServId, MasterServNum}, [{_, ZoneId}|Rest], MergedZoneSrvs, PowerRank) ->
    case lists:keyfind(ZoneId, 1, PowerRank) of
        {_, ZonePowerRank} ->
            NewZonePowerRank = lists:foldl(fun(Role, TempZoneScoreRank) -> 
                #god_dun_power_role{server_id = SrvId} = Role,
                NewRole = 
                case lists:keyfind(SrvId, 1, MergedZoneSrvs) of
                    {_, ZoneId} -> Role#god_dun_power_role{server_id = MasterServId, server_num = MasterServNum};
                    _           -> Role
                end,
                [NewRole|TempZoneScoreRank]
            end, [], ZonePowerRank),
            NewPowerRank = lists:keystore(ZoneId, 1, PowerRank, {ZoneId, NewZonePowerRank}),
            merge_power_rank({MasterServId, MasterServNum}, Rest, MergedZoneSrvs, NewPowerRank);
        _ ->
            merge_power_rank({MasterServId, MasterServNum}, Rest, MergedZoneSrvs, PowerRank)
    end.

recalc_score_rank_list([], ScoreRankL) -> ScoreRankL;
recalc_score_rank_list([{OldZoneId, NewZoneId}|Rest], ScoreRankL) ->
    case lists:keyfind(OldZoneId, 1, ScoreRankL) of
        {_, OldZoneScoreRankL} ->
            %% 删除旧区数据
            NewScoreRankL = lists:keydelete(OldZoneId, 1, ScoreRankL),
            %% 合并或修改新区数据
            case lists:keyfind(NewZoneId, 1, NewScoreRankL) of
                {_, NewZoneScoreRankL} ->
                    %% TODO 限制合并后的排行列表数量
                    NewZoneScoreRankL1 = lists:append(OldZoneScoreRankL, NewZoneScoreRankL),
                    NewZoneRankList = sort_rank_list(score_rank, NewZoneScoreRankL1),
                    NewScoreRankL1 = lists:keystore(NewZoneId, 1, NewScoreRankL, {NewZoneId, NewZoneRankList});
                _ ->
                    NewScoreRankL1 = lists:keystore(NewZoneId, 1, NewScoreRankL, {NewZoneId, OldZoneScoreRankL})
            end,
            recalc_score_rank_list(Rest, NewScoreRankL1);
        _ ->
            recalc_score_rank_list(Rest, ScoreRankL)
    end.

recalc_wave_score_rank_list(ZoneList, WaveScoreRankL) ->
    ChangeList = lists:foldl(fun({ZoneWaveInfo, _RankList}, Acc) ->
        case ZoneWaveInfo of
            {ZoneId, Wave} ->
                case lists:keyfind(ZoneId, 1, ZoneList) of
                    {_, NewZoneId} ->
                        [{ZoneId, NewZoneId, Wave}|Acc];
                    _ ->
                        Acc
                end;
            _ -> Acc
        end
    end, [], WaveScoreRankL),
    recalc_wave_score_rank_list_do(ChangeList, WaveScoreRankL).

recalc_wave_score_rank_list_do([], WaveScoreRankL) -> WaveScoreRankL;
recalc_wave_score_rank_list_do([{OldZoneId, NewZoneId, Wave}|Rest], WaveScoreRankL) ->
    case lists:keyfind({OldZoneId, Wave}, 1, WaveScoreRankL) of
        {_, OldRankL} ->
            WaveScoreRankL1 = lists:keydelete({OldZoneId, Wave}, 1, WaveScoreRankL),
            case lists:keyfind({NewZoneId, Wave}, 1, WaveScoreRankL1) of
                {_, NewRankL} ->
                    NewZoneWaveRankL = lists:append(OldRankL, NewRankL),
                    NewZoneWaveRankL1 = sort_rank_list(wave_score_rank, NewZoneWaveRankL),
                    WaveScoreRankL2 = lists:keystore({NewZoneId, Wave}, 1, WaveScoreRankL1, {{NewZoneId, Wave}, NewZoneWaveRankL1});
                _ ->
                    WaveScoreRankL2 = lists:keystore({NewZoneId, Wave}, 1, WaveScoreRankL1, {{NewZoneId, Wave}, OldRankL})
            end,
            recalc_wave_score_rank_list_do(Rest, WaveScoreRankL2);
        _ ->
            recalc_wave_score_rank_list_do(Rest, WaveScoreRankL)
    end.

recalc_power_rank([], PowerRankL) -> PowerRankL;
recalc_power_rank([{OldZoneId, NewZoneId}|Rest], PowerRankL) ->
    case lists:keyfind(OldZoneId, 1, PowerRankL) of
        {_, OldZonePowerRankL} ->
            NewPowerRankL = lists:keydelete(OldZoneId, 1, PowerRankL),
            case lists:keyfind(NewZoneId, 1, NewPowerRankL) of
                {_, NewZonePowerRankL} ->
                    ZonePowerRankL1 = lists:append(OldZonePowerRankL, NewZonePowerRankL),
                    NewZonePowerRankL1 = sort_rank_list(power_rank, ZonePowerRankL1),
                    ZonePowerRankL2 = lists:keystore(NewZoneId, 1, NewPowerRankL, {NewZoneId, NewZonePowerRankL1});
                _ ->
                    ZonePowerRankL2 = lists:keystore(NewZoneId, 1, NewPowerRankL, {NewZoneId, OldZonePowerRankL})
            end,
            recalc_power_rank(Rest, ZonePowerRankL2);
        _ ->
            recalc_power_rank(Rest, PowerRankL)
    end.

recalc_today_role([], TodayRoles) -> TodayRoles;
recalc_today_role([{OldZoneId, NewZoneId}|Rest], TodayRoles) ->
    case lists:keyfind(OldZoneId, 1, TodayRoles) of
        {_, OldRoles} ->
            NewTodayRoles = lists:keydelete(OldZoneId, 1, TodayRoles),
            case lists:keyfind(NewZoneId, 1, NewTodayRoles) of
                {_, NewRoles} ->
                    NewZoneRoles = lists:append(OldRoles, NewRoles),
                    NewTodayRoles1 = lists:keystore(NewZoneId, 1, NewTodayRoles, {NewZoneId, NewZoneRoles});
                _ ->
                    NewTodayRoles1 = lists:keystore(NewZoneId, 1, NewTodayRoles, {NewZoneId, OldRoles})
            end,
            recalc_today_role(Rest, NewTodayRoles1);
        _ ->
            recalc_today_role(Rest, TodayRoles)
    end.

recalc_srv_zone([], SrvZones) -> SrvZones;
recalc_srv_zone([{ServId, NewZoneId}|Rest], SrvZones) ->
    case maps:get(ServId, SrvZones, 0) of
        OldZoneId when OldZoneId =/= NewZoneId ->
            NewSrvZones = maps:put(ServId, NewZoneId, SrvZones),
            recalc_srv_zone(Rest, NewSrvZones);
        _ ->
            recalc_srv_zone(Rest, SrvZones)
    end.

recalc_zone_srv_info([], _OldSrvZones, ZoneSrvs) -> ZoneSrvs;
recalc_zone_srv_info([{ServId, NewZoneId}|Rest], OldSrvZones, ZoneSrvs) ->
    case maps:get(ServId, OldSrvZones, 0) of
        0 ->
            recalc_zone_srv_info(Rest, OldSrvZones, ZoneSrvs);
        NewZoneId ->
            recalc_zone_srv_info(Rest, OldSrvZones, ZoneSrvs);
        OldZoneId ->
            case lists:keyfind(OldZoneId, 1, ZoneSrvs) of
                {_, OldSrvs} ->
                    NewOldSrvs = lists:delete(ServId, OldSrvs),
                    NewZoneSrvs = lists:keystore(OldZoneId, 1, ZoneSrvs, {OldZoneId, NewOldSrvs});
                _ ->
                    NewZoneSrvs = ZoneSrvs
            end,
            case lists:keyfind(NewZoneId, 1, NewZoneSrvs) of
                {_, NewSrvs} ->
                    NewSrvs1 =
                    case lists:member(ServId, NewSrvs) of
                        true -> NewSrvs;
                        _    -> [ServId|NewSrvs]
                    end,
                    NewZoneSrvs1 = lists:keystore(NewZoneId, 1, NewZoneSrvs, {NewZoneId, NewSrvs1});
                _ ->
                    NewZoneSrvs1 = lists:keystore(NewZoneId, 1, NewZoneSrvs, {NewZoneId, [ServId]})
            end,
            recalc_zone_srv_info(Rest, OldSrvZones, NewZoneSrvs1)
    end.

%% 更新分区积分排行
update_score_rank(ScoreRankL) ->
    SortFun = fun({ZoneId, ZoneRankList}, AccX) ->
        NewZoneRankList = sort_rank_list(score_rank, ZoneRankList),
        [{ZoneId, NewZoneRankList}|AccX]
    end,
    lists:foldl(SortFun, [], ScoreRankL).

update_wave_score_rank(WaveRankL, DbType) ->
    MaxLen = 15,
    SortFun = fun({{ZoneIdX, WaveX}, WaveRankListX}, AccX) ->
            NewWaveRankListX = sort_rank_list(wave_score_rank, WaveRankListX),
            if length(NewWaveRankListX) > MaxLen ->
                    LeftRankL = lists:nthtail(MaxLen, NewWaveRankListX),
                    DbType == db_update andalso db_delete_rank_out_wave_score(LeftRankL),
                    NewWaveRankListX1 = lists:sublist(NewWaveRankListX, MaxLen);
                true ->
                    NewWaveRankListX1 = NewWaveRankListX
            end,
            [{{ZoneIdX, WaveX}, NewWaveRankListX1}|AccX]
    end,
    lists:foldl(SortFun, [], WaveRankL).


%% 更新分区战力排行
update_power_rank(RankList) ->
    lists:foldl(fun({ZoneId, ZoneRankL}, Acc) -> 
        NewPowerRankL = sort_rank_list(power_rank, ZoneRankL),
        [{ZoneId, NewPowerRankL}|Acc]
    end, [], RankList).


%% 统计分区活跃人数
update_zone_live_roles(SrvZones, TodayRoles) ->
    lists:foldl(fun({RoleId, ServerId, Time}, Acc) -> 
        case maps:get(ServerId, SrvZones, 0) of
            0 ->
                Acc;
            ZoneId ->
                case lists:keyfind(ZoneId, 1, Acc) of
                    {_, ZoneRoles = [_|_]} ->
                        case lists:keyfind(RoleId, 1, ZoneRoles) of
                            {_, _, _} ->
                                Acc;
                            _ ->
                                NewZoneRoles = lists:keystore(RoleId, 1, ZoneRoles, {RoleId, ServerId, Time}),
                                lists:keystore(ZoneId, 1, Acc, {ZoneId, NewZoneRoles})
                        end;
                    _ ->
                        NewZoneRoles = [{RoleId, ServerId, Time}],
                        lists:keystore(ZoneId, 1, Acc, {ZoneId, NewZoneRoles})
                end
        end
    end, [], TodayRoles).

calc_zone_live_roles(TodayRoles) ->
    lists:foldl(fun({ZoneId, ZoneRoles}, Acc) -> 
        [{ZoneId, length(ZoneRoles)}|Acc]
    end, [], TodayRoles).

sort_rank_list(score_rank, RankList) ->
    NewRankList = lists:keysort(#god_dun_score_role.score, RankList),
    {_, NewRankList1} = lists:foldr(fun(RankRole = #god_dun_score_role{}, {Index, Acc}) -> 
        #god_dun_score_role{score = Score} = RankRole,
        {Index+1, [RankRole#god_dun_score_role{display = Score, rank = Index}|Acc]}
    end, {1, []}, NewRankList),
    NewRankList1;

sort_rank_list(wave_score_rank, RankList) ->
    NewRankList = lists:keysort(#god_dun_wave_score_role.score, RankList),
    {_, NewRankList1} = lists:foldr(fun(RankRole = #god_dun_wave_score_role{}, {Index, Acc}) -> 
        % #god_dun_wave_score_role{score = Score} = RankRole,
        {Index+1, [RankRole#god_dun_wave_score_role{rank = Index}|Acc]}
    end, {1, []}, NewRankList),
    lists:sublist(lists:reverse(NewRankList1), 1, ?WAVE_SCORE_RANK_LEN);

sort_rank_list(power_rank, RankList) ->
    NewRankList = lists:keysort(#god_dun_power_role.power, RankList),
    {_, NewRankList1} = lists:foldr(fun(Role = #god_dun_power_role{}, {Index, Acc}) -> 
        {Index+1, [Role#god_dun_power_role{rank = Index}|Acc]}
    end, {1, []}, NewRankList),
    lists:sublist(lists:reverse(NewRankList1), 1, ?POWER_RANK_LEN).

make_record(god_dun_score_role, [RoleId, ServerId, ServerNum, Score, ShowBin, Pass|_]) ->
    #god_dun_score_role{
        role_id = RoleId,
        server_id = ServerId,
        server_num = ServerNum,
        score = Score,
        show = util:bitstring_to_term(ShowBin),
        pass = Pass
    };

make_record(god_dun_wave_score_role, [DunId, Wave, RoleId, ServerId, ServerNum, Score, ShowBin]) ->
    #god_dun_wave_score_role{
        dun_id = DunId,
        wave = Wave,
        role_id = RoleId,
        server_id = ServerId,
        server_num = ServerNum,
        score = Score,
        show = util:bitstring_to_term(ShowBin)
    }.

pack_score_rank(RankL) ->
    lists:foldl(fun(Role, Acc) -> 
        #god_dun_score_role{
            rank = Rank, score = Score, show = Show, server_num = ServerNum
        } = Role,
        if Rank == 0 -> Acc;
            true ->
                RoleInfo = tran_for_client_show(Show),
                [{ServerNum, Rank, RoleInfo, Score}|Acc]
        end
    end, [], RankL).

tran_for_client_show(Show) ->
    #role_key_figure{
        id = RoleId, name = RoleName, sex = Sex, career = Career,
        turn = Turn, vip_type = VipType, vip = Vip, sup_vip = SupVip,
        lv = Lv, power = Power, picture = Pic, picture_ver = PicVer,
        profile_photo_id = PhotoId, dress_list = DressList  
    } = Show,
    #figure{
        id = RoleId, name = RoleName, sex = Sex, career = Career,
        turn = Turn, vip_type = VipType, vip = Vip, sup_vip = SupVip,
        lv = Lv, power = Power, picture = Pic, picture_ver = PicVer,
        profile_photo_id = PhotoId, dress_list = DressList        
    }.

%% 游戏服执行
collect_power_rank() ->
    ServerId = config:get_server_id(),
    ServerNum = config:get_server_num(),
    ZoneId = czone_api:env_get_zone_id(),
    Mod = ?MODULE, 
    Fun = upload_local_power_rank, 
    Args = [ServerId, ServerNum, ZoneId],
    mod_common_rank:apply_cast(Mod, Fun, Args),
    ok.

%% 游戏服排行榜进程执行
upload_local_power_rank(CommonRankState, [ServerId, ServerNum, ZoneId|_]) ->
    PowerRankL = common_rank_logic:get_rank_list_by_rank_type(?RANK_TYPE_COMBAT),
    PowerRankL1 = lists:sublist(PowerRankL, 1, ?POWER_RANK_LEN),
    Fun = fun(#common_rank_role{role_id = RoleId} = RoleX, {Online, Offline}) -> 
       case lib_player:is_online_global(RoleId) of
            true ->
                {[RoleX|Online], Offline};
            _ ->
                {Online, [RoleX|Offline]}
        end
    end,
    {OnlineRank, OfflineRank} = lists:foldl(Fun, {[], []}, PowerRankL1),
    [lib_player:apply_cast(OnlineRole#common_rank_role.role_id, ?APPLY_CAST_STATUS, ?MODULE, upload_role_power_rank, [ServerId, ServerNum, ZoneId, OnlineRole])
     || OnlineRole <- OnlineRank, is_record(OnlineRole, common_rank_role)],

    Fun1 = fun(Role = #common_rank_role{role_id = RoleIdX}, {Index, Acc}) ->
        Index >= 10 andalso Index rem 10 == 0 andalso timer:sleep(1000),
        case catch db:get_one(?SQL_ROLE_ATTRE_SELECT, [RoleIdX]) of
            AttrBin when is_binary(AttrBin) orelse is_bitstring(AttrBin) ->
                Attr = util:bitstring_to_term(AttrBin),
                {Index+1, [change_data_format(ServerId, ServerNum, ZoneId, Attr, Role)|Acc]};
            _A ->
                {Index+1, [change_data_format(ServerId, ServerNum, ZoneId, [], Role)|Acc]}
        end
    end,
    spawn(fun() -> 
        {_, OfflineRank1} = lists:foldl(Fun1, {1, []}, OfflineRank),
        timer:sleep(urand:rand(500, 3500)),
        RankAfterZip = zlib:zip(util:term_to_bitstring({ZoneId, OfflineRank1})),
        clusters_node:apply_cast(god_dun_rank_mgr, batch_upload_power_rank, [RankAfterZip])
    end),
    {ok, CommonRankState}.


%% 游戏服玩家进程执行
upload_role_power_rank(Player, ServerId, ServerNum, ZoneId, OnlineRole) ->
    #player_status{role_god_dun = RoleGodDun} = Player,
    #role_god_dun{role_attrs = Attr} = RoleGodDun,
    GodDunPowerRole = change_data_format(ServerId, ServerNum, ZoneId, Attr, OnlineRole),
    clusters_node:apply_cast(god_dun_rank_mgr, upload_power_rank, [{ZoneId, [GodDunPowerRole]}]),
    ok.

%% TODO 过滤不正常数据
change_data_format(ServerId, ServerNum, _ZoneId, Attr, Role) ->
    case lib_role:get_role_show(Role#common_rank_role.role_id) of
        #ets_role_show{figure = #figure{name = RoleName, lv = RoleLv}} ->
            #god_dun_power_role{
                role_id = Role#common_rank_role.role_id,
                role_name = RoleName,
                server_id = ServerId,
                server_num = ServerNum,
                lv = RoleLv,
                power = Role#common_rank_role.value,
                attrs = Attr
            };
        _ ->
            #god_dun_power_role{}
    end.


broad_cast_rank(ZoneSrvs, ScoreRankL, TodayRoles) ->
    lists:map(fun({ZoneId, Srvs}) -> 
        Res1 =
        case lists:keyfind(ZoneId, 1, ScoreRankL) of
            {_, ZoneScoreRankL} -> ZoneScoreRankL;
            _ ->  []
        end,
        Res3 = 
        case lists:keyfind(ZoneId, 1, TodayRoles) of
            {_, ZoneRoleNum} -> ZoneRoleNum;
            _ -> 0
        end,
        Res = util:term_to_bitstring([{score_rank, Res1}, {today_roles, Res3}]),
        ZipRes = zlib:zip(Res),
        [begin clusters_center:apply_cast(ServId, god_dun_rank_local, update_rank_data, [ZipRes])  end|| ServId <- Srvs]
    end, ZoneSrvs),
    ok.


update_zone_serv_info(SrvZones) ->
    ZoneServInfos = 
    lists:foldl(fun({ServIdX, ZoneIdX}, Acc) -> 
        case lists:keyfind(ZoneIdX, 1, Acc) of
            {_, ZoneServIds} ->
                case lists:member(ServIdX, ZoneServIds) of
                    true ->
                        NewAcc = Acc;
                    _ ->
                        NewZoneServIds = [ServIdX|ZoneServIds],
                        NewAcc = lists:keystore(ZoneIdX, 1, Acc, {ZoneIdX, NewZoneServIds})
                end;
            _ ->
                NewZoneServIds = [ServIdX],
                NewAcc = lists:keystore(ZoneIdX, 1, Acc, {ZoneIdX, NewZoneServIds})
        end,
        NewAcc
    end, [], maps:to_list(SrvZones)),
    ZoneServInfos.

notice_power_rank_download() ->
    clusters_center:apply_to_all_node(?MODULE, local_notice_power_rank_download, []).

local_notice_power_rank_download() ->
    gen_server:cast(god_dun_rank_local, {download_power_rank_data}).

db_update_role_wave_score(WaveRole) ->
    #god_dun_wave_score_role{
        dun_id = DunId, wave = Wave, role_id = RoleId,
        server_id = ServerId, server_num = ServerNum,
        score = Score, show = Show
    } = WaveRole,
    ShowBin = util:term_to_bitstring(Show),
    db:execute(?SQL_GOD_DUN_WAVE_SCORE_REPLACE, [DunId, Wave, RoleId, ServerId, ServerNum, Score, ShowBin]),
    ok.

db_update_role_score(Role) ->
    #god_dun_score_role{
        role_id = RoleId, server_id = ServId, pass = Pass,
        server_num = ServerNum, score = Score, show = Show
    } = Role,
    ShowBin = util:term_to_bitstring(Show),
    db:execute(?SQL_ROLE_SCORE_REPLACE, [RoleId, ServId, ServerNum, Score, ShowBin, Pass]),
    ok.

db_delete_rank_out_wave_score(RankL) ->
    spawn(fun() -> 
        db_delete_rank_out_wave_score_do(RankL, 1)
    end),
    ok.

db_delete_rank_out_wave_score_do([], _) -> ok;
db_delete_rank_out_wave_score_do([#god_dun_wave_score_role{dun_id = DunId, wave = Wave, role_id = RoleId}|Rest], Index) ->
    Index >= 10 andalso Index rem 10 == 0 andalso timer:sleep(150),
    db:execute(?SQL_GOD_DUN_WAVE_SCORE_DELETE, [DunId, Wave, RoleId]),
    db_delete_rank_out_wave_score_do(Rest, Index+1).

db_update_role_god_live(RoleId, ServerId, Time) ->
    db:execute(?SQL_GOD_LIVE_ROLE_REPLACE, [RoleId, ServerId, Time]),
    ok.

test(N) ->
    RandL = create_rand_list(N, []),
    RankLBin = util:term_to_bitstring(RandL),
    LZ = zlib:zip(RankLBin),
    LC = zlib:compress(RankLBin),
    RankLBinLength = byte_size(RankLBin),
    LZLength = byte_size(LZ),
    LCLength = byte_size(LC),
    io:format("original size:~w ~n zip size:~w compress size:~w ~n", [RankLBinLength, LZLength, LCLength]),
    ptester:run(N, [
        {zip_test, fun(_) -> zlib:zip(RankLBin) end},
        {compress_test, fun(_) -> zlib:compress(RankLBin) end}
        ]),
    % clusters_node:apply_cast(?MODULE, unzip_test, [LZ, LC]),
    ok.

unzip_test(LZ, LC) ->
    UnZipList       = zlib:unzip(LZ),
    UnCompressList  = zlib:uncompress(LC),
    UZLength = byte_size(UnZipList),
    UCLength = byte_size(UnCompressList),
    io:format("uinzip size:~w uncompress size:~w", [UZLength, UCLength]),
    ok.

create_rand_list(N, Acc) when N =< 0 -> Acc;
create_rand_list(N, Acc) ->
    Role = #god_dun_score_role{
        role_id = urand:rand(1,10000),
        rank = urand:rand(1,500),
        server_id = urand:rand(1,10000),
        server_num = urand:rand(1,10000),
        score = urand:rand(1, 1000),
        display = urand:rand(1, 1000),
        show = #role_key_figure{}
    },
    create_rand_list(N-1, [Role|Acc]).


gm_notice_power_rank() ->
    clusters_center:apply_to_all_node(?MODULE, collect_power_rank, []),
    spawn(fun() -> 
            timer:sleep(15000),
            erlang:send(god_dun_rank_mgr, {daily_update_power_rank})
        end),
    ok.