%% ---------------------------------------------------------------------------
%% @doc lib_mon_event
%% @author ming_up@foxmail.com
%% @since  2016-12-29
%% @deprecated  怪物对外事件
%% ---------------------------------------------------------------------------
-module(lib_mon_event).
-include("scene.hrl").
-include("common.hrl").
-include("battle.hrl").
-include("predefine.hrl").
-include("dungeon.hrl").
-include("attr.hrl").
-include("drop.hrl").
-include("figure.hrl").
-include("def_fun.hrl").
-include("local_boss.hrl").
-export([
         be_killed/6,
         be_killed_direct/1,
         be_collected/2,
         be_collected_local/2,
         be_picked/2,
         be_hurted/6,
         be_rebound_hurt/4,
         hp_change/3,
         walk/1,
         walk_end/1,
         broadcast_hurt_list/2,
         be_stop/1,
         update_boss_hit/2,
         reset_boss_hit/1,
         update_notice_count_interval/1
        ]).

be_killed(Minfo, Klist, Atter, AtterSign, FirstAttr, BLWhos) ->
    AtterRealSign = mod_battle:calc_real_sign(AtterSign),
    #scene_object{
       id = AutoId, mon=Mon, scene = SceneId, scene_pool_id=ScenePoolId, copy_id = CopyId,
       sign = DieSign, skill_owner = SkillOwner, die_info = _DieInfoList, x = X, y = Y, d_x = _DX, d_y = _DY, figure = _Figure} = Minfo,
    #scene_mon{exp=Exp, kind = MonKind, mid=Mid, exp_share_type = ExpShareType, boss = Boss} = Mon,
    #battle_return_atter{node = _AtterNode, id = AtterId, real_name = AtterName, guild_id = GuildId} = Atter,
    % DieDatas = get_die_data(DieInfoList, Minfo, [], Klist),
    ?TRY(lib_dungeon_api:kill_mon(SceneId, CopyId, AutoId, DieSign, Mon, SkillOwner, [{killer, AtterId}, {mon_xy, X, Y}])),
    %% 触发增加亲密度
    ?TRY(lib_relationship_api:event_trigger({kill_mon, Mid, MonKind, Boss, BLWhos})),
    %% 掉落
    IsDrop = mod_guild_boss_mgr:check_drop(Mid),
    IsDrop andalso drop:mon_drop(Minfo, Atter, Klist, FirstAttr),
    %% TODO 考虑某些直接通知玩家进程的消息，通过统一的回调之后才分别处理
    case lib_scene:is_clusters_scene(SceneId) of
        true  ->
            ?IF(lib_void_fam:is_in_void_fam(SceneId), mod_void_fam:kill_mon(AtterId, AtterName, Mid, SceneId), skip),
            %% 跨服帮战
            ?TRY(cross_guild_war_api:kill_mon(Atter, Minfo));
        false -> %% 非跨服调用
            ?IF(lib_void_fam:is_in_void_fam(SceneId), mod_void_fam_local:kill_mon(AtterId, AtterName, Mid, SceneId), skip),
            ?TRY(lib_guild_order_mod:mon_die(Minfo, AtterSign, AtterId)),
            ?TRY(lib_guild_collect:mon_die(Minfo, AtterSign, AtterId, GuildId)),
            ?TRY(mod_guild_boss_mgr:mon_die(Atter,Minfo,Klist)),
            if
                AtterSign == ?BATTLE_SIGN_PLAYER ->
                    %% 避免队伍重复触发任务
                    Fun = fun(P, TeamIds) ->
                                  if
                                      P#mon_atter.team_id == 0 ->
                                          lib_task_api:kill_mon(P#mon_atter.id, Mid),
                                          lib_task_api:kill_boss(P#mon_atter.id, SceneId, Boss),
                                          TeamIds;
                                      true ->
                                          case lists:member(P#mon_atter.team_id, TeamIds) of
                                              true ->
                                                  TeamIds;
                                              false ->
                                                  lib_task_api:kill_mon(P#mon_atter.id, Mid),
                                                  lib_task_api:kill_boss(P#mon_atter.id, SceneId, Boss),
                                                  lib_team_api:kill_mon(P#mon_atter.team_id, AtterId, Mid),
                                                  [P#mon_atter.team_id|TeamIds]
                                          end
                                  end
                          end,
                    lists:foldl(Fun, [], Klist),
                    ?TRY(?IF(Exp =< 0, skip, share_mon_exp(Mid, ExpShareType, Klist, Exp, SceneId, ScenePoolId, CopyId, X, Y)));
                % AtterSign == ?BATTLE_SIGN_DUMMY andalso TeamId > 0 ->
                %   MonArgs = #mon_args{id = AutoId, mid = Mon#scene_mon.mid, kind = Mon#scene_mon.kind, lv = Figure#figure.lv,
                %                       ctime = Mon#scene_mon.ctime, boss = Boss, scene = SceneId,
                %                       scene_pool_id = ScenePoolId, copy_id = CopyId, x = X, y = Y, name = Figure#figure.name},
                %   [mod_team:cast_to_team(TeamId, {'alloc_drop', MonArgs, ?ALLOC_EQUAL, Klist}) || ?ALLOC_EQUAL <- data_drop:get_rule_list(Mon#scene_mon.mid)];
                true ->
                    skip
            end,
            case AtterRealSign of
                ?BATTLE_SIGN_PLAYER ->
                    treasure_house:kill_mon(AtterId, Boss),
                    ok;
                _ ->
                    ignore
            end
    end,
    lib_battle:check_get_anger(SceneId, ScenePoolId, Atter),
    ok.

%% 直接死亡回调接口(由self()!'die'触发)
be_killed_direct(_Minfo) ->
    ok.

be_collected(Minfo, CollectorId) ->
    #scene_object{
        id = OnlyId,
        scene=SceneId,
        scene_pool_id=_ScenePoolId,
        copy_id = CopyId,
        d_x  = _X,
        d_y = _Y,
        sign = DieSign,
        mon = #scene_mon{create_key = _CreateKey} = Mon,
        skill_owner = SkillOwner,
        mod_args = ModArgs
    } = Minfo,
    lib_dungeon_api:kill_mon(SceneId, CopyId, OnlyId, DieSign, Mon, SkillOwner, [{collecter, CollectorId, OnlyId}, {mod_args, ModArgs}]),
    lib_void_fam:be_collected(CollectorId,Minfo),
    case lib_scene:is_clusters_scene(SceneId) of
        true  ->
            case data_scene:get(SceneId) of
                #ets_scene{type= Type} ->
                    IsCast = if
                        SceneId =:= 1016 ->
                            true;
                        true->
                            lists:member(Type, [?SCENE_TYPE_VOID_FAM, ?SCENE_TYPE_DUNGEON, ?SCENE_TYPE_CAMP_WAR])
                    end,
                    case IsCast of
                        true  ->
                            ServId = mod_player_create:get_serid_by_id(CollectorId),
                            clusters_center:apply_cast(ServId, ?MODULE, be_collected_local, [Minfo, CollectorId]);
                        false ->
                            ignore
                    end;
                _ ->
                    ignore
            end;
        false ->
            be_collected_local(Minfo, CollectorId)
    end.

be_collected_local(Minfo, CollectorId) ->
    lib_player:apply_cast(CollectorId, ?APPLY_CAST_STATUS, collect_api, collect_succ, [Minfo]).

be_picked(Minfo, CollectorId) ->
    #scene_object{id = AutoId, mon=Mon, scene = SceneId, scene_pool_id=ScenePoolId, copy_id = CopyId, sign = DieSign, skill_owner = SkillOwner, skill=Skill} = Minfo,
    case Skill of
        %% 自己对自己释放，减少配置的难度
        [{SkillId, SkillLv}|_] -> ?TRY(lib_battle_api:assist_anything(SceneId, ScenePoolId, ?BATTLE_SIGN_PLAYER, CollectorId, ?BATTLE_SIGN_PLAYER, CollectorId, SkillId, SkillLv));
        _ -> skip
    end,
    ?TRY(lib_dungeon_api:kill_mon(SceneId, CopyId, AutoId, DieSign, Mon, SkillOwner, [{picker, CollectorId}])).
    % case lib_scene:is_clusters_scene(SceneId) of
    %     true  -> skip;
    %     false -> lib_dungeon_api:kill_mon(SceneId, CopyId, AutoId, DieSign, Mon, SkillOwner, [])
    % end.

%% 怪物受伤处理
be_hurted(Minfo, _OldMinfo, Atter, AtterSign, Hurt, IsFirstAttack) ->
    #scene_object{ scene = SceneId, scene_pool_id = ScenePoolId, copy_id = CopyId, mon = Mon, mod_args = ModArgs} = Minfo,
    #battle_return_atter{
        id = AtterId, guild_id = GuildId, server_id = ServId, flag = GuildFlag,
        real_sign = _RealSign, real_id = RealId, real_name = AtterName} = Atter,
    %% 跨服帮战
    cross_guild_war_api:attack_mon(Atter, Minfo, Hurt, IsFirstAttack),
    madness_land_api:when_mon_be_hurt(Minfo, Atter, Hurt, IsFirstAttack),
    camp_war_api:mon_be_hurt([SceneId, ScenePoolId, AtterId, ServId, GuildId, GuildFlag], Mon, Hurt),
    case lib_scene:is_clusters_scene(SceneId) of
        true  ->
            %% 跨服副本
            lib_dungeon_api:mon_be_hurt(SceneId, CopyId, RealId, AtterSign, AtterName, Hurt, ModArgs);
        false ->
            %% 副本
            lib_dungeon_api:mon_be_hurt(SceneId, CopyId, RealId, AtterSign, AtterName, Hurt, ModArgs),
            mod_guild_boss_mgr:attack_mon(Atter, Minfo, Hurt),
            lib_guild_collect:attack_mon(Atter, Minfo, Hurt)
    end.

%% 怪物反弹伤害处理
be_rebound_hurt(_, _, _,_) -> ok.

%% 血量发生变化
hp_change(Minfo, _OldMinfo, _Klist) ->
    #scene_object{battle_attr = #battle_attr{hp=_Hp}} = Minfo,
    ok.

%% 广播怪物伤害
broadcast_hurt_list(_Minfo, _Klist) ->
    ok.

%% 走路
walk(_Minfo) ->
    % #scene_object{id = Id, x = X, y = Y, mon = Mon, skill_owner = SkillOwner} = Minfo,
    ok.

%% 走完路径
walk_end(_Minfo) ->
    % #scene_object{battle_attr= BA, mon = Mon, skill_owner = SkillOwner} = Minfo,
    ok.

%% 被清理
be_stop(Minfo) ->
    #scene_object{id = AutoId, scene = SceneId, scene_pool_id = PoolId, copy_id = CopyId, sign = Sign, battle_attr= BA, mon = Mon, skill_owner = SkillOwner} = Minfo,
    lib_dungeon_api:stop_mon(SceneId, CopyId, AutoId, Sign, Mon, SkillOwner, BA#battle_attr.hp, BA#battle_attr.hp_lim),
    madness_land_api:stop_mon(SceneId, PoolId, AutoId),
    act_boss_mgr:when_boss_stop(Minfo),
    unshared_boss:when_monster_stop(Minfo),
    ok.

%% 怪物的掉落处理
%% common_mon_drop(Atter, Minfo, Klist, FirstAttr)->
%%     #scene_object{
%%        id = MonAutoId, scene = ObjectScene, scene_pool_id = ObjectPoolId,
%%        copy_id = ObjectCopyId, x = X, y = Y, mon = Mon, figure = Figure
%%       } = Minfo,
%%     MonArgs = #mon_args{
%%                  id = MonAutoId, mid = Mon#scene_mon.mid, kind = Mon#scene_mon.kind,
%%                  boss = Mon#scene_mon.boss, scene = ObjectScene, scene_pool_id = ObjectPoolId,
%%                  copy_id = ObjectCopyId, x = X, y = Y, name = Figure#figure.name},
%%     lists:foreach(fun(Alloc) ->
%%                           alloc_drop(PlayerStatus, MonArgs, Alloc, PList, FirstAttr)
%%                   end, data_drop:get_rule_list(Mon#scene_mon.mid)).
%%     do.

%% 获取死亡数据
% get_die_data([title|T], Minfo, Acc, Klist) ->
%     #scene_object{figure = #figure{title = Title}} = Minfo,
%     get_die_data(T, Minfo, [{title, Title}|Acc], Klist);
% get_die_data([klist|T], Minfo, Acc, Klist) ->
%     get_die_data(T, Minfo, [{klist, Klist}|Acc], Klist);
% get_die_data([drop|T], Minfo, Acc, Klist) ->
%     #scene_object{
%         id = MonAutoId, scene = SceneId, scene_pool_id= ScenePoolId,
%         copy_id= CopyId, mon=Mon, figure = Figure, x = X, y = Y
%     } = Minfo,
%     #scene_mon{mid = Mid, kind = Kind, boss = Boss, ctime = CTime} = Mon,
%     #figure{lv = Lv, name = Name} = Figure,
%     MonArgs = #mon_args{
%         id = MonAutoId, mid = Mid, kind = Kind, lv = Lv,
%         ctime = CTime, boss = Boss, scene = SceneId, scene_pool_id = ScenePoolId,
%         copy_id = CopyId, x = X, y = Y, name = Name
%     },
%     get_die_data(T, Minfo, [{mon_args, MonArgs}|Acc], Klist);
% get_die_data([_|T], Minfo, Acc, Klist) ->
%     get_die_data(T, Minfo, Acc, Klist);
% get_die_data([], _Minfo, Acc, _Klist) -> Acc.

%% 经验分享
share_mon_exp(_Mid, ?EXP_SHARE_ALL_TO_ATTACKER, Klist, Exp, _SceneId, _ScenePoolId, _CopyId, _X, _Y) ->
    [lib_player:apply_cast(P#mon_atter.pid, ?APPLY_CAST_SAVE, lib_player, add_exp, [Exp, role_kill_mon, []]) || P <- Klist];
share_mon_exp(_Mid, ?EXP_SHARE_PART_TO_ATTACKER, Klist, Exp0, _SceneId, _ScenePoolId, _CopyId, _X, _Y) ->
    AllHurt = lists:sum([P#mon_atter.hurt || P <- Klist]),
    [begin
         Exp = max(trunc(Exp0 * P#mon_atter.hurt / AllHurt), 1),
         lib_player:apply_cast(P#mon_atter.pid, ?APPLY_CAST_SAVE, lib_player, add_exp, [Exp, role_kill_mon, []])
     end || P <- Klist];
share_mon_exp(Mid, ?EXP_SHARE_TO_SCENE, _Klist, Exp, SceneId, ScenePoolId, CopyId, _X, _Y) ->
    mod_scene_agent:apply_cast(SceneId, ScenePoolId, lib_scene_agent, player_apply_cast,
                               [CopyId, ?APPLY_CAST_SAVE, lib_player, add_mon_dynamic_exp, [Mid, Exp]]);
share_mon_exp(Mid, ?EXP_SHARE_ALL_TO_ATTACKER_DYNAMIC, Klist, Exp, _SceneId, _ScenePoolId, _CopyId, _X, _Y) ->
    [lib_player:apply_cast(P#mon_atter.pid, ?APPLY_CAST_SAVE, lib_player, add_mon_dynamic_exp, [Mid, Exp]) || P <- Klist];
share_mon_exp(_, _, _, _, _, _, _, _, _) -> ok.

%% 每次攻击时，判断掉血
update_boss_hit(#scene_object{
    mod_args = ModArgs, id = Id, scene = SceneId, scene_pool_id = PoolId,
    battle_attr = #battle_attr{hp_lim = HpLim}
} = Object, Now) when is_list(ModArgs) ->
    case lists:keyfind(?LOCAL_BOSS_LAST_HIT, 1, ModArgs) of
        {?LOCAL_BOSS_LAST_HIT, #boss_hit_info{die_state = 1} = One} ->
            %% 进入死亡状态
            NewOne = One;
        {?LOCAL_BOSS_LAST_HIT, #boss_hit_info{last_time = LastTime, time_need = AliveTime,
            sum_time = SumTime, notice_count = NoticeCount} = One} ->
            Diff = Now - LastTime,
            NewCount = NoticeCount + 1,
            %% 达到攻击次数才广播血量，减少消息
            CalcCount = case NewCount >= One#boss_hit_info.count_need of
                            true  ->
                                0;
                            false ->
                                NewCount
                        end,
            %% 如果功击间隔超过2秒，那么认为不是连续攻击，将此时看作第一次攻击，时间忽略
            One1 =
                case Diff > 2000 of
                    true  ->
                        One#boss_hit_info{last_time = Now, sum_time = SumTime, notice_count = CalcCount};
                    false ->
                        One#boss_hit_info{last_time = Now, sum_time = SumTime + Diff, notice_count = CalcCount}
                end,
            NewOne =
                case One1#boss_hit_info.sum_time >= AliveTime of
                    true  ->
                        One1#boss_hit_info{die_state = 1};
                    false ->
                        One1
                end,
            if
                NewOne#boss_hit_info.die_state == 1  ->
                    %% BOSS死亡
                    %% 将BOSS的血量设置为100，之后可以正常扣血
                    lib_mon:change_attr_by_ids(SceneId, PoolId, [Id], [{hp, ?KEEP_ALIVE_MON_DIE_HP}]),
                    ok;
                CalcCount =:= 0 ->
                    %% 广播血量
                    NowHp = erlang:ceil(HpLim * (AliveTime - NewOne#boss_hit_info.sum_time) / AliveTime),
                    lib_mon:change_attr_by_ids(SceneId, PoolId, [Id], [{hp, NowHp}]),
                    true;
                true ->
                    true
            end;
        _ ->
            Key =
                case lib_scene:is_dungeon_scene(SceneId) of
                    true  ->
                        reward_boss_dun_killtime;
                    false ->
                        reward_boss_killtime
                end,
            TimeCfg = case data_local_boss:get_local_boss_kv(Key) of
                          Time when is_integer(Time) ->
                              Time;
                          _ ->
                              180
                      end,
            NewOne = #boss_hit_info{last_time = Now, sum_time = 0, notice_count = 1, time_need = TimeCfg * 1000}
    end,
    Object#scene_object{
        mod_args = lists:keystore(?LOCAL_BOSS_LAST_HIT, 1, ModArgs, {?LOCAL_BOSS_LAST_HIT, NewOne})
    };
update_boss_hit(Object, _) ->
    Object.

%% BOSS重置状态
reset_boss_hit(#scene_object{mon = #scene_mon{boss = ?MON_REWARD_BOSS}} = Object) ->
    Object#scene_object{mod_args = []};
reset_boss_hit(Object) -> Object.

%% 刷新更新间隔，刷新间隔按照玩家的出手次数来
%% 一个人每攻击一次实际上会出售3次，所以可以3次受击才刷新一次血量
%% 如果是N个人在打BOSS的话就是3（N-1）次受击刷新一次血量
update_notice_count_interval(MonState) ->
    #ob_act{object = #scene_object{mod_args = ModArgs} = Object, klist = Klist, support_klist = SKlist} = MonState,
    Total = max(?ROLE_HIT_FRE, (length(Klist) + length(SKlist) - 1) * ?ROLE_HIT_FRE),
    case lists:keyfind(?LOCAL_BOSS_LAST_HIT, 1, ModArgs) of
        {?LOCAL_BOSS_LAST_HIT, #boss_hit_info{count_need = CountNeed} = One}
            when CountNeed =/= Total ->
            NewOne = One#boss_hit_info{count_need = Total},
            NewObject =
                Object#scene_object{
                    mod_args = lists:keystore(?LOCAL_BOSS_LAST_HIT, 1, ModArgs, {?LOCAL_BOSS_LAST_HIT, NewOne})
                },
            MonState#ob_act{object = NewObject};
        _ ->
            MonState
    end.
