%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%% 游戏服合服：
%%% 游戏服连接上跨服中心，检测是否有合服发生。
%%% 读取数据库合服表里存储的所有服务器id，然后跟自己的服务器id比较，如果有差异，表明发生了合服。
%%% 将合服数据发送到跨服中心，跨服中心各个功能处理完成后，通知游戏服清理数据。
%%% 将游戏服合服表的非本服的数据清除。
%%% 合服处理完成。
%%% 跨服合服：
%%% 跨服中心启动后，稍后则检查是否有合服发生，处理方式跟游戏服合服类似。
%%%
%%% 其他功能在处理合服修正时，应当支持重复调用。如果流程中处理失败，可能需要重复判断合服。
%%% @end
%%%-------------------------------------------------------------------
-module(merge).
-author("liushl").

-behaviour(gen_server).

-include("common.hrl").
-include("clusters.hrl").

%% API
-export([start_link/0]).

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

-export([check_merge/0, apply_cast/1]).

-define(SERVER, ?MODULE).

-record(state, {cls_type, server_ids = []}).

%%%===================================================================
%%% API
%%%===================================================================
%% 检测是否合服
check_merge() ->
    ?MODULE ! check_merge.

apply_cast(Msg) ->
    gen_server:cast(?MODULE, Msg).
%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @end
%%--------------------------------------------------------------------
-spec(start_link() ->
    {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
-spec(init(Args :: term()) ->
    {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
    {stop, Reason :: term()} | ignore).
init([]) ->
    ClsType = clusters_api:get_cluster_type(),
    ServerIds = load_merge_server(ClsType),
    State =
        #state{
            cls_type = ClsType,
            server_ids = ServerIds
        },
    %% 跨服中心，稍后就检查是否合服
    ClsType =:= ?CLUSTER_CENTER andalso
        erlang:send_after(10000, self(), check_merge),
    {ok, State}.

load_merge_server(?CLUSTER_NODE) ->
    case db:get_all(<<"SELECT server_id FROM merge_server_local">>) of
        [_|_] = Ids ->
            lists:flatten(Ids);
        _ ->
            %% 第一次启动，写入服务器信息
            ServerId = config:get_server_id(),
            SQL = io_lib:format("INSERT INTO merge_server_local(server_id) VALUES(~p)", [ServerId]),
            db:execute(SQL),
            [ServerId]
    end;
load_merge_server(?CLUSTER_CENTER) ->
    case db:get_all(<<"SELECT server_id FROM merge_server_center">>) of
        [_|_] = Ids ->
            lists:flatten(Ids);
        _ ->
            %% 第一次启动，写入服务器信息
            ServerId = config:get_server_id(),
            SQL = io_lib:format("INSERT INTO merge_server_center(server_id) VALUES(~p)", [ServerId]),
            db:execute(SQL),
            [ServerId]
    end.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
    State :: #state{}) ->
    {reply, Reply :: term(), NewState :: #state{}} |
    {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
    {noreply, NewState :: #state{}} |
    {noreply, NewState :: #state{}, timeout() | hibernate} |
    {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
    {stop, Reason :: term(), NewState :: #state{}}).
handle_call(_Msg, _From, State) ->
    {reply, ok, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_cast(Request :: term(), State :: #state{}) ->
    {noreply, NewState :: #state{}} |
    {noreply, NewState :: #state{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #state{}}).

%% 游戏服合服后通知跨服中心处理(跨服中心处理)
handle_cast({local_merge, NowServerId, NowServerNum, MergeServers}, State) ->
    local_merge_center_handle(NowServerId, NowServerNum, MergeServers),
    %% 处理完成通知游戏服清理数据
    clusters_center:apply_cast(NowServerId, {?MODULE, apply_cast, [after_handle_merge]}),
    {noreply, State};

%% 合服完成处理
handle_cast(after_handle_merge, #state{cls_type = ?CLUSTER_NODE}) ->
    ServerId = config:get_server_id(),
    SQL = io_lib:format("DELETE FROM merge_server_local WHERE server_id <> ~p", [ServerId]),
    db:execute(SQL),
    ?INFO("local handle merge end!"),
    {noreply, #state{ server_ids = [ServerId] }};
handle_cast(after_handle_merge, #state{cls_type = ?CLUSTER_CENTER}) ->
    ServerId = config:get_server_id(),
    SQL = io_lib:format("DELETE FROM merge_server_center WHERE server_id <> ~p", [ServerId]),
    db:execute(SQL),
    ?INFO("center handle merge end!"),
    {noreply, #state{ server_ids = [ServerId] }};

handle_cast(_Msg, State) ->
    {noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
    {noreply, NewState :: #state{}} |
    {noreply, NewState :: #state{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #state{}}).

%% 检查是否合服(跨服中心和游戏服处理)
handle_info(check_merge, State) ->
    #state{ server_ids = ServerIds, cls_type = ClsType } = State,
    MyServerId = config:get_server_id(),
    MergeServers = lists:delete(MyServerId, ServerIds),
    case MergeServers =/= [] of
        true when ClsType =:= ?CLUSTER_CENTER  ->
            ?INFO("center_merge servers:~p", [MergeServers]),
            center_merge_handle(MyServerId, MergeServers),
            %% 处理完成清理数据
            apply_cast(after_handle_merge),
            ok;
        true when ClsType =:= ?CLUSTER_NODE ->
            ?INFO("local_merge servers:~p", [MergeServers]),
            MyServerNum = config:get_server_num(),
            clusters_node:apply_cast(?MODULE, apply_cast, [{local_merge, MyServerId, MyServerNum, MergeServers}]),
            local_merge_local_handle(MyServerId, MyServerNum, MergeServers);
        _ ->
            ok
    end,
    {noreply, State};
handle_info(_Msg, State) ->
    {noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
    State :: #state{}) ->
    term()).
terminate(_Reason, _State) ->
    ok.

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

%%%===================================================================
%%% Internal functions
%%%===================================================================
%% @doc 跨服中心处理：
%%      多个跨服中心合服处理
center_merge_handle(_NowServerId, _MergeServers) ->
    %% TODO
    ok.

%% @doc 游戏服处理：
%% 需要修正数据的模块在下面添加回调，回调应当是安全的，防止影响了后面的逻辑处理
-spec local_merge_local_handle(NowServerId, NowServerNum, MergeServers) -> ok when
    NowServerId  :: integer(),
    NowServerNum :: integer(),
    MergeServers :: [integer()].

local_merge_local_handle(_NowServerId, _NowServerNum, _MergeServers) ->
    %% TODO
    ok.

%% @doc 跨服中心处理：
%%      游戏服合服完，在跨服中心处理数据
%% 需要修正数据的模块在下面添加回调，回调应当是安全的，防止影响了后面的逻辑处理
-spec local_merge_center_handle(NowServerId, NowServerNum, MergeServers) -> ok when
    NowServerId  :: integer(),
    NowServerNum :: integer(),
    MergeServers :: [integer()].

local_merge_center_handle(NowServerId, NowServerNum, MergeServers) ->
    ZoneId = czone:get_zone_id(NowServerId),
    mod_vie_rank_center:handle_merge(NowServerId, NowServerNum, MergeServers),
    god_dun_rank_mgr:handle_merge(NowServerId, NowServerNum, MergeServers),
    beauty_contest:handle_merge(ZoneId, NowServerId, NowServerNum, MergeServers),
    cross_point_fight_center:handle_merge(NowServerId, NowServerNum, MergeServers),
    camp_war_mgr:handle_merge(NowServerId, NowServerNum, MergeServers),
    %% TODO
    ok.
