%% -------------------------------------------
%% desc     :跨服团战-阵营管理进程
%% module   :camp_war_worker.erl
%% author   :hh
%% time     :20/07/22
%% -------------------------------------------

-module(camp_war_worker).

-behaviour(gen_server).

-include("scene.hrl").
-include("camp_war.hrl").
-include("common.hrl").
-include("def_module.hrl").
-include("errcode.hrl").
-include("def_fun.hrl").
-include("server.hrl").
-include("guild.hrl").
-include("language.hrl").
-include("predefine.hrl").
-include("buff.hrl").
-include("goods.hrl").
-include("cross_guild_war.hrl").
-include("offline_hosting.hrl").
-include("def_act_label.hrl").
-include("figure.hrl").
-include("attr.hrl").
-include("cross_zone.hrl").
-include("czone.hrl").
-include("kf_legion.hrl").
-include("clusters.hrl").

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

%% API
-export([
    start_link/1,
    cast/2,
    info/2
    ]).

-export([
    enter_camp_war_scene/7,
    role_enter_camp_war/7,
    update_guild_buff/4,
    update_guild_member_buff/3,
    send_guild_camp_war_reward/3,
    role_get_rewards/5
    ]).

%% 怪物回调
-export([
    get_boss_unique_id/3
    % ,guild_proc_update_act_label/4
    % ,role_proc_update_act_label/3
    ]).

%% serv_id => 阵营进程
%% guild_id => 战场编号

%%%-------------------------------------------------------------------
%%% API Functions
%%%-------------------------------------------------------------------

start_link([ClsType, ZoneId, FacId, StartType]) ->
    gen_server:start_link(?MODULE, [ClsType, ZoneId, FacId, StartType], []).

-spec cast(ServId, Msg) -> ReturnType when
    ServId :: integer() | {ZoneId, FacId} | pid(),
    ZoneId :: integer(),
    FacId :: integer(),
    Msg   :: term(),
    ReturnType :: term().
cast({ZoneId, FacId}, Msg) ->
    gen_server:cast(get_worker_pid({ZoneId, FacId}), Msg);
cast(ServId, Msg) when is_integer(ServId) ->
    gen_server:cast(get_worker_pid(ServId), Msg);
cast(Pid, Msg) when is_pid(Pid) ->
    gen_server:cast(Pid, Msg);
cast(_, _Msg) ->
    skip.

-spec info(ServId, Msg) -> ReturnType when
    ServId :: integer() | {ZoneId, FacId} | pid(),
    ZoneId :: integer(),
    FacId :: integer(),
    Msg   :: term(),
    ReturnType :: term().
info({ZoneId, FacId}, Msg) ->
    erlang:send(get_worker_pid({ZoneId, FacId}), Msg);
info(ServId, Msg) when is_integer(ServId) ->
    erlang:send(get_worker_pid(ServId), Msg);
info(Pid, Msg) when is_pid(Pid) ->
    erlang:send(Pid, Msg);
info(_, _Msg) ->
    skip.

worker_name(ZoneId, FacId) ->
    list_to_atom(lists:concat(["camp_worker_", ZoneId, "_", FacId])).

worker_register(ZoneId, FacId, Pid) ->
    erlang:register(worker_name(ZoneId, FacId), Pid).

%% 根据注册名获取阵营工作进程
get_worker_pid({ZoneId, FacId}) ->
    erlang:whereis(worker_name(ZoneId, FacId));

get_worker_pid(ServId) when is_integer(ServId) ->
    case ets:lookup(?ETS_FAC_SRV_ZONE, ServId) of
        [{_, {ZoneId, FacId}}|_] ->
            get_worker_pid({ZoneId, FacId});
        _ ->
            noproc
    end;

get_worker_pid(_) ->
    noproc.

%%%-------------------------------------------------------------------
%%% Callback Functions
%%%-------------------------------------------------------------------
init([ClsType, ZoneId, FacId, _StartType]) ->
    %% 注册
    worker_register(ZoneId, FacId, self()),
    %% 初始化
    case ets:lookup(?ETS_FAC_ARENA_INFO, {ZoneId, FacId}) of
        [{_, FacIndex, _GuildInfo}|_] ->
            ZoneFacState = #zone_fac_state{
                zone_fac = {ZoneId, FacId},
                fac_index = FacIndex,
                max_pool = ?FAC_POOL_BASE(FacIndex),
                cls_type = ClsType,
                status   = ?CAMP_WAR_OPENING_2
            },
            NewZoneFacState = worker_init(ZoneFacState);
        _A ->
            NewZoneFacState = #zone_fac_state{
                zone_fac = {ZoneId, FacId}, status = ?CAMP_WAR_OPENING_2, cls_type = ClsType}
    end,
    {ok, NewZoneFacState}.

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

handle_cast(Msg, State) ->
    try do_handle_cast(Msg, State) of
    Reply -> Reply
    catch ?EXCEPTION(Class, Reason, StackTrace) ->
        Error = {Class, Reason, ?GET_STACK(StackTrace)},
        ?ERR("camp war worker ~w:state error:~p~n", [self(), Error]),
        {noreply, State}
    end.

handle_info(Msg, State) ->
    try do_handle_info(Msg, State) of
    Reply -> Reply
    catch ?EXCEPTION(Class, Reason, StackTrace) ->
        Error = {Class, Reason, ?GET_STACK(StackTrace)},
        ?ERR("camp war worker ~w:state error:~p~n", [self(), Error]),
        {noreply, State}
    end.

%% 采集阶段开启
do_handle_cast({collect_mon_stage}, State) ->
    #zone_fac_state{extra_map = Map} = State,
    Now = utime:unixtime(),
    NewMap = maps:put(?RES_START_TIME, Now, Map),
    Time = data_camp_war:get_kv(material_update_time),
    Ref = erlang:send_after(Time*1000, self(), {resource_update}),
    NewMap1 = maps:put(?RES_UPUPDATE_REF, Ref, NewMap),
    State1 = State#zone_fac_state{status = ?CAMP_WAR_OPENING_2, extra_map = NewMap1},
    NewState = init_arena_scene_info(State1),
    NewState1 = offline_hosting_camp_war:notice_hosting_role_go_move(NewState),
    {noreply, NewState1};

%% 进入场景
do_handle_cast({enter_scene, Args}, State) ->
    #zone_fac_state{
        zone_fac = {ZoneId, FacId}, status = Status, guild_arena_map = GuildArenaMap,
        arena_room_map = ArenaRoomMap, room_role_num = RoomRoleMap, max_pool = MaxPool,
        resource = Resource, res_id = ResId, role_rank = RoleRank, guild_rank = GuildRank,
        buff_list = GuildBuffList, extra_map = Map, cls_type = ClsType
    } = State,
    [ServId, SerNum, GuildId, GuildFlag, RoleId, RoleLv, Vip, SupVip, Sex, PhotoId, DressId, Name, GuildName, IsHosting] = Args,
    ArenaId = maps:get(GuildId, GuildArenaMap, 0),
    if ArenaId =/= 0 ->
            %% 更新战场分线相关
            {PoolIsNew, ScenePoolId, NewMaxPool, NewArenaRoomMap, NewRoomRoleMap} =
             get_arena_scene_pool(ClsType, ArenaId, MaxPool, ArenaRoomMap, RoomRoleMap),

            ResStart = maps:get(?RES_START_TIME, Map, 0),
            %% 若为新线,先初始化
            {NewResId, NewResource} = check_scene_resource_when_enter([ClsType, PoolIsNew, Status, ZoneId, FacId, ScenePoolId, ResId, Resource, ResStart]),

            %% 切换场景
            {X, Y} = get_guild_born_coordinate({ZoneId, FacId}, ArenaId, GuildId),
            case lists:keyfind(GuildId, #guild_res.guild_id, GuildBuffList) of
                #guild_res{buff_lists = BuffList} -> ok;
                _   -> BuffList = []
            end,
            if ClsType == ?NODE_CENTER ->
                    clusters_center:apply_cast(ServId, ?MODULE, enter_camp_war_scene, [RoleId, ScenePoolId, 0, X, Y, false, BuffList]);
                true ->
                    enter_camp_war_scene(RoleId, ScenePoolId, 0, X, Y, false, BuffList)
            end,

            %% 更新个人&社团数据
            [ChiefInfo, GuildPower|_] = camp_war_logic:get_short_guild_info({ZoneId, FacId}, GuildId),
            HurtGuild = #camp_war_guild{
                guild_id = GuildId, guild_name = GuildName, guild_flag = GuildFlag,
                chief_info = ChiefInfo, guild_power = GuildPower, zone_facs = {ZoneId, FacId},
                server_id = ServId, server_num = SerNum, nums = 0},
            HurtRole  = #camp_war_role{
                role_id = RoleId, role_name = Name, guild_id = GuildId, lv = RoleLv,
                photo_profile_id = PhotoId, dress_id = DressId, server_id = ServId,
                server_num = SerNum, zone_facs = {ZoneId, FacId},  in_act = 1,
                is_host = IsHosting, vip = Vip, sup_vip = SupVip, sex = Sex},

            NewGuildRank = update_guild_rank(HurtGuild, ArenaId, GuildRank),
            NewGuildRank1 = add_guild_role_num(GuildId, ArenaId, NewGuildRank),
            NewRoleRank  = update_role_rank(HurtRole, ArenaId, RoleRank),

            NewState = State#zone_fac_state{
                max_pool        = NewMaxPool,
                arena_room_map  = NewArenaRoomMap,
                room_role_num   = NewRoomRoleMap,
                res_id          = NewResId,
                resource        = NewResource,
                role_rank       = NewRoleRank,
                guild_rank      = NewGuildRank1
            };
        true -> %% 战场已先分配好,若找不到则有问题
            ?ERR("guild arena num error ~p ~p ~p", [GuildId, ArenaId, GuildArenaMap]),
            NewState = State
    end,
    {noreply, NewState};

