%% --------------------------------------
%% desc:跨服团战-阵营内公会战
%% moudle:camp_war_mgr.erl
%% author:hh
%% time:20/07/21
%% --------------------------------------

-module(camp_war_mgr).

-behaviour(gen_statem).

%% API
-export([
    start_link/0
    ,cast_to_mgr/1
    ,cast/1
    ,info/1
    ,apply_cast_data/1
    ,act_start/4

    % ,add_svrs/1
    ,rezone_callback/1
    ,handle_merge/3

    ,upload_local_guild_info/3  %% 收集百团大战结果
    ,submit_faction_rank/3      %% 收集跨服团战阵营结果
    ,notice_camp_war_ret/0      %% 通知上传团战结果
    ,get_camp_war_ret/4         %% 获取上一期团战结果

    ,check_act_time/0
    ,gm_act/2
    ,gm_reset_zones/0
    ,act_svr_notice/2

    ,is_during_camp_war/0       %% 活动是否开启
    ,is_loc_camp_war/0          %% 判断是否为本服活动
    ,del_env_camp_war/0         %% 删除本服活动标志缓存
    ,set_env_camp_war/1         %% 设置本服活动标志缓存
    ]).

%% gen_statem callbacks
-export([init/1, callback_mode/0, handle_event/4, terminate/2]).

-define(SERVER, ?MODULE).

-define(INIT_DELAY, 90000).

-define(CLOSE,      close).
-define(PREPARE,    prepare).
-define(COLLECT,    collect).
-define(MON,        mon).

-include("common.hrl").
-include("camp_war.hrl").
-include("errcode.hrl").
-include("def_module.hrl").
-include("def_fun.hrl").

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

%% 本地消息转发接口
cast_to_mgr(Msg) ->
    case is_loc_camp_war() of
        true -> cast(Msg);
        _    -> clusters_node:apply_cast(camp_war_mgr, cast, [Msg])
    end.

cast(Msg) ->
    gen_statem:cast(?MODULE, Msg).

info(Msg) ->
    ?MODULE ! Msg.

apply_cast_data({Mod, Fun, Arg}) ->
    gen_statem:cast(?MODULE, {apply_cast_data, {Mod, Fun, Arg}}).

act_start(Module, ModuleSub, AcSub, Now) ->
    gen_statem:cast(?MODULE, {act_start, Module, ModuleSub, AcSub, Now}).

%% 新增注册新服
%% Map :: #{cz_state => CZState, add_servers => AddServers}
% add_svrs(Map) ->
%     gen_statem:cast(?MODULE, {add_svrs, Map}).

%% 开服情况下动态分区调整回调
%% Map :: #{
%%     cz_state => CZState, svr_zone_map => SvrZoneMap,
%%     zone_map => ZoneMap, faction_map => FactionMap
%% }
rezone_callback(Map) ->
    gen_statem:cast(?MODULE, {rezone_callback, Map}).

%% 合服调整回调
handle_merge(MasterServId, MasterServNum, MergeServers) ->
    gen_statem:cast(?MODULE, {handle_merge, MasterServId, MasterServNum, MergeServers}).

%% 上传百团大战信息
upload_local_guild_info({ZoneId, FacId, ServId}, SerNum, GuildInfo) ->
    gen_statem:cast(?MODULE, {upload_local_guild_info, {ZoneId, FacId, ServId}, SerNum, GuildInfo}).

%% 收集团战结果
submit_faction_rank(ZoneId, FacId, GuildRank) ->
    gen_statem:cast(?MODULE, {submit_faction_rank, ZoneId, FacId, GuildRank}).

%% 国战通知获取团战阵营排名第一的社团
notice_camp_war_ret() ->
    gen_statem:cast(?MODULE, {notice_camp_war_ret}).

%% 获取团战上一期结果
get_camp_war_ret(ZoneId, FacId, Node, Sid) ->
    cast_to_mgr({get_camp_war_ret, ZoneId, FacId, Node, Sid}).

check_act_time() ->
    gen_statem:cast(?MODULE, {check_act_time}).

gm_act(Type, ClsType) ->
    NodeClsType = config:get_cls_type(),
    if NodeClsType == ClsType ->
            gen_statem:cast(?MODULE, {gm_act, Type});
        NodeClsType =/= ClsType andalso NodeClsType == ?NODE_GAME -> %% 本服开启跨服玩法
            clusters_node:apply_cast(gen_statem, cast, [?MODULE, {gm_act, Type}]);
        true ->
            skip
    end.

