%% ---------------------------------------------------------------------------
%% @doc 跨服多人副本大厅管理进程
%% @author zhaoyu
%% @since  2017-04-13
%% ---------------------------------------------------------------------------
-module(mod_dun_multi_mgr_cluster).

-behaviour(gen_server).

%% API
-export([
    start_link/0                %% 启动跨服多人副本大厅管理进程
    ,call/1                     %% call跨服多人副本大厅管理进程
    ,cast/1                     %% cast跨服多人副本大厅管理进程
    ,get_team_id/0              %% 获得多人副本组队自增Id
    ]).

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

-include("dungeon_multi.hrl").
-include("dungeon.hrl").
-include("common.hrl").

%% ---------------------------------------------------------------------------
%% @doc call跨服多人副本大厅管理进程
-spec call(Msg) -> term() | {error, Reason} when
    Msg     :: term(),
    Reason  :: any().
%% ---------------------------------------------------------------------------
call(Msg) ->
    gen_server:call(?MODULE, Msg, 4000).


%% ---------------------------------------------------------------------------
%% @doc cast跨服多人副本大厅管理进程
-spec cast(Msg) -> ok when
    Msg     :: term().
%% ---------------------------------------------------------------------------
cast(Msg) ->
    gen_server:cast(?MODULE, Msg).


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


%% ---------------------------------------------------------------------------
%% @doc Initializes the server
-spec init(Args) ->
    {ok, State}
    | {ok, State, Timeout}
    | ignore
    | {stop, Reason} when
    Args    :: term() ,
    State   :: #state{},
    Timeout :: non_neg_integer() | infinity,
    Reason  :: term().
%% ---------------------------------------------------------------------------
init([]) ->
    process_flag(trap_exit, true),

    %% 保存每种类型有哪些组队 #dun_multi_room{}
    EtsArgsH = [{keypos, #dun_multi_room.id}, named_table, public, set],
    ets:new(?DUN_MULTI_HALL_ETS, EtsArgsH),

    %% 保存组队数据 #dun_multi_team{}
    EtsArgsT = [{keypos, #dun_multi_team.id}, named_table, public, set],
    ets:new(?DUN_MULTI_TEAM_ETS, EtsArgsT),
    
    %% 保存组队自增Id #dun_multi_team_id{}
    EtsArgsTi = [{keypos, 1}, named_table, public, set],
    ets:new(?DUN_MULTI_TEAM_ID_ETS, EtsArgsTi),
    ets:insert(?DUN_MULTI_TEAM_ID_ETS, #dun_multi_team_id{}),
    
    %% 保存跨服多人副本大厅进程Pid #dun_multi_hall_pid{}
    EtsArgsP = [{keypos, #dun_multi_hall_pid.id}, named_table, public, set],
    ets:new(?DUN_MULTI_HALL_PID_ETS, EtsArgsP),
    
    self() ! init,
    {ok, #state{}}.


%% ---------------------------------------------------------------------------
%% @doc Handling call messages
-spec handle_call(Request, From, State) ->
    {reply, Reply, State}
    | {reply, Reply, State, Timeout}
    | {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, Reply, State}
    | {stop, Reason, State} when
    Request :: term(),
    From :: pid() | {pid(), Tag},
    Tag :: term(),
    State :: #state{},
    Reply :: term(),
    State :: #state{},
    Timeout :: non_neg_integer() | infinity.
%% ---------------------------------------------------------------------------
handle_call(Request, From, State) ->
    case catch do_handle_call(Request, From, State) of
        {reply, Reply, NewState} ->
            {reply, Reply, NewState};
        Reason ->
            ?ERR("handle_call Request: ~p, Reason=~p~n", [Request, Reason]),
            {reply, error, State}
    end.

do_handle_call({Fun, Args}, _From, State) ->
    {reply, apply(lib_dun_multi_cluster, Fun, Args), State};
do_handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.


%% ---------------------------------------------------------------------------
%% @doc Handling cast messages
-spec handle_cast(Msg, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Msg :: term(),
    State :: #state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
handle_cast(Msg, State) ->
    case catch do_handle_cast(Msg, State)of
        {noreply, NewState} ->
            {noreply, NewState};
        Reason ->
            ?ERR("handle_cast Msg: ~p, Reason:=~p~n",[Msg, Reason]),
            {noreply, State}
    end.

do_handle_cast({Fun, Args}, State) ->
    apply(lib_dun_multi_cluster, Fun, Args),
    {noreply, State};
do_handle_cast(_Msg, State) ->
    {noreply, State}.


%% ---------------------------------------------------------------------------
%% @doc Handling all non call/cast messages
-spec handle_info(Info, State) ->
    {noreply, State}
    | {noreply, State, Timeout}
    | {stop, Reason, State} when
    Info :: term(),
    State :: #state{},
    Timeout :: non_neg_integer() | infinity,
    Reason :: term().
%% ---------------------------------------------------------------------------
%% 跨服多人副本大厅进程异常退出
handle_info({'EXIT', FromPid, ExitReason}, State) ->
    case ets:match_object(
            ?DUN_MULTI_HALL_PID_ETS, #dun_multi_hall_pid{pid = FromPid, _ = '_' }
        ) of
        [#dun_multi_hall_pid{id = Id, dun_type = DunType}] ->
            ?ERR("mod_dun_multi_mgr_cluster[~w] error:~w", [Id, ExitReason]),
            case mod_dun_multi_hall_cluster:start_link(Id, DunType) of
                {ok, _Pid} -> ok;
                _ -> ?ERR("mod_dun_multi_mgr_cluster，[Id:~w]", [Id])
            end;
        _ ->
            ?ERR("mod_dun_multi_mgr_cluster!~w", [ExitReason])
    end,
    {noreply, State};
handle_info(Info, State) ->
    case catch do_handle_info(Info, State)of
        {noreply, NewState} ->
            {noreply, NewState};
        _Reason ->
            ?ERR("handle_info error: ~p, Reason:=~p~n",[Info, _Reason]),
            {noreply, State}
    end.

%% 启动跨服多人副本大厅进程
do_handle_info(init, State) ->
    Fun = fun(DunType)->
        case DunType of
            ?DUNGEON_TYPE_CRUSADE ->
                IdList = data_dun_multi_team:get_sub_type(?ACT_TYPE_CRUSADE);
            _ ->
                IdList = data_dungeon:get_ids_by_type(DunType)
        end,
        [mod_dun_multi_hall_cluster:start_link(TypeId, DunType) || TypeId <- IdList]
    end,
    lists:map(Fun, ?TEAM_DUNGEON),
    {noreply, State};
do_handle_info(_Msg, State) ->
    {noreply, State}.

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


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


%% ---------------------------------------------------------------------------
%% @doc 获得多人副本组队自增Id
-spec get_team_id() -> TeamId when
    TeamId      :: integer().
%% ---------------------------------------------------------------------------
get_team_id() ->
    ets:update_counter(
        ?DUN_MULTI_TEAM_ID_ETS, dun_multi_team_id, {#dun_multi_team_id.id, 1}
    ).

