%% --------------------------------
%% module   :mod_legion_rank_mgr.erl
%% desc     :跨服军功榜单保存
%% author   :zmh
%% time     :21/01/25
%% --------------------------------

-module (mod_legion_rank_mgr).

-behaviour (gen_server).

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

-export ([
    start_link/0,
    update_legion_role/1,
    get_fact_rank/5,
    get_award_data/6,
    get_click_role_rank/3,
    get_last_role_rank/3,
    day_timer/0,
    get_last_rank/4,
    issue_reset/1,  %%赛季榜单清理
    load_rank_map/1,
    recalc_zone/1,
    db_reset_issue/1 %%赛季db清理
    ]).

-include("kf_legion.hrl").
-include("figure.hrl").
-include("def_fun.hrl").
-include("czone.hrl").
-include("common.hrl").
-include("errcode.hrl").

%%榜单管理
    %rank_map = #{},         %%#{zone->#{fact->ranks}} state
    %last_rank_map = #{},    %%#{zone->#{fact->ranks}} dict
    %week_rank_map = #{}     %%#{zone->#{fact->ranks}} dict


%% 重新分区
recalc_zone(CBMap) ->
    #{zone_map := ZoneMap,faction_maps := FactionMaps} = CBMap,
    gen_server:cast(?MODULE,{recalc_zone, ZoneMap,FactionMaps}).


%%榜单上新
update_legion_role(LegionRankRole)->
    gen_server:cast(?MODULE,{update_legion_role,LegionRankRole}).

%%榜单获取
get_fact_rank(Zone,Fact,Node,RoleID,RankScore)->
    gen_server:cast(?MODULE,{get_fact_rank,Zone,Fact,Node,RoleID,RankScore}).

%%获取每日奖励和膜拜信息
get_award_data(Zone,Fact,Node,RoleID,CRid,FStatus)->
    gen_server:cast(?MODULE,{get_award_data,Zone,Fact,Node,RoleID,CRid,FStatus}).

%%获取被膜拜的排名
get_click_role_rank(Zone,Fact,Aim)->
    gen_server:call(?MODULE,{get_click_role_rank,Zone,Fact,Aim}).

%%获取昨日榜单
get_last_role_rank(Zone,Fact,RoleID)->
    gen_server:call(?MODULE,{get_last_role_rank,Zone,Fact,RoleID}).

%%每日触发
day_timer()->
    gen_server:cast(?MODULE,{day_timer}).

%%赛期榜单清理
issue_reset(Zone)->
     gen_server:cast(?MODULE,{issue_reset,Zone}).

%%获取上期榜单
get_last_rank(Zone,Fact,Node,RoleID)->
    gen_server:cast(?MODULE,{get_last_rank,Zone,Fact,Node,RoleID}).

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