gm_reset_zones() ->
    gen_statem:cast(?MODULE, {gm_reset_zones}).

is_during_camp_war() ->
    gen_statem:call(?MODULE, {is_during_camp_war}).

is_loc_camp_war() ->
    LocCache = game_env:get(camp_war),
    LocCache.

del_env_camp_war() ->
    game_env:del(camp_war).

set_env_camp_war(IsLocCampWar) ->
    game_env:set(camp_war, IsLocCampWar).


%% gen_statem回调方式
callback_mode() ->
    [handle_event_function, state_enter].

%%%-------------------------------------------------------------------
%%% Callback Functions
%%%-------------------------------------------------------------------
init(_Args) ->
    process_flag(trap_exit, true),
    
    %% 分区信息
    ets:new(?ETS_FAC_SRV_ZONE, 
        [{keypos, 1}, named_table, public, set, {read_concurrency, true}]
    ),
    
    %% 分区阵营列表
    ets:new(?ETS_FAC_ZONE_FACS, 
        [{keypos, 1}, named_table, public, set, {read_concurrency, true}]
    ),
    
    %% 社团收集数据
    ets:new(?ETS_FAC_LOCAL_GWAR_INFO, 
        [{keypos, 1}, named_table, public, set, {read_concurrency, true}]
    ),
    
    %% 社团战场分配数据
    ets:new(?ETS_FAC_ARENA_INFO, 
        [{keypos, 1}, named_table, public, set, {read_concurrency, true}]
    ),

    ets:new(?ETS_FAC_LOG_RET, 
        [{keypos, 1}, named_table, public, set, {read_concurrency, true}]
    ),
    
    ClsType = config:get_cls_type(),
    if ClsType == ?NODE_CENTER ->
            {ok, close, #camp_war_state{cls_type = ClsType}};
        true ->
            OpenDay = util:get_open_day(),
            CfgOpen = data_camp_war:get_kv(open_day),
            if OpenDay =< CfgOpen ->
                    {ok, close, #camp_war_state{cls_type = ClsType}};
                true ->
                    {ok, close, #camp_war_state{cls_type = ClsType}}
            end
    end.


handle_event(EventType, EventContent, StateName, StateData) ->
    try do_handle_event(EventType, EventContent, StateName, StateData) of
        Ret -> Ret
    catch ?EXCEPTION(Class, Reason, Stacktrace) ->
        Error = {Class, Reason, ?GET_STACK(Stacktrace)},
        ?ERR("camp_war error:[~p]", [Error]),
        keep_state_and_data
    end.

terminate(_Reason, _State) ->
    ok.

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

% You are advised to use {keep_state,...}, {keep_state_and_data,_} or keep_state_and_data 
% since changing states from a state enter call is not possible anyway.
% state_enter_result(State) =
%    {next_state, State, NewData :: data()} | {next_state, State,
%     NewData :: data(), 
%    Actions :: [enter_action()] | enter_action()} | state_callback_result(enter_action())

% state_callback_result(ActionType) =
%    {keep_state, NewData :: data()} |
%    {keep_state,
%     NewData :: data(),
%     Actions :: [ActionType] | ActionType} |
%    keep_state_and_data |
%    {keep_state_and_data, Actions :: [ActionType] | ActionType}

% Any event that arrives cancels timeout action

do_handle_event(enter, _OldStateName = close, _NewStateName = close, StateData) ->
    #camp_war_state{cls_type = ClsType} = StateData,
    ModuleSub   = ?IF(ClsType == ?NODE_CENTER, ?CLS_ACT_MOD, ?LOC_ACT_MOD),
    ActionMsg   = [{state_timeout, ?INIT_DELAY, {init, ModuleSub}}],
    % ?ERR("camp war enter init ~p", [ActionMsg]),
    {keep_state_and_data, ActionMsg};

%% 进入入场状态
% do_handle_event(enter, _OldStateName = close, _NewStateName = prepare, StateData) ->
%     #camp_war_state{end_time = ActEndTime, status = Status, cls_type = ClsType, worker_pids = PidMap} = StateData,
%     Now    = utime:unixtime(),
%     {LastTime, NextStatusTime} = calc_next_status_time(Now, Status),
%     ActionMsg = get_next_status_msg(Status),
%     %% 开启阵营战场
%     NewPidMap = camp_war_logic:start_fac_guild_room(ClsType, PidMap),
%     %% 通知活动日历活动开启
%     lib_activitycalen_api:success_start_activity(?MOD_CAMP_WAR, 1),
%     {ok, BinData} = pt_410:write(41000, [Status, Now, NextStatusTime, ActEndTime]),
%     if ClsType == ?NODE_CENTER ->
%             TvData = [{all}, ?MOD_CAMP_WAR, 1, []],
%             clusters_center:apply_to_all_node(?MODULE, act_svr_notice, [TvData, BinData]);
%         true ->
%             lib_server_send:send_to_all(BinData),
%             lib_chat:send_TV({all}, ?MOD_CAMP_WAR, 1, [])
%     end,
%     NewStateData  = StateData#camp_war_state{
%         next_time = NextStatusTime, worker_pids = NewPidMap
%     },
%     {keep_state, NewStateData, [{state_timeout, LastTime*1000, ActionMsg}]};

%% 进入采集状态
do_handle_event(enter, _OldStateName = close, _NewStateName = collect, StateData) ->
    #camp_war_state{status = Status, end_time = EndTime,
     cls_type = ClsType, worker_pids = PidMap, extra_map = ExtraMap} = StateData,
    Ref = maps:get(?ZONE_INIT_INTERVAL, ExtraMap, undefined),
    util:cancel_timer(Ref),
    NewExtraMap = maps:put(?ZONE_INIT_INTERVAL, undefined, ExtraMap),
    Now = utime:unixtime(),
    {LastTime, NewTime} = calc_next_status_time(Now, Status),
    ActionMsg = get_next_status_msg(Status),
    %% 开启阵营战场
    NewPidMap = camp_war_logic:start_fac_guild_room(ClsType, PidMap),
    camp_war_logic:broad_fac_worker(NewPidMap, cast, {collect_mon_stage}),
    %% 通知活动日历活动开启
    lib_activitycalen_api:success_start_activity(?MOD_CAMP_WAR, 1),
    IsClsAct = ?IF(ClsType == ?NODE_CENTER, 1, 2),
    {ok, BinData} = pt_410:write(41000, [Status, Now, NewTime, EndTime, IsClsAct]),
    if ClsType == ?NODE_CENTER ->
            TvData = [{all}, ?MOD_CAMP_WAR, 1, []],
            clusters_center:apply_to_all_node(?MODULE, act_svr_notice, [TvData, BinData]);
        true ->
            lib_server_send:send_to_all(BinData),
            lib_chat:send_TV({all}, ?MOD_CAMP_WAR, 1, [])
    end,
    % {ok, BinData} = pt_410:write(41000, [Status, Now, NewTime, EndTime]),
    % if ClsType == ?NODE_CENTER ->
    %         clusters_center:apply_to_all_node(lib_server_send, send_to_all, [BinData]);
    %     true ->
    %         lib_server_send:send_to_all(BinData)
    % end,
    NewStateData = StateData#camp_war_state{
        start_time  = Now, 
        next_time   = NewTime,
        worker_pids = NewPidMap,
        extra_map   = NewExtraMap
    },
    {keep_state, NewStateData, [{state_timeout, LastTime*1000, ActionMsg}]};

