%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     跳一跳：匹配
%%% @end
%%%-------------------------------------------------------------------
-module(jump_act_match).
-author("liushl").

-include("jump_act.hrl").
-include("errcode.hrl").
-include("figure.hrl").
-include("predefine.hrl").
%% API
-export([handle/3, patch_match_info/1]).

%% 取消匹配
handle({'CANCEL_MATCH', RoleId}, State, ActInfo) ->
    case remove_match_info(ActInfo, RoleId) of
        {NewActInfo, Role} ->
            jump_act_center:cancel_role_match(Role),
            {State, NewActInfo};
        _ ->
            ok
    end;
%% 接受玩家匹配:单人模式
handle({'MATCH_ACCEPT', RoleInfo, ?JUMP_ACT_PATTERN_SINGLE}, State, ActInfo) ->
    NewActInfo = case remove_match_info(ActInfo, RoleInfo#jump_act_role.role_id) of
        {_NewActInfo, _Role} ->
            _NewActInfo;
        _ ->
            ActInfo
    end,
    RoomInfo = #jump_act_room{roles = [RoleInfo]},
    handle({'READY_TO_PLAY', RoomInfo, ?JUMP_ACT_PATTERN_SINGLE, 0}, State, NewActInfo);
%% 接受玩家匹配:多人模式
handle({'MATCH_ACCEPT', RoleInfo, ?JUMP_ACT_PATTERN_MULTIPLE}, State, ActInfo) ->
    #jump_act_act{
        sub_type = SubType,
        end_time = EndTime,
        match_list = MatchList,
        match_ref  = MatchRef
    } = ActInfo,
    IsRepeat = lists:keymember(RoleInfo#jump_act_role.role_id, #jump_act_role.role_id, MatchList),
    Res =
    if
        EndTime =:= 0 ->
            {false, ?ERRCODE(err459_jump_close)};
        IsRepeat ->
            ignore;
        true ->
            true
    end,
    case Res of
        true ->
            {ok, Bin} = pt_459:write(45902, [1, 1]),
            lib_server_send:send_to_uid(RoleInfo#jump_act_role.node, RoleInfo#jump_act_role.role_id, Bin),
            custom_act_jump_act:match_start_callback(RoleInfo),
            NewMatchList = [RoleInfo|MatchList],
            Num = length(NewMatchList),
            case Num >= ?JUMP_ACT_MATCH_MAX_NUM of
                true  ->
                    %% 达到4人直接开始/取消3人超时开启的定时器
                    util:cancel_timer(MatchRef),
                    NewActInfo = ActInfo#jump_act_act{match_list = NewMatchList},
                    handle({'MATCH_SUCCESS', ?JUMP_ACT_MATCH_TIME_4}, State, NewActInfo);
                false ->
                    NewActInfo =
                        case Num =:= ?JUMP_ACT_MATCH_MIN_NUM of
                            true  ->
                                %% 3人180秒后就可以进入
                                WaitTime = ?JUMP_ACT_MATCH_WAIT_TIME,
                                Msg = {common, SubType, ?JUMP_ACT_EVENT_MATCH, 'MATCH_OVERDUE'},
                                ActInfo#jump_act_act{
                                    match_list = NewMatchList,
                                    match_time = utime:unixtime() + WaitTime,
                                    match_ref  = erlang:send_after(WaitTime * 1000, self(), Msg)
                                };
                            false ->
                                ActInfo#jump_act_act{
                                    match_list = NewMatchList
                                }
                        end,
                    broadcast_match_info(NewActInfo),
                    {State, NewActInfo}
            end;
        ignore  ->
            ok;
        {false, Error} ->
            {ok, Bin} = pt_459:write(45902, [Error, 1]),
            lib_server_send:send_to_uid(RoleInfo#jump_act_role.node, RoleInfo#jump_act_role.role_id, Bin),
            ok
    end;

%% 3人匹配超时进入玩法
handle('MATCH_OVERDUE', State, ActInfo) ->
    util:cancel_timer(ActInfo#jump_act_act.match_ref),
    handle({'MATCH_SUCCESS', 0}, State, ActInfo);

%% 3人匹配超时进入玩法
handle({'MATCH_SUCCESS', WaitTime}, State, #jump_act_act{ match_list = MatchList } = ActInfo)
    when MatchList =/= [] ->
    #jump_act_center{room_id = RoomId} = State,
    case WaitTime > 0 of
        true  ->
            %% 等候期间不可取消匹配，所以给所有玩家加玩法锁
            apply_cast_to_roles(MatchList, custom_act_jump_act, after_match_success, [RoomId]),
            broadcast_match_info(ActInfo#jump_act_act{match_time = utime:unixtime() + WaitTime});
        false ->
            ok
    end,
    RoomInfo = #jump_act_room{roles = MatchList},
    NewActInfo = ActInfo#jump_act_act{
        match_list = [],
        match_time = 0,
        match_ref  = undefined
    },
    handle({'READY_TO_PLAY', RoomInfo, ?JUMP_ACT_PATTERN_MULTIPLE, WaitTime}, State, NewActInfo);

%% 装备开始
handle({'READY_TO_PLAY', RoomInfo, Pattern, WaitTime}, State, ActInfo) ->
    #jump_act_center{
        room_id    = RoomId
    } = State,
    Msg = {common, ActInfo#jump_act_act.sub_type, ?JUMP_ACT_EVENT_PLAY, {'JUMP_INIT', RoomId}},
    NewRoomInfo = RoomInfo#jump_act_room{
        id          = RoomId
        , pattern   = Pattern
        %% 之后的处理都在玩法模块jump_act_play
        , ref_act   = erlang:send_after(WaitTime * 1000, self(), Msg)
    },
    NewActInfo = jump_act_play:set_room_data(NewRoomInfo, ActInfo),
    NewState = State#jump_act_center{
        room_id    = RoomId + 1
    },
    {NewState, NewActInfo};

%% 更新玩家信息
handle({'UPDATE_ROLE', RoleId, Key, Value}, State, ActInfo) ->
    #jump_act_act{
        match_list = MatchList
    } = ActInfo,
    case lists:keyfind(RoleId, #jump_act_role.role_id, MatchList) of
        #jump_act_role{figure = Figure} = Role ->
            NewRole =
            case Key of
                name ->
                    Role#jump_act_role{figure = Figure#figure{name = Value}};
                vip  ->
                    Role#jump_act_role{figure = Figure#figure{vip = Value}};
                sup_vip  ->
                    Role#jump_act_role{figure = Figure#figure{sup_vip = Value}};
                photo ->
                    Role#jump_act_role{figure = Figure#figure{profile_photo_id = Value}};
                lv ->
                    Role#jump_act_role{figure = Figure#figure{lv = Value}};
                dress_list ->
                    Role#jump_act_role{figure = Figure#figure{dress_list = Value}}
            end,
            NewMatchList = lists:keyreplace(RoleId, #jump_act_role.role_id, MatchList, NewRole),
            NewActInfo = ActInfo#jump_act_act{match_list = NewMatchList},
            patch_match_info(NewActInfo),
            {State, NewActInfo};
        _ ->
            ok
    end;

handle(_, State, _) -> State.

%%%-------------------------------------------------------------------
%%% Internal Function
%%%-------------------------------------------------------------------
%% 删除匹配数据
remove_match_info(ActInfo, RoleId) ->
    #jump_act_act{
        match_list = MatchList,
        match_time = MatchTime,
        match_ref  = MatchRef
    } = ActInfo,
    case lists:keytake(RoleId, #jump_act_role.role_id, MatchList) of
        {value, Role, LeftList} ->
            NewActInfo =
                case MatchTime > 0 of
                    true  ->
                        %% 如果是正在倒计时开始，则需要取消
                        util:cancel_timer(MatchRef),
                        ActInfo#jump_act_act{
                            match_list = LeftList,
                            match_time = 0,
                            match_ref  = undefined
                        };
                    false ->
                        ActInfo#jump_act_act{
                            match_list = LeftList
                        }
                end,
            broadcast_match_info(NewActInfo),
            {NewActInfo, Role};
        false ->
            ActInfo
    end.

%% 广播匹配信息
broadcast_match_info(ActInfo) when ActInfo#jump_act_act.match_list =/= [] ->
    #jump_act_act{
        match_list = MatchList
    } = ActInfo,
    Bin = patch_match_info(ActInfo),
    [
        lib_server_send:send_to_uid(Node, RoleId, Bin)
        || #jump_act_role{node = Node, role_id = RoleId} <- MatchList
    ];
broadcast_match_info(_) -> ok.

patch_match_info(ActInfo) ->
    #jump_act_act{
        match_list = MatchList,
        match_time = MatchTime
    } = ActInfo,
    Info =
        [
            {Figure, ServerNum}
            ||#jump_act_role{figure = Figure, server_num = ServerNum} <- MatchList
        ],
    {ok, Bin} = pt_459:write(45903, [Info, MatchTime]),
    Bin.

%% 让玩家执行方法
apply_cast_to_roles(Roles, Mod, Func, Arg) ->
    [
        clusters_center:apply_cast(Node, {lib_player, apply_cast, [RoleId, ?APPLY_CAST_STATUS, Mod, Func, Arg]})
        || #jump_act_role{node = Node, role_id = RoleId, is_out = IsOut} <- Roles, IsOut =:= 0
    ].