%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     羁绊管理
%%%     缓存羁绊结交和解除信息
%%%     每周1删除一次一周前的过期数据
%%% @end
%%%-------------------------------------------------------------------
-module(mate_mgr).
-author("liushl").

-behaviour(gen_server).

-include("mate.hrl").
-include("predefine.hrl").
-include("common.hrl").
-include("def_module.hrl").
-include("goods.hrl").
-include("def_act_label.hrl").
-include("errcode.hrl").
%% API
-export([
    start_link/0
    , login/1
    , logout/1

    , is_mate_ask_no_response/2                 %% 查看玩家是有请求尚未回应
    , is_mate_relieve_no_response/2             %% 查看玩家是有解除请求尚未回应
    , is_mate_act_no_response/3                 %% 查看玩家是有活动请求尚未回应
    , is_mate_act_no_response_check_invite/3    %% 查看玩家是有活动请求尚未回应

    , ask_mate/2                        %% 请求羁绊
    , del_ask_mate/2                    %% 处理一条请求羁绊数据
    , get_ask_mate_info/1               %% 查看请求羁绊数据

    , consult_relieve_mate/2            %% 协议解除
    , del_relieve_mate/2                %% 处理一条解除协议
    , remove_relieve_mate/2             %% 删除一条解除协议
    , get_relieve_mate_info/1           %% 查看解除数据
    
    , get_mate_act_invite_info/1        %% 查看活动邀请信息

    , mate_act_invite/3                 %% 活动: 邀请
    , mate_act_cancel_invite/1          %% 活动: 取消邀请
    , mate_act_response/5               %% 活动: 回应邀请
    , mate_act_get_ready_state/3        %% 活动: 准备状态
    , mate_act_get_ready/2              %% 活动: 准备好
    , mate_act_quit/2                   %% 活动: 退出游戏

    , rhythm_master_tap/3               %% 节奏大师 玩家点击

    , mate_catch_ball_begin/2           %% 抛接球 开球
    , mate_catch_ball_catch/2           %% 抛接球 击球

    , is_mate_act_open/1                %% 判断当前活动是否开启
    , set_mate_act_timer/4              %% 启动玩法定时器

    , ask_friendship_hall/1             %% 交友大厅-查看交友大厅
    , mate_hall_release/3               %% 交友大厅-发布交友信息
    , mate_hall_remove/1                %% 交友大厅-移除发布的信息

    , take_photo_invite/2               %% 拍照日常-邀请
    , take_photo_info/1                 %% 拍照日常-请求活动信息
    , take_photo_response/4             %% 拍照日常-回应邀请
    , take_photo_ready/3                %% 拍照日常-设置准备
    , take_photo_do/1                   %% 拍照日常-拍照
    , take_photo_cancel/2               %% 拍照日常-取消游戏
]).
-export([
    get_target_role/2
    , rand_role/1
]).
%% gen_server callbacks
-export([init/1,
    handle_call/3,
    handle_cast/2,
    handle_info/2,
    terminate/2,
    code_change/3]).

-define(SERVER, ?MODULE).

%% 判断当前活动是否开启 1/3/5 开节奏大师，2/4/6开抛接球 周日双开
is_mate_act_open(ActId) ->
    Week = utime:day_of_week(),
    IsOpen =
        case Week of
            1 -> ActId =:= ?MATE_ACT_RHYTHM_MASTER;
            2 -> ActId =:= ?MATE_ACT_CATCH_BALL;
            3 -> ActId =:= ?MATE_ACT_RHYTHM_MASTER;
            4 -> ActId =:= ?MATE_ACT_CATCH_BALL;
            5 -> ActId =:= ?MATE_ACT_RHYTHM_MASTER;
            6 -> ActId =:= ?MATE_ACT_CATCH_BALL;
            7 -> true
        end,
    case IsOpen of
        true  -> 1;
        false -> 1
    end.
%%%===================================================================
%%% API
%%%===================================================================
%% 玩家上线判断是否继续活动
login(RoleId) ->
    gen_server:cast(?MODULE, {login, RoleId}).

%% 离线判断是否离开活动
logout(RoleId) ->
    gen_server:cast(?MODULE, {logout, RoleId}).