%% 进入杀怪状态
do_handle_event(enter, _OldStateName = collect, _NewStateName = mon, StateData) ->
    #camp_war_state{
        extra_map = ExtraMap, status = Status, end_time = EndTime, 
        cls_type = ClsType, worker_pids = PidMap
    } = StateData,
    camp_war_logic:broad_fac_worker(PidMap, cast, {kill_mon_stage}),
    Now = utime:unixtime(),
    {LastTime, NextTime} = calc_next_status_time(Now, Status),
    ActionMsg = get_next_status_msg(Status),
    PreKillTime = NextTime - 5,
    PreKillLastTime = PreKillTime - Now,
    KillMonRef = util:send_after(undefined, PreKillLastTime*1000, self(), {pre_kill_mon}),
    NewExtraMap = maps:put(?PRE_KILL_REF, KillMonRef, ExtraMap),
    NewStateData = StateData#camp_war_state{
        start_time  = Now, 
        next_time   = NextTime, 
        extra_map   = NewExtraMap
    },
    IsClsAct = ?IF(ClsType == ?NODE_CENTER, 1, 2),
    {ok, BinData} = pt_410:write(41000, [Status, Now, NextTime, EndTime, IsClsAct]),
    if ClsType == ?NODE_CENTER ->
            clusters_center:apply_to_all_node(lib_server_send, send_to_all, [BinData]);
        true ->
            lib_server_send:send_to_all(BinData)
    end,
    {keep_state, NewStateData, [{state_timeout, LastTime*1000, ActionMsg}]};

