%% ---------------------------------------------------------
%% Author:  xyj
%% Email:   156702030@qq.com
%% Created: 2012-7-4
%% Description: 物品掉落(本服和跨服)
%% --------------------------------------------------------
-module(mod_drop).
-export([start_link/0, add_drop/1, delete_drop/1, get_drop/1, clean_drop/0, get_drop_id/0]).
-export([get_drops_info/5, pick_all/6, drop_pickup/3, clean_dun_drop/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-include("drop.hrl").
-include("common.hrl").
-include("def_fun.hrl").

%% 插入掉落物品
add_drop(DropInfo) ->
    %% 掉落日志(只有boss的才写日志)
    try
        lib_drop:log_drop(DropInfo)
    catch
        ?EXCEPTION(_Class, _Reason, _Stack) ->
            Error = {_Class, _Reason, ?GET_STACK(_Stack)},
            ?ERR("error:~p~n", [Error])
    end,
    gen_server:call(?MODULE, {add, DropInfo}).

%% 删除掉落物品
delete_drop(DropId) ->
    gen_server:call(?MODULE, {delete, DropId}).

%% 获得掉落物品
get_drop(DropId) ->
    gen_server:call(?MODULE, {get, DropId}).

%% 清理掉落物品
clean_drop() ->
    gen_server:cast(?MODULE, {clean}).

%% 取掉落物品ID
get_drop_id() ->
    gen_server:call(?MODULE, {drop_id}).

%% 拾取所有的掉落
pick_all(Node, RoleId, SceneId, ScenePoolId, Copy, MFA) ->
    gen_server:cast(?MODULE, {pick_all, Node, RoleId, SceneId, ScenePoolId, Copy, MFA}).

%% 客户端登录请求掉落包信息(跨服和本服)
get_drops_info(Node, RoleId, Scene, PoolId, CopyId)->
    gen_server:cast(?MODULE, {get_drops_info, Node, RoleId, Scene, PoolId, CopyId}).

%% 拾取掉落
drop_pickup(Node, DropId, PsArgs)->
    gen_server:cast(?MODULE, {drop_pickup, Node, DropId, PsArgs}).

%% 副本退出,删除掉落
clean_dun_drop(DropList) ->
    gen_server:cast(?MODULE, {clean_dun_drop, DropList}).

%% -----------------------------------------------------------------------

start_link() ->
    gen_server:start_link({local,?MODULE}, ?MODULE, [], []).

init([]) ->
    AutoId = case config:get_cls_type() of
                 ?NODE_CENTER -> 2;
                 _ -> 1
             end,
    D = dict:new(),
    {ok, {D, AutoId}}.

handle_call(Req, From, State) ->
    case catch do_handle_call(Req, From, State) of
        {'EXIT', _R} ->
            ?ERR("Req Error:~p~n", [[Req, _R]]),
            {reply, ok, State};
        Res ->
            Res
    end.

handle_cast(Msg, State) ->
    case catch do_handle_cast(Msg, State) of
        {'EXIT', _R} ->
            ?ERR("Msg Error:~p~n", [[Msg, _R]]),
            {noreply, State};
        Res ->
            Res
    end.

handle_info(Info, State) ->
    case catch do_handle_info(Info, State) of
        {'EXIT', _R} ->
            ?ERR("Info Error:~p~n", [[Info, _R]]),
            {noreply, State};
        Res ->
            Res
    end.

%% ================================= private fun =================================
do_handle_call({drop_id}, _FROM, Status) ->
    {Status1, N} = Status,
    N1 = N + 2,
    {reply, N1, {Status1, N1}};

do_handle_call({add, DropInfo}, _FROM, Status) ->
    {Status1, N} = Status,
    case is_record(DropInfo, ets_drop) of
        true ->
            Key = DropInfo#ets_drop.id,
            D = dict:store(Key, DropInfo, Status1),
            NewD = D;
        false ->
            NewD = Status1
    end,
    NewStatus = {NewD, N},
    {reply, ok, NewStatus};

do_handle_call({delete, DropId}, _FROM, Status) ->
    {Status1, N} = Status,
    case dict:is_key(DropId, Status1) of
        true ->
            NewD = dict:erase(DropId, Status1);
        false ->
            NewD = Status1
    end,
    NewStatus = {NewD, N},
    {reply, ok, NewStatus};

do_handle_call({get, DropId}, _FROM, Status) ->
    {Status1, _N} = Status,
    case dict:is_key(DropId, Status1) of
        true ->
            {ok, DropInfo} = dict:find(DropId, Status1);
        false ->
            DropInfo = {}
    end,
    {reply, DropInfo, Status};

do_handle_call(_Req, _From, State) ->
    {reply, ok, State}.

%%
do_handle_cast({clean}, Status) ->
    {Status1, N} = Status,
    NowTime = utime:unixtime(),
    List = dict:to_list(Status1),
    List2 = filter_list(List, [], NowTime),
    NewD = delete_expire_drop(List2,  Status1),
    NewStatus = {NewD, N},
    {noreply, NewStatus};

do_handle_cast({pick_all, Node, RoleId, SceneId, ScenePoolId, CopyId, Handler}, Status) ->
    {Dict, N} = Status,
    F = fun(_, DropInfo) ->
                case DropInfo of %% 删除自己的
                    #ets_drop{player_id = RoleId, scene = SceneId,id = DropId,
                              scene_pool_id = ScenePoolId, copy_id = CopyId} ->
                        %% 广播掉落消失
                        {ok, BinData} = pt_120:write(12019, DropId),
                        lib_server_send:send_to_scene(SceneId, ScenePoolId, CopyId, BinData),
                        false;
                    _ ->
                        true
                end
        end,
    Dict2 = dict:filter(F, Dict),

    NowTime = utime:unixtime(),
    F1 = fun(_, DropInfo, Acc) ->
                 case DropInfo of %% 筛选出自己的
                     #ets_drop{player_id = RoleId, scene = SceneId, copy_id = CopyId,
                               scene_pool_id = ScenePoolId, expire_time = ExpireTime}
                       when NowTime =< ExpireTime -> [DropInfo|Acc];
                     _ -> Acc
                 end
         end,
    {ok, Bin} = pt_150:write(15054, [1]),
    send_drop_msg_to_role(Node, RoleId, Bin),
    case dict:fold(F1, [], Dict) of
        [] -> ok;
        MyDrops ->
            case Handler of
                {M, Func, A} ->
                    spawn(fun() -> apply(M, Func, [Node, MyDrops, RoleId|A]) end);
                _ ->
                    lib_drop:leave_scene_auto_pick_up(MyDrops, Node, RoleId, SceneId)
            end
    end,
    {noreply, {Dict2, N}};

