%% ---------------------------------------------------------------------------
%% @doc lib_team_mod.erl

%% @author  hjh
%% email  hejiahua@163.com
%% @since  2017-04-07
%% @deprecated 队伍进程的副本处理
%% ---------------------------------------------------------------------------
-module(lib_team_mod).
-export([]).

-compile(export_all).

-include("team.hrl").
-include("dungeon.hrl").
-include("errcode.hrl").
-include("predefine.hrl").
-include("chat.hrl").
-include("common.hrl").
-include("figure.hrl").
-include("def_module.hrl").
-include("activitycalen.hrl").
-include("scene.hrl").
-include("def_fun.hrl").
-include("def_daily.hrl").

%% 队伍中的操作,注意要限制住:
%%  任命队长，同意别人进入队伍，退出队伍（主动退出，离线退出）
%%  邀请别人加入队伍，被邀请人回应加入队伍请求，踢出队伍，更改队伍目标，申请加入队伍

%% ---------------------------------------------------------------------------
%% 仲裁
%% ---------------------------------------------------------------------------

%% ---------------------------------------------------------------------------
%% #team_enlist{activity_id = ActivityId, subtype_id = SubtypeId, module_id = Module, sub_module = SubModule}:
%%  ActivityId和SubtypeId只作为排序和分组
%%  Module和SubModule:队伍逻辑根据模块id和子模块id进行处理
%% 流程
%%  1、发起仲裁:arbitrate_req
%%      根据模块id和子模块id判断检查.默认是成功
%%  2、回应仲裁:arbitrate_res
%%      根据模块id和子模块id判断检查是否能回应.默认是成功
%%  3、仲裁结果:arbitrate_result
%%      (1)回应仲裁会调用该函数,该函数需要根据模块id和子模块id来进行判断处理,成功就进入,否则拒绝。
%%      (2)超时会调用该结果,该函数需要根据模块id和子模块id来进行判断处理,成功就进入,否则拒绝。默认:超时是仲裁失败
%% ---------------------------------------------------------------------------