%% 进入关闭状态
do_handle_event(enter, _OldStateName = mon, _NewStateName = close, StateData) ->
    #camp_war_state{status = Status, extra_map = Map, cls_type = ClsType, worker_pids = PidMap} = StateData,

    db:execute(?SQL_DELETE_CAMP_WAR_RESULT),
    
    ets:delete_all_objects(?ETS_FAC_LOCAL_GWAR_INFO),
    ets:delete_all_objects(?ETS_FAC_ARENA_INFO),
    ets:delete_all_objects(?ETS_FAC_LOG_RET),

    NewMap = maps:put(?FAC_ARENA_INIT, 0, Map),
    NewMap1 = maps:put(?FAC_GUILD_INIT, 0, NewMap),
    NewMap2 = maps:remove(?CAMP_WAR_GM_TYPE, NewMap1),
    
    IsClsAct = ?IF(ClsType == ?NODE_CENTER, 1, 2),
    {ok, BinData} = pt_410:write(41000, [Status, 0, 0, 0, IsClsAct]),
    if ClsType == ?NODE_CENTER ->
            clusters_center:apply_to_all_node(lib_server_send, send_to_all, [BinData]);
        true ->
            set_env_camp_war(false),
            lib_server_send:send_to_all(BinData)
    end,
    lib_activitycalen_api:success_end_activity(?MOD_CAMP_WAR, 1),
    camp_war_logic:broad_fac_worker(PidMap, cast, {act_end}),

    CfgTime = data_camp_war:get_kv(exit_time),
    ExitTime = round(CfgTime*1000),
    ActionMsg = [{state_timeout, ExitTime, {worker_stop}}],    
    
    NewStateData = StateData#camp_war_state{
        next_time = 0, extra_map = NewMap2, 
        end_time  = 0, fac_index = 1, start_time = 0
    },
    % ?ERR("camp exit action ~p", [ActionMsg]),
    {keep_state, NewStateData, ActionMsg};

do_handle_event(enter, _OldStateName, _NewStateName = close, StateData) ->
    #camp_war_state{extra_map = Map} = StateData,
    GmType = maps:get(?CAMP_WAR_GM_TYPE, Map, normal),
    if GmType == gm andalso
     (_OldStateName == prepare orelse _OldStateName == mon orelse _OldStateName == collect) ->
            do_handle_event(enter, mon, close, StateData);
        true ->
            keep_state_and_data
    end;

do_handle_event(state_timeout, {collect_mon_stage}, _OldStateName = prepare, StateData) ->
    % ?ERR("collect mon", []),
    NewStatus    = ?CAMP_WAR_OPENING_2,
    NewStateName = get_state(NewStatus),
    NewStateData = StateData#camp_war_state{status = NewStatus},
    {next_state, NewStateName, NewStateData};

do_handle_event(state_timeout, {kill_mon_stage}, _OldStateName = collect, StateData) ->
    % ?ERR("kill mon", []),
    NewStatus = ?CAMP_WAR_OPENING_3,
    NewStateName = get_state(NewStatus),
    NewStateData = StateData#camp_war_state{status = NewStatus},
    {next_state, NewStateName, NewStateData};

do_handle_event(state_timeout, {act_end}, _OldStateName, StateData) ->
    #camp_war_state{extra_map = Map} = StateData,
    GmType = maps:get(?CAMP_WAR_GM_TYPE, Map, normal),
    NewStatus    = ?CAMP_WAR_CLOSE,
    NewStateData = StateData#camp_war_state{status = NewStatus},
    NewStateName = get_state(NewStatus),
    if GmType == normal ->
            db:execute(?SQL_DELETE_LOCAL_GUILD_SCORE, []);
        true ->
            skip
    end,
    {next_state, NewStateName, NewStateData};