%% 获取场景掉落
do_handle_cast({get_drops_info, Node, RoleId, Scene, PoolId, CopyId}, {Dict, _N}=State) ->
    NowTime = utime:unixtime(),
    Fun =
        fun
            (_, _DropInfo, {Acc, 0}) -> {Acc, 0};
            (_, DropInfo, {Acc, Num}) ->
                Visible =
                    if
                        %% 过期了，不可见
                        NowTime > DropInfo#ets_drop.expire_time ->
                            false;
                        %% 不是同一个场景
                        Scene =/= DropInfo#ets_drop.scene
                            orelse PoolId =/= DropInfo#ets_drop.scene_pool_id
                            orelse CopyId =/= DropInfo#ets_drop.copy_id ->
                            false;
                        %% 独享BOSS，只有归属者可见
                        DropInfo#ets_drop.origin =:= ?DROP_ORIGIN_REWARD_BOSS_UNSHARED
                            orelse DropInfo#ets_drop.origin =:= ?DROP_ORIGIN_DESERTED_BOSS_UNSHARED ->
                             DropInfo#ets_drop.player_id =:= RoleId;
                        %% 均等分配，只能看自己的
                        DropInfo#ets_drop.alloc =:= ?ALLOC_EQUAL ->
                            DropInfo#ets_drop.player_id =:= RoleId;
                        %% 其他情形，都可以见
                        true ->
                            true
                    end,
                case Visible of
                    true  ->
                        #ets_drop{id = DId, drop_thing_type = DTType, goods_id = GId,
                            num = GoodsNum, x = X, y = Y, drop_leff = DropLeff, drop_icon = DropIcon,
                            player_id=PlayerId, expire_time = ExpireTime} = DropInfo,
                        BDropLeff = pt:write_string(DropLeff),
                        BDropIcon = pt:write_string(DropIcon),
                        Bin = <<DId:32, DTType:8, GId:32, GoodsNum:32, PlayerId:64, X:16, Y:16, BDropLeff/binary, BDropIcon/binary, ExpireTime:32>>,
                        {[Bin|Acc], Num - 1};
                    false ->
                        {Acc, Num}
                end
          end,
    {MyDrops, _} = dict:fold(Fun, {[], 30}, Dict),
    {ok, Bin} = pt_120:write(12018, [MyDrops]),
    send_drop_msg_to_role(Node, RoleId, Bin),
    {noreply, State};