%% 退出场景
do_handle_cast({exit_scene, ServId, _SerNum, GuildId, RoleId, PoolId}, State) ->
    #zone_fac_state{
        guild_arena_map = GuildArenaMap,
        arena_room_map  = ArenaRoomMap,      %% 战场与分线映射 arena_id => [pool_id,...]
        room_role_num   = RoomRoleMap,       %% 分线人数 pool_id => num
        guild_rank      = GuildRank,
        role_rank       = RoleRank,
        cls_type        = ClsType
    } = State,
    ArenaId = maps:get(GuildId, GuildArenaMap, 0),
    if ArenaId =/= 0 ->
            NewRoomRoleMap = del_arena_scene_pool_role(ArenaId, PoolId, ArenaRoomMap, RoomRoleMap),
            NewGuildRank   = del_guild_role_num(GuildId, ArenaId, GuildRank),
            NewRoleRank    = update_role_act_status(RoleId, ArenaId, 0, RoleRank),
            Kvs = [{action_free, ?ERRCODE(err410_on_camp_war)}], %{act_label, [{?ACT_LABEL_CAMP_WAR_TOP, 0}]}],
            if ClsType == ?NODE_CENTER ->
                    clusters_center:apply_cast(ServId, lib_scene, change_player_scene, [RoleId, 0, 0, 0, 0, 0, true, Kvs]);
                true ->
                    lib_scene:change_player_scene(RoleId, 0, 0, 0, 0, 0, true, Kvs)
            end,
            NewState = State#zone_fac_state{room_role_num = NewRoomRoleMap, guild_rank = NewGuildRank, role_rank = NewRoleRank};
        true ->
            NewState = State
    end,
    {noreply, NewState};