%% 发起仲裁
arbitrate_req(State, RoleId, Node, ActivityId1, SubTypeId1, Data) ->
    case check_arbitrate_req(State, RoleId, Node, ActivityId1, SubTypeId1) of
        {false, CodeToMe} -> TvErrorCode = CodeToMe, NewState2 = State;
        {false, CodeToMe, _MyCode} -> TvErrorCode = CodeToMe, NewState2 = State;
        {my_false, OtherErrorCode, CodeToMe} -> TvErrorCode = OtherErrorCode, NewState2 = State;
        {true, CheckState} ->
            CodeToMe = ?SUCCESS,
            TvErrorCode = CodeToMe,
            NewState2 = do_arbitrate_req(CheckState, RoleId, Node, Data); %% ,
            % NewState2 = arbitrate_result(NewState, {vote, RoleId, 1});
        {true, NewState, _Args} ->
            CodeToMe = ?SUCCESS,
            TvErrorCode = CodeToMe,
            NewState2 = do_arbitrate_req(NewState, RoleId, Node, Data) %%,
            % NewState2 = arbitrate_result(NewState1, {vote, RoleId, 1})
    end,
    {ErrorCodeInt, ErrorCodeArgs} = util:parse_error_code(CodeToMe),
    #team{arbitrate = Arbitrate, member = Members, id = TeamId, cls_type = ClsType} = NewState2,
    #team_arbitrate{id = ArbitrateId, end_time = EndTime} = Arbitrate,
    #team_enlist{activity_id = ActivityId, subtype_id = SubTypeId, scene_id = SceneId, module_id = _ModuleId, sub_module = _SubId}
    = ?IF(CodeToMe =:= ?SUCCESS, NewState2#team.target_enlist, NewState2#team.enlist),
    %% ?PRINT("arbitrate_req ActivityId:~p ArbitrateId:~p RoleId:~p ErrorCodeInt:~p, ErrorCodeArgs:~w ~n",
    %%     [ActivityId, ArbitrateId, RoleId, ErrorCodeInt, ErrorCodeArgs]),
    {ok, BinData} = pt_240:write(24020, [ErrorCodeInt, ErrorCodeArgs, ActivityId, SubTypeId, SceneId, ArbitrateId]),
    case Node of
        undefined -> lib_server_send:send_to_uid(RoleId, BinData);
        _ -> lib_server_send:send_to_uid(Node, RoleId, BinData)
    end,
    % 广播仲裁
    case CodeToMe == ?SUCCESS of
        true ->
            Args = [TeamId, ActivityId, SubTypeId, SceneId, ArbitrateId, EndTime],
            lib_team:members_apply_cast(ClsType, Members, ?APPLY_CAST_STATUS, lib_team, start_arbitrate, [Args]),
            [erlang:send_after(urand:rand(1000, 3000), self(), {fake_agree_arbitrate, M#mb.id}) || M <- Members, lib_team:is_fake_mb(M)];
            %lib_team:send_team(NewState2, BinData3);
        false ->
            {ErrorCodeInt2, ErrorCodeArgs2} = util:parse_error_code(TvErrorCode),
            {ok, BinData2} = pt_240:write(24038, [ErrorCodeInt2, ErrorCodeArgs2]),
            lib_team:send_team(NewState2, BinData2, RoleId)
    end,
    % 系统消息 11017协议
    send_team_channel(NewState2, TvErrorCode),
    {ok, NewState2}.

%% 检查投票
%% @returns {true, []} | {true, State, _Args} | {false, ErrorCode} | {false, ErrorCode, OtherErrorCode} | {my_false, OtherErrorCode, MyErrorCode}
%%  ErrorCode : integer() | {integer(), Args}
check_arbitrate_req(State, RoleId, _Node, ActivityId, SubTypeId) ->
    #team{leader_id = LeaderId, arbitrate_result_ref = ArbRef, dungeon = Dungeon, member = Mbs, is_matching = IsMatching, enlist = #team_enlist{activity_id = ActId, subtype_id = SubId} = EnList} = State,
    #team_dungeon{dun_pid = DunPid} = Dungeon,
    IsAlivePid = misc:is_process_alive(DunPid),
    Cfg = data_team_ui:get(ActivityId, SubTypeId),
    SameTargetCheckList
    = if
        ActivityId =:= ActId andalso SubTypeId =:= SubId ->
            [{any_not_help, Mbs}];
        true ->
            []
    end,
    case check_arbitrate_req(
        SameTargetCheckList ++ [
            {is_leader, LeaderId == RoleId},
            {is_record, is_record(Cfg, team_enlist_cfg)},
            {repeat, ArbRef},
            {is_matching, IsMatching},
            {dunpid_alive, IsAlivePid},
            {all_online, Mbs},
            {safe_scene, Mbs}
        ]
    ) of
        true ->
            #team_enlist_cfg{module_id=Module, sub_module=SubModule, dun_id=DunId} = Cfg,
            TargetEnList = EnList#team_enlist{activity_id=ActivityId, subtype_id=SubTypeId, dun_id=DunId,  module_id=Module, sub_module=SubModule},
            NewState = State#team{target_enlist = TargetEnList},
            {true, NewState};
        Result ->
            Result
    end.

check_arbitrate_req([{repeat, ArbRef}|T]) ->
    if
        is_reference(ArbRef) -> {false, ?ERRCODE(err240_not_again_arbitrate_req)};
        true -> check_arbitrate_req(T)
    end;

check_arbitrate_req([{is_matching, IsMatching}|T]) ->
    if
        IsMatching =:= 1 -> {false, ?ERRCODE(err240_change_when_matching)};
        true -> check_arbitrate_req(T)
    end;

check_arbitrate_req([{dunpid_alive, IsAlivePid}|T]) ->
    if
        IsAlivePid -> {false, ?ERRCODE(err240_not_arbitrate_req_in_dungeon)};
        true -> check_arbitrate_req(T)
    end;

check_arbitrate_req([{is_leader, IsLeader}|T]) ->
    if
        not IsLeader -> {false, ?ERRCODE(err240_not_leader_to_arbitrate_req)};
        true -> check_arbitrate_req(T)
    end;

check_arbitrate_req([{is_record, Value}|T]) ->
    if
        Value ->
            check_arbitrate_req(T);
        true ->
            {false, ?ERRCODE(err_config)}
    end;

check_arbitrate_req([{any_not_help, Mbs}|T]) ->
    case lists:keyfind(?HELP_TYPE_NO, #mb.help_type, Mbs) of
        false ->
            {false, ?ERRCODE(err240_all_for_help_other)};
        _ ->
            check_arbitrate_req(T)
    end;
check_arbitrate_req([{all_online, Mbs}|T]) ->
    case ulists:find(fun
        (#mb{online = OL}) ->
            OL =/= ?ONLINE_ON
    end, Mbs) of
        {ok, #mb{figure = #figure{name = RoleName}}} ->
            {false, {?ERRCODE(err240_teammates_offline), [RoleName]}};
        _ ->
            check_arbitrate_req(T)
    end;
check_arbitrate_req([{safe_scene, Mbs}|T]) ->
    F = fun
        (#mb{scene = SceneId} = One) ->
            IsFake = lib_team:is_fake_mb(One),
            if
                IsFake -> false;
                true ->
                    case data_scene:get(SceneId) of
                        #ets_scene{type = Type} ->
                            Type =/= ?SCENE_TYPE_NORMAL andalso Type =/= ?SCENE_TYPE_OUTSIDE;
                        _ ->
                            true
                    end
            end
    end,
    case ulists:find(F, Mbs) of
        {ok, #mb{figure = #figure{name = RoleName}}} ->
            {false, {?ERRCODE(err610_not_on_safe_scene_to_enter_dungeon_by_other), [RoleName]}};
        _ ->
            check_arbitrate_req(T)
    end;

check_arbitrate_req([]) ->
    true.


%% 处理发起仲裁
do_arbitrate_req(State, RoleId, _Node, Data) ->
    #team{arbitrate_result_ref = OldArbRef, arbitrate = Arbitrate, target_enlist = Enlist, member = Mbs} = State,
    #team_enlist{module_id = Module, sub_module = SubModule} = Enlist,
    LeftTime = data_team_m:get_arbitrate_time(Module, SubModule),
    ArbRef = util:send_after(OldArbRef, LeftTime*1000 + 2000, self(), {'arbitrate_result'}),
    NewMbs = [if Mb#mb.id == RoleId -> Mb#mb{target_start_data = Data}; true -> Mb end || Mb <- Mbs],
    NewArbitrate = Arbitrate#team_arbitrate{end_time = utime:unixtime()+LeftTime},
    State#team{arbitrate_result_ref = ArbRef, arbitrate = NewArbitrate, member = NewMbs}.

%% 回应仲裁
arbitrate_res(State, RoleId, ArbitrateId, Res, Node) ->
    {Agree, _} = Res,
    case check_arbitrate_res(State, RoleId, ArbitrateId, Res, Node) of
        {false, MyErrorCode} ->
            NewState = do_arbitrate_res(State, RoleId, 0, Node, []),
            NewState2 = arbitrate_result(NewState, {vote, RoleId, 0});
        {false, OtherErrorCode, MyErrorCode} ->
            NewState = do_arbitrate_res(State, RoleId, 0, Node, {false, OtherErrorCode}),
            NewState2 = arbitrate_result(NewState, {vote, RoleId, 0});
        {ok, Data} ->
            MyErrorCode = ?SUCCESS,
            NewState = do_arbitrate_res(State, RoleId, Agree, Node, Data),
            NewState2 = arbitrate_result(NewState, {vote, RoleId, Agree});
        vote_repeat ->
            MyErrorCode = ?ERRCODE(err240_arbitrate_vote_repeat),
            NewState2 = State
    end,
    % 是否归属于非安全区域的错误码
    SceneErrorCodeList = [?ERRCODE(err610_not_on_safe_scene_to_enter_dungeon), ?ERRCODE(err610_not_on_enter_scene)],
    case lists:member(MyErrorCode, SceneErrorCodeList) of
        true ->
            #team{member = Mbs} = NewState2,
            case lists:keyfind(RoleId, #mb.id, Mbs) of
                false -> skip;
                #mb{figure = #figure{name = Name} } ->
                    {ErrorCodeInt, ErrorCodeArgs} = util:parse_error_code({?ERRCODE(err240_agree_to_enter_safety_scene), [Name]}),
                    {ok, BinData} = pt_110:write(11017, [?CHAT_WINDOW_ONLY, ErrorCodeInt, ErrorCodeArgs]),
                    lib_team:send_team(NewState2, BinData)
            end;
        false ->
            {ErrorCodeInt, ErrorCodeArgs} = util:parse_error_code(MyErrorCode),
            {Agree, _} = Res,
            {ok, BinData} = pt_240:write(24021, [ErrorCodeInt, ErrorCodeArgs, Agree]),
            lib_team:send_to_uid(Node, RoleId, BinData),
            % ?PRINT("arbitrate_res RoleId:~p ErrorCodeInt:~p, ErrorCodeArgs:~w ~n", [RoleId, ErrorCodeInt, ErrorCodeArgs]),
            case MyErrorCode == ?SUCCESS of
                true ->
                    {ok, BinData3} = pt_240:write(24036, [RoleId, ArbitrateId, Agree]),
                    lib_team:send_team(NewState2, BinData3);
                false ->
                    skip
            end
    end,
    {ok, NewState2}.

%% 检查回应
check_arbitrate_res(State, RoleId, OldArbitrateId, {_, ErrorCode}, _Node) ->
    #team{arbitrate_result_ref = ArbRef, arbitrate = Arbitrate} = State,
    #team_arbitrate{id = ArbitrateId, vote_list = VoteList} = Arbitrate,
    % #team_enlist{module_id = Module, sub_module = SubModule} = Enlist,
    if
        is_reference(ArbRef) == false -> {false, ?ERRCODE(err240_not_on_arbitrate_req_to_vote)};
        ArbitrateId =/= OldArbitrateId -> {false, ?ERRCODE(err240_not_current_aribirate_req)};
        true ->
            case lists:keyfind(RoleId, #vote_info.role_id, VoteList) of
                false ->
                    ErrorCode;
                _ ->
                    vote_repeat
            end
    end.

%% 处理回应仲裁
do_arbitrate_res(State, RoleId, Res, _Node, Data) ->
    #team{arbitrate = Arbitrate, member = Mbs} = State,
    #team_arbitrate{id = ArbitrateId, agree_num = AgreeNum, refuse_num = RefuseNum, vote_list = VoteList} = Arbitrate,
    NewArbitrateId = ArbitrateId,
    case Res == 1 of
        true -> NewAgreeNum = AgreeNum+1, NewRefuseNum = RefuseNum, Res1 = 1;
        false -> NewAgreeNum = AgreeNum, NewRefuseNum = RefuseNum+1, Res1 = 0
    end,
    NewVoteList = [#vote_info{role_id = RoleId, res = Res1}|VoteList],
    NewArbitrate = Arbitrate#team_arbitrate{id = NewArbitrateId, agree_num = NewAgreeNum, refuse_num = NewRefuseNum, vote_list = NewVoteList},
    NewMbs = [if Mb#mb.id == RoleId -> Mb#mb{target_start_data = Data}; true -> Mb end || Mb <- Mbs],
    State#team{arbitrate = NewArbitrate, member = NewMbs}.

%% 仲裁结果
%% Args: timeout | {vote, RoleId, Res}
arbitrate_result(State, {vote, _RoleId, _}) ->
    #team{arbitrate = Arbitrate, member = Mbs} = State,
    #team_arbitrate{vote_list = VoteList} = Arbitrate,
    IsWithRefuse = lists:keymember(0, #vote_info.res, VoteList),
    % ?PRINT("arbitrate_result IsWithRefuse:~p IsOk:~p ~n", [IsWithRefuse, length(VoteList) == length(Mbs)]),
    if
        % 是否有拒绝
        IsWithRefuse ->
            NewState = arbitrate_result_common(State),
            #vote_info{role_id = RefuseRoleId} = lists:keyfind(0, #vote_info.res, VoteList),
            case lists:keyfind(RefuseRoleId, #mb.id, Mbs) of
                #mb{target_start_data = {false, ErrorCode}} -> ok;
                #mb{figure = #figure{name = Name}} -> ErrorCode = {?ERRCODE(err240_arbitrate_refuse), [Name]};
                _ -> Name = "", ErrorCode = {?ERRCODE(err240_arbitrate_refuse), [Name]}
            end,
            % ErrorCode = {?ERRCODE(err240_arbitrate_refuse), [Name]},
            arbitrate_result_fail(NewState, ErrorCode, RefuseRoleId);
        length(VoteList) == length(Mbs) ->
            % ?PRINT("arbitrate_result_success ErrorCode:~p ~n", [1]),
            NewState = arbitrate_result_common(State),
            arbitrate_result_success(NewState);
        true ->
            % ?PRINT("arbitrate_result_success ErrorCode:~p ~n", [no_match]),
            State
    end;

arbitrate_result(State, timeout) ->
    if
        true ->
            NewState = arbitrate_result_common(State),
            arbitrate_result_fail(NewState, ?ERRCODE(err240_arbitrate_timeout))
    end.

%% 通用(仲裁取消)
arbitrate_result_common(State) ->
    % State1 = ?IF(ReturnCost, return_target_cost(State), State),
    #team{arbitrate = Arbitrate, arbitrate_result_ref = ArbRef} = State,
    #team_arbitrate{id = ArbitrateId} = Arbitrate,
    util:cancel_timer(ArbRef),
    NewArbitrate = Arbitrate#team_arbitrate{id = ArbitrateId, agree_num = 0, refuse_num = 0, vote_list = [], end_time = 0},
    State#team{arbitrate = NewArbitrate, arbitrate_result_ref = 0}.

%% 成功
%% @returns #team{}
arbitrate_result_success(State) ->
    ErrorCode = ?SUCCESS,
    broadcast_arbitrate_result(State, ErrorCode),
    send_team_channel(State, ErrorCode),
    broadcast_arbitrate_result(State, ErrorCode),
    send_team_channel(State, ErrorCode),
    State.

%% 失败
arbitrate_result_fail(State, ErrorCode) ->
    arbitrate_result_fail(State, ErrorCode, 0).
arbitrate_result_fail(State, ErrorCode, RefuseRoleId) ->
    broadcast_arbitrate_result(State, ErrorCode, RefuseRoleId),
    send_team_channel(State, ErrorCode),
    {ok, BinData} = pt_240:write(24040, []),
    lib_team:send_team(State, BinData),
    State.
    % State.

%% 广播仲裁的结果
broadcast_arbitrate_result(State, ErrorCode) ->
    broadcast_arbitrate_result(State, ErrorCode, 0).
%% 广播仲裁的结果
broadcast_arbitrate_result(State, ErrorCode, SkipId) ->
    {ErrorCodeInt, ErrorCodeArgs} = util:parse_error_code(ErrorCode),
    {ok, BinData} = pt_240:write(24037, [ErrorCodeInt, ErrorCodeArgs]),
    lib_team:send_team(State, BinData, SkipId),
    ok.

%% 广播超时成功的信息
broadcast_arbitrate_timeout_success_msg(State) ->
    #team{arbitrate = Arbitrate, member = Mbs} = State,
    #team_arbitrate{id = ArbitrateId, vote_list = VoteList} = Arbitrate,
    F = fun(#mb{id = RoleId}) ->
        case lists:keymember(RoleId, #vote_info.role_id, VoteList) of
            true -> skip;
            false ->
                {ok, BinData} = pt_240:write(24036, [RoleId, ArbitrateId, 1]),
                lib_team:send_team(State, BinData)
        end
    end,
    lists:foreach(F, Mbs),
    ok.

%% 发送到聊天窗口
%% @returns ok | skip
send_team_channel(State, ErrorCode) ->
    {ErrorCodeInt, ErrorCodeArgs} = util:parse_error_code(ErrorCode),
    BroadcastList = [
        ?ERRCODE(err610_not_on_safe_scene_to_enter_dungeon_by_other)
        , ?ERRCODE(err610_not_enough_lv_to_enter_dungeon_by_other)
        , ?ERRCODE(err610_dungeon_count_daily_help_by_other)
        , ?ERRCODE(err610_dungeon_count_permanent_by_other)
        , ?ERRCODE(err610_dungeon_count_week_by_other)
        , ?ERRCODE(err610_dungeon_count_daily_by_other)
        , ?ERRCODE(err610_cannot_join_act_on_battle_with_name)
        , ?ERRCODE(err610_cannot_transfer_scene_on_fill_magic_with_name)
        , ?ERRCODE(err240_me_to_other)

        , ?ERRCODE(err240_not_enough_lv_to_enter_target_with_name)
    ],
    case lists:member(ErrorCodeInt, BroadcastList) of
        true ->
            ?PRINT("ErrorCodeInt:~p ~n", [ErrorCodeInt]),
            {ok, BinData} = pt_110:write(11017, [?CHAT_WINDOW_ONLY, ErrorCodeInt, ErrorCodeArgs]),
            lib_team:send_team(State, BinData),
            ok;
        false ->
            skip
    end.

%% 其他操作自动使仲裁结束
%% @returns #team{}
auto_interrupt_arbitrate(State) ->
    case check_auto_interrput_arbitrate(State) of
        true ->
            NewState = arbitrate_result_common(State),
            ErrCode = ?ERRCODE(err240_change_state_to_interrput_arbitrate),
            {ok, BinData} = pt_110:write(11017, [?CHAT_WINDOW_ONLY, ErrCode, []]),
            lib_team:send_team(NewState, BinData),
            % 取消仲裁界面
            {ok, BinData2} = pt_240:write(24040, []),
            lib_team:send_team(NewState, BinData2),
            NewState;
        false ->
            State
    end.

check_auto_interrput_arbitrate(State) ->
    #team{arbitrate_result_ref = ArbRef, dungeon = Dungeon} = State,
    #team_dungeon{dun_pid = DunPid} = Dungeon,
    IsAlivePid = misc:is_process_alive(DunPid),
    if
        is_reference(ArbRef) == false -> false;
        IsAlivePid -> false;
        true -> true
    end.

%% ---------------------------------------------------------------------------
%% 检查函数
%% ---------------------------------------------------------------------------

%% 通用检查
%% @returns true | {false, OtherErrorCode, MyErrorCode}
%%  OtherErrorCode, MyErrorCode :: is_integer()|{is_integer(), is_list()}
%% 检查多人
common_check(State) ->
    #team{member = Mbs} = State,
    do_common_check(Mbs, State).

do_common_check([], _State) -> true;
do_common_check([Mb|T], State) ->
    case common_check(State, Mb) of
        true -> do_common_check(T, State);
        Result -> Result
    end.

%% 检查单人
common_check(State, Mb) ->
    CheckList = [lv],
    do_common_check(CheckList, State, Mb).

join_team_check(State, Mb) ->
    #team{password = Password} = State,
    CheckList = case lib_team_api:need_check_password(Password) of
        true -> %% 密码非0,前面已经检查过了,无视战力检查
            [lv];
        _    ->
            [lv,power]
    end,
    do_common_check(CheckList, State, Mb).

%% 重连数据处理
do_common_check([], _State, _Mb) -> true;
do_common_check([H|T], State, Mb) ->
    case common_check_help(H, State, Mb) of
        true -> do_common_check(T, State, Mb);
        {false, OtherErrorCode, MyErrorCode} -> {false, OtherErrorCode, MyErrorCode};
        {false, ErrorCode} ->   {false, ErrorCode, ErrorCode}
    end.

common_check_help(lv, State, Mb) ->
    #team{lv_limit_min = Min, lv_limit_max = Max} = State,
    #mb{figure = #figure{lv = Lv, name = Name}} = Mb,
    if
        Lv < Min ->
            {false, {?ERRCODE(err240_not_enough_lv_to_enter_target_with_name), [Name]}, {?ERRCODE(player_lv_less), [Min]}};
        Max > 0 andalso Lv > Max ->
            {false, {?ERRCODE(err240_not_enough_lv_to_enter_target_with_name), [Name]}, {?ERRCODE(player_lv_large), [Max]}};
        true ->
            true
    end;
common_check_help(power, State, Mb) ->
    #team{power_limit = PowerLimit} = State,
    #mb{figure = #figure{power = Power}} = Mb,
    if Power >= PowerLimit ->
            true;
        true ->
            {false, ?ERRCODE(err240_not_enough_power), ?ERRCODE(err240_not_enough_power)}
    end;
common_check_help(_, _State, _Mb) ->
    {false, ?FAIL, ?FAIL}.




%% ---------------------------------------------------------------------------
%% 数据处理
%% ---------------------------------------------------------------------------

%% 更新队伍信息
update_team_mb(State, RoleId, AttrList) ->
    #team{member=Mbs, leader_id = Leader, cls_type = ClsType, id = TeamId}=State,
    case lists:keyfind(RoleId, #mb.id, Mbs) of
        false -> {ok, State};
        Mb ->
            NewMb = do_update_team_mb(AttrList, Mb),
            NewMbs = lists:keyreplace(RoleId, #mb.id, Mbs, NewMb),
            RoleId =:= Leader andalso mod_team_enlist:change_team(ClsType, TeamId, AttrList),
            NewState = State#team{member = NewMbs},
            lib_team:send_team_info(NewState),
            {ok, NewState}
    end.

%% 是否需要广播队伍信息
is_need_broadcast_team_info([]) -> false;
is_need_broadcast_team_info([{name, _Name}|_]) -> true;
is_need_broadcast_team_info([{lv, _Lv}|_]) -> true;
is_need_broadcast_team_info([_|L]) ->
    is_need_broadcast_team_info(L).

do_update_team_mb([], Mb) -> Mb;
do_update_team_mb([H|T], Mb) ->
    #mb{figure = Figure} = Mb,
    case H of
        {name, Name} -> NewMb = Mb#mb{figure = Figure#figure{name = Name}};
        {lv, Lv} -> NewMb = Mb#mb{figure = Figure#figure{lv = Lv}};
        {power, Power} -> NewMb = Mb#mb{power = Power};
        {welfare, Welfare} -> NewMb = Mb#mb{welfare = Welfare};
        _ -> NewMb = Mb
    end,
    do_update_team_mb(T, NewMb).

%% ---------------------------------------------------------------------------
%% 其他函数
%% ---------------------------------------------------------------------------

%% 是否在副本中
is_on_dungeon(#team{dungeon = #team_dungeon{dun_pid = DunPid}}) ->
    misc:is_process_alive(DunPid).


%% 是否允许掉落
is_dungeon_allow_drop(State, Mb) ->
    #team{dungeon = #team_dungeon{dun_id = DunId}} = State,
    Dun = data_dungeon:get_dun(DunId),
    IsOnDungeon = is_on_dungeon(State),
    #mb{help_type = HelpType} = Mb,
    if
        % 是否在副本中
        IsOnDungeon == false -> true;
        is_record(Dun, dun_cfg) == false -> true;
        % Dun#dungeon.type == ?DUNGEON_TYPE_TSMAP -> true;
        % 在副本中助战不能有奖励
        HelpType == ?HELP_TYPE_YES -> false;
        true -> true
    end.

check_team_matching_state(State, RoleId, MatchingState) ->
    #team{leader_id = LeaderId,
          is_matching = MatchingState0,
          enlist = #team_enlist{activity_id = ActivityId, subtype_id = SubTypeId},
          free_location = FreeLoaction} = State,
    case data_team_ui:get(ActivityId, SubTypeId) of
        #team_enlist_cfg{auto_pair = AutoPair} ->
            ok;
        _ ->
            AutoPair = ?MATCH_NO
    end,
    NowTime = utime:unixtime(),
    if
        AutoPair =:= ?MATCH_NO ->
            {false, ?ERRCODE(err240_auto_pair_limit)};
        ActivityId =:= 1 ->
            {false, ?ERRCODE(err240_choose_target_first)};
        FreeLoaction =:= [] ->
            {false, ?ERRCODE(err240_max_member)};
        LeaderId =/= RoleId andalso MatchingState =:= 1 ->
            {false, ?ERRCODE(err240_not_leader)};
        MatchingState0 > 255 andalso NowTime - MatchingState0 < 5 ->
            {false, ?ERRCODE(operation_too_quickly)};
        true ->
            true
    end.
%% 修改队伍目标时判断
check_change_team_enlist(State, RoleId, ActivityId, SubTypeId, LvMin, LvMax, _MentorArgs) ->
    #team{
        cls_type = ClsType, leader_id = LeaderId, member = Members, 
        is_matching = IsMatching, leader_node = LeaderNode
    } = State,
    Cfg = data_team_ui:get(ActivityId, SubTypeId),
    IsOnDungeon = lib_team_mod:is_on_dungeon(State),
    TargetClsType = case lib_team:is_cls_target(ActivityId, SubTypeId) of
      true -> ?NODE_CENTER;
      _ -> ?NODE_GAME
    end,
    NowTime = utime:unixtime(),
    if
        LeaderId =/= RoleId -> {false, ?ERRCODE(err240_not_leader)};
        IsMatching =:= 1 -> {false, ?ERRCODE(err240_change_when_matching)};
        IsMatching > 255 andalso NowTime - IsMatching < 5 -> {false, ?ERRCODE(err240_change_when_matching)};
        is_record(Cfg, team_enlist_cfg) == false -> {false, ?ERRCODE(err_config)};
        LvMax < LvMin -> {false, ?ERRCODE(err240_choose_lv_error)};
        Cfg#team_enlist_cfg.default_lv_min > LvMin -> {false, ?ERRCODE(err240_choose_lv_error)};
        Cfg#team_enlist_cfg.default_lv_max > 0 andalso Cfg#team_enlist_cfg.default_lv_max < LvMax ->
            {false, ?ERRCODE(err240_choose_lv_error)};
        Cfg#team_enlist_cfg.num < length(Members) ->
            {false, ?ERRCODE(err240_too_many_members)};
        IsOnDungeon -> {false, ?ERRCODE(err240_on_dungeon_not_to_do)};
        TargetClsType =/= ClsType -> %% 不同节点
            case TargetClsType =:= ?NODE_GAME andalso lists:any(fun
                (#mb{node = N}) ->
                    N =/= LeaderNode
            end, Members) of
                true ->
                    {false, ?ERRCODE(err240_local_team_with_other_serv)};
                _ ->
                    {take_over, TargetClsType}
                    % take_over({'change_team_enlist', RoleId, ActivityId, SubTypeId, SceneId, LvMin, LvMax}, Node, TargetClsType, State)
            end;
        true ->
            case check_each_one(State#team{enlist = #team_enlist{dun_id = Cfg#team_enlist_cfg.dun_id},
                lv_limit_min = LvMin, lv_limit_max = LvMax}, Members) of
                true  ->
                    {true, Cfg};
                {false, Err} ->
                    {false, Err}
            end
    end.

check_each_one(_, []) -> true;
check_each_one(State, [MbInfo|T]) ->
    #team{member = Members} = State,
    ErrorCode = ?ERRCODE(err610_dungeon_num_not_satisfy),
    case lib_team_mod:common_check(State#team{member = Members -- [MbInfo]}, MbInfo) of
        {false, ErrorCode, _} ->
            check_each_one(State, T);
        {false, {_, _}, _MyErrorCode} ->
            {false, ?ERRCODE(err240_error_not_change)};
        {false, OtherErrorCode, _MyErrorCode} ->
            {false, OtherErrorCode};
        true ->
            check_each_one(State, T)
    end.
change_team_enlist(State, Cfg, SceneId, LvMin, LvMax, Password) ->
    #team{id = TeamId, cls_type = ClsType, enlist = EnList, member = Members, max_num = MaxNum, leader_id = Leader} = State,
    #team_enlist_cfg{activity_id = ActivityId, subtype_id = SubTypeId, module_id=Module, sub_module=SubModule, dun_id=DunId, num = MaxMemberNum} = Cfg,
    NewEnList = EnList#team_enlist{activity_id=ActivityId, subtype_id=SubTypeId, dun_id=DunId, scene_id=SceneId, module_id=Module, sub_module=SubModule},
    StateAfInterrupt = lib_team_mod:auto_interrupt_arbitrate(State),
    LeaderInfo = lists:keyfind(Leader, #mb.id, Members),
    UpdateList = [{enlist, EnList, NewEnList}, {lv, LvMin, LvMax}, {set_help_type, {Leader, LeaderInfo#mb.help_type}}, {password, Password}],
    mod_team_enlist:change_team(ClsType, TeamId, UpdateList),
    [lib_player:apply_cast(MNode, Id, ?APPLY_CAST_STATUS, ?NOT_HAND_OFFLINE, lib_team, change_team_enlist, [TeamId, NewEnList, HelpType])||#mb{id = Id, help_type = HelpType, node = MNode}<-Members],
    case Cfg#team_enlist_cfg.subtype_name == [] of
        true->
            Target = Cfg#team_enlist_cfg.activity_name;
        false->
            Target = Cfg#team_enlist_cfg.subtype_name
    end,
    lib_chat:send_TV({team, TeamId}, 240, 7, [Target]),
    TmpState = delay_notify_teammates_update(StateAfInterrupt),
    TmpState1
    = if
        MaxNum =/= MaxMemberNum ->
            {NewMemberList, NewFreeLocation} = lib_team:calc_location(Members, MaxMemberNum),
            TmpState#team{member = NewMemberList, free_location = NewFreeLocation};
        true ->
            TmpState
    end,
    TmpState1#team{enlist=NewEnList, lv_limit_min = LvMin, lv_limit_max = LvMax, max_num = MaxMemberNum}.

delay_notify_teammates_update(State) ->
    #team{delay_update_ref = Ref} = State,
    if
        is_reference(Ref) -> State;
        true ->
            Ref1 = erlang:send_after(2000, self(), 'notify_teammates_update'),
            State#team{delay_update_ref = Ref1}
    end.
%% 设置队伍的匹配状态
set_matching_state(State, RoleId, MatchingState) ->
    case lib_team_mod:check_team_matching_state(State, RoleId, State) of
        true ->
            if
                State#team.is_matching =/= MatchingState ->
                    {Code, NewState} = set_team_matching_state(State, MatchingState);
                true ->
                    Code = ?SUCCESS, NewState = State
            end;
        {false, Code} ->
            NewState = State
    end,
    if
        Code =:= skip ->
            skip;
        true ->
            #team{enlist = #team_enlist{activity_id = ActivityId, subtype_id = SubTypeId}} = NewState,
            {ok, BinData} = pt_240:write(24048, [Code, MatchingState, ActivityId, SubTypeId, RoleId]),
            lib_team:send_team(NewState, BinData)
    end,
    NewState.
set_team_matching_state(State, MatchingState) ->
    #team_enlist{activity_id = ActivityId, subtype_id = SubTypeId} = State#team.enlist,
    lib_team:when_change_match_status(State, MatchingState),
    case lib_team_match:get_match_mod(ActivityId, SubTypeId) of
        undefined ->
            #team{id = TeamId, cls_type = ClsType} = State,
            Code = ?SUCCESS,
            mod_team_enlist:change_team(ClsType, TeamId, [{matching, MatchingState}]),
            if
                MatchingState == 1 ->
                    NewState
                    = case lib_team_match:try_match_fake_mb(State#team{is_matching = MatchingState}) of
                        {ok, S} -> S;
                        _ -> State#team{is_matching = MatchingState}
                    end;
                true ->
                    util:cancel_timer(State#team.fake_mb_ref),
                    NewState = State#team{is_matching = MatchingState, fake_mb_ref = undefined}
            end,
            {Code, NewState};
        Mod ->
            Mod:team_set_match_state(State, MatchingState)
    end.

interrupt_special_match(#team{is_matching = 0} = State) -> State;
interrupt_special_match(State) ->
    #team{enlist = #team_enlist{activity_id = ActivityId, subtype_id = SubTypeId}} = State,
    case lib_team_match:get_match_mod(ActivityId, SubTypeId) of
        undefined ->
            State;
        Mod ->
            {Code, NewState} = Mod:team_set_match_state(State, 0),
            {ok, BinData} = pt_240:write(24048, [Code, 0, ActivityId, SubTypeId, 0]),
            lib_team:send_team(NewState, BinData),
            NewState
    end.

%% 更新玩家属性
set_member_attr(State, Node, MemberId, TeamMemberType, TeamId, LeaderName) ->
    #team{id = TId, cls_type=ClsType} = State,
    Args = [MemberId, [{team_flag, {TId, TeamMemberType, TeamId, LeaderName} }]],
    mod_team:cast_to_game(ClsType, Node, lib_player, update_player_info, Args),
    ok.

%% 通知队员更改队长名字
set_member_leader_name(State) ->
    #team{cls_type=ClsType, member = Members, leader_name = LeaderName} = State,
    
    [
    begin
        Arg = [RoleId, [{team_leader_name, LeaderName}]],
        mod_team:cast_to_game(ClsType, Node, lib_player, update_player_info, Arg)
    end|| #mb{id = RoleId, node = Node} <- Members
    ].