%% 查看玩家是有请求尚未回应
is_mate_ask_no_response(RoleId, ToId) ->
    gen_server:call(?MODULE, {is_mate_ask_no_response, RoleId, ToId}).

%% 查看玩家是有请求尚未回应
is_mate_relieve_no_response(RoleId, ToId) ->
    gen_server:call(?MODULE, {is_mate_relieve_no_response, RoleId, ToId}).

%% 查看玩家是有活动请求尚未回应
is_mate_act_no_response(RoleId, ToId, ActId) ->
    gen_server:call(?MODULE, {is_mate_act_no_response, RoleId, ToId, ActId}).
is_mate_act_no_response_check_invite(RoleId, ToId, ActId) ->
    gen_server:call(?MODULE, {is_mate_act_no_response_check_invite, RoleId, ToId, ActId}).

%% 请求羁绊
ask_mate(FromId, ToId) ->
    Record = #mate_message{key = get_mate_key(FromId, ToId), from_role = FromId, time = utime:unixtime()},
    gen_server:cast(?MODULE, {ask_mate, Record}).
%% 处理一条请求羁绊数据 会判断是不是FromId发出的，然后删掉它
del_ask_mate(FromId, ToId) ->
    gen_server:call(?MODULE, {del_ask_mate, FromId, ToId}).
%% 查看请求羁绊数据/一般上线会求一次
get_ask_mate_info(RoleId) ->
    gen_server:cast(?MODULE, {get_ask_mate_info, RoleId}).

%% 协议解除
consult_relieve_mate(FromId, ToId) ->
    Record = #mate_message{key = get_mate_key(FromId, ToId), from_role = FromId, time = utime:unixtime()},
    gen_server:cast(?MODULE, {consult_relieve_mate, Record}).
%% 处理一条解除协议 会判断是不是FromId发出的，然后删掉它
del_relieve_mate(FromId, ToId) ->
    gen_server:call(?MODULE, {del_relieve_mate, FromId, ToId}).
%% 删除一条解除协议 直接删除
remove_relieve_mate(FromId, ToId) ->
    gen_server:cast(?MODULE, {remove_relieve_mate, FromId, ToId}).
%% 查看解除数据/上线会求一次
get_relieve_mate_info(RoleId) ->
    gen_server:cast(?MODULE, {get_relieve_mate_info, RoleId}).

%% 查看活动邀请信息
get_mate_act_invite_info(RoleId) ->
    gen_server:cast(?MODULE, {get_mate_act_invite_info, RoleId}).

%% 活动: 邀请
mate_act_invite(FromId, ToId, ActId) ->
    ?MODULE ! {mate_act_invite, FromId, ToId, ActId}.
%% 活动: 取消邀请
mate_act_cancel_invite(FromId) ->
    ?MODULE ! {mate_act_cancel_invite, FromId}.
%% 活动: 回应邀请
mate_act_response(FromId, ToId, ActId, Response, DailyScores) ->
    ?MODULE ! {mate_act_response, FromId, ToId, ActId, Response, DailyScores}.
%% 活动: 准备状态
mate_act_get_ready_state(RoleId, MateRole, Sid) ->
    ?MODULE ! {mate_act_get_ready_state, get_mate_key(RoleId, MateRole), Sid}.
%% 活动: 准备好
mate_act_get_ready(RoleId, MateRole) ->
    ?MODULE ! {mate_act_get_ready, get_mate_key(RoleId, MateRole), RoleId}.
%% 活动: 退出游戏
mate_act_quit(RoleId, MateRole) ->
    ?MODULE ! {mate_act_quit, get_mate_key(RoleId, MateRole), RoleId}.
%% 节奏大师 玩家点击
rhythm_master_tap(RoleId, MateRole, TapId) ->
    ?MODULE ! {mate_act, get_mate_key(RoleId, MateRole), {mate_rhythm_master_tap, RoleId, TapId}}.

%% 抛接球 开球
mate_catch_ball_begin(RoleId, MateRole) ->
    ?MODULE ! {mate_act, get_mate_key(RoleId, MateRole), {mate_catch_ball_begin, RoleId}}.