%% 获取社团采集信息
do_handle_cast({get_guild_res_info, ServId, GuildId, Sid}, State) ->
    #zone_fac_state{buff_list = BuffList, cls_type = ClsType} = State,
    case lists:keyfind(GuildId, #guild_res.guild_id, BuffList) of
        #guild_res{buff_lists = GuildBuffList} -> ok;
        _ -> GuildBuffList = []
    end,
    notice_svr_role(ClsType, ServId, Sid, pt_410, 41003, [?SUCCESS, GuildBuffList]),
    {noreply, State};

%% 获取场景房间剩余资源数量
do_handle_cast({get_arena_pool_res_info, ServId, ScenePool, _GuildId, Sid}, State) ->
    #zone_fac_state{resource = Resource, extra_map = Map, cls_type = ClsType} = State,
    SceneRes = maps:get(ScenePool, Resource, []),
    ResStart = maps:get(?RES_START_TIME, Map, 0),
    SceneResList = calc_scene_res_info(SceneRes),
    NextTime = calc_next_res_time(ResStart),
    notice_svr_role(ClsType, ServId, Sid, pt_410, 41004, [?SUCCESS, NextTime, SceneResList]),
    {noreply, State};

do_handle_cast({get_scene_res_collect_coordinate, ServId, _GuildId, GoodResId, ScenePool, Sid}, State) ->
    #zone_fac_state{resource = Resource, cls_type = ClsType} = State,
    SceneRes = maps:get(ScenePool, Resource, []),
    ResCoordinates = lists:filtermap(fun({GoodResIdX, {X, Y}, _Index}) ->
        if
            GoodResIdX == GoodResId ->
                {true, {X, Y}};
            true ->
                false
        end
    end, SceneRes),
    notice_svr_role(ClsType, ServId, Sid, pt_410, 41008, [?SUCCESS, GoodResId, ResCoordinates]),
    {noreply, State};

do_handle_cast({get_hury_rank, ServId, GuildId, Sid, RankType, PageSize, PageNum}, State) ->
    #zone_fac_state{role_rank = RoleRank, guild_rank = GuildRank, guild_arena_map = GuildArenaMap, cls_type = ClsType} = State,
    RankList = ?IF(RankType == 0, RoleRank, GuildRank),
    ArenaId = maps:get(GuildId, GuildArenaMap, 0),
    if ArenaId =/= 0 ->
            case lists:keyfind(ArenaId, 1, RankList) of
                {_, ArenaRankList} -> ok;
                _ -> ArenaRankList = []
            end,
            {TotalPage, HurtRank} = get_hurt_rank_info(RankType, PageSize, PageNum, ArenaId, ArenaRankList),
            notice_svr_role(ClsType, ServId, Sid, pt_410, 41005, [RankType, PageSize, PageNum, TotalPage, HurtRank]),
            {noreply, State};
        true ->
            notice_svr_role(ClsType, ServId, Sid, pt_410, 41005, [RankType, PageSize, PageNum, 0, []]),
            {noreply, State}
    end;

%% 资源采集
do_handle_cast({collect_mon, Mid, MonResId, ScenePool, GuildId, CollectorId, RecFigure}, State) ->
    #zone_fac_state{
        resource = Resource, buff_list = BuffList, role_rank = RoleRank, extra_map = Map,
        arena_map = ArenaMap, guild_arena_map = GuildArenaMap, cls_type = ClsType
        } = State,
    PoolRes = maps:get(ScenePool, Resource, []),
    NewPoolRes = lists:keydelete(MonResId, 3, PoolRes),
    NewResource = maps:put(ScenePool, NewPoolRes, Resource),
    % debug_pool_res(collect_mon, ScenePool, NewPoolRes),
    NewBuffList =
    case lists:keyfind(GuildId, #guild_res.guild_id, BuffList) of
        GuildRes = #guild_res{buff_lists = GuildBuffList} ->
            case lists:keyfind(Mid, 1, GuildBuffList) of
                {_, BuffNum} ->
                    NewGuildBuffList = lists:keystore(Mid, 1, GuildBuffList, {Mid, BuffNum+1}),
                    lists:keystore(GuildId, #guild_res.guild_id, BuffList, GuildRes#guild_res{buff_lists = NewGuildBuffList});
                _ ->
                    NewGuildBuffList = lists:keystore(Mid, 1, GuildBuffList, {Mid, 1}),
                    lists:keystore(GuildId, #guild_res.guild_id, BuffList, GuildRes#guild_res{buff_lists = NewGuildBuffList})
            end;
        _ ->
            NewGuildBuffList = [{Mid, 1}],
            GuildRes = #guild_res{guild_id = GuildId, buff_lists = NewGuildBuffList},
            lists:keystore(GuildId, #guild_res.guild_id, BuffList, GuildRes)
    end,
    %% 社团加buff
    ArenaId = maps:get(GuildId, GuildArenaMap, 0),
    ArenaGuildList = maps:get(ArenaId, ArenaMap, []),
    case lists:keyfind(GuildId, 1, ArenaGuildList) of
        {_, GuildServId} -> ok;
        _   -> GuildServId = 0
    end,
    update_camp_war_buff(ClsType, GuildServId, GuildId, Mid),
    %% 通知物资更新
    case lists:keyfind(ArenaId, 1, RoleRank) of
        {_, ArenaRoles} ->
            case lists:keyfind(CollectorId, #camp_war_role.role_id, ArenaRoles) of
                #camp_war_role{server_id = ServId} ->
                    {ok, Bin} = pt_410:write(41003, [?SUCCESS, NewGuildBuffList]),
                    case lists:keyfind(Mid, 1, NewGuildBuffList) of
                        {_, NewBuffNum} -> ok;
                        _               -> NewBuffNum = 0
                    end,
                    {ok, Bin2} = pt_410:write(41009, [RecFigure, Mid, NewBuffNum]),
                    BinData = <<Bin/binary, Bin2/binary>>,
                    if ClsType == ?NODE_CENTER -> clusters_center:apply_cast(ServId, lib_server_send, send_to_guild, [GuildId, BinData]);
                        true  -> lib_server_send:send_to_guild(GuildId, BinData)
                    end;
                _ -> skip
            end;
        _ -> skip
    end,
    SceneId = get_camp_scene(ClsType),
    SceneInfo = calc_scene_res_info(NewPoolRes),
    ResStart = maps:get(?RES_START_TIME, Map, 0),
    NextTime = calc_next_res_time(ResStart),
    {ok, Bin1} = pt_410:write(41004, [?SUCCESS, NextTime, SceneInfo]),
    lib_server_send:send_to_scene(SceneId, ScenePool, Bin1),
    %% 成就
    case clusters_api:get_cluster_type() of
        ?CLUSTER_NODE ->
            lib_achievement_api:async_event(CollectorId, lib_achievement_api, cross_camp_war_collect_event, 1);
        _ ->
            Node = clusters_center:get_node_by_role_id(CollectorId),
            clusters_center:apply_cast(Node, lib_achievement_api, cross_camp_war_collect_event, [CollectorId, 1])
    end,
    NewState = State#zone_fac_state{
        resource = NewResource,
        buff_list = NewBuffList
    },
    {noreply, NewState};

do_handle_cast({kill_mon_stage}, State) ->
    #zone_fac_state{arena_room_map = ArenaRoomMap, extra_map = Map, resource = Resource, cls_type = ClsType} = State,
    NewStatus = ?CAMP_WAR_OPENING_3,
    %% 先清一遍采集怪
    SceneId = get_camp_scene(ClsType),

    maps:map(fun(_ArenaId, Pools) ->
        [begin
            case maps:get(ScenePoolId, Resource, []) of
                SceneRes = [_|_] ->
                    Mids = lists:foldl(fun({MonId, _, _}, Acc) ->
                        case lists:member(MonId, Acc) of
                            true -> Acc;
                            _    -> [MonId|Acc]
                        end
                    end, [], SceneRes),
                    lib_mon:clear_scene_mon_by_mids(SceneId, ScenePoolId, 0, 1, Mids);
                _ ->
                    skip
            end
        end || ScenePoolId <- Pools]
    end, ArenaRoomMap),
    maps:map(fun(_ArenaId, PoolList) ->
        init_arena_pool_boss(ClsType, PoolList)
    end, ArenaRoomMap),
    % ?ERR("turn into kill mon stage", []),
    util:cancel_timer(maps:get(?RES_UPUPDATE_REF, Map, undefined)),
    %% 刷新间隔修改
    HurtRef = erlang:send_after(5000, self(), {hurt_rank_notice}),
    %% 延后通知因boss回调处理需要时间
    HostingRef = erlang:send_after(3000, self(), {notice_hosting_role_attack}),
    NewMap  = maps:put(?RES_UPUPDATE_REF, undefined, Map),
    NewMap1 = maps:put(?HURT_UPDATE_REF, HurtRef, NewMap),
    NewMap2 = maps:put(?HOSTING_REF, HostingRef, NewMap1),
    NewState = State#zone_fac_state{status = NewStatus, extra_map = NewMap2, resource = #{}},
    {noreply, NewState};

do_handle_cast({leave_scene_when_logout, _ServId, RoleId, _SceneId, PoolId, GuildId}, State) ->
    #zone_fac_state{
        role_rank = RoleRank, room_role_num = RoomRoleNumMap, guild_rank = GuildRank,
        arena_room_map = ArenaRoomMap, guild_arena_map = GuildArenaMap
    } = State,
    ArenaId = maps:get(GuildId, GuildArenaMap, 0),
    case lists:keyfind(ArenaId, 1, RoleRank) of
        {_, ArenaRoles} ->
            case lists:keyfind(RoleId, #camp_war_role.role_id, ArenaRoles) of
                Role = #camp_war_role{} ->
                    NewRole = Role#camp_war_role{in_act = 0},
                    NewArenaRoles = lists:keystore(RoleId, #camp_war_role.role_id, ArenaRoles, NewRole),
                    NewRoleRank = lists:keystore(ArenaId, 1, RoleRank, {ArenaId, NewArenaRoles});
                _ ->
                    NewRoleRank = RoleRank
            end;
        _ ->
            NewRoleRank = RoleRank
    end,
    NewGuildRank = del_guild_role_num(GuildId, ArenaId, GuildRank),
    NewRoomRoleNumMap = del_arena_scene_pool_role(ArenaId, PoolId, ArenaRoomMap, RoomRoleNumMap),
    NewState = State#zone_fac_state{role_rank = NewRoleRank, guild_rank = NewGuildRank, room_role_num = NewRoomRoleNumMap},
    {noreply, NewState};

do_handle_cast({act_end}, State) ->
    #zone_fac_state{
        zone_fac = {ZoneId, FacId}, extra_map = Map, arena_map = ArenaMap,
        role_rank = RoleRank, guild_rank = GuildRank, cls_type = ClsType} = State,
    % ?ERR("act end", []),
    %% 结算
    GuildRankData = camp_war_settlement(guild_rank, ClsType, GuildRank),
    RoleRankData  = camp_war_settlement(role_rank, ClsType, RoleRank),
    %% 推送
    notice_camp_war_result(ClsType, RoleRankData, GuildRankData, ArenaMap),

    %% 停止托管进程
    Pid = maps:get(?OFF_HOST_PID, Map, undefined),
    misc:is_process_alive(Pid) andalso erlang:exit(Pid, kill),
    %% 踢人
    % role_kickout_scene(RoleRank),
    %% TODO LOG
    camp_war_log(GuildRankData, RoleRankData, State),

    {NewZoneId, NewFaction} = calc_zone_fact(ClsType, ZoneId, FacId),
    SortedGuilds = sort_faction_guild_rank(GuildRank),
    report_directors(NewZoneId, NewFaction, SortedGuilds),
    submit_faction_guild_rank(ClsType, NewZoneId, NewFaction, SortedGuilds),
    % db_update_fac_guild_rank(SortedGuilds, NewZoneId, NewFaction),
    {noreply, State};

do_handle_cast({hurt_mon, [_ScenePoolId, AtterId, ServId, GuildId, GuildFlag], Hurt}, State) ->
    #zone_fac_state{zone_fac = ZoneFac, role_rank = RoleRank, guild_arena_map = GuildArenaMap, guild_rank = GuildRank} = State,
    HurtGuild = #camp_war_guild{guild_id = GuildId, zone_facs = ZoneFac, server_id = ServId, hurt = Hurt, guild_flag = GuildFlag},
    HurtRole  = #camp_war_role{role_id = AtterId, guild_id = GuildId, server_id = ServId, zone_facs = ZoneFac, hurt = Hurt, in_act = 1},
    case maps:get(GuildId, GuildArenaMap, 0) of
        ArenaId when ArenaId =/= 0 ->
            NewGuildRank = update_guild_rank(HurtGuild, ArenaId, GuildRank),
            NewRoleRank  = update_role_rank(HurtRole, ArenaId, RoleRank),
            NewState = State#zone_fac_state{role_rank = NewRoleRank, guild_rank = NewGuildRank};
        _A ->
            NewState = State
    end,
    {noreply, NewState};

do_handle_cast({recover_worker_with_backup, FacState}, State) ->
    #zone_fac_state{status = Status, extra_map = Map} = State,
    InitRef = maps:get(?INIT_REF, Map, undefined),
    if is_reference(InitRef) ->
    case erlang:read_timer(InitRef) of
        Time when is_integer(Time) andalso Time > 0 ->
            util:cancel_timer(InitRef);
        _ ->
                    skip
            end;
        true ->
            skip
    end,
    if
        Status == ?CAMP_WAR_OPENING_1 ->
            #zone_fac_state{extra_map = OldMap} = FacState,
            Time1 = data_camp_war:get_kv(material_update_time),
            Ref = erlang:send_after(Time1*1000, self(), {resource_update}),
            NewMap = maps:put(?RES_UPUPDATE_REF, Ref, OldMap),
            NewState = FacState#zone_fac_state{extra_map = NewMap};
        true -> %% 伤害定时刷新
            NewState = FacState
    end,
    {noreply, NewState};

do_handle_cast({return_boss_unique_id, Mid, ScenePoolId}, State) ->
    #zone_fac_state{extra_map = Map} = State,
    BossList = maps:get(?BOSS_UNIQUE_ID, Map, []),
    case lists:keyfind(ScenePoolId, 1, BossList) of
        {_, _} ->
            NewBossList = BossList;
        _ ->
            NewBossList = lists:keystore(ScenePoolId, 1, BossList, {ScenePoolId, Mid})
    end,
    NewMap = maps:put(?BOSS_UNIQUE_ID, NewBossList, Map),
    NewState = State#zone_fac_state{extra_map = NewMap},
    {noreply, NewState};

do_handle_cast({relogin, _ServId, GuildId, RoleId}, State) ->
    #zone_fac_state{role_rank = RoleRank, guild_arena_map = GuildArenaMap} = State,
    ArenaId = maps:get(GuildId, GuildArenaMap, 0),
    if ArenaId =/= 0 ->
            case lists:keyfind(ArenaId, 1, RoleRank) of
                {_, ArenaRoles} ->
                    case lists:keyfind(RoleId, #camp_war_role.role_id, ArenaRoles) of
                        Role = #camp_war_role{is_host = 1} ->
                            NewArenaRoles = lists:keystore(RoleId, #camp_war_role.role_id, ArenaRoles, Role#camp_war_role{is_host = 0}),
                            NewRoleRank = lists:keystore(ArenaId, 1, RoleRank, {ArenaId, NewArenaRoles}),
                            NewState = State#zone_fac_state{role_rank = NewRoleRank},
                            {noreply, NewState};
                        _ ->
                            {noreply, State}
                    end;
                _ ->
                    {noreply, State}
            end;
        true ->
            {noreply, State}
    end;

%% 预清理阶段
do_handle_cast({pre_kill_mon}, State) ->
    #zone_fac_state{arena_room_map = ArenaRoomMap, cls_type = ClsType} = State,
    SceneId = get_camp_scene(ClsType),
    %% 清理场景资源
    maps:map(fun(_ArenaId, Pools) ->
        [lib_mon:kill_scene_mon(SceneId, ScenePoolId, 0) || ScenePoolId <- Pools]
        % [ lib_scene:clear_scene_room(SceneId, ScenePoolId, 0) || ScenePoolId <- Pools ]
    end, ArenaRoomMap),
    {noreply, State};

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

% do_handle_info({worker_init}, State) ->
%     #zone_fac_state{
%         zone_fac = {ZoneId, FacId}, arena_map = ArenaMap, guild_arena_map = GuildMap,
%         extra_map = Map, cls_type = ClsType
%     } = State,

%     NewArenaMap = load_fac_arena_info({ZoneId, FacId}, ArenaMap),
%     NewGuildMap = load_guild_arena_map(NewArenaMap, GuildMap),
%     % ?ERR("worker init ~p ~p", [NewArenaMap, NewGuildMap]),
%     Time = data_camp_war:get_kv(material_update_time),
%     Ref = erlang:send_after(Time*1000, self(), {resource_update}),
%     NewMap = maps:put(?RES_UPUPDATE_REF, Ref, Map),

%     WorkerInitRef = maps:get(?INIT_REF, NewMap, undefined),
%     util:cancel_timer(WorkerInitRef),
%     NewMap1 = maps:put(?INIT_REF, undefined, NewMap),

%     Pid = offline_hosting_camp_war:camp_war_offline_hosting(ClsType, NewArenaMap),
%     NewMap2 = maps:put(?OFF_HOST_PID, Pid, NewMap1),

%     NewState = State#zone_fac_state{arena_map = NewArenaMap, guild_arena_map = NewGuildMap, extra_map = NewMap2},
%     {noreply, NewState};

do_handle_info({resource_update}, State) ->
    #zone_fac_state{zone_fac = {ZoneId, FacId}, extra_map = Map, resource = Resource, res_id = ResIndex, cls_type = ClsType} = State,
    Ref = maps:get(?RES_UPUPDATE_REF, Map, undefined),
    ResStart = maps:get(?RES_START_TIME, Map, 0),
    NextTime = calc_next_res_time(ResStart),
    util:cancel_timer(Ref),
    % ?ERR("resource_update ~p", [NextTime]),
    {NewResIndex, NewResource} = update_scene_resource(ClsType, {ZoneId, FacId}, Resource, ResIndex, NextTime),
    Time = data_camp_war:get_kv(material_update_time),
    NewRef = erlang:send_after(Time*1000, self(), {resource_update}),
    NewMap = maps:put(?RES_UPUPDATE_REF, NewRef, Map),
    NewState = State#zone_fac_state{res_id = NewResIndex, resource = NewResource, extra_map = NewMap},
    {noreply, NewState};

do_handle_info({hurt_rank_notice}, State) ->
    #zone_fac_state{
        role_rank = RoleRank, guild_rank = GuildRank, extra_map = Map, cls_type = ClsType
    } = State,
    util:cancel_timer(maps:get(?HURT_UPDATE_REF, Map, undefined)),
    HurtRef = erlang:send_after(5000, self(), {hurt_rank_notice}),
    NewMap = maps:put(?HURT_UPDATE_REF, HurtRef, Map),
    lists:map(fun({ArenaId, ArenaGuilds}) ->
        ArenaGuilds1 = lists:filter(fun(#camp_war_guild{hurt = HurtX}) -> HurtX =/= 0 end, ArenaGuilds),
        SortedArenaGuilds = lists:reverse(lists:keysort(#camp_war_guild.hurt, ArenaGuilds1)),
        {_, HurtData} = lists:foldl(fun(Guild, {Index, Acc}) ->
            #camp_war_guild{guild_id = GuildId, guild_name = GuildName, server_num = ServerNum, hurt = Hurt} = Guild,
            {Index+1, [{Index, GuildId, GuildName, ServerNum, Hurt}|Acc]}
        end, {1, []}, SortedArenaGuilds),
        % ?ERR("hurt rank notice ~p", [HurtData]),
        case lists:keyfind(ArenaId, 1, RoleRank) of
            {_, ArenaRoles} -> ok;
            _   -> ArenaRoles = []
        end,
        lists:map(fun(Role) ->
            notice_camp_war_hurt_rank(ClsType, Role, HurtData)
        end, ArenaRoles)
    end, GuildRank),
    NewState = State#zone_fac_state{extra_map = NewMap},
    {noreply, NewState};

do_handle_info({notice_hosting_role_attack}, State) ->
    #zone_fac_state{extra_map = Map} = State,
    %% 通知托管玩家开始攻击boss
    offline_hosting_camp_war:notice_hosting_role_go_attack(State),
    util:cancel_timer(maps:get(?HOSTING_REF, Map, undefined)),
    NewState = State#zone_fac_state{extra_map = maps:put(?HOSTING_REF, undefined, Map)},
    {noreply, NewState};

do_handle_info({stop},State) ->
    #zone_fac_state{arena_room_map = ArenaRoomMap, role_rank = RoleRank, cls_type = ClsType} = State,
    SceneId = get_camp_scene(ClsType),
    role_kickout_scene(ClsType, RoleRank),
    spawn(fun() ->
        timer:sleep(5000),
        maps:map(fun(_ArenaId, Pools) ->
            [ mod_scene_agent:close_scene(SceneId, ScenePoolId) || ScenePoolId <- Pools ]
        end, ArenaRoomMap)
    end),
    {stop, normal, #zone_fac_state{}};

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

terminate(_Reason, State) ->
    if _Reason == normal ->
            skip;
        true ->
            SceneId = get_camp_scene(State#zone_fac_state.cls_type),
            #zone_fac_state{arena_room_map = ArenaRoomMap, extra_map = Map, role_rank = RoleRank} = State,
            Ref = maps:get(?RES_UPUPDATE_REF, Map, undefined),
            Ref1 = maps:get(?HURT_UPDATE_REF, Map, undefined),
            util:cancel_timer(Ref),
            util:cancel_timer(Ref1),
            role_kickout_scene(State#zone_fac_state.cls_type, RoleRank),
            %% 清理场景资源
            spawn(fun() ->
                    timer:sleep(3000),
                    maps:map(fun(_ArenaId, Pools) ->
                        [ mod_scene_agent:close_scene(SceneId, ScenePoolId) || ScenePoolId <- Pools ]
                    end, ArenaRoomMap)
            end),

            %% 停止托管进程
            Pid = maps:get(?OFF_HOST_PID, Map, undefined),
            misc:is_process_alive(Pid) andalso erlang:exit(Pid, kill)
            % BackUpData = get_backup_data(State),
            % ets:insert(?ETS_FAC_WORKER_BACKUP, BackUpData),
    end,
    ok.

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

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

load_fac_arena_info({ZoneId, FacId}, ArenaMap) ->
    case ets:lookup(?ETS_FAC_ARENA_INFO, {ZoneId, FacId}) of
        [{_, _FacIndex, ArenaList}|_] ->
            maps:from_list(ArenaList);
        _ ->
            ArenaMap
    end.

get_guild_born_coordinate({ZoneId, FacId}, ArenaId, GuildId) ->
    case ets:lookup(?ETS_FAC_ARENA_INFO, {ZoneId, FacId}) of
        [{_, _FacIndex, ArenaList}|_] ->
            case lists:keyfind(ArenaId, 1, ArenaList) of
                {_, GuildList} ->
                    BornList = data_camp_war:get_kv(born_position),
                    Index = ulists:find_index(fun({GuildIdX, _}) ->
                        GuildIdX == GuildId
                    end, GuildList),
                    case lists:nth(Index, BornList) of
                        {X, Y} ->
                            if GuildId rem 2 == 0 ->
                                    RandX = urand:rand(X, X+350),
                                    RandY = urand:rand(Y, Y+350);
                                true ->
                                    RandX = urand:rand(X-350, X),
                                    RandY = urand:rand(Y-350, Y)
                            end,
                            {RandX, RandY};
                        _ -> camp_war_api:normal_born_coordinate()
                    end;
                _ ->
                    camp_war_api:normal_born_coordinate()
            end;
        _ ->
            camp_war_api:normal_born_coordinate()
    end.

load_guild_arena_map(ArenaMap, GuildMap) ->
    ArenaList = maps:to_list(ArenaMap),
    lists:foldl(fun({ArenaId, Guilds}, TempMap) ->
        lists:foldl(fun({GuildId, _ServId}, TempMap1) ->
            maps:put(GuildId, ArenaId, TempMap1)
        end, TempMap, Guilds)
    end, GuildMap, ArenaList).


%% 初始化分线物资信息
init_arena_scene_info(State) ->
    #zone_fac_state{
        zone_fac = {ZoneId, FacId}, resource = Resource, arena_map = _ArenaMap,
        arena_room_map = ArenaRoomMap, res_id = ResId, extra_map = Map, cls_type = ClsType
    } = State,
    % ?ERR("room_map ~p", [ArenaRoomMap]),
    ResStart = maps:get(?RES_START_TIME, Map, 0),
    %% 初始化场景物资信息
    {NewResId, NewResource} =
     maps:fold(
        fun(_ArenaIdX, ArenaPools, {TmpResId, TempResource}) ->
            lists:foldl(fun(ScenePool, {TmpResId1, TempResource1}) ->
                {NewTmpResId1, NewTempResource1} = init_scene_resource(ClsType, {ZoneId, FacId}, ScenePool, TmpResId1, TempResource1, ResStart),
                {NewTmpResId1, NewTempResource1}
            end, {TmpResId, TempResource}, ArenaPools)
        end, {ResId, Resource}, ArenaRoomMap),
    NewState = State#zone_fac_state{res_id = NewResId, resource = NewResource},
    NewState.

%% 初始化阵营内一条场景分线的物资资源
%% Return :: {ResId, Resource}
init_scene_resource(ClsType, {ZoneId, FacId}, ScenePool, ResId, Resource, ResStart) ->
    SceneRes = maps:get(ScenePool, Resource, []),
    ResNum   = length(SceneRes),
    SceneResMax = data_camp_war:get_kv(material_max),
    if ResNum >= SceneResMax ->
            {ResId, Resource};
        true ->
            CfgUpdateNum = data_camp_war:get_kv(material_update_num),
            UpdateNum   = min(SceneResMax - ResNum, CfgUpdateNum),
            NewRes      = get_rand_update_res(ClsType, UpdateNum, SceneRes),
            init_scene_resource_do(ClsType, {ZoneId, FacId}, ScenePool, ResId, NewRes, Resource, ResStart)
    end.

%% 获取本次刷新的物资数
get_rand_update_res(ClsType, Num, SceneRes) ->
    CfgChance       = data_camp_war:get_kv(material_probability),
    CfgResPos       = get_scene_cfg_res_pos(),
    RestCfgResPos   = calc_update_pos(CfgResPos, SceneRes),
    SceneId         = get_camp_scene(ClsType),
    rand_update_res(SceneId, CfgChance, RestCfgResPos, Num, [], 1).

get_scene_cfg_res_pos() ->
    CfgPos1 = data_camp_war:get_kv(mon_seat_1),
    CfgPos2 = data_camp_war:get_kv(mon_seat_2),
    CfgPos3 = data_camp_war:get_kv(mon_seat_3),
    CfgResPos = CfgPos1 ++ CfgPos2 ++ CfgPos3,
    CfgResPos.

calc_update_pos(CfgResPos, SceneRes) ->
    SceneRes1 = [{X, Y} || {_MonId, {X, Y}, _TempResId} <- SceneRes],
    RestCfgResPos = CfgResPos -- SceneRes1,
    ulists:list_shuffle(RestCfgResPos).

rand_update_res(_SceneId, _, _, 0, Acc, _) -> Acc;
rand_update_res(SceneId, CfgChance, RestCfgResPos, Num, Acc, ErrCount) ->
    ResId = urand:rand_with_weight(CfgChance),
    {X, Y} = urand:list_rand(RestCfgResPos),
    RestCfgResPos1 = RestCfgResPos -- [{X, Y}],
    rand_update_res(SceneId, CfgChance, RestCfgResPos1, Num-1, [{ResId, {X, Y}}|Acc], ErrCount).


init_scene_resource_do(ClsType, {ZoneId, FacId}, ScenePool, ResId, NewRes, Resource, ResStart) ->
    SceneId = get_camp_scene(ClsType),
    % ?ERR("scene pool init ~p", [ScenePool]),

    {NewResId, PoolResource} = lists:foldl(fun({MonId, {X, Y}}, {TempResId, TempAcc}) ->
        MonKey = {?MOD_CAMP_WAR, ZoneId, FacId, ScenePool, TempResId},
        Attrs  = [{create_key, MonKey}],
        case data_mon:get(MonId) of
            #mon{type = MonType} -> ok;
            _ -> MonType = 0
        end,
        lib_mon:async_create_mon(MonId, SceneId, ScenePool, X, Y, MonType, 0, 1, Attrs),
        {TempResId+1, [{MonId, {X, Y}, TempResId}|TempAcc]}
    end, {ResId, []}, NewRes),
    % debug_pool_res(init, ScenePool, PoolResource),
    SceneResList = calc_scene_res_info(PoolResource),
    NextTime     = calc_next_res_time(ResStart),
    {ok, Bin}    = pt_410:write(41004, [?SUCCESS, NextTime, SceneResList]),
    lib_server_send:send_to_scene(SceneId, ScenePool, Bin),
    NewResource  = maps:put(ScenePool, PoolResource, Resource),
    {NewResId, NewResource}.

%% 定时更新资源操作
update_scene_resource(ClsType, {ZoneId, FacId}, Resource, ResIndex, NextTime) ->
    TotalLimit = data_camp_war:get_kv(material_max),
    UpdateNum  = data_camp_war:get_kv(material_update_num),
    SceneId    = get_camp_scene(ClsType),
    ResourceList = maps:to_list(Resource),
    {NewResIndex, NewResourceList} = lists:foldl(fun({ScenePool, SceneRes}, {TempIndex, TempResources}) ->
        SceneNum = length(SceneRes),
        NewSceneNum = SceneNum + UpdateNum,
        if
            NewSceneNum > TotalLimit ->
                NewUpdateNum = TotalLimit - SceneNum;
            true ->
                NewUpdateNum = UpdateNum
        end,
        {NewTempIndex, NewSceneRes} = update_scene_resource_do(ClsType, {ZoneId, FacId}, ScenePool, SceneRes, TempIndex, NewUpdateNum),
        SceneResInfo = calc_scene_res_info(NewSceneRes),
        % debug_pool_res(update, ScenePool, NewSceneRes),
        {ok, Bin} = pt_410:write(41004, [?SUCCESS, NextTime, SceneResInfo]),
        lib_server_send:send_to_scene(SceneId, ScenePool, Bin),
        NewTempResources = lists:keystore(ScenePool, 1, TempResources, {ScenePool, NewSceneRes}),
        {NewTempIndex, NewTempResources}
    end, {ResIndex, ResourceList}, ResourceList),
    {NewResIndex, maps:from_list(NewResourceList)}.

update_scene_resource_do(ClsType, {ZoneId, FacId}, ScenePool, SceneRes, Index, Num) ->
    NewRes  = get_rand_update_res(ClsType, Num, SceneRes),
    SceneId = get_camp_scene(ClsType),
    {NewIndex, NewSceneRes} = lists:foldl(fun({ResId, {RandX, RandY}}, {TempIndex, TempSceneRes}) ->
        MonKey = {?MOD_CAMP_WAR, ZoneId, FacId, ScenePool, TempIndex},
        Attrs  = [{create_key, MonKey}],
        case data_mon:get(ResId) of
            #mon{type = MonType} -> ok;
            _ -> MonType = 0
        end,
        lib_mon:async_create_mon(ResId, SceneId, ScenePool, RandX, RandY, MonType, 0, 1, Attrs),
        {TempIndex+1, [{ResId, {RandX, RandY}, TempIndex}|TempSceneRes]}
    end, {Index, SceneRes}, NewRes),
    {NewIndex, NewSceneRes}.

%% 初始化场景boss
init_arena_pool_boss(ClsType, PoolList) ->
    SceneId = get_camp_scene(ClsType),
    BossId = data_camp_war:get_kv(boss_id),
    [{X, Y}|_] = data_camp_war:get_kv(boss_position),
    case data_mon:get(BossId) of
        #mon{type = MonType} -> ok;
        _  -> MonType = 0
    end,
    BornMfa = {?MODULE, get_boss_unique_id, [self()]},
    lists:map(fun(PoolId) ->
        lib_mon:async_create_mon(BossId, SceneId, PoolId, X, Y, MonType, 0, 1, [{born_handler, BornMfa}])
    end, PoolList),
    ok.


camp_war_settlement(role_rank, ClsType, RoleRank) ->
    lists:map(fun({ArenaId, ArenaRoles}) ->
        ArenaRoles1       = lists:filter(fun(#camp_war_role{hurt = Hurt}) -> Hurt =/= 0 end, ArenaRoles),
        SortedArenaRoles  = lists:keysort(#camp_war_role.hurt, ArenaRoles1),
        SortedArenaRoles1 = lists:reverse(SortedArenaRoles),
        {_, NewSortedArenaRoles} =
        lists:foldl(fun(Role, {Index, Acc}) ->
            #camp_war_role{
                role_id = RoleId, server_id = ServId,
                guild_id = GuildId, is_host = IsHost
            } = Role,
            Rewards =
            if
                IsHost == 0 -> data_camp_war:get_role_rank_rewards(Index);
                IsHost == 1 -> data_camp_war:get_mirror_reward(Index);
                true        -> []
            end,
            case Rewards of
                [_|_] -> role_settlement_reward(ClsType, Role, Index, Rewards);
                _     -> skip
            end,
            {Index+1, [{Index, RoleId, ServId, GuildId}|Acc]}
        end, {1, []}, SortedArenaRoles1),
        {ArenaId, NewSortedArenaRoles}
    end, RoleRank);

camp_war_settlement(guild_rank, ClsType, GuildRank) ->
    lists:map(fun({ArenaId, ArenaGuilds}) ->
        ArenaGuilds1 = lists:filter(fun(#camp_war_guild{hurt = Hurt}) -> Hurt =/= 0 end, ArenaGuilds),
        SortedArenaGuilds  = lists:keysort(#camp_war_guild.hurt, ArenaGuilds1),
        SortedArenaGuilds1 = lists:reverse(SortedArenaGuilds),
        IsTopArena = ?IF(ArenaId == 1, 1, 0),
        {_, NewSortedArenaGuilds} =
        lists:foldl(fun
            (Guild = #camp_war_guild{guild_id = GuildId, guild_name = GuildName}, {Index, Acc}) ->
            case data_camp_war:get_rank_rewards(IsTopArena, Index) of
                Rewards = [_|_] -> guild_settlement_reward(ClsType, Guild, Index, Rewards);
                _               -> skip
            end,
            {Index+1, [{Index, GuildId, GuildName}|Acc]};
            (_, {Index, Acc}) ->
                {Index, Acc}
        end, {1, []}, SortedArenaGuilds1),
        {ArenaId, NewSortedArenaGuilds}
    end, GuildRank).

role_settlement_reward(ClsType, Role, Index, Rewards) ->
    #camp_war_role{role_id = RoleId, server_id = ServId, is_host = IsHost} = Role,
    if
        IsHost == 0 ->
            Title = ?LAN_MSG(4100003),
            Content = uio:format(?LAN_MSG(4100004), [Index]);
        true ->
            Title = ?LAN_MSG(4100005),
            Content = uio:format(?LAN_MSG(4100006), [Index])
    end,
    if ClsType == ?NODE_CENTER ->
            clusters_center:apply_cast(ServId, ?MODULE, role_get_rewards, [RoleId, IsHost, Title, Content, Rewards]);
        true ->
            role_get_rewards(RoleId, IsHost, Title, Content, Rewards)
    end,
    ok.

role_kickout_scene(ClsType, RoleRank) ->
    lists:map(fun({_ArenaId, ArenaRoles}) ->
        lists:map(fun(Role) ->
            #camp_war_role{role_id = RoleId, server_id = ServId, in_act = ActStatus, is_host = InHost} = Role,
            if
                InHost == 1 ->
                    if ClsType == ?NODE_CENTER ->
                            clusters_center:apply_cast(ServId, camp_war_api, host_end, [RoleId]);
                        true ->
                            camp_war_api:host_end(RoleId)
                    end;
                ActStatus == 1 ->
                    if ClsType == ?NODE_CENTER ->
                            clusters_center:apply_cast(ServId, lib_scene, change_player_scene, [RoleId, 0, 0, 0, 0, 0, true, [{action_free, ?ERRCODE(err410_on_camp_war)}]]);
                        true ->
                            lib_scene:change_player_scene(RoleId, 0, 0, 0, 0, 0, true, [{action_free, ?ERRCODE(err410_on_camp_war)}])
                    end;
                true ->
                    skip
            end
        end, ArenaRoles)
    end, RoleRank),
    ok.

guild_settlement_reward(ClsType, Guild, Index, Rewards) ->
    #camp_war_guild{server_id = ServId, guild_id = GuildId} = Guild,
    if ClsType == ?NODE_CENTER ->
            spawn(fun() ->
                timer:sleep(urand:rand(1000,5000)),
                clusters_center:apply_cast(ServId, mod_guild, apply_cast_guild_member, [GuildId, 0, ?MODULE, send_guild_camp_war_reward, [Index, Rewards]])
            end);
        true ->
            mod_guild:apply_cast_guild_member(GuildId, 0, ?MODULE, send_guild_camp_war_reward, [Index, Rewards])
    end,
    ok.

send_guild_camp_war_reward(Player, Index, Rewards) ->
    #player_status{id = RoleId, offline_hosting = #offline_hosting{handler = Handler}} = Player,
    % ProduceType = camp_war_guild_reward,
    Title       = ?LAN_MSG(4100001),
    Content     = uio:format(?LAN_MSG(4100002), [Index]),
    lib_mail_api:send_sys_mail([RoleId], ?MOD_CAMP_WAR, Title, Content, Rewards),
    case Handler of
        undefined ->
            {ok, Player};
        _ ->
            {ok, NewPlayer} = offline_hosting:host_behavior_reward(Player, Rewards),
            {ok, NewPlayer}
    end.

notice_camp_war_result(ClsType, RoleRankData, GuildRankData, _ArenaMap) ->
    ArenaRoleRankData = merge_same_arena_info(RoleRankData, GuildRankData),
    lists:map(fun({ArenaId, ArenaRoles, ArenaGuilds}) ->
        case lists:keyfind(1, 1, ArenaGuilds) of
            {_, _, GuildName} -> ok;
            _   -> GuildName = ""
        end,
        IsTopArena = ?IF(ArenaId == 1, 1, 0),
        lists:map(fun({Index, RoleId, ServId, GuildId}) ->
            case lists:keyfind(GuildId, 2, ArenaGuilds) of
                {GuildIndex, _, _} -> ok;
                _   -> GuildIndex = 0
            end,
            notice_svr_role(ClsType, ServId, RoleId, pt_410, 41007, [?SUCCESS, GuildIndex, IsTopArena, Index, GuildName])
        end, ArenaRoles)
    end, ArenaRoleRankData),
    ok.
    %% TODO 现在改为战场单位下的社团排名,原先的阵营社团排名传闻不太适用了
    % ServList = lists:foldl(fun({_, Guilds}, Acc) ->
    %     lists:foldl(fun({_, ServIdX}, TempAcc) ->
    %         case lists:member(ServIdX, TempAcc) of
    %             true -> TempAcc;
    %             _    -> [ServIdX|TempAcc]
    %         end
    %     end, Acc, Guilds)
    % end, [], maps:to_list(ArenaMap)),
    % lists:append([begin [ServIdX || {_, ServIdX} <- Guilds] end || {_, Guilds} <- maps:to_list(ArenaMap)]),
    % lists:map(fun(ServIdX) ->
    %     clusters_center:apply_cast(ServIdX, lib_chat, send_TV, [{all}, ?MOD_CAMP_WAR, 2, [GuildName]])
    % end, ServList).

merge_same_arena_info(RoleRankData, GuildRankData) ->
    lists:foldl(fun({ArenaId, ArenaRoles}, Acc) ->
        case lists:keyfind(ArenaId, 1, GuildRankData) of
            {_, ArenaGuilds} ->
                [{ArenaId, ArenaRoles, ArenaGuilds}|Acc];
            _ ->
                Acc
        end
    end, [], RoleRankData).

%% 进入战场选择合适分线
% ArenaRoomMap         %% 战场与分线映射 arena_id => [pool_id,...]
% RoomRoleMap          %% 分线人数 pool_id => num
get_arena_scene_pool(ClsType, ArenaId, MaxPoolId, ArenaRoomMap, RoomRoleMap) ->
    Rooms = maps:get(ArenaId, ArenaRoomMap, []),
    RoomRoles = get_room_role(Rooms, RoomRoleMap),
    case get_scene_pool(ClsType, MaxPoolId, RoomRoles) of
        {new_pool, NewPoolId, NewMaxPool} ->
            NewArenaRoomMap = maps:put(ArenaId, [NewPoolId|Rooms], ArenaRoomMap),
            NewRoomRoleMap  = maps:put(NewPoolId, 1, RoomRoleMap),
            {true, NewPoolId, NewMaxPool, NewArenaRoomMap, NewRoomRoleMap};
        {available, OldPoolId, NewPoolRoleNum} ->
            NewRoomRoleMap = maps:put(OldPoolId, NewPoolRoleNum, RoomRoleMap),
            {false, OldPoolId, MaxPoolId, ArenaRoomMap, NewRoomRoleMap}
    end.


get_room_role(Rooms, RoomRoleMap) ->
    lists:foldl(fun(Pool, Acc) ->
        RoomRole = maps:get(Pool, RoomRoleMap, 0),
        lists:keystore(Pool, 1, Acc, {Pool, RoomRole})
    end, [], Rooms).


get_scene_pool(ClsType, MaxPoolId, RoomRoles) ->
    case lists:keysort(2, RoomRoles) of
        [{PoolId, MinRole}|_] ->
            %% 获取场景分线人数上限
            SceneId = get_camp_scene(ClsType),
            MaxScenePoolRole = lib_scene:get_room_max_role(SceneId),
            % MaxScenePoolRole = 2,
            if MinRole >= MaxScenePoolRole ->
                    {new_pool, MaxPoolId, MaxPoolId+1};
                true ->
                    {available, PoolId, MinRole+1}
            end;
        _ ->
            {new_pool, MaxPoolId, MaxPoolId+1}
    end.

get_hurt_rank_info(RankType, PageSize, PageNum, ArenaId, RankList) ->
    TotalPage = umath:ceil(length(RankList)/PageSize),
    SortedRankList = ?IF(RankType == 0, lists:keysort(#camp_war_role.hurt, RankList), lists:keysort(#camp_war_guild.hurt, RankList)),
    SortedRankList1 = lists:reverse(SortedRankList),
    RankData = get_hurt_rank_info_by_page(PageSize, PageNum, SortedRankList1),
    {_, RankData1} = pack_hurt_rank_data(RankType, ArenaId, RankData),
    {TotalPage, RankData1}.

%% PageSize 页码大小
%% PageNum  第几页
get_hurt_rank_info_by_page(PageSize, PageNum, RankList) ->
    %% 起始位置
    StartLen = round(max(0, PageNum-1)*PageSize) + 1,
    %% 结束位置
    EndLen   = PageNum*PageSize,
    TotalLen = length(RankList),
    if
        TotalLen == 0 ->
            SubRankList = [];
        StartLen == 0 ->
            SubRankList = lists:sublist(RankList, StartLen, max(0, EndLen));
        StartLen > TotalLen+1 ->
            SubRankList = [];
        true ->
            SubRankList = lists:sublist(RankList, StartLen, max(0, EndLen-StartLen))
    end,
    SubRankList.

pack_hurt_rank_data(0, ArenaId, RankList) ->
    IsTopArena = ?IF(ArenaId == 1, 1, 0),
    lists:foldl(fun(Role, {Index, Acc}) ->
        #camp_war_role{
            role_id = RoleId, role_name = Name, server_num = ServerNum,
            hurt = Hurt, vip = Vip, sup_vip = SupVip, sex = Sex, lv = Lv,
            photo_profile_id = PhotoId, dress_id = DressId
        } = Role,
        {Index+1, [{Index, RoleId, IsTopArena, Name, ServerNum, 0, Hurt, Lv, Vip, SupVip, Sex, PhotoId, DressId}|Acc]}
    end, {1, []}, RankList);
pack_hurt_rank_data(1, ArenaId, RankList) ->
    IsTopArena = ?IF(ArenaId == 1, 1, 0),
    lists:foldl(fun(Guild, {Index, Acc}) ->
        #camp_war_guild{
            guild_id = GuildId, guild_name = GuildName, server_num = ServerNum,
            hurt = Hurt, guild_flag = GuildFlag} = Guild,
        {Index+1, [{Index, GuildId, IsTopArena, GuildName, ServerNum, GuildFlag, Hurt, 0, 0, 0, 0, 0, 0}|Acc]}
    end, {1, []}, RankList).


del_arena_scene_pool_role(ArenaId, PoolId, ArenaRoomMap, RoomRoleMap) ->
    Rooms = maps:get(ArenaId, ArenaRoomMap, []),
    case lists:member(PoolId, Rooms) of
        true ->
            RoomRoleNum = maps:get(PoolId, RoomRoleMap, 0),
            NewRoomRoleMap = maps:put(PoolId, max(RoomRoleNum-1, 0), RoomRoleMap);
        _ ->
            NewRoomRoleMap = RoomRoleMap
    end,
    NewRoomRoleMap.

%% 更新战场内的社团排名
update_guild_rank(Guild, ArenaId, Guilds) ->
    #camp_war_guild{guild_id = GuildId, hurt = HurtAdd} = Guild,
    case lists:keyfind(ArenaId, 1, Guilds) of
        {_, ArenaGuilds} ->
            NewArenaGuilds =
            case lists:keyfind(GuildId, #camp_war_guild.guild_id, ArenaGuilds) of
                OldGuild = #camp_war_guild{hurt = OldHurt} ->
                    lists:keystore(GuildId, #camp_war_guild.guild_id, ArenaGuilds, OldGuild#camp_war_guild{hurt = round(OldHurt+HurtAdd)});
                _ ->
                    lists:keystore(GuildId, #camp_war_guild.guild_id, ArenaGuilds, Guild)
            end,
            lists:keyreplace(ArenaId, 1, Guilds, {ArenaId, NewArenaGuilds});
        _ ->
            NewArenaGuilds = [Guild],
            lists:keystore(ArenaId, 1, Guilds, {ArenaId, NewArenaGuilds})
    end.

%% 更新战场内的个人排名
update_role_rank(Role, ArenaId, Roles) ->
    #camp_war_role{role_id = RoleId, hurt = HurtAdd, in_act = ActStatus} = Role,
    case lists:keyfind(ArenaId, 1, Roles) of
        {_, ArenaRoles} ->
            NewArenaRoles =
            case lists:keyfind(RoleId, #camp_war_role.role_id, ArenaRoles) of
                OldRole = #camp_war_role{hurt = OldHurt} ->
                    lists:keystore(RoleId, #camp_war_role.role_id, ArenaRoles, OldRole#camp_war_role{hurt = round(OldHurt+HurtAdd), in_act = ActStatus});
                _ ->
                    lists:keystore(RoleId, #camp_war_role.role_id, ArenaRoles, Role)
            end,
            lists:keyreplace(ArenaId, 1, Roles, {ArenaId, NewArenaRoles});
        _ ->
            NewArenaRoles = [Role],
            lists:keystore(ArenaId, 1, Roles, {ArenaId, NewArenaRoles})
    end.

update_role_act_status(RoleId, ArenaId, ActStatus, RoleRank) ->
    case lists:keyfind(ArenaId, 1, RoleRank) of
        {_, ArenaRoles} ->
            NewArenaRoles =
            case lists:keyfind(RoleId, #camp_war_role.role_id, ArenaRoles) of
                OldRole = #camp_war_role{} ->
                    lists:keystore(RoleId, #camp_war_role.role_id, ArenaRoles, OldRole#camp_war_role{in_act = ActStatus});
                _ ->
                    ArenaRoles
            end,
            lists:keystore(ArenaId, 1, RoleRank, {ArenaId, NewArenaRoles});
        _ ->
            RoleRank
    end.

del_guild_role_num(GuildId, ArenaId, GuildRank) ->
    if ArenaId =/= 0 ->
            case lists:keyfind(ArenaId, 1, GuildRank) of
                {_, ArenaGuilds} ->
                    NewArenaGuilds =
                    case lists:keyfind(GuildId, #camp_war_guild.guild_id, ArenaGuilds) of
                        Guild = #camp_war_guild{nums = Num} ->
                            lists:keystore(GuildId, #camp_war_guild.guild_id, ArenaGuilds, Guild#camp_war_guild{nums = max(0, Num-1)});
                        _ ->
                            ArenaGuilds
                    end,
                    lists:keystore(ArenaId, 1, GuildRank, {ArenaId, NewArenaGuilds});
                _ ->
                    GuildRank
            end;
        true ->
            GuildRank
    end.

add_guild_role_num(GuildId, ArenaId, GuildRank) ->
    case lists:keyfind(ArenaId, 1, GuildRank) of
        {_, ArenaGuilds} ->
            NewArenaGuilds =
            case lists:keyfind(GuildId, #camp_war_guild.guild_id, ArenaGuilds) of
                Guild = #camp_war_guild{nums = Num} ->
                    lists:keystore(GuildId, #camp_war_guild.guild_id, ArenaGuilds, Guild#camp_war_guild{nums = Num+1});
                _ ->
                    ArenaGuilds
            end,
            lists:keystore(ArenaId, 1, GuildRank, {ArenaId, NewArenaGuilds});
        _ ->
            GuildRank
    end.

notice_camp_war_hurt_rank(ClsType, Role, HurtData) ->
    #camp_war_role{role_id = RoleId, server_id = ServId, guild_id = GuildId, in_act = ActStatus} = Role,
    if
        ActStatus == 1 ->
            case lists:keyfind(GuildId, 2, HurtData) of
                {Index, _, _, _, _} -> Index;
                _  -> Index = 0
            end,
            notice_svr_role(ClsType, ServId, RoleId, pt_410, 41006, [Index, HurtData]);
        true ->
            ok
    end.

calc_scene_res_info(SceneRes) ->
    lists:foldl(fun({MonId, {_X, _Y}, _Index}, Acc) ->
        case lists:keyfind(MonId, 1, Acc) of
            {_, OldNum} ->
                lists:keystore(MonId, 1, Acc, {MonId, OldNum+1});
            _ ->
                lists:keystore(MonId, 1, Acc, {MonId, 1})
        end
    end, [], SceneRes).

calc_next_res_time(ResStart) ->
    Now = utime:unixtime(),
    LastTime = max(0, Now - ResStart),
    StatusTime = data_camp_war:get_kv(status_time),
    ResInterval = data_camp_war:get_kv(material_update_time),
    case lists:keyfind(?CAMP_WAR_OPENING_2, 1, StatusTime) of
        {_, StatusTotalTime} ->
            TotalCount = StatusTotalTime div ResInterval,
            NowCount = (LastTime div ResInterval) + 1,
            if NowCount >= TotalCount ->
                    0;
                true ->
                    ResStart + NowCount*ResInterval
            end;
        _ ->
            0
    end.

%% 采集时更新社团buff加成
update_camp_war_buff(ClsType, GuildServId, GuildId, Mid) ->
    CfgBuff = data_camp_war:get_kv(material_attr),
    case lists:keyfind(Mid, 1, CfgBuff) of
        {_, AttrId, Add} ->
            if ClsType == ?NODE_CENTER ->
                    clusters_center:apply_cast(GuildServId, ?MODULE, update_guild_buff, [add, GuildId, AttrId, Add]);
                true ->
                    update_guild_buff(add, GuildId, AttrId, Add)
            end;
        _ ->
            ok
    end.

% 游戏服执行
update_guild_buff(UpdateType, GuildId, AttrId, Add) ->
    mod_guild:apply_cast_guild_member(GuildId, 1, ?MODULE, update_guild_member_buff, [UpdateType, [{AttrId, Add}]]).

update_guild_member_buff(Player, UpdateType, AttrList) ->
    #player_status{battle_attr = BattleAttr} = Player,
    #battle_attr{attr = AttrRec} = BattleAttr,
    OldAttrList = attr_conv:to_attr_tuple_list(AttrRec),
    F = fun({Index, Value}, Result) ->
            if UpdateType == replace ->
                    [{Index, Value} | lists:keydelete(Index, 1, Result)];
                true -> %% add
                    case lists:keyfind(Index, 1, Result) of
                        {_, OldVal} -> lists:keystore(Index, 1, Result, {Index, round(OldVal+Value)});
                        _   ->         lists:keystore(Index, 1, Result, {Index, Value})
                    end
            end
    end,
    CombineAttrList = lists:foldl(F, OldAttrList, AttrList),
    NewAttr = attr_conv:to_attr_record(CombineAttrList),
    HpLim = NewAttr#attr.hp,
    #player_status{battle_attr = NewBattleAttr} = NewPlayer = Player#player_status{battle_attr = BattleAttr#battle_attr{hp_lim = HpLim, hp = HpLim, attr = NewAttr}},
    mod_scene_agent:update(NewPlayer, [{battle_attr, NewBattleAttr}]),
    lib_player:send_attribute_change_notify(NewPlayer, ?NOTIFY_ATTR),
    {ok, NewPlayer}.


enter_camp_war_scene(RoleId, ScenePoolId, CopyId, X, Y, AutoSelect, BuffList) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, role_enter_camp_war, [ScenePoolId, CopyId, X, Y, AutoSelect, BuffList]).

role_enter_camp_war(Player, ScenePoolId, CopyId, X, Y, AutoSelect, BuffList) ->
    #player_status{id = RoleId, sid = Sid, role_camp_war = RoleCampWar} = Player,
    %% 进场时更新社团buff加成
    if
        BuffList == [] -> NewPlayer = Player;
        true ->
            GuildBuff = calc_camp_war_guild_buff(BuffList),
            {ok, NewPlayer} = update_guild_member_buff(Player, add, GuildBuff)
    end,
    case RoleCampWar of
        #role_camp_war{} ->
            NewRoleCampWar = RoleCampWar#role_camp_war{coordinate = {X, Y}};
        _ ->
            NewRoleCampWar = #role_camp_war{coordinate = {X, Y}}
    end,
    NewPlayer1 = NewPlayer#player_status{role_camp_war = NewRoleCampWar},

    LocRet = camp_war_mgr:is_loc_camp_war(),
    TarScene = ?IF(LocRet == true, data_camp_war:get_kv(scene), data_camp_war:get_kv(cls_scene)),
    Kvs = [{action_lock, ?ERRCODE(err410_on_camp_war)}],
    NewPlayer2 = lib_scene:change_scene(NewPlayer1, TarScene, ScenePoolId, CopyId, X, Y, AutoSelect, Kvs),

    IsHost = offline_hosting_api:is_offline_hosting(NewPlayer2),
    if
        IsHost ->
            {ok, NewPlayer3} = pp_scene:handle(12002, NewPlayer2, []);
        true ->
            NewPlayer3 = NewPlayer2
    end,
    lib_server_send:send_to_sid(Sid, pt_410, 41002, [?SUCCESS]),
    case lib_daily:get_count(RoleId, ?MOD_CAMP_WAR, 1, 1) of
        Value when is_integer(Value), Value >= 1 -> ignore;
        _ ->
            lib_achievement_api:cross_camp_war_event(NewPlayer3, 1),
            lib_daily:increment(RoleId, ?MOD_CAMP_WAR, 1, 1)
    end,
    NewPlayer4 = lib_activity_reserve:enter_act(NewPlayer3, ?MOD_CAMP_WAR),
    {ok, NewPlayer4}.

%% 计算社团加成
calc_camp_war_guild_buff(BuffList) ->
    CfgBuff = data_camp_war:get_kv(material_attr),
    lists:foldl(fun({MonId, Num}, Result) ->
        case lists:keyfind(MonId, 1, CfgBuff) of
            {_, AttrId, AttrVal} ->
                case lists:keyfind(AttrId, 1, Result) of
                    {_, TotalVal} -> lists:keystore(AttrId, 1, Result, {AttrId, round(Num*AttrVal+TotalVal)});
                    _   -> lists:keystore(AttrId, 1, Result, {AttrId, AttrVal})
                end;
            _ ->
                Result
        end
    end, [], BuffList).

%% 怪物进程回调
get_boss_unique_id(init, Minfo, [From]) ->
    #scene_object{id = Mid, scene_pool_id = ScenePoolId} = Minfo,
    camp_war_worker:cast(From, {return_boss_unique_id, Mid, ScenePoolId}).


role_get_rewards(RoleId, IsHost, Title, Content, Rewards) ->
    spawn(fun() ->
        timer:sleep(urand:rand(1000, 5000)),
        lib_mail_api:send_sys_mail([RoleId], ?MOD_CAMP_WAR, Title, Content, Rewards)
    end),
    if IsHost == 1 ->
            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?NOT_HAND_OFFLINE, offline_hosting, host_behavior_reward, [Rewards]);
        true ->
            skip
    end.

check_scene_resource_when_enter([ClsType, true, ?CAMP_WAR_OPENING_2, ZoneId, FacId, ScenePoolId, ResId, Resource, ResStart|_]) ->
    init_scene_resource(ClsType, {ZoneId, FacId}, ScenePoolId, ResId, Resource, ResStart);
check_scene_resource_when_enter([ClsType, true, ?CAMP_WAR_OPENING_3, _ZoneId, _FacId, ScenePoolId, ResId, Resource, _ResStart|_]) ->
    init_arena_pool_boss(ClsType, [ScenePoolId]),
    {ResId, Resource};
check_scene_resource_when_enter([_, _, _, _, _, _, ResId, Resource, _|_]) ->
    {ResId, Resource}.

% 日志
camp_war_log(GuildRankData, RoleRankData, State) ->
    #zone_fac_state{zone_fac = {ZoneId, FacId}, role_rank = RoleRank, guild_rank = GuildRank} = State,
    ArenaList = ets:tab2list(?ETS_FAC_ARENA_INFO),
    case lists:keyfind({ZoneId, FacId}, 1, ArenaList) of
        {_, _FacIndex, FacArenaList} -> ok;
        _ -> FacArenaList = []
    end,
    Now = utime:unixtime(),
    spawn(fun() ->
        camp_war_guild_log_do(FacArenaList, GuildRankData, ZoneId, GuildRank, Now),
        camp_war_role_log_do(FacArenaList, RoleRankData, ZoneId, RoleRank, Now)
    end),
    ok.

% 社团排行日志
camp_war_guild_log_do(_ArenaList, [], _ZoneId, _, _) ->
    ok;
camp_war_guild_log_do(ArenaList, [{ArenaId, ArenaGuilds}|Rest], ZoneId, GuildRank, Now) ->
    camp_war_guild_log_do_2(ArenaList, ArenaId, ArenaGuilds, ZoneId, GuildRank, Now),
    camp_war_guild_log_do(ArenaList, Rest, ZoneId, GuildRank, Now).

camp_war_guild_log_do_2(_ArenaList, _ArenaId, [], _ZoneId, _GuildRank, _Now) ->
    ok;
camp_war_guild_log_do_2(ArenaList, ArenaId, [{Index, GuildId, GuildName}|Rest], ZoneId, GuildRank, Now) ->
    if ArenaId rem 4 == 0 ->
            timer:sleep(300);
        true -> skip
    end,
    Enemys = get_arena_guild_enemy(ArenaId, GuildId, ArenaList),
    case lists:keyfind(GuildId, #camp_war_guild.guild_id, GuildRank) of
        {_, ArenaGuildRank} -> ok;
        _   -> ArenaGuildRank = []
    end,
    case lists:keyfind(GuildId, #camp_war_guild.guild_id, ArenaGuildRank) of
        #camp_war_guild{server_id = ServerId, server_num = ServerNum} -> ok;
        _   -> ServerId = 0, ServerNum = 0
    end,
    lib_log_api:log_camp_war_rank_guild([ZoneId, ServerId, ServerNum, GuildId, GuildName, util:term_to_bitstring(Enemys), Index, Now]),
    camp_war_guild_log_do_2(ArenaList, ArenaId, Rest, ZoneId, GuildRank, Now).

get_arena_guild_enemy(ArenaId, GuildId, ArenaList) ->
    case lists:keyfind(ArenaId, 1, ArenaList) of
        {_, GuildLx} ->
            Enemys = lists:filter(fun({GuildIdx, _}) ->
                GuildId =/= GuildIdx
            end, GuildLx),
            [GuildIdx1 || {GuildIdx1, _ServerId} <- Enemys];
        _ ->
            []
    end.

% 个人排行日志
camp_war_role_log_do(_ArenaList, [], _ZoneId, _RoleRank, _Now) ->
    ok;
camp_war_role_log_do(ArenaList, [{ArenaId, ArenaRoles}|Rest], ZoneId, RoleRank, Now) ->
    camp_war_role_log_do_2(ArenaList, ArenaId, ArenaRoles, ZoneId, RoleRank, Now),
    camp_war_role_log_do(ArenaList, Rest, ZoneId, RoleRank, Now).

camp_war_role_log_do_2(_ArenaList, _ArenaId, [], _ZoneId, _RoleRank, _Now) ->
    ok;
camp_war_role_log_do_2(ArenaList, ArenaId, [{Index, RoleId, ServerId, GuildId}|Rest], ZoneId, RoleRank, Now) ->
    if Index rem 10 == 0 ->
            timer:sleep(300);
        true -> skip
    end,
    case lists:keyfind(ArenaId, 1, RoleRank) of
        {_, ArenaRoles} -> ok;
        _ -> ArenaRoles = []
    end,
    case lists:keyfind(RoleId, #camp_war_role.role_id, ArenaRoles) of
        #camp_war_role{server_num = ServerNum, is_host = IsHost} -> ok;
        _   -> ServerNum = 0, IsHost = 0
    end,
    lib_log_api:log_camp_war_rank_role([ZoneId, ServerId, ServerNum, RoleId, GuildId, IsHost, Index, Now]),
    camp_war_role_log_do_2(ArenaList, ArenaId, Rest, ZoneId, RoleRank, Now).

%% 上报最新一期前三战果
report_directors(ZoneId, FacId, GuildRank = [_|_]) ->
    {TopThreeGuilds1, _} = ulists:split(3, GuildRank),
    ClsType      = config:get_cls_type(),
    GuildInfo    = get_guild_log_data(ZoneId, FacId, TopThreeGuilds1),
    if ClsType == ?NODE_CENTER ->
            mod_legion_mgr:report_directors(0, GuildInfo);
            % czone:zone_apply_cast(ZoneId, {?MODULE, zone_proc_add_camp_war_log, [ZoneId, FacId, TopThreeGuilds1]});
        true ->
            clusters_node:apply_cast(mod_legion_mgr, report_directors, [0, GuildInfo])
    end,
    ok;
report_directors(_ZoneId, _FacId, _GuildRank) ->
    ok.

%% 上报阵营top 20
submit_faction_guild_rank(ClsType, ZoneId, FacId, GuildRank = [_|_]) ->
    case ClsType == ?NODE_CENTER of
        true ->
            MFA = {lib_guild_badge, trigger_camp_war_badge_event, [GuildRank, 2]},
            czone:zone_apply_cast(ZoneId, MFA);
        _ ->
            lib_guild_badge:trigger_camp_war_badge_event(GuildRank, 2)
    end,
    LogRank = get_log_guild_rank(GuildRank),
    spawn(fun() ->
        timer:sleep(urand:rand(1000, 5000)),
        if ClsType == ?NODE_CENTER ->
                camp_war_mgr:submit_faction_rank(ZoneId, FacId, LogRank);
            true -> %% 本地模式的团战结果应保存到跨服中心去
                clusters_node:apply_cast(camp_war_mgr, submit_faction_rank, [ZoneId, FacId, LogRank])
        end
    end),
    ok;
submit_faction_guild_rank(_, _ZoneId, _FacId, _GuildRank) ->
    ok.

% submit_faction_guild_rank(?NODE_GAME, ZoneId, FacId, GuildRank) ->
%     LogRank = get_log_guild_rank(GuildRank),
%     clusters_node:apply_cast(camp_war_mgr, submit_faction_rank, [ZoneId, FacId, LogRank]).

%% 按从大到小对阵营内所有战场的社团进程排序
sort_faction_guild_rank(GuildRank) ->
    GuildRank1   = lists:foldl(fun({_, ArenaGuilds}, Result) ->
        ArenaGuilds ++ Result
    end, [], GuildRank),
    GuildRank2   = lists:filter(fun(#camp_war_guild{hurt = Hurt}) -> Hurt =/= 0 end, GuildRank1),
    SortedGuilds = lists:reverse(lists:keysort(#camp_war_guild.hurt, GuildRank2)),
    SortedGuilds.

get_guild_log_data(ZoneId, FacId, TopThreeGuilds) ->
    {_, GuildData0} =
    lists:foldl(fun(#camp_war_guild{chief_info = #rec_picture{id = RoleId} = ChiefInfo}, {Index, Acc}) ->
        NewAcc = [#director_role{zone=ZoneId,fact=FacId,pos = Index, rid = RoleId, figure = lib_player:trans_to_figure(ChiefInfo)}|Acc],
        {Index+1, NewAcc}
    end, {1, []}, TopThreeGuilds),
    GuildData = lists:keysort(#director_role.pos,GuildData0),
    [{ZoneId, [{FacId, GuildData}]}].


notice_svr_role(?NODE_CENTER, SvrId, Dest, ProtoMod, Protocol, Data) when is_integer(Dest) ->
    clusters_center:apply_cast(SvrId, lib_server_send, send_to_uid, [Dest, ProtoMod, Protocol, Data]);
notice_svr_role(?NODE_CENTER, SvrId, Dest, ProtoMod, Protocol, Data) ->
    clusters_center:apply_cast(SvrId, lib_server_send, send_to_sid, [Dest, ProtoMod, Protocol, Data]);
notice_svr_role(?NODE_GAME, _, Dest, ProtoMod, Protocol, Data) when is_integer(Dest) ->
    lib_server_send:send_to_uid(Dest, ProtoMod, Protocol, Data);
notice_svr_role(?NODE_GAME, _, Dest, ProtoMod, Protocol, Data) ->
    lib_server_send:send_to_sid(Dest, ProtoMod, Protocol, Data);
notice_svr_role(_, _, _, _, _, _) ->
    skip.

get_log_guild_rank(GuildRank) ->
    NewRankLen = length(GuildRank),
    RankMax = data_legion:get(chairman_max_rank),
    GuildRank1 =
    if NewRankLen > RankMax ->
            lists:sublist(GuildRank, RankMax);
        true ->
            GuildRank
    end,
    GuildRank1.
    % {_, GuildData} =
    % lists:foldl(fun(Guild, {Index, Acc}) ->
    %     #camp_war_guild{guild_name = GuildName, server_num = SvrNum, hurt = Hurt} = Guild,
    %     {Index+1, [{Index, SvrNum, GuildName, Hurt}|Acc]}
    % end, {1, []}, GuildRank1),
    % GuildData.

get_camp_scene(?NODE_GAME) ->
    data_camp_war:get_kv(scene);
get_camp_scene(?NODE_CENTER) ->
    data_camp_war:get_kv(cls_scene).


calc_zone_fact(?NODE_CENTER, ZoneId, FacId) ->
    {ZoneId, FacId};
calc_zone_fact(?NODE_GAME, _ZoneId, _FacId) ->
    case czone_api:env_get_zone() of
        #czone_svr_local{zone_id = NewZoneId, faction = NewFaction} ->
            {NewZoneId, NewFaction};
        _ ->
            {_ZoneId, _FacId}
    end.

worker_init(State) ->
    #zone_fac_state{
        zone_fac = {ZoneId, FacId}, arena_map = ArenaMap, guild_arena_map = GuildMap,
        extra_map = Map, cls_type = ClsType
    } = State,
    NewArenaMap = load_fac_arena_info({ZoneId, FacId}, ArenaMap),
    NewGuildMap = load_guild_arena_map(NewArenaMap, GuildMap),
    Pid      = offline_hosting_camp_war:camp_war_offline_hosting(ClsType, NewArenaMap),
    NewMap   = maps:put(?OFF_HOST_PID, Pid, Map),
    NewState = State#zone_fac_state{arena_map = NewArenaMap, guild_arena_map = NewGuildMap, extra_map = NewMap},
    NewState.

% {MonId, {X, Y}, TempResId}
% debug_pool_res(Action, ScenePool, PoolResource) ->
%     MonResIds =
%     lists:foldl(fun({MonId, _, _}, Acc) ->
%         case lists:member(MonId, Acc) of
%             true -> Acc;
%             _    -> [MonId|Acc]
%         end
%     end, [], PoolResource),
%     ResNum = length(PoolResource),
%     ?ERR("action ~p pool ~p res ~p total num ~p", [Action, ScenePool, MonResIds, ResNum]),
%     ok.