do_handle_event(state_timeout, {worker_stop}, _StateName, StateData) ->
    % ?ERR("worker_stop", []),
    util:cancel_timer(maps:get(?WORKER_STOP_REF, StateData#camp_war_state.extra_map, undefined)),
    lists:map(fun({_, Pid}) -> camp_war_worker:info(Pid, {stop}) end, maps:to_list(StateData#camp_war_state.worker_pids)),
    NewMap = maps:put(?WORKER_STOP_REF, undefined, StateData#camp_war_state.extra_map),
    NewStateData = StateData#camp_war_state{extra_map = NewMap, worker_pids = #{}},
    {keep_state, NewStateData};

do_handle_event(state_timeout, {init, ModuleSub}, OldStateName = close, StateData) ->
    %% 初始化分区信息
    NewStateData  = camp_war_logic:init_zone_info(StateData),
    %% 初始化收集的社团信息
    NewStateData1 = camp_war_logic:init_local_guild_info(NewStateData),
    %% 阵营战场信息暂不处理
    % NewStateData2 = camp_war_logic:init_fac_arena_info(NewStateData1),
    %% 初始化上一期团战战果
    NewStateData2 = camp_war_logic:init_camp_war_ret(NewStateData1),
    NewMap1       = maps:put(?MGR_INIT_STATE, 1, NewStateData2#camp_war_state.extra_map),

    %% 判断活动状态
    {NewStatus, NewStateData3} = camp_war_logic:calc_act(NewStateData2#camp_war_state{extra_map = NewMap1}, ModuleSub),
    NewStateData5   = NewStateData3#camp_war_state{status = NewStatus},
    NewStateName    = get_state(NewStatus),
    check_camp_war_env_init(StateData#camp_war_state.cls_type),
    % ?ERR("camp war init fin", []),
    if NewStateName =/= OldStateName ->
            {next_state, NewStateName, NewStateData5};
        true ->
            {keep_state, NewStateData5}
    end;

do_handle_event({call, From}, {is_during_camp_war}, _StateName, _StateData) ->
    Res    = _StateName =/= close,
    Action = [{reply, From, Res}],
    {keep_state_and_data, Action};

do_handle_event(cast, {act_start, _Module, ModuleSub, _AcSub, _Now}, StateName = close, StateData) ->
    #camp_war_state{extra_map = Map, cls_type = ClsType} = StateData,
    InitStatus = maps:get(?MGR_INIT_STATE, Map, 0),
    if
        InitStatus =< 0 ->
            do_handle_event(state_timeout, {init, ModuleSub}, StateName, StateData);
        true ->
            %% 判断活动状态
            {NewStatus, NewStateData} = camp_war_logic:calc_act(StateData, ModuleSub),
            NewStateData2  = NewStateData#camp_war_state{status = NewStatus},
            NewStateName   = get_state(NewStatus),
            check_camp_war_env_init(ClsType),
            % ?ERR("camp war act start ~p ~p", [{_Module, ModuleSub, _AcSub}, is_loc_camp_war()]),
            if NewStateName =/= StateName ->
                    {next_state, NewStateName, NewStateData2};
                true ->
                    {keep_state, NewStateData2}
            end
    end;

do_handle_event(cast, {query_camp_war_act_info, ServId, Sid}, _StateName, State) ->
    #camp_war_state{
        status = Status, start_time = StartTime, next_time = NextTime, 
        end_time = EndTime, cls_type = ClsType
    } = State,
    IsClsAct = ?IF(ClsType == ?NODE_CENTER, 1, 2),
   {ok, BinData} = pt_410:write(41000, [Status, StartTime, NextTime, EndTime, IsClsAct]),
   if ClsType == ?NODE_CENTER ->
            clusters_center:apply_cast(ServId, lib_server_send, send_to_sid, [Sid, BinData]);
        true ->
            lib_server_send:send_to_sid(Sid, BinData)
    end,
   keep_state_and_data;

do_handle_event(cast, {get_camp_war_arena_info, ServId, GuildId, Sid, [RankType, PageSize, PageNum]}, _StateName, _StateData) ->
    #camp_war_state{cls_type = ClsType} = _StateData,
    case ets:lookup(?ETS_FAC_SRV_ZONE, ServId) of
        [{_, {ZoneId, FacId}}|_] ->
            {TotalPage, DataList} = camp_war_logic:get_guild_arena_info(ZoneId, FacId, GuildId, RankType, PageSize, PageNum),
            Data = [?SUCCESS, RankType, PageSize, PageNum, TotalPage, DataList],
            act_status_notice_1(ClsType, ServId, Sid, pt_410, 41001, Data);
        _ ->
            Data = [?ERRCODE(err410_serv_zone_error), RankType, 0, 0, 0, []],
            act_status_notice_1(ClsType, ServId, Sid, pt_410, 41001, Data)
    end,
    keep_state_and_data;

do_handle_event(cast, Msg = {get_guild_res_info, ServId, _GuildId, Sid}, _StateName, _StateData) ->
    do_handle_worker_event(cast, 41003, [ServId, Sid], Msg, _StateName, _StateData#camp_war_state.cls_type),
    keep_state_and_data;

do_handle_event(cast, Msg = {get_arena_pool_res_info, ServId, _ScenePool, _GuildId, Sid}, _StateName, _StateData) ->
    do_handle_worker_event(cast, 41004, [ServId, Sid], Msg, _StateName, _StateData#camp_war_state.cls_type),
    keep_state_and_data;

do_handle_event(cast, Msg = {get_scene_res_collect_coordinate, ServId, _GuildId, _GoodsResId, _ScenePool, Sid}, _StateName, _StateData) ->
    do_handle_worker_event(cast, 41008, [ServId, Sid], Msg, _StateName, _StateData#camp_war_state.cls_type),
    keep_state_and_data;

do_handle_event(cast, Msg = {get_hury_rank, ServId, _GuildId, Sid, RankType, _PageSize, _PageNum}, _StateName, _StateData) ->
    if
        _StateName =/= close  ->
            camp_war_api:cast_to_worker(ServId, cast, Msg);
        true ->
            #camp_war_state{cls_type = ClsType} = _StateData,
            act_status_notice_1(ClsType, ServId, Sid, pt_410, 41005, [RankType, 0, 0, 0, []])
    end,
    keep_state_and_data;

do_handle_event(cast, {upload_local_guild_info, {ZoneId, FacId, ServId}, SerNum, GuildInfo}, _StateName, _StateData) ->
    if
        _StateName == close ->
            camp_war_logic:update_guild_info({ZoneId, FacId, ServId}, SerNum, GuildInfo);
        true -> %% 正在跨服团战中,忽略结果
            ?ERR("server ~p upload ignore ", [ServId])
    end,
    keep_state_and_data;

do_handle_event(cast, Msg = {leave_scene_when_logout, ServId, _RoleId, _SceneId, _PoolId, _GuildId}, _StateName, _StateData) ->
    if
        _StateName =/= close  ->
            camp_war_api:cast_to_worker(ServId, cast, Msg);
        true ->
            skip
    end,
    keep_state_and_data;

do_handle_event(cast, {submit_faction_rank, ZoneId, FacId, GuildRank}, _StateName, _StateData) ->
    ets:insert(?ETS_FAC_LOG_RET, {{ZoneId, FacId}, GuildRank}),
    camp_war_logic:db_update_fact_guild_rank(ZoneId, FacId, GuildRank),
    keep_state_and_data;

do_handle_event(cast, {get_camp_war_ret, ZoneId, FacId, Node, Sid}, _StateName, _StateData) ->
    case ets:lookup(?ETS_FAC_LOG_RET, {ZoneId, FacId}) of
        [{_, ZoneFactionRanks}|_] ->
            ok;
        _ -> ZoneFactionRanks = []
    end,
    F = fun(Guild, {Index, Acc}) -> 
        #camp_war_guild{guild_name = GuildName, server_num = ServerNum, hurt = Hurt} = Guild,
        {Index+1, [{Index, ServerNum, GuildName, Hurt}|Acc]}
    end,
    {_, PackGuildRankInfo} = lists:foldl(F, {1, []}, ZoneFactionRanks),
    {ok, BinData} = pt_603:write(60314, [PackGuildRankInfo]),
    lib_server_send:send_to_sid(Node, Sid, BinData),
    keep_state_and_data;

%% gm命令操作 0开启|1关闭
do_handle_event(cast, {gm_act, Type}, StateName, StateData) ->
    #camp_war_state{extra_map = Map, cls_type = ClsType} = StateData,
    InitState = maps:get(?MGR_INIT_STATE, Map, 0),
    ?ERR("gm_act", []),
    if Type =< 0 andalso StateName =/= close ->
            keep_state_and_data;
        Type > 0 andalso StateName == close ->
            keep_state_and_data;
        true ->
            if
                Type =< 0 -> %% 开启
                    NewStateData = StateData#camp_war_state{
                        extra_map = maps:put(?CAMP_WAR_GM_TYPE, gm, Map)
                    },
                    ModuleSub = ?IF(ClsType == ?NODE_CENTER, ?CLS_ACT_MOD, ?LOC_ACT_MOD),
                    do_handle_event(state_timeout, {init, ModuleSub}, close, NewStateData);
                Type > 0 -> %% 关闭
                    NewStateData = StateData#camp_war_state{
                        extra_map = maps:put(?CAMP_WAR_GM_TYPE, gm, Map)
                    },
                    do_handle_event(state_timeout, {act_end}, StateName, NewStateData);
                true ->
                    ?ERR("gm act error ~p ~p", [InitState, Type]),
                    keep_state_and_data
            end
    end;

do_handle_event(cast, {apply_cast_data, {Mod, Fun, Args}}, _StateName, StateData) ->
    case catch erlang:apply(Mod, Fun, [Args|StateData]) of
        {noreply, NewStateData} ->
            {keep_state, NewStateData};
        NewStateData = #camp_war_state{} ->
            {keep_state, NewStateData};
        {next_state, NewStateName, NewStateData} ->
            {next_state, NewStateName, NewStateData};
        {keep_state, NewStateData} ->
            {keep_state, NewStateData};
        keep_state_and_data ->
            keep_state_and_data;
        ok ->
            keep_state_and_data;
        skip ->
            keep_state_and_data;
        ignore ->
            keep_state_and_data;
        Error ->
            ?ERR("camp_war_mgr apply_cast_data error ~n ~p", [Error]),
            keep_state_and_data
    end;

do_handle_event(cast, {add_svrs, Map}, close, StateData) ->
    NewStateData = camp_war_logic:add_svrs_logic(Map, StateData),
    {keep_state, NewStateData};

%% 
do_handle_event(cast, {rezone_callback, Map}, close, StateData) ->
    NewStateData = camp_war_logic:czone_recalc_callback(Map, StateData),
    {keep_state, NewStateData};
    
do_handle_event(cast, {check_act_time}, close, StateData) ->
    NewStateData = camp_war_logic:calc_today_act(StateData),
    if 
        NewStateData#camp_war_state.cls_type == ?NODE_GAME ->
            OpenDay = util:get_open_day(),
            CfgOpen = data_camp_war:get_kv(open_day),
            if OpenDay > CfgOpen ->
                    IsLocCampWar = is_loc_camp_war(),
                    if IsLocCampWar -> set_env_camp_war(false);
                        true        -> skip
                    end;
                true -> skip
            end;
        true -> skip
    end,
    {keep_state, NewStateData};

%% 合服调整
do_handle_event(cast, {handle_merge, MasterServId, MasterServNum, MergeServers}, close, StateData) ->
    NewStateData = camp_war_logic:handle_merge(MasterServId, MasterServNum, MergeServers, StateData),
    {keep_state, NewStateData};

%% 通知上报团战top one社团
do_handle_event(cast, {notice_camp_war_ret}, _StateName, _StateData) ->
    #camp_war_state{cls_type = ClsType} = _StateData,
    if 
        ClsType == ?NODE_GAME -> skip;
        true ->
            CampWarRet    = ets:tab2list(?ETS_FAC_LOG_RET),
            CampWarTopRet = camp_war_logic:calc_upload_data(CampWarRet),
            cross_guild_war_mgr:update_join_guild(CampWarTopRet),
            ok
    end,
    keep_state_and_data;

do_handle_event(info, {pre_kill_mon}, _StateName = mon, StateData) ->
    #camp_war_state{extra_map = ExtraMap, worker_pids = PidMap} = StateData,
    util:cancel_timer(maps:get(?PRE_KILL_REF, ExtraMap, undefined)),
    NewExtraMap = maps:remove(?PRE_KILL_REF, ExtraMap),
    % ?ERR("{pre_kill_mon}", []),
    camp_war_logic:broad_fac_worker(PidMap, cast, {pre_kill_mon}),
    NewStateData = StateData#camp_war_state{extra_map = NewExtraMap},
    {keep_state, NewStateData};

do_handle_event(info, {zone_calc}, _StateName = close, StateData) ->
    #camp_war_state{extra_map = Map} = StateData,
    ZoneInit = maps:get(?FAC_ZONE_INIT, Map, 0),
    NewStateData =
    if
        ZoneInit == 1 ->
            StateData;
        true ->
            %% 尝试初始化操作(一般是启动初始化失败才操作)
            NewStateData0 = camp_war_logic:init_zone_info(StateData),
            NewStateData1 = camp_war_logic:init_local_guild_info(NewStateData0),
            NewStateData1
    end,
    {keep_state, NewStateData};

do_handle_event(_, {'EXIT', Pid, Reason}, _StateName, StateData) ->
    if 
        Reason == normal ->
            skip;
        true ->
            ?ERR("worker pid: ~p exit reason ~p", [Pid, Reason]),
            camp_war_logic:clear_arena_worker(Pid, StateData)
    end,
    keep_state_and_data;

do_handle_event(_EventType, _EventContent, _StateName, _StateData) ->
    case _EventContent of
        {act_start, _, _, _, _} -> skip;
        _ ->
            ?ERR("unkown cast msg ~p ~p ~p", [_EventType, _EventContent, _StateName])
    end,
    keep_state_and_data.

do_handle_worker_event(EventType, Protocol, Source = [SvrId|_], EventContent, StateName, ClsType) ->
    if StateName =/= close ->
            camp_war_api:cast_to_worker(SvrId, EventType, EventContent);
        true ->
            act_status_notice(ClsType, Protocol, Source)
    end.

act_status_notice(ClsType, 41003, [ServId, Sid|_]) ->
    Data = [?ERRCODE(err410_not_on_camp_war), []],
    act_status_notice_1(ClsType, ServId, Sid, pt_410, 41003, Data);

act_status_notice(ClsType, 41004, [ServId, Sid|_]) ->
    Data = [?ERRCODE(err410_not_on_camp_war), 0, []],
    act_status_notice_1(ClsType, ServId, Sid, pt_410, 41004, Data);

act_status_notice(ClsType, 41008, [ServId, Sid|_]) ->
    Data = [?ERRCODE(err410_not_on_camp_war), 0, []],
    act_status_notice_1(ClsType, ServId, Sid, pt_410, 41008, Data);

act_status_notice(_ClsType, _Cmd, _From) ->
    ?ERR("camp act notice error ~p ~p", [_Cmd, _From]),
    ok.

act_status_notice_1(?NODE_CENTER, ServId, Sid, ProtoMod, Protocol, Data) ->
    camp_war_api:cast_to_role(ServId, Sid, ProtoMod, Protocol, Data);
act_status_notice_1(?NODE_GAME, _ServId, Sid, ProtoMod, Protocol, Data) ->
    if
        is_integer(Sid) ->
            lib_server_send:send_to_uid(Sid, ProtoMod, Protocol, Data);
        true ->
            lib_server_send:send_to_sid(Sid, ProtoMod, Protocol, Data)
    end.


get_state(?CAMP_WAR_CLOSE)     -> close;            
get_state(?CAMP_WAR_OPENING_1) -> prepare;
get_state(?CAMP_WAR_OPENING_2) -> collect;
get_state(?CAMP_WAR_OPENING_3) -> mon.

% get_status(close)              -> ?CAMP_WAR_CLOSE;
% get_status(prepare)            -> ?CAMP_WAR_OPENING_1;
% get_status(collect)            -> ?CAMP_WAR_OPENING_2;
% get_status(mon)                -> ?CAMP_WAR_OPENING_3.

calc_next_status_time(Now, Type) ->
    StatusTimes = data_camp_war:get_kv(status_time),
    case lists:keyfind(Type, 1, StatusTimes) of
        {_, LastTime} ->
            {LastTime, round(Now+LastTime)};
        _ -> %% 默认给个5分钟
            {300, round(Now+300)}
    end.

get_next_status_msg(?CAMP_WAR_OPENING_1) ->
    {collect_mon_stage};
get_next_status_msg(?CAMP_WAR_OPENING_2) ->
    {kill_mon_stage};
get_next_status_msg(?CAMP_WAR_OPENING_3) ->
    {act_end};
get_next_status_msg(_) ->
    error.

check_camp_war_env_init(ClsType) ->
    if ClsType == ?NODE_GAME ->
            OpenDay = util:get_open_day(),
            LocOpenLimit = data_camp_war:get_kv(open_day),
            if OpenDay =< LocOpenLimit ->
                    set_env_camp_war(true);
                true -> skip
            end;
        true -> skip
    end.

act_svr_notice(TvData, BinData) ->
    OpenDay = util:get_open_day(),
    LocOpenLimit = data_camp_war:get_kv(open_day),
    % LocOpenLimit1 = LocOpenLimit + 7,
    if OpenDay =< LocOpenLimit -> skip;
        true ->
            lib_server_send:send_to_all(BinData),
            [Range, Module, Sub, Args|_] = TvData,
            lib_chat:send_TV(Range, Module, Sub, Args)       
    end.