%%% @author root <root@localhost.localdomain>
%%% @copyright (C) 2017, root
%%% @doc
%%%
%%% @end
%%% Created : 18 Dec 2017 by root <root@localhost.localdomain>

-module(lib_drop).
-include("common.hrl").
-include("predefine.hrl").
-include("drop.hrl").
-include("goods.hrl").
-include("def_goods.hrl").
-include("scene.hrl").
-include("figure.hrl").
-include("team.hrl").
-include("def_fun.hrl").
-include("def_module.hrl").
-include("attr.hrl").
-include("server.hrl").
-include("errcode.hrl").
-include("custom_act.hrl").
-include("def_event.hrl").
-include("guild.hrl").
-include("language.hrl").
-include("battle.hrl").
-include("treasure_map.hrl").
-include("czone.hrl").

-compile(export_all).

%% ================================= pickup drop =================================
%% 转成玩家的掉落参数
trans_to_ps_args(Ps, _GameNodeType)->
    #player_status{id = RoleId, figure = Figure, scene = Scene,
                   scene_pool_id = PoolId, copy_id = CopyId, x = X, y = Y,battle_attr = BA, team = Team} = Ps,
    #figure{name = Name, lv = Lv} = Figure,
    CellNums = lib_goods_api:get_cell_num_list(Ps),
    IsHomeBossScene = local_boss_api:is_home_boss_scene(Scene),
    if 
        not IsHomeBossScene -> 
            ExtraData = #{};
        true ->
            [
                {_, ChipGet},
                {_, PurpleGet},
                {_, FosterChipGet}
            ] =
            lib_daily:get_count(RoleId,
                [
                    {?MOD_GOODS, ?MOD_GOODS_HOME_DROP_COUNT, ?GOODS_STUFF_SUBTYPE_EQUIPMENT_CHIP},
                    {?MOD_GOODS, ?MOD_GOODS_HOME_DROP_COUNT, ?PURPLE},
                    {?MOD_GOODS, ?MOD_GOODS_HOME_DROP_COUNT, ?ORANGE}
                ]),
            ExtraData = #{orange_equipment_chip => ChipGet, purple_equipment => PurpleGet, foster_skin_chip => FosterChipGet}
    end,
    #ps_args{role_id = RoleId, name = Name, lv = Lv, cell_num = CellNums,
        scene = Scene, pool_id = PoolId, copy_id = CopyId, x = X, y = Y,
        hp = BA#battle_attr.hp, team_id = Team#status_team.team_id, extra_data = ExtraData
    }.

%% ================================= new drop =================================
%% 发送掉落给玩家
send_drop_reward(RoleId, DropArgs, DropReward, ProduceType) ->
    Pid = misc:get_player_process(RoleId),
    case misc:is_process_alive(Pid) of
        true -> gen_server:cast(Pid, {'send_drop_reward', DropArgs, DropReward, ProduceType});
        false -> false
    end.

%% 玩家离开场景自动发放未捡起的掉落
leave_scene_auto_pick_up([], _Node, _RoleId, _SceneId) -> ok;
leave_scene_auto_pick_up(MyDrops, Node, RoleId, SceneId) ->
    [
        begin
            Reward = {?TYPE_BIND_GOODS*Bind, GoodsTypeId, DropNum},
            DropArgs = [DropId, SceneId, MonId, Notice, GoodsTypeId, Origin, DropNum],
            MFA = {?MODULE, send_drop_reward, [RoleId, DropArgs, [Reward], mon_drop]},
            clusters_api:rpc_cast_do_mfa(Node, MFA),
            Reward
        end
        ||#ets_drop{
            id = DropId, goods_id = GoodsTypeId,
            bind = Bind, num = DropNum, mon_id = MonId,
            notice = Notice, origin = Origin
            } <- MyDrops
    ].