%% 抛接球 击球
mate_catch_ball_catch(RoleId, MateRole) ->
    ?MODULE ! {mate_act, get_mate_key(RoleId, MateRole), {mate_catch_ball_catch, RoleId}}.

%% 交友大厅-查看交友大厅
ask_friendship_hall(RoleId) ->
    ?MODULE ! {mate_hall, {ask_friendship_hall, RoleId}}.
%% 交友大厅-发布交友信息
mate_hall_release(RoleId, Msg, Type) ->
    Record = #mate_hall_role{id = RoleId, declaration = Msg, type = Type, time = utime:unixtime()},
    ?MODULE ! {mate_hall, {release, Record}}.
%% 交友大厅-移除发布的信息
mate_hall_remove(RoleId) ->
    ?MODULE ! {mate_hall, {reomve_release, RoleId}}.

%% 拍照日常-邀请
take_photo_invite(RoleId, InviteRoles) ->
    ?MODULE ! {mate_take_photo, {send_invite, RoleId, InviteRoles}}.

%% 拍照日常-请求活动信息
take_photo_info(RoleId) ->
    ?MODULE ! {mate_take_photo, {ask_invite_info, RoleId}}.

%% 拍照日常-回应邀请
take_photo_response(RoleId, FromId, Position, Res) ->
    ?MODULE ! {mate_take_photo, {response_invite, RoleId, FromId, Position, Res}}.

%% 拍照日常-设置准备
take_photo_ready(RoleId, FromId, Status) ->
    ?MODULE ! {mate_take_photo, {set_ready_status, RoleId, FromId, Status}}.

%% 拍照日常-拍照
take_photo_do(RoleId) ->
    ?MODULE ! {mate_take_photo, {take_photo, RoleId}}.

%% 拍照日常-取消游戏
take_photo_cancel(RoleId, FromId) ->
    ?MODULE ! {mate_take_photo, {cancel, RoleId, FromId}}.