%% 掉落拾取：本服和跨服
do_handle_cast({drop_pickup, Node, DropId, PsArgs}, State) ->
    {Dict, N} = State,
    case lib_drop:check_drop_pickup(Dict, DropId, PsArgs) of
        {fail, ErrorCode} ->
            {ok, Bin} = pt_150:write(15053, [ErrorCode, DropId]),
            send_drop_msg_to_role(Node, PsArgs#ps_args.role_id, Bin),
            {noreply, State};
        {ok, #ets_drop{mon_id = MonId, notice = Notice, goods_id = GoodsId, num = GoodsNum, origin = Origin} = DropInfo} ->
            case lib_drop:check_drop_pickup_info(DropInfo, PsArgs) of
                {fail, ErrorCode} ->
                    {ok, Bin} = pt_150:write(15053, [ErrorCode, DropId]),
                    send_drop_msg_to_role(Node, PsArgs#ps_args.role_id, Bin),
                    {noreply, State};
                {ok, DropReward} ->
                    #ps_args{role_id = RoleId, scene = Scene, pool_id = PoolId, copy_id = CopyId, name = _Name} = PsArgs,
                    DropArgs = [DropId, Scene, MonId, Notice, GoodsId, Origin, GoodsNum],
                    if
                        Node == none -> %% 本服发送
                            lib_drop:send_drop_reward(RoleId, DropArgs, DropReward, mon_drop);
                        true ->  %% 跨服发送
                            clusters_center:apply_cast(Node, lib_drop, send_drop_reward, [RoleId, DropArgs, DropReward, clusters_mon_drop])
                    end,
                    %% 发送给玩家自己
                    {ok, Bin} = pt_150:write(15053, [1, DropId]),
                    send_drop_msg_to_role(Node, PsArgs#ps_args.role_id, Bin),
                    %% 广播掉落消失
                    {ok, BinData} = pt_120:write(12019, DropId),
                    lib_server_send:send_to_scene(Scene, PoolId, CopyId, BinData),
                    NewDict = dict:erase(DropId, Dict),
                    {noreply, {NewDict, N}}
            end
    end;

do_handle_cast({clean_dun_drop, DropList}, Status) ->
    {Status1, N} = Status,
    NewD = clean_dun_drop_do(Status1, DropList),
    NewStatus = {NewD, N},
    {noreply, NewStatus};

do_handle_cast(_Msg, State) ->
    {noreply, State}.

do_handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, Status) ->
    {ok, Status}.

code_change(_OldVsn, Status, _Extra)->
    {ok, Status}.

%% ---------------------------------------------------------------------
%% 过滤列表
filter_list([], L, _) ->
    L;
filter_list([{_Key, DropInfo} | H], L, NowTime) ->
    case is_record(DropInfo, ets_drop) of
        true ->
            if NowTime > DropInfo#ets_drop.expire_time ->
                    filter_list(H, [DropInfo|L], NowTime);
               true ->
                    filter_list(H, L, NowTime)
            end;
        false ->
            filter_list(H, L, NowTime)
    end.

%% 删除过期物品
delete_expire_drop([], Status) ->
    Status;
delete_expire_drop([DropInfo | H], Status) ->
    case dict:is_key(DropInfo#ets_drop.id, Status) of
        true ->
            Status2 = dict:erase(DropInfo#ets_drop.id, Status);
        false ->
            Status2 = Status
    end,
    delete_expire_drop(H, Status2).

%% 发送掉落信息给玩家
send_drop_msg_to_role(Node, RoleId, Bin) ->
    if
        Node == none ->
            lib_server_send:send_to_uid(RoleId, Bin);
        true ->
            clusters_api:send_to_uid(Node, RoleId, Bin)
    end.


clean_dun_drop_do(Status, []) -> Status;
clean_dun_drop_do(Status, [DropId|Rest]) ->
    Status1 = case dict:is_key(DropId, Status) of
        true ->
            dict:erase(DropId, Status);
        false ->
            Status
    end,
    clean_dun_drop_do(Status1, Rest).