%% 玩家进程发送
send_drop_reward_helper(Ps, DropArgs, DropReward, ProduceType)->
    #player_status{id = RoleId, figure = #figure{name = _Name}} = Ps,
    [DropId, Scene, MonId, Notice, GoodsId, _Origin, GoodsNum|_] = DropArgs,
    send_drop_tv(Notice, Ps, DropArgs),
    {ok, TmpPS} = lib_player_event:dispatch(Ps, ?EVENT_DROP_CHOOSE, #{goods => DropReward, id => DropId, mon_id => MonId, scene => Scene}),
    Produce = #produce{type = ProduceType, subtype = MonId, reward = DropReward, remark = ""},
    NewPS = lib_goods_api:send_reward(TmpPS, Produce),
    IsHomeBossScene = local_boss_api:is_home_boss_scene(Scene),
    if IsHomeBossScene ->
            case drop_logic:is_color_goods(GoodsId) of
                {true, orange_equipment_chip} ->
                    lib_daily:plus_count(RoleId, ?MOD_GOODS, ?MOD_GOODS_HOME_DROP_COUNT, ?GOODS_STUFF_SUBTYPE_EQUIPMENT_CHIP, GoodsNum);
                {true, purple_equipment} ->
                    lib_daily:plus_count(RoleId, ?MOD_GOODS, ?MOD_GOODS_HOME_DROP_COUNT, ?PURPLE, GoodsNum);
                {true, foster_skin_chip} ->
                    lib_daily:plus_count(RoleId, ?MOD_GOODS, ?MOD_GOODS_HOME_DROP_COUNT, ?ORANGE, GoodsNum);
                _ ->
                    ignore
            end;
        true ->
            skip
    end,
    NewPS.

%% 掉落拾取检查
check_drop_pickup(DropDict, DropId, PsArgs)->
    case dict:find(DropId, DropDict) of
        error ->
            {fail, ?ERRCODE(err150_no_drop)};
        {ok, DropInfo} ->
            NowTime = utime:unixtime(),
            #ets_drop{player_id = PlayerId, expire_time = ExpireTime,  mon_id = MonId,
                    scene = Scene, scene_pool_id = PoolId, copy_id = CopyId, team_id = TeamId,
                    hurt_id_list = HurtIdList, goods_id = GoodsId,
                    num = GoodsNum
                    } = DropInfo,
            #mon{module = Module} = data_mon:get(MonId),
            FilterHurtModule = [?MOD_DUNGEON,?MOD_GUILD_BOSS],
            IsFilterHurt = lists:member(Module, FilterHurtModule),
            {IsDropCountLimit, DropTypeKey} = check_color_drop_count_limit(Scene, GoodsId, GoodsNum, PsArgs#ps_args.extra_data),
            if
                %% 掉落包过期
                ExpireTime < NowTime ->
                    {fail, ?ERRCODE(err150_no_drop)};
                %% 不同场景
                Scene =/= PsArgs#ps_args.scene
                    orelse PoolId =/= PsArgs#ps_args.pool_id->
                    {fail, ?ERRCODE(err150_notin_drop_scene)};
                %% 不同房间
                CopyId =/= PsArgs#ps_args.copy_id ->
                    {fail, ?ERRCODE(err150_not_drop_copyid)};
                %% 死亡
                PsArgs#ps_args.hp =< 0 ->
                    {fail, ?ERRCODE(err150_drop_no_hp)};
                %% 特殊都可以捡
                IsFilterHurt ->
                    {ok, DropInfo};
                %% 过了保护期，都可以捡，后面策划又说不可以捡了
%%                (ExpireTime - NowTime) < (?DROP_ALIVE_TIME - ?DROP_PROTECT_TIME) ->
%%                    {ok, DropInfo};
                %% 不是归属者,不可捡
                PlayerId > 0 andalso PlayerId =/= PsArgs#ps_args.role_id ->
                    {fail, ?ERRCODE(err150_no_drop_per)};
                %% 不是同队伍，在可拾取列表里可以捡，一般掉落后玩家退队
                %% 最后策划说不同队就不让捡
                TeamId > 0 andalso TeamId =/= PsArgs#ps_args.team_id->
                    {fail, ?ERRCODE(err150_no_drop_per)};
                not IsDropCountLimit ->
                    if DropTypeKey == purple_equipment ->
                            {fail, ?ERRCODE(err150_purple_drop_limit)};
                        DropTypeKey == orange_equipment_chip ->
                            {fail, ?ERRCODE(err150_color_drop_limit)};
                        DropTypeKey == foster_skin_chip ->
                            {fail, ?ERRCODE(err150_zhenbao_drop_limit)};
                        true ->
                            {fail, ?FAIL}
                    end;
                TeamId > 0 andalso TeamId == PsArgs#ps_args.team_id ->
                   case lists:keymember(PsArgs#ps_args.role_id, 1, HurtIdList) of
                       true  ->
                           {ok, DropInfo};
                       false ->
                           {fail, ?ERRCODE(err150_no_drop_per)}
                   end;
                true ->
                    {ok, DropInfo}
            end
    end.

%% 检查掉落物品数据
check_drop_pickup_info(DropInfo, PsArgs) ->
    #ets_drop{drop_thing_type = ThingType, goods_id = GoodsTypeId, bind = Bind, num = DropNum} = DropInfo,
    case ThingType of
        ?TYPE_COIN -> {ok, [{?TYPE_COIN, DropNum}]};
        ?TYPE_GOODS ->
            case data_goods_type:get(GoodsTypeId) of
                #ets_goods_type{max_overlap = MaxOverLap, bag_location = BagLocation}->
                    #ps_args{cell_num = CellNums} = PsArgs,
                    case lists:keyfind(BagLocation, 1, CellNums) of
                        false -> {fail, ?ERRCODE(err150_location_err)};
                        {_, CellNum} ->
                            if
                                MaxOverLap == 0 andalso DropNum > CellNum -> {fail, ?ERRCODE(err150_no_cell)};
                                true -> {ok, [{?TYPE_BIND_GOODS*Bind, GoodsTypeId, DropNum}]}
                            end
                    end;
                _ -> {fail, ?ERRCODE(err150_no_goods_type)}
            end;
        _ -> {fail, ?FAIL}
    end.

%% 检查橙装碎片掉落是否已超上限
check_color_drop_count_limit(Scene, GoodsId, GoodsNum, ExtraData) ->
    IsHomeBossScene = local_boss_api:is_home_boss_scene(Scene),
    if
        IsHomeBossScene ->
            Res =
            case drop_logic:is_color_goods(GoodsId) of
                {true, purple_equipment} ->
                    Limit = data_local_boss:get_local_boss_kv(boss_home_limit_purple),
                    {true, purple_equipment, Limit};
                {true, orange_equipment_chip} ->
                    [{_, _, Limit}|_] = data_local_boss:get_local_boss_kv(boss_home_limit),
                    {true, orange_equipment_chip, Limit};
                {true, foster_skin_chip} ->
                    Limit = 10,
                    {true, foster_skin_chip, Limit};
                _ ->
                    false
            end,
            case Res of
                {true, Key, MaxCount} ->
                    ColorDropCount = maps:get(Key, ExtraData),
                    AccessDropCount = MaxCount - ColorDropCount,
                    if AccessDropCount < GoodsNum -> %% 掉落数量已超可拾取上限
                            {false, Key};
                        true ->
                            {true, true}
                    end;
                _ ->
                    {true, true}
            end;
        true ->
            {true, true}
    end.

%% 格式物品
format_goods([], Goods) -> Goods;
format_goods([{?TYPE_GOODS, GoodId, Num}|T], Goods)->
    format_goods(T, [{goods, GoodId, Num, ?UNBIND}|Goods]);
format_goods([{?TYPE_BIND_GOODS, GoodId, Num}|T], Goods)->
    format_goods(T, [{goods, GoodId, Num, ?BIND}|Goods]);
format_goods([_|T], Goods)-> format_goods(T, Goods).

%% 本服星域判断场景
check_send_tv(Player, ?DROP_ORIGIN_DESERTED_BOSS, ?MOD_DESERTED_BOSS, 3, H) ->
    case lib_scene:is_clusters_scene(Player#player_status.scene) of
        false  ->
            check_send_tv_do(?DROP_ORIGIN_DESERTED_BOSS, ?MOD_DESERTED_BOSS, 3, H);
        true ->
            false
    end;
%% 跨服星域判断场景
check_send_tv(Player, ?DROP_ORIGIN_DESERTED_BOSS, ?MOD_DESERTED_BOSS, 5, H) ->
    case lib_scene:is_clusters_scene(Player#player_status.scene) of
        true  ->
            check_send_tv_do(?DROP_ORIGIN_DESERTED_BOSS, ?MOD_DESERTED_BOSS, 5, H);
        false ->
            false
    end;
check_send_tv(_Player, Origin, Module, Id, H) ->
    check_send_tv_do(Origin, Module, Id, H).
check_send_tv_do(Origin, Module, Id, {MatchOrigin, {MatchModule, MatchId}}) ->
    case Origin =:= MatchOrigin andalso Module =:= MatchModule andalso Id =:= MatchId of
        true  ->
            %% 完全匹配上，那就需要传闻
            true;
        false ->
            %% 有一个匹配不上，那就不发传闻呢
            false
    end.

check(_Player, _Origin, _Module, _Id, []) ->
    %% 应该是不在限制条件内，那就可以传闻
    out;
check(Player, Origin, Module, Id, [H|T]) ->
    case check_send_tv(Player, Origin, Module, Id, H) of
        true  ->
            true;
        false ->
            check(Player, Origin, Module, Id, T)
    end.

send_drop_tv([], _Ps, _DropArgs) -> ok;
send_drop_tv([{ModuleId, Id}|T], Ps, DropArgs) ->
    [_DropId, Scene, MonId, _Notice, GoodsId, Origin|_] = DropArgs,
    CheckList =
        [
            %% 普通赏金掉落
            {?DROP_ORIGIN_REWARD_BOSS, {?MOD_LOCAL_BOSS, ?LAN_TV_LOCAL_BOSS_DROP}},
            %% 本服普通星域掉落
            {?DROP_ORIGIN_DESERTED_BOSS, {?MOD_DESERTED_BOSS, 3}},
            %% 跨服普通星域掉落
            {?DROP_ORIGIN_DESERTED_BOSS, {?MOD_DESERTED_BOSS, 5}},
            %% 赏金独享掉落
            {?DROP_ORIGIN_REWARD_BOSS_UNSHARED, {?MOD_LOCAL_BOSS, ?LAN_TV_LOCAL_BOSS_DROP_UNSHARED}},
            %% 星域独享掉落
            {?DROP_ORIGIN_DESERTED_BOSS_UNSHARED, {?MOD_DESERTED_BOSS, 7}},
            %% 个人BOSS掉落
            {?DROP_ORIGIN_PERSONAL_BOSS, {?MOD_LOCAL_BOSS, ?LAN_TV_PERSONAL_BOSS_DROP}}
        ],
    Res = check(Ps, Origin, ModuleId, Id, CheckList),
    IsShow = case Res of
        true  ->
            true;
        out ->
            %% 不在检查列表里
            not lists:keymember({ModuleId, Id}, 2, CheckList);
        false ->
            false
    end,
    case IsShow of
        true  ->
            #player_status{
                guild = #status_guild{id = GuildId},
                id = RoleId,
                figure = #figure{name = Name},
                team  = #status_team{team_id = TeamId},
                server_num = ServerNum
            } = Ps,
            MonName = case data_mon:get(MonId) of
                          #mon{name = _MonName} -> _MonName;
                          _ -> ""
                      end,
            Args = [Name, RoleId, MonName, GoodsId],
            case data_language:get(ModuleId, Id) of
                #language{subtype = 10} ->               %% 系统频道
                    case lib_scene:is_clusters_scene(Scene) of
                        true  ->
                            if
                                Origin =:= ?DROP_ORIGIN_REWARD_BOSS_UNSHARED
                                    orelse Origin =:= ?DROP_ORIGIN_DESERTED_BOSS_UNSHARED ->
                                    %% 独享BOSS尽管可以在开服场景，但是也不用开服传闻
                                    lib_chat:send_TV({all}, ModuleId, Id, Args);
                                true ->
                                    case czone_api:env_get_zone() of
                                        #czone_svr_local{zone_id = ZoneId, faction = Faction} ->
                                            lib_chat:send_TV({zone, ZoneId, Faction}, ModuleId, Id, [ServerNum|Args]);
                                        _ ->
                                            ok
                                    end
                            end;
                        false ->
                            lib_chat:send_TV({all}, ModuleId, Id, Args)
                    end;
                #language{subtype = 4} when GuildId>0 -> %% 公会频道
                    lib_chat:send_TV({guild, GuildId}, ModuleId, Id, Args);
                #language{subtype = 5} when TeamId>0 ->  %% 队伍频道
                    lib_chat:send_TV({team, TeamId}, ModuleId, Id, Args);
                _ -> skip
            end;
        false ->
            send_drop_tv(T, Ps, DropArgs)
    end;
send_drop_tv([_Unkown|T], PlayerStatus, DropArgs) ->
    ?ERR("send_drop_tv error config:~p", [_Unkown]),
    send_drop_tv(T, PlayerStatus, DropArgs).

%% 统计队伍和人最大伤害
calc_max_plist(PList)->
    Fun = fun(P, [TL, PL]) ->
        if
            P#mon_atter.team_id =< 0 -> [TL, [P|PL]];
            true ->
                case lists:keyfind(P#mon_atter.team_id, 1, TL) of
                    false -> [[{P#mon_atter.team_id, P#mon_atter.hurt, [P]}|TL], PL];
                    {TeamId, OV, OP} ->
                        E = {TeamId, P#mon_atter.hurt+OV, [P|OP]},
                        NewTL = lists:keystore(TeamId, 1, TL, E),
                        [NewTL, PL]
                end
        end
          end,
    [TLists, PLists] = lists:foldl(Fun, [[], []], PList),
    if
        TLists == [] andalso PLists == [] -> [[], []];
        TLists == [] ->
            [Player|_T] = lists:reverse(lists:keysort(#mon_atter.hurt, PLists)),
            [[Player], []];
        PLists =:= [] ->
            [{_TId, _TVal, TPL}|_Team] = lists:reverse(lists:keysort(2, TLists)),
            [[], lists:reverse(lists:keysort(#mon_atter.hurt, TPL))];
        true ->
            [{_TId, TVal, TPL}|_Team] = lists:reverse(lists:keysort(2, TLists)),
            [Player|_] = lists:reverse(lists:keysort(#mon_atter.hurt, PLists)),
            ?IF(TVal > Player#mon_atter.hurt,
                [[], lists:reverse(lists:keysort(#mon_atter.hurt, TPL))], [[Player], []])
    end.

%% ---------------------------------------------------------------------------
%% @doc 掉落日志
-spec log_drop(DropInfo) -> ok when
    DropInfo    :: #ets_drop{}.
%% ---------------------------------------------------------------------------
log_drop(DropInfo) ->
    #ets_drop{scene = Scene, mon_id = MonId} = DropInfo,
    case data_mon:get(MonId) of
        #mon{boss = BossType} -> ok;
        _ -> BossType = 0
    end,
    %% 如果是boss掉落的话，写日志
    case (local_boss_api:is_reward_boss_scene(Scene) andalso BossType == ?MON_REWARD_BOSS)
        orelse (local_boss_api:is_deserted_boss_scene(Scene) andalso (BossType == ?MON_DESERTED_BOSS)
            orelse BossType == ?MON_DESERTED_ELITE) of
        true ->
            do_log_drop(DropInfo, BossType);
        false ->
            ok
    end,
    ok.

do_log_drop(DropInfo, BossType) ->
    #ets_drop{
        mon_id = MonId, goods_id = GoodsId, num = GoodsNum
        ,bind = Bind, expire_time = ExpireTime, hurt_id_list = HurtInfoList
        ,player_id = RoleId, team_id = TeamId
    } = DropInfo,
    HurtListStr = util:term_to_string(HurtInfoList),
    lib_log_api:log_boss_drop([BossType, MonId, RoleId, TeamId, GoodsId, GoodsNum, Bind, ExpireTime, HurtListStr, utime:unixtime()]),
    ok.