%%-----------------------------------------------------------------------------
%% @Module  :       lib_arena_battle.erl
%% @Author  :       Fwx
%% @Email   :       421307471@qq.com
%% @Created :       2017-12-06
%% @Description:    排位赛战斗
%%-----------------------------------------------------------------------------

-module(lib_arena_battle).
% -behaviour(lib_battle_field).
-include("battle_field.hrl").
-include("def_fun.hrl").
-include("predefine.hrl").
-include("server.hrl").
-include("arena.hrl").
-include("scene.hrl").
-include("attr.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("battle.hrl").

%% 行为接口
-export([
    %% 战场进程
    init/2
    , player_enter/3
    , pack_enter_args/2
    , player_quit/3
    , player_logout/3
    , player_disconnect/3
    , player_finish_change_scene/3
    , player_die/3
    , player_revive/3
    %% 玩家进程
    , evt_out_of_battle/2
    , mon_born/3
    , mon_die/3
    , terminate/2
]).

%% 其它接口
-export([
    fake_man_die_handler/5
    , fake_man_born_handler/3
    , battle_time_out/2
    , ready_time_out/2
    , reward_time_out/2
    , send_fake_data/2
    , skip_battle/2
    , dummy_start_battle/2
    , clear_dummy/1
]).


init(State, Args) ->
    case Args of
        [RoleId, RivalId, fake_man, FakeManMine, FakeManRival, SelfRank, RivalRank] ->
            RoleIdAsCopyId = RoleId,
            [Figure, BattleAttr, Skills, _Combat1, SceneId, X1, Y1] = FakeManMine,
            [Figure2, BattleAttr2, Skills2, _Combat2, SceneId, X2, Y2] = FakeManRival,
            %% 计算假人行走坐标
            EndX1 = (X1 + X2) div 2 - 60,
            EndX2 = (X1 + X2) div 2 + 60,
            EndY1 = (Y1 + Y2) div 2 - 30,
            EndY2 = (Y1 + Y2) div 2 + 30,
    
            ScenePoolId = scene_mgr:manual_divide_pool(SceneId, [RoleId]),
            
            RoleMap = #{
                RoleId => #battle_role{key = RoleId, in_info = #{x => X1, y => Y1}}
            },
            %% 创建假人 (假人找目标适当延迟几秒)
            MyArgs = [{figure, Figure}, {battle_attr, BattleAttr#battle_attr{pk = #pk{pk_status = ?PK_ALL}}}, {skill, Skills},
                {die_handler, {?MODULE, fake_man_die_handler, [State#battle_state.self, RoleId]}},
                {born_handler, {?MODULE, fake_man_born_handler, [State#battle_state.self]}},
                {group, 2}, {warning_range, 3000}, {path, [{EndX1, EndY1}]}
            ],
            Id = lib_scene_object:sync_create_object(?BATTLE_SIGN_DUMMY, 0, SceneId, ScenePoolId, X1, Y1, 1, RoleIdAsCopyId, 1, MyArgs),
            
            MyArgs2 = [{figure, Figure2}, {battle_attr, BattleAttr2#battle_attr{pk = #pk{pk_status = ?PK_ALL}}}, {skill, Skills2},
                {die_handler, {?MODULE, fake_man_die_handler, [State#battle_state.self, RivalId]}},
                {group, 1}, {warning_range, 3000}, {path, [{EndX2, EndY2}]}
            ],
            Id2 = lib_scene_object:sync_create_object(?BATTLE_SIGN_DUMMY, 0, SceneId, ScenePoolId, X2, Y2, 1, RoleIdAsCopyId, 1, MyArgs2),
            
            Data = #{
                fake_man_mine => #{data => FakeManMine, id => Id, role_id => RoleId, alive_num => 1, ss_ids => []},
                fake_man_rival => #{data => FakeManRival, id => Id2, role_id => RivalId, alive_num => 1, ss_ids => []},
                rank => [SelfRank, RivalRank], copy_id => RoleIdAsCopyId
            },
            NewState = State#battle_state{cur_scene = SceneId, scene_pool_id = ScenePoolId, copy_id = RoleIdAsCopyId, roles = RoleMap, data = Data};
        _ ->
            NewState = State
    end,
    {ok, NewState}.

player_enter(RoleKey, Args, State) ->
    #battle_state{roles = RoleMap} = State,
    case maps:find(RoleKey, RoleMap) of
        {ok, #battle_role{out_info = OutInfo, in_info = InInfo} = Role} ->
            [OSceneId, OPoolId, OCopyId, X, Y, Hp, HpLim] = Args,
            NewOutInfo = OutInfo#{
                scene => OSceneId,
                scene_pool_id => OPoolId,
                copy_id => OCopyId,
                x => X,
                y => Y,
                hp => max(1, Hp),
                hp_lim => HpLim,
                scene_args => [{hide, 0}]
            },
            if
                Hp > 0 ->
                    InSceneArgs = [{hp_lim, HpLim}, {hp, HpLim}, {hide, 1}, {group, 2}];
                true ->
                    InSceneArgs = [{change_scene_hp, 1}, {hide, 1}, {group, 2}]
            end,
            {ok, Role#battle_role{out_info = NewOutInfo, in_info = InInfo#{scene_args => InSceneArgs}}, State};
        _ ->
            skip
    end.

%% 进入参数
pack_enter_args(_Role, _NewState) ->
    [{hide, 1}].

%% 玩家退出场景
player_quit(#battle_role{key = RoleKey}, _Args, #battle_state{is_end = false} = State) ->
    #battle_state{roles = NewRoleMap} = NewState = handle_result(State),
    NewRole = maps:get(RoleKey, NewRoleMap),
    {ok, NewRole, NewState};
player_quit(_, _, _) ->
    skip.

%% 掉线
player_logout(#battle_role{key = RoleKey}, _Args, #battle_state{is_end = false} = State) ->
    #battle_state{roles = NewRoleMap} = NewState = handle_result(State),
    NewRole = maps:get(RoleKey, NewRoleMap),
    {ok, NewRole, NewState};
player_logout(_Role, _Args, _State) ->
    skip.

%% 断开客户端
player_disconnect(#battle_role{}, _Args, #battle_state{is_end = false} = State) ->
    NewState = handle_result(State),
    {ok, NewState};
player_disconnect(_Role, _Args, _State) ->
    skip.

player_die(_Role, _Args, _State) ->
    skip.

player_revive(_Role, _Args, _State) ->
    skip.

player_finish_change_scene(Role, _Args, State) ->
    #battle_state{roles = RoleMap, self = Self, data = Data, cur_scene = SceneId, scene_pool_id = ScenePoolId} = State,
    RoleList = maps:to_list(RoleMap),
    case lists:all(fun
                       ({_key, R}) ->
                           R#battle_role.state =:= ?ROLE_STATE_IN orelse R#battle_role.key =:= Role#battle_role.key
                   end, RoleList) of
        true ->
            case data_arena:get_arena_value(?ARENA_START_TIME) of
                [] -> BeforeStartTime = 3;
                [BeforeStartTime] -> skip
            end,
            UnixTime = utime:unixtime(),
            [lib_server_send:send_to_uid(RoleId, pt_280, 28014, [0, UnixTime + BeforeStartTime + 1])
                || {RoleId, _} <- RoleList],
            erlang:send_after((BeforeStartTime) * 1000, Self, {apply, ?MODULE, ready_time_out, []}),
            case Data of
                #{fake_man_mine := #{id := DummyId1,data := FakeManMine}, fake_man_rival := #{id := DummyId2,data := FakeManRival, ss_ids := SSIds2}} ->
                    [_, _, _, CombatM, _, _, _] = FakeManMine,
                    [_, _, _, CombatR, _, _, _] = FakeManRival,
                    DummyL = case CombatM>=CombatR of
                        true->
                            [DummyId1,DummyId2];
                        _->
                            [DummyId2,DummyId1]
                    end,
                    mod_scene_agent:apply_cast(SceneId, ScenePoolId, ?MODULE, dummy_start_battle,
                        [DummyL ++ SSIds2, BeforeStartTime]);
                _ ->
                    ok
            end;
        _ ->
            ok
    end,
    {ok, Role, State}.

mon_born(MonId, _, State) ->
    #battle_state{data = Data} = State,
    NewData = Data#{my_mon_id => MonId},
    {ok, State#battle_state{data = NewData}}.

mon_die(_, [Id, _SignType, Aid] = _Args, #battle_state{is_end = false, roles = RoleMap} = State) ->
    #battle_state{data = Data} = State,
    case maps:get(clear_status, Data, 0) of
        0 ->
            RoleList = maps:to_list(RoleMap),
            case data_arena:get_arena_value(?ARENA_REWARD_TIME) of
                [] ->
                    ?ERR("arena cfg error:reward time!~n", []),
                    RewardTime = 10;
                [RewardTime] -> skip
            end,
            [NewData, Result] = update_alive_num(Data, Id),
            [
                spawn(
                    fun() ->
                        timer:sleep(2000),
                        if
                            Result =/= ?ARENA_FAIL andalso Result =/= ?ARENA_WIN ->
                                skip;
                            true ->
                                %% 如果是对手死了，将我的假人移动到出生点
                                case Id =/= RoleId of
                                    true  ->
                                        #battle_role{in_info = #{x := X, y := Y}} = RoleInfo,
                                        MonId = maps:get(my_mon_id, Data, 0),
                                        mon_mgr:send(Aid, MonId, {move, X, Y});
                                    false ->
                                        ok
                                end,
                                UnixTime = utime:unixtime(),
                                EndType = ?ARENA_BATTLE_END_TYPE_NORMAL,
                                mod_arena:cast_to_arena(challenge_image_role, [RoleId, Result, EndType, 0]),
                                lib_server_send:send_to_uid(RoleId, pt_280, 28014, [2, UnixTime + RewardTime])
                        end
                   end
                )
                || {RoleId, RoleInfo} <- RoleList
            ],
            if
                Result == 3 ->
                    NewState = State#battle_state{data = NewData};
                true ->
                    NewState = State#battle_state{data = maps:put(clear_status, 1, NewData)}
            end;
        1 -> NewState = State
    end,
    {ok, NewState};

mon_die(_, _, _) -> ok.

dummy_start_battle([], _Time)->
    skip;
dummy_start_battle(List, Time) ->
    dummy_start_battle(List, Time, 1000).

dummy_start_battle([], _Time,_)->
    skip;
dummy_start_battle([Id|T], Time, Boost)->
    case lib_scene_object_agent:get_object(Id) of
        #scene_object{aid = Aid, id = MonId} ->
            mon_mgr:send_after(Time * Boost, Aid, MonId, rush_and_find_target);
        _ ->
            skip
    end,
    dummy_start_battle(T,Time,Boost+100).



send_fake_data(State, _Args) ->
    #battle_state{roles = RoleMap, data = Data} = State,
    RoleList = maps:to_list(RoleMap),
    case Data of
        #{fake_man_mine := #{id := SelfRobotId, role_id := SelfRoleId},
            fake_man_rival := #{id := RivalRobotId, role_id := RivalRoleId}} ->
            lists:map(fun({RoleId, _}) ->
                lib_server_send:send_to_uid(RoleId, pt_280, 28013, [SelfRobotId, SelfRoleId, RivalRobotId, RivalRoleId])
            end, RoleList);
        _ -> skip
    end,
    State.

ready_time_out(#battle_state{is_end = false, roles = RoleMap, self = Self}, []) ->
    RoleList = maps:to_list(RoleMap),
    case data_arena:get_arena_value(?ARENA_BATTLE_TIME) of
        [] -> BattleTime = 30;
        [BattleTime] -> skip
    end,
    UnixTime = utime:unixtime(),
    [lib_server_send:send_to_uid(RoleId, pt_280, 28014, [1, UnixTime + BattleTime])
        || {RoleId, _} <- RoleList],
    erlang:send_after(BattleTime * 1000, Self, {apply, ?MODULE, battle_time_out, []});
ready_time_out(_State, _) -> ok.

battle_time_out(#battle_state{is_end = false, roles = RoleMap, self = Self, data = Data} = State, []) ->
    case data_arena:get_arena_value(?ARENA_REWARD_TIME) of
        [] -> RewardTime = 10;
        [RewardTime] -> skip
    end,
    case maps:get(clear_status, Data, 0) of
        0 ->
            RoleList = maps:to_list(RoleMap),

            UnixTime = utime:unixtime(),
            [begin
                 %% 时间结束没出结果，则挑战者失败
                 EndType = ?ARENA_BATTLE_END_TYPE_FORCE,
                 mod_arena:cast_to_arena(challenge_image_role, [RoleId, ?ARENA_FAIL, EndType, 0]),
                 lib_server_send:send_to_uid(RoleId, pt_280, 28014, [2, UnixTime + RewardTime])
             end
                || {RoleId, _} <- RoleList],
            NewState = State#battle_state{data = maps:put(clear_status, 1, Data)};
        1 -> NewState = State
    end,
    erlang:send_after(RewardTime * 1000, Self, {apply, ?MODULE, reward_time_out, []}),
    NewState;
battle_time_out(_State, _) -> ok.

reward_time_out(#battle_state{is_end = false} = State, []) ->
    handle_result(State);
reward_time_out(_State, _) -> ok.

handle_result(#battle_state{roles = RoleMap, self = Self, data = _Data} = State) ->
    mod_battle_field:stop(Self),
    case maps:keys(RoleMap) of
        [RoleId|_] ->
            mod_arena:cast_to_arena(cancel_battle, [RoleId]);
        _ ->
            ok
    end,
    State#battle_state{is_end = true}.

evt_out_of_battle(Player, _Reason) ->
    #player_status{sid = Sid} = Player,
    %% 通知客户端结束
    lib_server_send:send_to_sid(Sid, pt_280, 28012, [?SUCCESS]),
    lib_player:break_action_lock(Player#player_status{arena_battle_pid = undefined}, ?ERRCODE(err280_on_battle_state)).

terminate(_Reason, State) ->
    #battle_state{data = Data, cur_scene = SceneId, scene_pool_id = ScenePoolId, copy_id = CopyId} = State,
    lib_scene:clear_scene_room(SceneId, ScenePoolId, CopyId),
    case Data of
        #{rank := RankList} ->
            mod_arena:cast_to_arena(set_battle_status, [RankList, ?NOT_BATTLE_STATUS]);
        _ -> skip
    end.

fake_man_die_handler(#ob_act{object = #scene_object{id = Id, aid = Aid}}, _KList, _Atter, _AtterSign, [BattleFieldPid, RoleId]) ->
    mod_battle_field:mon_die(BattleFieldPid, Id, [RoleId, ?BATTLE_SIGN_DUMMY, Aid]).

fake_man_born_handler(init, State, [BattleFieldPid|_]) ->
    #ob_act{object = #scene_object{id = Id}} = State,
    mod_battle_field:mon_born(BattleFieldPid, Id, []).

skip_battle(#battle_state{data = Data, cur_scene = SceneId, scene_pool_id = ScenePoolId} = State, [RoleId]) ->
    case maps:get(clear_status, Data, 0) of
        0 ->
            case Data of
                #{fake_man_mine := #{data := [_SelfFigure, _SelfBattleAttr, _SelfSkills, SelfCombat|_]},
                    fake_man_rival := #{data := [_RivalFigure, _RivalBattleAttr, _RivalSkills, RivalCombat|_]}} ->
                    Res = if
                        SelfCombat > RivalCombat -> ?ARENA_WIN;
                        true -> ?ARENA_FAIL
                    end,
                    EndType = ?ARENA_BATTLE_END_TYPE_FORCE,
                    mod_arena:cast_to_arena(challenge_image_role, [RoleId, Res, EndType, 0]),
                    NewState = State#battle_state{data = maps:put(clear_status, 1, Data)};
                _ ->
                    NewState = State
            end;
        _ ->
            NewState = State
    end,
    case Data of
        #{copy_id := CopyId} -> mod_scene_agent:apply_cast(SceneId, ScenePoolId, ?MODULE, clear_dummy, [CopyId]);
        _ -> skip
    end,
    NewState.

clear_dummy(CopyId) ->
    lib_scene_object_agent:clear_scene_object(0, CopyId, 1).

%% 修改存活数
update_alive_num(Data, Id) ->
    case Data of
        #{fake_man_mine := FakeManMine = #{id := SelfRobotId, role_id := RoleId, alive_num := SelfAliveNum}, fake_man_rival := FakeManRival = #{alive_num := RivalAliveNum}} ->
            if
                %% 挑战者存活人数减一
                SelfRobotId == Id orelse RoleId == Id ->
                    NewSelfAliveNum = SelfAliveNum - 1,
                    NewRivalAliveNum = RivalAliveNum;
                true ->
                    NewSelfAliveNum = SelfAliveNum,
                    NewRivalAliveNum = RivalAliveNum - 1
            end,
            %% 判断胜负
            if
                NewSelfAliveNum =< 0 ->
                    Result = ?ARENA_FAIL;
                NewRivalAliveNum =< 0 ->
                    Result = ?ARENA_WIN;
                true ->
                    Result = 3
            end,
            NewData = Data#{fake_man_mine => FakeManMine#{alive_num => NewSelfAliveNum}, fake_man_rival => FakeManRival#{alive_num => NewRivalAliveNum}};
        _ ->
            Result = ?ARENA_FAIL,
            NewData = Data
    end,
    [NewData, Result].