init(_) ->
    erlang:send_after(1000, self(), {init_rank}),
    {ok, #{}}.


handle_call(Msg, From, State) ->
    try
        do_handle_call(Msg, From, State)
    catch _:_Error ->
        io:format("do_handle_call Msg:~p, Error:~p, ~p~n", [Msg, _Error, erlang:get_stacktrace()]),
        {reply, error, State}
    end.

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

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

terminate(_Reason, _State) ->
    ok.

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


do_handle_call({get_click_role_rank,Zone,Fact,Aim},_From,RankMap)->
    FactRankMap = maps:get(Zone,RankMap,#{}),
    RankList = maps:get(Fact,FactRankMap,[]),
    case lists:keyfind(Aim,#legion_rank.id,RankList) of
        #legion_rank{rank=LRank}->
            skip;
        _->
            LRank = 0
    end,
    {reply,LRank,RankMap};

do_handle_call({get_last_role_rank,Zone,Fact,RoleID},_From,State)->
    LRankMap = get_rank_map(last_rank_map),
    FactRankMap = maps:get(Zone,LRankMap,#{}),
    RankList = maps:get(Fact,FactRankMap,[]),
    case lists:keyfind(RoleID,#legion_rank.id,RankList) of
        #legion_rank{rank=LRank}->
            skip;
        _->
            LRank = case RankList /= [] of
                true->
                    999; %%表示排名之外
                _->
                    0 %%表示无前日榜单
            end
    end,
    {reply,LRank,State};

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


do_handle_cast({get_last_rank,Zone,Fact,Node,RoleID},State)->
    LRankMap = get_rank_map(week_rank_map),
    FactRankMap = maps:get(Zone,LRankMap,#{}),
    RankList = maps:get(Fact,FactRankMap,[]),
    DataList = lib_role_legion:pack_last_rank_data(RankList, []),
    {ok,Bin} = pt_603:write(60311,[DataList]),
    lib_server_send:send_to_uid(Node,RoleID,Bin),
    {noreply,State};

do_handle_cast({update_legion_role,LegionRankRole},RankMap)->
    NRankMap = update_rank(LegionRankRole, RankMap, true, true),
    {noreply,NRankMap};

do_handle_cast({get_fact_rank,Zone,Fact,Node,RoleID,RankScore},RankMap)->
    FactRankMap = maps:get(Zone,RankMap,#{}),
    RankList = maps:get(Fact,FactRankMap,[]),
    Bin = pack_rank_data(Fact,RoleID,RankScore,RankList),
    lib_server_send:send_to_uid(Node, RoleID, Bin),
    {noreply,RankMap};

do_handle_cast({get_award_data,Zone,Fact,Node,RoleID,CRid,FStatus},State)->
    LRankMap = get_rank_map(last_rank_map),
    FactRankMap = maps:get(Zone,LRankMap,#{}),
    RankList = maps:get(Fact,FactRankMap,[]),
    case lists:keyfind(RoleID,#legion_rank.id,RankList) of
        #legion_rank{rank=LRank}->
            skip;
        _->
            LRank = 0
    end,
    {ok,Bin} = pt_603:write(60308,[CRid,LRank,FStatus]),
    lib_server_send:send_to_uid(Node, RoleID, Bin),
    {noreply,State};

%%数据库清理相关抽出来 沙盘那边事务处理
do_handle_cast({issue_reset,Zone},RankMap)->
    FactMap = maps:get(Zone,RankMap,#{}),

    OWRankMap = get_rank_map(week_rank_map),
    WRankMap  = maps:put(Zone,FactMap,OWRankMap),
    put_rank_map(week_rank_map,WRankMap),

    %%排名前七的筛出来
    report_fact_seven(Zone,RankMap),

    batch_save_legion_rank(week_rank_map,WRankMap),
    NRankMap = maps:remove(Zone,RankMap),

    {noreply,NRankMap};

%%每天把榜单放到前天榜单
do_handle_cast({day_timer},RankMap)->
    put_rank_map(last_rank_map,RankMap),
    db:execute("TRUNCATE TABLE legion_rank_last"),
    batch_save_legion_rank(last_rank_map,RankMap),
    {noreply,RankMap};

%% ZoneMap       #{OldZoneId  => NewZoneId}
%% FactionMaps   #{OldZoneId  => FactionMap :: #{OldFaction => NewFaction}}
%% 更新数据库里的zone fact 然后重新加载
%% now last的合并 week的删除
do_handle_cast({recalc_zone, ZoneMap,FactionMaps},RankMap)->
    ZoneList = maps:to_list(ZoneMap),
    KvList   = to_zone_fact(ZoneList,FactionMaps,[]),
    LRankMap = load_rank_map(last_rank_map),
    NRankMap = merge_zone_fact(RankMap,KvList,0),
    NLRankMap = merge_zone_fact(LRankMap,KvList,0),
    put_rank_map(last_rank_map,NLRankMap),
    db_reset_zone_fact(KvList),
    {noreply,NRankMap};

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

%%榜单初始化
do_handle_info({init_rank},_)->
    RankMap  = load_rank_map(now),
    LRankMap = load_rank_map(last_rank_map),
    WRankMap = load_rank_map(week_rank_map),
    put_rank_map(last_rank_map,LRankMap),
    put_rank_map(week_rank_map,WRankMap),
    {noreply,RankMap};

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


%%上榜更新
update_rank(RankRole, RankMap, IsBroadcast, IsSave) ->
    #legion_rank{
        zone = ZoneId, fact=FactId, score = RoleScore
    } = RankRole,
    FactRankMap = maps:get(ZoneId,RankMap,#{}),
    Ranks = maps:get(FactId,FactRankMap,[]),
    %% 最大上榜人数
    MaxLen = get_rank_max(),
    case Ranks of
        [
            _LastRole = #legion_rank{rank = LRank, score = LScore} | _
        ] when LRank >= MaxLen andalso LScore >= RoleScore ->
            %% 上不了榜
            RankMap;
        _ ->
            %% 更新排行榜
            NewRanks = update_rank_core(MaxLen, RankRole, Ranks, IsSave, IsBroadcast),
            NFactRankMap = maps:put(FactId,NewRanks,FactRankMap),
            NRankMap = maps:put(ZoneId,NFactRankMap,RankMap),
            NRankMap
    end.


update_rank_core(RankMax, RankRole, Ranks, IsSave, _IsBroadcast) ->
    {Len, DescRankList} = insert_into_sorted_list(RankRole, Ranks),
    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,
    ClickRank = data_legion:get(click_rank),
    #legion_rank{
        id=RoleID,
        zone = ZoneId,
        fact = FactId
    } = RankRole,
    case lists:keyfind(RoleID,#legion_rank.id,Ranks) of
        #legion_rank{rank=ORank}->
            skip;
        _->
            ORank = 999
    end,
    case lists:keyfind(RoleID,#legion_rank.id,NewAscRankList) of
        #legion_rank{rank=NRank}->
            skip;
        _->
            NRank = 999
    end,
    case ORank>ClickRank andalso NRank=<ClickRank of
        true->
            {ok,Bin} = pt_603:write(60317,[]),
            czone:zone_faction_apply_cast(ZoneId,FactId,{lib_server_send,send_to_all,[Bin]});
        _->
            skip
    end,
    NewAscRankList.


%%积分降序
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#legion_rank.id =:= H#legion_rank.id ->
            %% 已经在列表中，保留新的数据
            insert_into_sorted_list(Element, T, Len, List);
        Element#legion_rank.score > H#legion_rank.score ->
            insert_into_sorted_list(Element, T, Len + 1, [H | List]);
        (
            Element#legion_rank.score =:= H#legion_rank.score andalso
            Element#legion_rank.time < H#legion_rank.time
        ) ->
            insert_into_sorted_list(Element, T, Len + 1, [H | List]);
        true -> %% 新加入的元素加入到列表中
            insert_into_sorted_list(false, [H | T], Len + 1, [Element | List])
    end.


%% 根据排名名次需要的积分要求对排行榜进行排名。
set_ranking([], _RankMax, _Ranking, RankList) ->
    RankList;
set_ranking([RankRole | T], RankMax, Ranking, RankList) ->
    case Ranking > RankMax of
        true -> %% 已经排到最大排名之后了，剩下的排名都设置为0
            RankRole2 = RankRole#legion_rank{rank  = 0},
            RankList2 = [RankRole2 | RankList],
            set_ranking(T, RankMax, Ranking + 1, RankList2);
        false ->
            RankRole2 = RankRole#legion_rank{rank  = Ranking},
            RankList2 = [RankRole2 | RankList],
            set_ranking(T, RankMax, Ranking + 1, RankList2)

    end.


save_rank_role(RankRole) ->
    #legion_rank{
        zone = Zone,fact=Fact,id = RoleId
        ,server_id = SerId,server_num = SerNum,figure=Figure
        ,gname=GName,score = Score,rank=Rank,time = Time
    } = RankRole,
    #figure{
        name = Name,
        sex = Sex,
        career = Career,
        turn = Turn,
        lv = Lv,
        vip = Vip,
        sup_vip = SVip,
        dress_list = DressList0,
        profile_photo_id = PhoId,
        post = Post
    }=Figure,
    DressList1 = [{K,V}||{K,V}<-DressList0,K=:=2],
    DressList = util:term_to_string(DressList1),
    Data = [
        RoleId, Name, Zone, Fact, SerId, SerNum, Sex,Career,Turn,Lv, Vip,SVip,DressList,PhoId,Post,GName, Score, Rank, Time
    ],
    db:execute(io_lib:format(?SQL_LEGION_RANK_INS1, Data)),
    ok.

del_rank_role(RankRole) ->
    #legion_rank{id = RId} = RankRole,
    Sql = io_lib:format(?SQL_LEGION_RANK_DEL, [RId]),
    db:execute(Sql),
    ok.

%% @doc 把排行榜数据更新给榜单上的所有人
% broadcast_rank(_RecieverList, []) ->
%     ok;
% broadcast_rank(RecieverList, RankList) ->
%     ServerRList = split_server_role(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 || #legion_rank{id = RoleId} <- RoleList],
%     clusters_center:apply_cast(
%         ServerId, {lib_server_send, send_to_uid, [RoleIdList, BinData]}
%     ),
%     broadcast_rank_do(T, BinData).

%% 打包排行榜数据
pack_rank_data(Fact,RoleID,MyScore,RankList)->
    case lists:keyfind(RoleID,#legion_rank.id,RankList) of
        #legion_rank{rank=MyRank}->
            skip;
        _->
            MyRank = 0
    end,
    DataList = lib_role_legion:pack_rank_data(RankList, []),
    {ok, BinData} = pt_603:write(60307, [Fact,MyRank,MyScore,DataList]),
    BinData.


% %%按照服务器拆分
% split_server_role([], ServerList) ->
%     ServerList;
% split_server_role([RankRole | T], ServerList) ->
%     #legion_rank{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,
%     split_server_role(T, ServerList2).

get_rank_max()->
    data_legion:get(honor_max_rank).


%%榜单存储
put_rank_map(Key,RankMap)->
    put(Key,RankMap).
get_rank_map(Key)->
    RankMap = get(Key),
    case RankMap /= underfined of
        true->
            RankMap;
        _->
            #{}
    end.

%%上报服务器阵营前七
report_fact_seven(Zone,RankMap)->
    ZoneRanks = maps:get(Zone,RankMap,#{}),
    Sevenors  = do_report_zone_seven([{Zone,ZoneRanks}],[]),
    mod_legion_mgr:report_directors(1,Sevenors).

do_report_zone_seven([],Sevenors)->
    Sevenors;
do_report_zone_seven([{Zone,FactRankMap}|T],Sevenors)->
    FactRanks = maps:to_list(FactRankMap),
    FactSevenors = do_report_fact_seven(FactRanks,[]),
    do_report_zone_seven(T,[{Zone,FactSevenors}|Sevenors]).

do_report_fact_seven([],FactSevenors)->
    FactSevenors;
do_report_fact_seven([{Fact,Ranks}|T],FactSevenors)->
    FactRanks1 = trans_to_director(Ranks,7,[]),
    FactRanks = lists:keysort(#director_role.pos,FactRanks1),
    do_report_fact_seven(T,[{Fact,FactRanks}|FactSevenors]).


trans_to_director([],_Limit,FactRanks)->
    FactRanks;
trans_to_director([H|T],Limit,FactRanks)->
    #legion_rank{
        id=RoleID,
        rank=Rank,
        zone = Zone,
        fact = Fact,
        figure=Figure
    }=H,
    case Rank=<Limit andalso Rank>0 of
        true->
            Director = #director_role{
                pos = Rank,  %%用rank临时充当
                rid = RoleID,
                zone = Zone,
                fact = Fact,
                figure = Figure
            },
            trans_to_director(T,Limit,[Director|FactRanks]);
        _->
            trans_to_director(T,Limit,FactRanks)
    end.

%%批量回写榜单
batch_save_legion_rank(Key,RankMap)->
    Sql = case Key of
        last_rank_map->
            ?SQL_LEGION_RANK_BATCHL;
        _->
            ?SQL_LEGION_RANK_BATCHW
    end,
    ZoneRanks = maps:to_list(RankMap),
    batch_zone_legion_rank(Sql,ZoneRanks).

batch_zone_legion_rank(_Sql,[])->
    ok;
batch_zone_legion_rank(Sql,[{_Zone,FactMap}|T])->
    FactRanks = maps:to_list(FactMap),
    batch_fact_legion_rank(Sql,FactRanks),
    batch_zone_legion_rank(Sql,T).

batch_fact_legion_rank(_Sql,[])->
    ok;
batch_fact_legion_rank(Sql,[{_Fact,Ranks}|T])->
    DbRoleList = format_rank_roles([], Ranks),
    batch_save(Sql,DbRoleList),
    batch_fact_legion_rank(Sql,T).

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


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

format_rank_role(RankRole)->
    #legion_rank{
        zone = Zone,fact=Fact,id = RoleId
        ,server_id = SerId,server_num = SerNum,figure=Figure
        ,gname=GName,score = Score,rank=Rank,time = Time
    } = RankRole,
    #figure{
        name = Name,
        sex = Sex,
        career = Career,
        turn = Turn,
        lv = Lv,
        vip = Vip,
        sup_vip = SVip,
        profile_photo_id = PhoId,
        dress_list = DressList0,
        post = Post
    }=Figure,
    DressList1 = [{K,V}||{K,V}<-DressList0,K=:=2],
    DressList = util:term_to_string(DressList1),
    Data = [
        RoleId, Name, Zone, Fact, SerId, SerNum, Sex,Career,Turn,Lv,Vip,SVip,DressList,PhoId,Post,GName, Score, Rank, Time
    ],
    io_lib:format("(~p,'~ts',~p,~p,~p,~p,~p,~p,~p,~p,~p,~p,'~ts',~p,~p,'~ts',~p,~p,~p)", Data).




%%数据库加载
load_rank_map(Key)->
    Sql = case Key of
        last_rank_map->
            ?SQL_LEGION_RANK_GETL;
        week_rank_map->
            ?SQL_LEGION_RANK_GETW;
        _->
            ?SQL_LEGION_RANK_GETN
    end,
    case db:get_all(Sql) of
        DataList when is_list(DataList) andalso DataList =/= []->
            RankRoles = to_rank_roles(DataList,[]),
            RankMap0  = init_rank_map(RankRoles),
            RankMap   = case Key of
                now->%%重新排序
                    MaxLen = get_rank_max(),
                    maps:fold(fun(Zone,FactMap,Args)->
                        NFactMap = maps:fold(fun(Fact,Ranks,ArgsB)->
                            DescRankList = sort_legion_rank(Ranks),
                            AscRanks  = set_ranking(DescRankList, MaxLen, 1, []),
                            maps:put(Fact,AscRanks,ArgsB)
                        end,#{},FactMap),
                        maps:put(Zone,NFactMap,Args)
                    end,#{},RankMap0);
                _->
                    RankMap0
            end,
            RankMap;
        _->
            #{}
    end.


sort_legion_rank(Ranks)->
    lists:sort(fun
        (A, B) ->
            sort_legion_rank(A, B)
    end, Ranks).

sort_legion_rank(A, B)->
    #legion_rank{score=Sa,time=Ta}=A,
    #legion_rank{score=Sb,time=Tb}=B,
    case Sa==Sb of
        true->
            Ta=<Tb;
        _->
            Sa>Sb
    end.

init_rank_map(RankRoles)->
    init_rank_map(RankRoles,#{}).

init_rank_map([],RankMap)->
    RankMap;
init_rank_map([H|T],RankMap)->
    #legion_rank{zone=Zone,fact=Fact}=H,
    FactMap = maps:get(Zone,RankMap,#{}),
    Ranks   = maps:get(Fact,FactMap,[]),
    NRanks  = [H|Ranks],
    NFactMap = maps:put(Fact,NRanks,FactMap),
    NRankMap = maps:put(Zone,NFactMap,RankMap),
    init_rank_map(T,NRankMap).

to_rank_roles([],RankRoles)->
    RankRoles;
to_rank_roles([[RoleId, Name, Zone, Fact, SerId, SerNum, Sex,Career,Turn,Lv,Vip,SVip,DressList0,PhoId,Post,GName, Score, Rank, Time]|T],RankRoles)->
    Figure = #figure{
        id = RoleId,
        name = Name,
        sex = Sex,
        career = Career,
        turn = Turn,
        lv = Lv,
        vip = Vip,
        sup_vip = SVip,
        dress_list = util:bitstring_to_term(DressList0),
        profile_photo_id = PhoId,
        post = Post
    },
    Role = #legion_rank{
        id = RoleId,
        server_id = SerId,
        server_num = SerNum,
        zone = Zone,
        fact = Fact,
        figure = Figure,
        gname = GName,
        score = Score,
        rank = Rank,
        time = Time
    },
    to_rank_roles(T,[Role|RankRoles]).


%%赛季db清理
db_reset_issue(Zone)->
    Sql = io_lib:format(?SQL_LEGION_RANK_DEL2,[Zone]),
    db:execute(Sql),
    db:execute("TRUNCATE TABLE legion_rank_week").

db_reset_zone_fact([])->
    ok;
%%只有分区变化
db_reset_zone_fact([{OZone,_NZone}|T])->
    Sql3 = io_lib:format(?SQL_LEGION_RANK_DEL3,[OZone]),
    db:execute(Sql3),
    db_reset_zone_fact(T);
db_reset_zone_fact([{OZone,_OFact,_NZone,_ZFact}|T])->
    Sql3 = io_lib:format(?SQL_LEGION_RANK_DEL3,[OZone]),
    db:execute(Sql3),
    db_reset_zone_fact(T).

to_zone_fact([],_FactionMaps,KvList)->
    KvList;
to_zone_fact([{OZone,NZone}|T],FactionMaps,KvList)->
    FactMap = maps:get(OZone,FactionMaps,#{}),
    FactList = maps:to_list(FactMap),
    List = case length(FactList)=<0 of
        true->
            [{OZone,NZone}];
        _->
            [{OZone,OFact,NZone,NFact}||{OFact,NFact}<-FactList]
    end,
    to_zone_fact(T,FactionMaps,List++KvList).

merge_zone_fact(RankMap,[],0)->
    RankMap;
merge_zone_fact(RankMap,[],OZone)->
    NRankMap  = maps:remove(OZone,RankMap),
    NRankMap;
merge_zone_fact(RankMap,[{OZone,NZone}|T],_)->
    OFactMap = maps:get(OZone,RankMap,#{}),
    FactList = maps:to_list(OFactMap),
    NRankMap0 = do_merge_zone_fact(RankMap,{OZone,NZone},FactList),
    NRankMap  = maps:remove(OZone,NRankMap0),
    merge_zone_fact(NRankMap,T,0);
merge_zone_fact(RankMap,[{OZone,OFact,NZone,NFact}|T],LZone)->
    OFactMap = maps:get(OZone,RankMap,#{}),
    FactList = maps:to_list(OFactMap),
    NRankMap0 = do_merge_zone_fact(RankMap,{OZone,OFact,NZone,NFact},FactList),
    case LZone /= OZone andalso LZone>0 of
        true->
            NRankMap  = maps:remove(LZone,NRankMap0);
        _->
            NRankMap = NRankMap0
    end,
    merge_zone_fact(NRankMap,T,OZone).

do_merge_zone_fact(RankMap,_Args,[])->
    RankMap;
do_merge_zone_fact(RankMap,{_OZone,OFact,NZone,NFact},FactList)->
    case lists:keyfind(OFact,1,FactList) of
        {_,Roles}->
            NRankMap = lists:foldl(fun(RankRole,Map)->
                NRankRole = RankRole#legion_rank{
                    zone = NZone,
                    fact = NFact
                },
                NMap = update_rank(NRankRole, Map, false, true),
                NMap
            end,RankMap,Roles),
            NRankMap;
        _->
            RankMap
    end;
do_merge_zone_fact(RankMap,{OZone,NZone},[{Fact,Roles}|T])->
    NRankMap = lists:foldl(fun(RankRole,Map)->
        NRankRole = RankRole#legion_rank{
            zone = NZone,
            fact = Fact
        },
        NMap = update_rank(NRankRole, Map, false, true),
        NMap
    end,RankMap,Roles),
    do_merge_zone_fact(NRankMap,{OZone,NZone},T).