%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @end
%%--------------------------------------------------------------------
-spec(start_link() ->
    {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
-spec(init(Args :: term()) ->
    {ok, State :: #mate_mgr{}} | {ok, State :: #mate_mgr{}, timeout() | hibernate} |
    {stop, Reason :: term()} | ignore).
init([]) ->
    HallData = db:get_all(?SQL_MATE_GET_HALL),
    Hall =
    [
        #mate_hall_role{
            id = RoleId
            ,declaration = Declaration
            ,type = Type
            ,time =Time
        }
        || [RoleId, Type, Declaration, Time] <- HallData
    ],
    self() ! {mate_hall, check_overdue},
    erlang:send_after(3000, self(), {loading_ets_data}),
    {ok, #mate_mgr{mate_hall = Hall}}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
    State :: #mate_mgr{}) ->
    {reply, Reply :: term(), NewState :: #mate_mgr{}} |
    {reply, Reply :: term(), NewState :: #mate_mgr{}, timeout() | hibernate} |
    {noreply, NewState :: #mate_mgr{}} |
    {noreply, NewState :: #mate_mgr{}, timeout() | hibernate} |
    {stop, Reason :: term(), Reply :: term(), NewState :: #mate_mgr{}} |
    {stop, Reason :: term(), NewState :: #mate_mgr{}}).
handle_call(Msg, From, State) ->
    try
        do_handle_call(Msg, From, State)
    catch _:_Error ->
        io:format("do_handle_call Msg:~w, Error:~p, ~p", [Msg, _Error, erlang:get_stacktrace()]),
        {reply, error, State}
    end.

%% 查看玩家是有请求尚未回应
do_handle_call({is_mate_ask_no_response, RoleId, ToId}, _From, State) ->
    {reply, lists:keymember(get_mate_key(RoleId, ToId), #mate_message.key, State#mate_mgr.mate_ask_msg), State};
%% 查看玩家是有解除尚未回应
do_handle_call({is_mate_relieve_no_response, RoleId, ToId}, _From, State) ->
    {reply, lists:keymember(get_mate_key(RoleId, ToId), #mate_message.key, State#mate_mgr.mate_relieve_msg), State};
%% 查看玩家是有活动邀请尚未回应，自己只能发出一条邀请|如果邀请方已经对我发出了邀请则也不需要再发
do_handle_call({is_mate_act_no_response, RoleId, ToId, _ActId}, _From, State) ->
    Bool = lists:keymember(RoleId, #mate_act.from_role, State#mate_mgr.mate_act) orelse
        lists:keymember(get_mate_key(RoleId, ToId), #mate_act.key, State#mate_mgr.mate_act),
    {reply, Bool, State};
%% 查看玩家是有活动邀请尚未回应, 判断邀请者 且 活动未开启
do_handle_call({is_mate_act_no_response_check_invite, FromId, ToId, ActId}, _From, State) ->
    case lists:keyfind(get_mate_key(FromId, ToId), #mate_act.key, State#mate_mgr.mate_act) of
        #mate_act{from_role = FromId, act_id = ActId, daily_score = [], data = undefined} ->
            {reply, true, State};
        #mate_act{from_role = FromId, act_id = ActId, daily_score = [_|_]} ->
            {reply, {false, ?ERRCODE(err149_already_response)}, State};
        _ ->
            {reply, {false, ?ERRCODE(err149_ask_not_exist)}, State}
    end;

%% 处理一条请求羁绊数据
do_handle_call({del_ask_mate, FromId, ToId}, _From, State) ->
    case lists:keytake(get_mate_key(FromId, ToId), #mate_message.key, State#mate_mgr.mate_ask_msg) of
        {value, #mate_message{from_role = FromId}, NewAskInfo} ->
            {reply, true, State#mate_mgr{mate_ask_msg = NewAskInfo}};
        _ ->
            {reply, false, State}
    end;
%% 处理一条解除协议
do_handle_call({del_relieve_mate, FromId, ToId}, _From, State) ->
    case lists:keytake(get_mate_key(FromId, ToId), #mate_message.key, State#mate_mgr.mate_relieve_msg) of
        {value, #mate_message{from_role = FromId}, NewRelieveInfo} ->
            lib_server_send:send_to_uid(FromId, pt_149, 14908, [lib_role:get_role_name(ToId), ?MATE_RESPONSE_NO]),
            {reply, true, State#mate_mgr{mate_relieve_msg = NewRelieveInfo}};
        _ ->
            {reply, false, State}
    end;

do_handle_call(_Request, _From, State) ->
    io:format("module:~p: handle_call unexpect message:~p", [?MODULE, _Request]),
    {reply, ok, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_cast(Request :: term(), State :: #mate_mgr{}) ->
    {noreply, NewState :: #mate_mgr{}} |
    {noreply, NewState :: #mate_mgr{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #mate_mgr{}}).
handle_cast(Msg, State) ->
    try
        do_handle_cast(Msg, State)
    catch _:_Error ->
        io:format("do handle_cast Msg:~w, Error:~p, ~p", [Msg, _Error, erlang:get_stacktrace()]),
        {noreply, State}
    end.
%%---------------------------------------------------------------------
%% @doc 羁绊结交
%%---------------------------------------------------------------------
%% 请求羁绊
do_handle_cast({ask_mate, #mate_message{} = NewOne}, State) ->
    NewAskInfo = lists:keystore(NewOne#mate_message.key, #mate_message.key, State#mate_mgr.mate_ask_msg, NewOne),
    {noreply, State#mate_mgr{mate_ask_msg = NewAskInfo}};

%% 查看请求羁绊数据/一般上线会求一次
do_handle_cast({get_ask_mate_info, RoleId}, State) ->
    Msg = get_mate_message(RoleId, State#mate_mgr.mate_ask_msg),
    lib_server_send:send_to_uid(RoleId, pt_149, 14902, [Msg]),
    {noreply, State};

%%---------------------------------------------------------------------
%% @doc 羁绊解除
%%---------------------------------------------------------------------
%% 协议解除
do_handle_cast({consult_relieve_mate, Record}, State) ->
    NewRelieveInfo = lists:keystore(Record#mate_message.key, #mate_message.key, State#mate_mgr.mate_relieve_msg, Record),
    {noreply, State#mate_mgr{mate_relieve_msg =  NewRelieveInfo}};

%% 羁绊解除后强制去掉协议
do_handle_cast({remove_relieve_mate, FromId, ToId}, State) ->
    NewRelieveInfo = lists:keydelete(get_mate_key(FromId, ToId), #mate_message.key, State#mate_mgr.mate_relieve_msg),
    {noreply, State#mate_mgr{mate_relieve_msg = NewRelieveInfo}};

%% 解除羁绊消息
do_handle_cast({get_relieve_mate_info, RoleId}, State) ->
    Msg = get_mate_message(RoleId, State#mate_mgr.mate_relieve_msg),
    lib_server_send:send_to_uid(RoleId, pt_149, 14906, [Msg]),
    {noreply, State};

%%---------------------------------------------------------------------
%% @doc 玩家
%%---------------------------------------------------------------------
%% 检查活动是否继续
do_handle_cast({login, RoleId}, State) ->
    NewState = login_check(State, RoleId, State#mate_mgr.mate_act),
    {noreply, NewState};

%% 检查活动是否断开
do_handle_cast({logout, RoleId}, State) ->
    NewState = try_cancel_invite(RoleId, State),
    {noreply, NewState};

%% 活动邀请信息
do_handle_cast({get_mate_act_invite_info, RoleId}, State) ->
    Msg =
        lists:foldl(
            fun
                (#mate_act{from_role = FromRole, key = Key, data = undefined, act_id = ActId}, Temp) ->
                ToRole = get_target_role(Key, FromRole),
                if
                    FromRole =:= RoleId -> %% 我发出的请求
                        [{?MATE_INFO_SEND, lib_role:get_role_figure(ToRole), ActId}|Temp];
                    ToRole =:= RoleId ->   %% 我收到的请求
                        [{?MATE_INFO_RECEIVE, lib_role:get_role_figure(FromRole), ActId}|Temp];
                    true ->
                        Temp
                end;
                (_, Temp) ->
                    Temp
            end, [],  State#mate_mgr.mate_act
        ),
    lib_server_send:send_to_uid(RoleId, pt_149, 14922, [Msg]),
    {noreply, State};

do_handle_cast(_Request, State) ->
    io:format("module:~p: handle_cast unexpect message:~p", [?MODULE, _Request]),
    {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 :: #mate_mgr{}) ->
    {noreply, NewState :: #mate_mgr{}} |
    {noreply, NewState :: #mate_mgr{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #mate_mgr{}}).
handle_info(Msg, State) ->
    try
        do_handle_info(Msg, State)
    catch _:_Error ->
        io:format("do handle_info Msg:~w, Error:~w, ~p", [Msg, _Error, erlang:get_stacktrace()]),
        {noreply, State}
    end.

%%---------------------------------------------------------------------
%% @doc 活动 play 邀请的时候/取消邀请/回应邀请
%%---------------------------------------------------------------------
%% 收到一个邀请
do_handle_info({mate_act_invite, FromId, ToId, ActId}, State) ->
    ActInfo = #mate_act{
        key = get_mate_key(FromId, ToId),
        from_role = FromId,
        act_id = ActId,
        time = utime:unixtime()
    },
    NewMateAct = lists:keystore(ActInfo#mate_act.key, #mate_act.key, State#mate_mgr.mate_act, ActInfo),
    {noreply, State#mate_mgr{mate_act = NewMateAct}};
%% 回应一个邀请：1同意|2拒绝
do_handle_info({mate_act_response, FromId, ToId, ActId, ?MATE_RESPONSE_NO, _}, State) ->
    case lists:keytake(get_mate_key(FromId, ToId), #mate_act.key, State#mate_mgr.mate_act) of
        {value, #mate_act{from_role = FromId, act_id = ActId, daily_score = []}, NewMateAct} ->
            %% 只有没有同意的邀请才能处理拒绝
            lib_server_send:send_to_uid(FromId, pt_149, 14932, [ActId, ToId, lib_role:get_role_name(ToId), ?MATE_RESPONSE_NO]),
            get_mate_act_invite_info(ToId),
            get_mate_act_invite_info(FromId),
            {noreply, State#mate_mgr{mate_act = NewMateAct}};
        _ ->
            {noreply, State}
    end;
do_handle_info({mate_act_response, FromId, ToId, ActId, ?MATE_RESPONSE_YES, DailyScores}, State) ->
    lib_server_send:send_to_uid(FromId, pt_149, 14932, [ActId, ToId, lib_role:get_role_name(ToId), ?MATE_RESPONSE_YES]),
    lib_server_send:send_to_uid(ToId, pt_149, 14922, [[]]),
    get_mate_act_invite_info(FromId),
    do_handle_info({mate_act, get_mate_key(FromId, ToId), {mate_act_response_yes, DailyScores}}, State);
%% 取消邀请
do_handle_info({mate_act_cancel_invite, FromId}, State) ->
    NewState = try_cancel_invite(FromId, State),
    {noreply, NewState};
%% 请求准备状态
do_handle_info({mate_act_get_ready_state, Key, Sid}, State) ->
    case lists:keyfind(Key, #mate_act.key, State#mate_mgr.mate_act) of
        #mate_act{is_ready = Ready} ->
            {ok, Bin} = pt_149:write(14940, [Ready]),
            lib_server_send:send_to_sid(Sid, Bin);
        _ ->
            ok
    end,
    {noreply, State};
%% 设置准备
do_handle_info({mate_act_get_ready, Key, RoleId}, State) ->
    MateAct = State#mate_mgr.mate_act,
    case lists:keyfind(Key, #mate_act.key, MateAct) of
        #mate_act{is_ready = Ready} = Record ->
            case lists:member(RoleId, Ready) of
                true  ->
                    {noreply, State};
                false ->
                    NewReady   = [RoleId|Ready],
                    NewRecord  = Record#mate_act{is_ready = NewReady},
                    NewMateAct = lists:keyreplace(Key, #mate_act.key, MateAct, NewRecord),
                    {ok, Bin} = pt_149:write(14940, [NewReady]),
                    lib_server_send:send_to_uid(RoleId, Bin),
                    lib_server_send:send_to_uid(get_target_role(Key, RoleId), Bin),
                    NewState = State#mate_mgr{mate_act = NewMateAct},
                    case NewReady of
                        [_, _] ->
                            do_handle_info({mate_act, Key, all_get_ready}, NewState);
                        _ ->
                            {noreply, NewState}
                    end
            end;
        _ ->
            {noreply, State}
    end;
%% 退出玩法
do_handle_info({mate_act_quit, Key, RoleId}, State) ->
    case lists:keytake(Key, #mate_act.key, State#mate_mgr.mate_act) of
        {value, #mate_act{act_id = ActId, key = {RoleA, RoleB}} = Record, Left} ->
            %% 给玩家去掉标识
            ActLabel = mate_logic:act_label(ActId),
            [
                begin
                    lib_scene:change_act_label(Id, ActLabel, 0),
                    lib_player:break_action_lock(Id, ?ERRCODE(err149_on_game))
                end
                || Id <- [RoleA, RoleB]
            ],
            Handler = get_act_handler(ActId),
            NewState = Handler:handle_event(mate_act_quit, Record, State),
            {ok, Bin} = pt_149:write(14934, [?ERRCODE(err149_close_act)]),
            lib_server_send:send_to_uid(get_target_role(Key, RoleId), Bin),
            {noreply, NewState#mate_mgr{mate_act = Left}};
        _ ->
            {noreply, State}
    end;

%% 玩法逻辑有对应活动处理
do_handle_info({mate_act, Key, Msg}, State) ->
    case lists:keyfind(Key, #mate_act.key, State#mate_mgr.mate_act) of
        #mate_act{act_id = ActId} = Record ->
            Handler = get_act_handler(ActId),
            NewState = Handler:handle_event(Msg, Record, State),
            {noreply, NewState};
        _ ->
            {noreply, State}
    end;

%% 交友大厅处理
do_handle_info({mate_hall, Msg}, State) ->
    NewState = mate_hall:handle(Msg, State),
    {noreply, NewState};

%% 拍照玩法处理
do_handle_info({mate_take_photo, Msg}, State) ->
    NewState = mate_take_photo:handle(Msg, State),
    {noreply, NewState};

%% 加载ets表数据
do_handle_info({loading_ets_data}, State) ->
    mate_api:loading_ets_data(),
    {noreply, State};

do_handle_info(_Info, State) ->
    io:format("module:~p: handle_info unexpect message:~p", [?MODULE, _Info]),
    {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 :: #mate_mgr{}) ->
    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 :: #mate_mgr{},
    Extra :: term()) ->
    {ok, NewState :: #mate_mgr{}} | {error, Reason :: term()}).
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================
%% 获得活动处理 模块
get_act_handler(?MATE_ACT_RHYTHM_MASTER) -> mate_rhythm_master;
get_act_handler(?MATE_ACT_CATCH_BALL) -> mate_catch_ball.
%% 羁绊主键
get_mate_key(Role_A, Role_B) ->
    case Role_A > Role_B of
        true  -> {Role_B, Role_A};
        false -> {Role_A, Role_B}
    end.

%% 获得双方另一个玩家
get_target_role({FromRole, Role_B}, FromRole) -> Role_B;
get_target_role({Role_A, FromRole}, FromRole) -> Role_A.

%% 开局随机一个玩家
rand_role({Role_A, Role_B}) ->
    case urand:rand(1,100) >= 50 of
        true  -> Role_B;
        false -> Role_A
    end.

get_mate_message(RoleId, List) ->
    lists:foldl(
        fun(#mate_message{from_role = FromRole, key = Key}, Temp) ->
            ToRole = get_target_role(Key, FromRole),
            if
                FromRole =:= RoleId -> %% 我发出的请求
                    [{?MATE_INFO_SEND, lib_role:get_role_figure(ToRole)}|Temp];
                ToRole =:= RoleId ->   %% 我收到的请求
                    [{?MATE_INFO_RECEIVE, lib_role:get_role_figure(FromRole)}|Temp];
                true ->
                    Temp
            end
        end, [], List
    ).

%% 玩家上线判断:是否需要回到活动
login_check(State, _, []) -> State;
login_check(State, RoleId, [#mate_act{key = {RoleA, RoleB}, act_id = ActId} = ActInfo|T]) ->
    case RoleId =:= RoleA orelse RoleId =:= RoleB of
        true  ->
            Handler = get_act_handler(ActId),
            NewState = Handler:handle_event({login, RoleId}, ActInfo, State),
            login_check(NewState, RoleId, T);
        false ->
            login_check(State, RoleId, T)
    end.

%% 取消邀请
try_cancel_invite(FromId, State) ->
    NewMateAct = lists:foldl(
        fun
            (#mate_act{key = {RoleA, RoleB} = Key, daily_score = DailyScore, is_ready = IsReady} = One, Temp) ->
                case FromId =:= RoleA orelse FromId =:= RoleB of
                    true  ->
                        if
                            DailyScore =:= [] ->
                                %% 还未同意，则直接结束邀请
                                get_mate_act_invite_info(get_target_role(Key, FromId)),
                                Temp;
                            true ->
                                case IsReady of
                                    [] ->
                                        %% 还没人准备，则取消游戏
                                        mate_act_quit(FromId, get_target_role(Key, FromId)),
                                        Temp;
                                    [_] ->
                                        %% 只有一个人准备了，也取消游戏
                                        mate_act_quit(FromId, get_target_role(Key, FromId)),
                                        Temp;
                                    _ ->
                                        %% 都准备了，则活动已经开启，不用管，等游戏自动结束就行
                                        [One|Temp]
                                end
                        end;
                    false ->
                        [One|Temp]
                end
        end, [], State#mate_mgr.mate_act
    ),
    State#mate_mgr{mate_act = NewMateAct}.
%%try_cancel_invite(FromId, State) ->
%%    case lists:keytake(FromId, #mate_act.from_role, State#mate_mgr.mate_act) of
%%        {value, #mate_act{key = Key, data = undefined}, NewMateAct} ->
%%            get_mate_act_invite_info(get_target_role(Key, FromId)),
%%            State#mate_mgr{mate_act = NewMateAct};
%%        _ ->
%%            State
%%    end.

%% 启动玩法定时器
set_mate_act_timer(Time, Pid, Key, Msg) ->
    erlang:send_after(Time, Pid, {mate_act, Key, Msg}).

%% 玩家双方去获得坐标
    