%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     物品增加内部处理逻辑，常规调用使用lib_goods_api里的接口
%%% @end
%%%-------------------------------------------------------------------
-module(goods_add).
-author("liushl").
-include("common.hrl").
-include("def_goods.hrl").
-include("sql_goods.hrl").
-include("goods.hrl").
-include("server.hrl").
-include("scene.hrl").
-include("predefine.hrl").
-include("figure.hrl").
-include("guild.hrl").
-include("errcode.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("def_module.hrl").
-include("def_fun.hrl").
-include("goods_extra.hrl").
-include("def_id_create.hrl").
-include("vip.hrl").
-include ("contract.hrl").
%% API
-export([]).
-compile(export_all).

send_reward_helper(PS, [], [], _Produce) ->
    PS;
send_reward_helper(PS, Gains,GoodsList,Produce)->
    Tvs = lib_gift_new:merge_helper(Gains,[],[]),
    NewPS = do_send_reward_helper(PS,Gains,GoodsList,Produce),
    lib_goods_api:send_tv_tip(PS#player_status.id,Tvs),
    NewPS.
%% 相同的物品合并后 物品格式为以下三种格式
%% RewardList = [{{?TYPE_GOODS, GoodsTypeId}, GoodsNum }, ...]
%% RewardList = [{{?TYPE_BIND_GOODS, GoodsTypeId}, GoodsNum }, ...]
%% RewardList = [{{?TYPE_ATTR_GOODS, GoodsTypeId}, GoodsNum, AttrList}, ...]
do_send_reward_helper(PS, [], [], _Produce)->
    PS;
do_send_reward_helper(PS, [], GoodsList, Produce) ->
    F = fun
            ({ {?TYPE_GOODS, GoodsTypeId}, GoodsNum}) ->
                {goods, GoodsTypeId, GoodsNum};
            ({ {?TYPE_BIND_GOODS, GoodsTypeId}, GoodsNum}) ->
                {goods, GoodsTypeId, GoodsNum, ?BIND};
            ({ {?TYPE_ATTR_GOODS, GoodsTypeId}, GoodsNum, Args}) ->
                {goods_attr, GoodsTypeId, GoodsNum, Args}
        end,
    GoodsList2 = lists:map(F, GoodsList),

    case give_goods_by_pile(PS, GoodsList2, Produce) of
        1 ->
            FF = fun
                     ({_, GoodsTypeId, GoodsNum}) -> {GoodsTypeId, GoodsNum};
                     ({_, GoodsTypeId, GoodsNum, _Args}) -> {GoodsTypeId, GoodsNum}
                 end,
            FFGoodsList = lists:map(FF, GoodsList2),
            %% 触发任务收集
            lib_task_api:collect_goods(PS, FFGoodsList),
            PS1 = lib_role_galaxy:collect_goods(PS,FFGoodsList),
            PS2 = warsoul_logic:collect_goods(PS1,FFGoodsList),
            lib_vie_rank:update_role_vie(FFGoodsList,PS2),
            lib_guild_collect:collect_goods(FFGoodsList, PS2, Produce#produce.type, Produce#produce.subtype),
            PS2;
        {Res, ErrorGoodsList} ->
            ProduceType = Produce#produce.type,
            if
                %% 怪物掉落的道具由于背包满了发不进去奖励屏蔽掉输出
                (ProduceType == mon_drop orelse ProduceType == clusters_mon_drop
                    orelse ProduceType == circle_foster_return
                ) andalso Res == 3 ->
                    skip;
                Res == 3 ->
                    F1 = fun(X, Acc) ->
                        case X of
                            {{?TYPE_GOODS, GId}, GNum} -> [{?TYPE_GOODS, GId, GNum}|Acc];
                            {{?TYPE_BIND_GOODS, GId}, GNum} -> [{?TYPE_BIND_GOODS, GId, GNum}|Acc];
                            {{?TYPE_ATTR_GOODS, GId}, GNum, Args} -> [{?TYPE_ATTR_GOODS, GId, GNum, Args}|Acc];
                            {goods, GId, GNum} -> [{?TYPE_GOODS, GId, GNum}|Acc];
                            {goods, GId, GNum, ?BIND} -> [{?TYPE_BIND_GOODS, GId, GNum}|Acc];
                            {goods, GId, GNum, ?UNBIND} -> [{?TYPE_ATTR_GOODS, GId, GNum, [{bind, ?UNBIND}]}|Acc];
                            {goods_attr, GId, GNum, Args} -> [{?TYPE_ATTR_GOODS, GId, GNum, Args}|Acc];
                            _ -> Acc
                        end
                         end,
                    MGoodsList = lists:foldl(F1, [], ErrorGoodsList),
                    lib_chat:send_sys_error_code(PS#player_status.sid, ?ERRCODE(bag_not_enough), ""),
                    lib_goods_api:do_send_mail(PS#player_status.id, #produce{reward = MGoodsList}, 0, online);
                true ->
                    ?ERR("Res = ~p, produce_type = ~p, role_id = ~p,~nerror_goods_list = ~p",
                        [Res, ProduceType, PS#player_status.id, ErrorGoodsList])
            end,
            PS
    end;
do_send_reward_helper(PS, [H|T], GoodsList, Produce) ->
    #produce{type = ProduceType, remark = Remark} = Produce,
    case H of
        {Type = ?TYPE_GOODS, GoodsTypeId, GoodsNum} ->
            NewGoodsList = case lists:keyfind({Type, GoodsTypeId}, 1, GoodsList) of
                               {_, Num} -> [{ {Type, GoodsTypeId}, GoodsNum+Num}| lists:keydelete({Type, GoodsTypeId}, 1, GoodsList)];
                               false    -> [{ {Type, GoodsTypeId}, GoodsNum} | GoodsList]
                           end,
            do_send_reward_helper(PS, T, NewGoodsList, Produce);
        {Type = ?TYPE_BIND_GOODS, GoodsTypeId, GoodsNum} ->
            NewGoodsList = case lists:keyfind({Type, GoodsTypeId}, 1, GoodsList) of
                               {_, Num} -> [{ {Type, GoodsTypeId}, GoodsNum+Num}| lists:keydelete({Type, GoodsTypeId}, 1, GoodsList)];
                               false    -> [{ {Type, GoodsTypeId}, GoodsNum} | GoodsList]
                           end,
            do_send_reward_helper(PS, T, NewGoodsList, Produce);
        {Type = ?TYPE_ATTR_GOODS, GoodsTypeId, GoodsNum, Args} ->
            NewGoodsList = [{ {Type, GoodsTypeId}, GoodsNum, Args} | GoodsList],
            do_send_reward_helper(PS, T, NewGoodsList, Produce);
        Currency -> %% 增加虚拟货币
            NewPS = send_reward_currency(PS, Currency, ProduceType, Remark),
            do_send_reward_helper(NewPS, T, GoodsList, Produce)
    end.

send_reward_currency(PS, H, ProduceType, Remark) ->
    case H of
        {?TYPE_COIN, _, Coin} ->
            NewPS = add_money(PS, Coin, ?TYPE_COIN, ProduceType, Remark);

        {?TYPE_GOLD, _, Gold} ->
            NewPS = add_money(PS, Gold, ?TYPE_GOLD, ProduceType, Remark);

        {?TYPE_BGOLD, _, BGold} ->
            NewPS = add_money(PS, BGold, ?TYPE_BGOLD, ProduceType, Remark);

        {?TYPE_FGOLD, _, FGold} ->
            NewPS = add_money(PS, FGold, ?TYPE_FGOLD, ProduceType, Remark);

        {?TYPE_GFAME, _, GFame} ->
            NewPS = add_money(PS, GFame, ?TYPE_GFAME, ProduceType, Remark);

        {?TYPE_EXP, _, Exp} ->
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = lib_player:add_exp(PS, Exp, ProduceType, []);

        {?TYPE_LEGION, _, Add} ->
            NewPS = lib_role_legion:add_legion_arms(PS,Add);
        % 增加名誉
        {?TYPE_FAME, _, Num} ->
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = lib_flower_api:add_fame(PS, Num);

        % 增加魅力值
        {?TYPE_CHARM, _, Num} ->
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = lib_flower_api:add_charm(PS, Num);

        {?TYPE_GFUNDS, _, Gfunds} ->
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = PS,
            #player_status{id = RoleId} = PS,
            mod_guild:add_gfunds(RoleId, PS#player_status.guild#status_guild.id, Gfunds, ProduceType);

		%% 名望礼包不限制使用
	    {?TYPE_HONOUR, _, Num} when ProduceType == honour_not_limit ->
		    NewPS = add_money(PS, Num, ?TYPE_HONOUR, honour_not_limit, ""),
		    lib_log_api:log_produce_honour(ProduceType, PS, NewPS, Remark);

        {?TYPE_HONOUR, _, Num} ->
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = add_money(PS, Num, ?TYPE_HONOUR),
            lib_log_api:log_produce_honour(ProduceType, PS, NewPS, Remark);

        {?TYPE_GUILD_GROWTH, _, Val} -> %%20200312转为加个人活跃值和社团经验值
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = lib_guild_labor:goods_add(Val,PS);
        {?TYPE_MATE, _, Val} ->
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = add_currency(PS, ?GOODS_ID_MATE, Val),
            lib_log_api:log_produce(ProduceType, ?TYPE_MATE, PS, NewPS, {?GOODS_ID_MATE, Remark});

        {?TYPE_CHILD_TOY, _, Val} ->
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = add_currency(PS, ?GOODS_ID_CHILD_TOY, Val),
            lib_log_api:log_produce(ProduceType, ?TYPE_CURRENCY, PS, NewPS, {?GOODS_ID_CHILD_TOY, Remark});

        {?TYPE_WORKROOM, _, Val} ->
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = add_currency(PS, ?GOODS_ID_FASHION_COIN, Val),
            lib_log_api:log_produce(ProduceType, ?TYPE_CURRENCY, PS, NewPS, {?GOODS_ID_FASHION_COIN, Remark});

        {?TYPE_DESIGN, GoodsId, _Val} ->
            lib_designation_api:active_dsgt_common(PS#player_status.id, GoodsId),
            NewPS = PS;

        {?TYPE_DESIGN, GoodsId} ->
            lib_designation_api:active_dsgt_common(PS#player_status.id, GoodsId),
            NewPS = PS;

        {?TYPE_FASHION, GoodsId, _Val} ->
            lib_fashion:active_fashion_by_goods(PS#player_status.id, GoodsId),
            NewPS = PS;

        {?TYPE_FASHION, GoodsId} ->
            lib_fashion:active_fashion_by_goods(PS#player_status.id, GoodsId),
            NewPS = PS;

        {?TYPE_VIP_EXP, _, Val} ->
            NewPS = lib_vip_api:add_vip_exp(PS, Val),
            lib_log_api:log_produce(ProduceType, ?TYPE_VIP_EXP, PS, NewPS, {?TYPE_VIP_EXP, Val});

        {?TYPE_CONTRACT_SCORE, GoodsId = ?CONTRACT_GOODS_1, Val} ->
            custom_act_contract:add_score_by_goods(PS#player_status.id, GoodsId, Val),
            NewPS = PS;

        {?TYPE_CONTRACT_SCORE, GoodsId = ?PHANTOM_GOODS_1, Val} ->
            custom_act_phantom:add_score_by_goods(PS#player_status.id, GoodsId, Val),
            NewPS = PS;

        {?TYPE_HI_POINT, _, Val} ->
            mod_hi_point:use_goods_add(PS#player_status.id, Val),
            NewPS = PS;

        {?TYPE_ADVENTURE_POINT, _, Val} ->
            custom_act_adventure:use_goods_add(PS#player_status.id, Val),
            NewPS = PS;

        {?TYPE_CURRENCY, CurrencyId, Val} ->
            % lib_goods_api:send_tv_tip(PS#player_status.id, [H]),
            NewPS = add_currency(PS, CurrencyId, Val),
            lib_log_api:log_produce(ProduceType, ?TYPE_CURRENCY, PS, NewPS, {CurrencyId, Remark})
    end,
    NewPS.
%% 分离堆奖励后,发送物品
%% 处理奖励列表中出现同类型的的物品（比如存在绑定和非绑定的相同物品类型）
%% @return 1 | {Res, 没有发送成功的物品列表}
give_goods_by_pile(PlayerInfo, GoodsList, Produce) when GoodsList=/=[] ->
    PileList = spilt_reward_pile(GoodsList),
    do_give_goods_by_pile(PileList, PlayerInfo, Produce);
give_goods_by_pile(_PlayerInfo, GoodsList, _Produce) ->
    ?ERR("~p:~p param error:~p", [?MODULE, ?LINE, GoodsList]),
    0.

do_give_goods_by_pile([], _PlayerInfo, _Produce) -> 1;
do_give_goods_by_pile([GoodsList|PileList] = L, PlayerInfo, Produce) ->
    case give_goods_by_list(PlayerInfo, GoodsList, Produce) of
        {1, _GoodsInfoList} -> do_give_goods_by_pile(PileList, PlayerInfo, Produce);
        Res -> {Res, lists:flatten(L)}
    end.

%% ---------------------------------------------------------------------------
%% 发送物品(列表中GoodsTypeId不能重复)
%%      根据物品类型Id列表赠送物品并刷新客户端以及做物品日志
-spec give_goods_by_list(Player, GoodsList, Type, SubType) -> Res when
    Player      :: #player_status{},        %% 玩家记录
    GoodsList   :: [tuple()],               %% 物品列表 具体格式参照上面
    Type        :: atom(),                  %% 物品产出类型
    %% 需要后台定义"产出消耗类型--产出类型"
    SubType     :: integer(),               %% 物品产出子类型
    Res         :: integer().               %% 结果码： 1 成功， 2物品类型不存在, 3 背包数量不足, 4动态属性设置权限不足, 5列表中GoodsTypeId重复
%% ---------------------------------------------------------------------------
%% (列表中GoodsTypeId不能重复)
%% !! 请确保列表里面只有物品，不能有其它格式的  !!
%% GoodsList = [{goods, GoodsTypeId, GoodsNum}, ...]
%% GoodsList = [{goods, GoodsTypeId, GoodsNum, BindBind(0非绑1绑定)}, ...]
%% GoodsList = [{goods_attr, GoodsTypeId, GoodsNum, AttrList}, ...]
%% return ErrorCode|{1, GoodsInfoList}
give_goods_by_list(PlayerInfo, GoodsList, Type, SubType) ->
    Produce = #produce{type = Type, subtype = SubType, remark = ""},
    give_goods_by_list(PlayerInfo, GoodsList, Produce).

give_goods_by_list(PlayerInfo, GoodsList) ->
    give_goods_by_list(PlayerInfo, GoodsList, "", 0).

give_goods_by_list(PlayerInfo, GoodsList, Produce) when GoodsList=/=[] ->
    #produce{type = Type, subtype = SubType, remark = Remark} = Produce,
    Res = give_more(PlayerInfo, GoodsList, Type),
    case Res of
        {ErrorCode, GoodsInfoList} when ErrorCode == 1 ->
            NowTime = utime:unixtime(),
            ProduceTypeId = data_goods:get_produce_type(Type),
            case Type =/= undefined of
                true ->
                    F = fun(OneAdd, AccList) ->
                        {GoodsTypeId, GoodsNum} =
                            case OneAdd of
                                {goods, TypeId, Num} -> {TypeId, Num};
                                {?TYPE_GOODS, TypeId, Num} -> {TypeId, Num};
                                {?TYPE_BIND_GOODS, TypeId, Num} -> {TypeId, Num};
                                {goods, TypeId, Num, _Bind} -> {TypeId, Num};
                                {goods_attr, TypeId, Num, _DyAttrList} -> {TypeId, Num};
                                {TypeId, Num} -> {TypeId, Num}
                            end,
                        [[NowTime, ProduceTypeId, SubType, GoodsTypeId, GoodsNum, PlayerInfo#player_status.id, Remark]|AccList]
                        end,
                    LogList = lists:foldl(F, [], GoodsList),
                    updata_dict_add(Produce,GoodsInfoList),
                    lib_log_api:log_goods(LogList);
                false -> skip
            end;
        _ ->
            skip
    end,
    Res;
give_goods_by_list(_PlayerInfo, GoodsList, _Produce) ->
    ?ERR("~p:~p param error:~p", [?MODULE, ?LINE, GoodsList]),
    0.

%% 分离堆奖励,保证GoodsTypeId不唯一
%% GoodsList = [{goods, GoodsTypeId, GoodsNum }, ...]
%% GoodsList = [{goods, GoodsTypeId, GoodsNum, BindBind(0非绑1绑定)}, ...]
%% GoodsList = [{goods_attr, GoodsTypeId, GoodsNum, AttrList}, ...]
spilt_reward_pile(GoodsList) ->
    spilt_reward_pile(GoodsList, []).
%% 分离奖励组 将物品id相同的奖励 分散到不同的奖励堆
%% @return [组1列表,组2列表,...] 每一个组别内的GoodsTypeId都不一样
spilt_reward_pile([], L) -> L;
spilt_reward_pile([H|T], L) ->
    NewL = spilt_reward_pile_help(L, H, []),
    spilt_reward_pile(T, NewL).
spilt_reward_pile_help([], Key, L) ->
    lists:reverse([[Key]]++L);
spilt_reward_pile_help([GoodsList|T], Key, L) ->
    case is_same(Key, GoodsList) of
        true -> spilt_reward_pile_help(T, Key, [GoodsList|L]);
        false ->
            NewGoodsList = lists:reverse([Key|GoodsList]),
            lists:reverse(L) ++ [NewGoodsList] ++ T
    end.
%% 是否存在相同Key值
is_same(Key, GoodsList) ->
    case Key of
        {goods, GoodsTypeId, _GoodsNum} -> ok;
        {goods, GoodsTypeId, _GoodsNum, _Bind} -> ok;
        {goods_attr, GoodsTypeId, _GoodsNum, _AttrList} -> ok
    end,
    is_same_help(GoodsList, GoodsTypeId).
is_same_help([], _GoodsTypeId) -> false;
is_same_help([{goods, GoodsTypeId, _GoodsNum}|_T], GoodsTypeId) -> true;
is_same_help([{goods, GoodsTypeId, _GoodsNum, _Bind}|_T], GoodsTypeId) -> true;
is_same_help([{goods_attr, GoodsTypeId, _GoodsNum, _AttrList}|_T], GoodsTypeId) -> true;
is_same_help([_|T], GoodsTypeId) -> is_same_help(T, GoodsTypeId).

%% ---------------------------------------------------------------------------
%% 开始发物品，GoodsList 是整理好了的，不存在重复的物品，并且只有以下三种格式
%% GoodsList = [{goods, GoodsTypeId, GoodsNum }, ...]
%% GoodsList = [{goods, GoodsTypeId, GoodsNum, BindBind(0非绑1绑定)}, ...]
%% GoodsList = [{goods_attr, GoodsTypeId, GoodsNum, AttrList}, ...]
give_more(_Player, GoodsList, Type) ->
    GoodsStatus = lib_goods_do:get_goods_status(),
    case give_more_do(GoodsStatus, GoodsList) of
        {ok, NewStatus, UpdateGoodsList} ->
            lib_goods_do:set_goods_status(NewStatus),
            lib_goods_api:notify_client(NewStatus#goods_status.player_id, UpdateGoodsList, Type),
            % lib_goods_api:send_tv_tip(Player#player_status.id, 2, GoodsList),
            {1, UpdateGoodsList};
        %% 物品类型不存在
        {db_error, {error,{_, not_found}}} ->
            2;
        %% 背包格子不足
        {db_error, {error,{cell_num, not_enough}}} ->
            3;
        %% 动态属性设置权限不足
        {db_error, {error,{_Type, cannot_overlap}}} ->
            4;
        %% 背包类型配置错误
        {db_error, {error,{GTypeId, bag_location_fail}}} ->
            ?ERR("bag_location_fail error GTypeId:~p", [GTypeId]),
            ?ERRCODE(err150_bag_location_fail);
        Error ->
            ?ERR("give_more error:~p", [Error]),
            0
    end.
give_more_do(GoodsStatus, GoodsList) ->
    case lib_goods_check:check_give_list(GoodsList) of
        {ok} ->
            F = fun() ->
                ok = lib_goods_dict:start_dict(),
                {ok, NewStatus} = lib_goods_check:list_handle(fun goods_add:give_goods/2, GoodsStatus, GoodsList),
                {Dict, GoodsL} =
                    lib_goods_dict:handle_dict_and_notify(NewStatus#goods_status.dict),
                NewStatus2 = NewStatus#goods_status{dict = Dict, temp_bag = #{}},
                {ok, NewStatus2, GoodsL}
                end,
            goods_transaction:transaction(F);
        {false, duplicate} -> %% 列表中GoodsTypeId重复
            {false, duplicate}
    end.

give_goods({GoodsTypeId, GoodsNum}, GoodsStatus) ->
    give_goods({goods, GoodsTypeId, GoodsNum}, GoodsStatus);
give_goods({goods, GoodsTypeId, GoodsNum}, GoodsStatus) ->
    case data_goods_type:get(GoodsTypeId) of
        %% 物品不存在
        [] ->
            throw({error, {GoodsTypeId, not_found}});
        #ets_goods_type{} = GoodsTypeInfo ->
            give_goods_then(GoodsStatus, GoodsTypeInfo, GoodsNum, [])
    end;
give_goods({goods, GoodsTypeId, GoodsNum, Bind}, GoodsStatus) ->
    case data_goods_type:get(GoodsTypeId) of
        %% 物品不存在
        [] ->
            throw({error, {GoodsTypeId, not_found}});
        #ets_goods_type{} = GoodsTypeInfo ->
            give_goods_then(GoodsStatus, GoodsTypeInfo, GoodsNum, [{bind, Bind}])
    end;
give_goods({goods_attr, GoodsTypeId, GoodsNum, DyAttrList} = GoodsInfo, GoodsStatus) ->
    case data_goods_type:get(GoodsTypeId) of
        %% 物品不存在
        [] ->
            throw({error, {GoodsTypeId, not_found}});
        #ets_goods_type{
            max_overlap = MaxOverlap
        } = GoodsTypeInfo ->
            CanOverlap = lib_goods_util:check_attr_goods_can_overlap(GoodsInfo),
            case MaxOverlap > 1 andalso CanOverlap=/=true of
                true ->
                    throw({error, {GoodsTypeId, cannot_overlap}});
                false ->
                    give_goods_then(GoodsStatus, GoodsTypeInfo, GoodsNum, DyAttrList)
            end
    end;
%% 发物品到指定背包
give_goods({location, BagLocation, GoodsTypeId, GoodsNum}, GoodsStatus) ->
    case data_goods_type:get(GoodsTypeId) of
        [] -> %% 物品不存在
            throw({error, {GoodsTypeId, not_found}});
        #ets_goods_type{} = GoodsTypeInfo ->
            NewGoodsTypeInfo = GoodsTypeInfo#ets_goods_type{bag_location = BagLocation},
            give_goods_then(GoodsStatus, NewGoodsTypeInfo, GoodsNum, [])
    end.

give_goods_then(GoodsStatus, GoodsTypeInfo, GoodsNum, DyAttrList) ->
    #goods_status{dict = GoodDict, temp_bag = TempBag} = GoodsStatus,
    %% 构造新物品
    GoodsInfo   = lib_goods_util:get_new_goods(GoodsTypeInfo, DyAttrList),
    Location    = GoodsInfo#goods.location,
    %% 放入背包是否合法
    IsLocationLegal = lists:member(Location, ?GOODS_LOC_BAG_TYPES),
    %% 获取同类物品列表
    GoodsList   = lib_goods_util:get_same_goods_list(GoodsInfo, GoodDict),
    %% 检查增加同类型的物品还需要多少格子数
    CellNum     = lib_goods_util:get_goods_need_cell_num(GoodsList, GoodsInfo, GoodsTypeInfo#ets_goods_type.max_overlap, GoodsNum),
    %% 批量增加物品需要的格子数
    AlreadyNeed = maps:get(Location, TempBag, 0),
    TotalNeed   = CellNum + AlreadyNeed,
    %% 获取空格子的数量
    HaveCellNum = lib_goods_util:get_null_cell_num(GoodsStatus, Location),
    if
        not IsLocationLegal ->
            throw({error, {GoodsTypeInfo#ets_goods_type.goods_id, bag_location_fail}});
        HaveCellNum < TotalNeed ->
            throw({error, {cell_num, not_enough}});
        true ->
            add_goods_base(GoodsStatus#goods_status{temp_bag = TempBag#{Location => TotalNeed}}, GoodsTypeInfo, GoodsNum, GoodsInfo, GoodsList)
    end.
%%------------------------------------------------------------------------------------------------
%% @doc 增加物品
%% -spec add_goods_base(GoodsStatus, GoodsTypeInfo, GoodsNum, GoodsInfo, GoodsList, DyAttrList) -> {ok, NewGoodsStatus}
%% GoodsStatus | NewGoodsStatus         :: #goods_status{}  物品进程状态
%% GoodsTypeInfo                        :: #ets_goods_type{} 物品类型记录
%% GoodsNum                             :: integer()         物品数量
%% GoodsInfo                            :: #goods{}         物品记录
%% GoodsList                            :: List [#goods{}]  同类型物品列表
%% DyAttrList                           :: List [{K, V}...] 该物品的动态属性:只有不可叠加的物品才能有唯一的动态属性
%% @end
%%-----------------------------------------------------------------------------------------------
add_goods_base(GoodsStatus, GoodsTypeInfo, GoodsNum, GoodsInfo, GoodsList) ->
    add_goods_base(GoodsStatus, GoodsTypeInfo, GoodsNum, GoodsInfo, GoodsList, GoodsTypeInfo#ets_goods_type.bag_location).

add_goods_base(GoodsStatus, GoodsTypeInfo, GoodsNum, GoodsInfo, GoodsList, BagLocation) ->
    %% 插入物品记录
    case GoodsTypeInfo#ets_goods_type.max_overlap > 1 of
        true ->
            %% 更新原有的可叠加物品-还可以添加 GoodsNum2 个
            [GoodsNum2,_, _, NewGoodsStatus] = lists:foldl(fun update_overlap_goods/2, [GoodsNum, GoodsTypeInfo#ets_goods_type.max_overlap, GoodsInfo, GoodsStatus], GoodsList),
            %% 添加新的可叠加物品
            [NewGoodsStatus2,_,_,_] = lib_goods_util:deeploop(fun add_overlap_goods/2, GoodsNum2,
                [NewGoodsStatus, GoodsInfo, BagLocation, GoodsTypeInfo#ets_goods_type.max_overlap]);
        false ->
            NumList = lists:seq(1, GoodsNum),
            [NewGoodsStatus2,_,_] = lists:foldl(fun add_nonlap_goods/2, [GoodsStatus, GoodsInfo, BagLocation], NumList)
    end,
    {ok, NewGoodsStatus2}.
%%------------------------------------------------------------------------------------------------
%% @doc 更新原有的可叠加物品
%% -spec update_overlap_goods(GoodsInfo, [Num, Max, AddGoodsInfo, GoodsStatus]) -> [NewNum, Max, AddGoodsInfo, NewGoodsStatus]
%% GoodsInfo                   :: #goods{}  物品记录
%% Num | NewNum                :: integer() 要增加的物品数量|超过最大叠加数后剩余的数量
%% Max                         :: integer() 最大叠加数
%% GoodsStatus|NewGoodsStatus  :: #goods_status{} 物品状态
%% @end
%%-------------------------------------------------------------------------------------------------
update_overlap_goods(GoodsInfo, [Num, Max, AddGoodsInfo, GoodsStatus]) when Num> 0 ->
    case lib_goods_util:can_overlap_same_cell(GoodsInfo, AddGoodsInfo) of
        true ->
            do_update_overlap_goods(GoodsInfo, [Num, Max, AddGoodsInfo, GoodsStatus]);
        _ ->
            [Num, Max, AddGoodsInfo, GoodsStatus]
    end;
update_overlap_goods(_GoodsInfo, [_Num, Max, AddGoodsInfo, GoodsStatus]) ->
    [0, Max, AddGoodsInfo, GoodsStatus].

do_update_overlap_goods(GoodsInfo, [Num, Max, AddGoodsInfo, GoodsStatus]) ->
    case Num > 0 of
        true when GoodsInfo#goods.num =/= Max andalso Max > 0 ->
            case Num + GoodsInfo#goods.num > Max of
                true -> %%超出最大数量
                    NewNum = Num + GoodsInfo#goods.num - Max,
                    OldNum = Max;
                false ->
                    NewNum = 0,
                    OldNum = Num + GoodsInfo#goods.num
            end,
            [_, NewGoodsStatus] = goods_logic:change_goods_num(GoodsInfo, OldNum, GoodsStatus);
        true ->
            NewGoodsStatus  = GoodsStatus,
            NewNum = Num;
        false ->
            NewGoodsStatus  = GoodsStatus,
            NewNum = 0
    end,
    [NewNum, Max, AddGoodsInfo, NewGoodsStatus].

%%----------------------------------------------------------------------------------------
%% @doc 添加新的可叠加物品,该接口如果背包没有空格，就会不处理
%% -spec add_overlap_goods(Num, [GoodsStatus, GoodsInfo, Location, MaxOverlap]) ->
%%              [NewNum, [NewGoodsStatus, GoodsInfo, Location, MaxOverlap]].
%% Num | NewNum                 :: integer()        要添加的物品数量 | 超过最大叠加数的数量
%% GoodsStatus | NewGoodsStatus :: #goods_status{}  物品进程状态
%% GoodsInfo                    :: #goods{}         物品记录
%% Location                     :: integer()        位置
%% MaxOverlap                   :: integer()        最大叠加数
%% @end
%%--------------------------------------------------------------------------------------------
add_overlap_goods(Num, [GoodsStatus, GoodsInfo, Location, MaxOverlap]) ->
    case Num > MaxOverlap of
        true ->
            NewNum = Num - MaxOverlap,
            OldNum = MaxOverlap;
        false ->
            NewNum = 0,
            OldNum = Num
    end,
    case OldNum > 0 of
        true ->
            NewGoodsStatus1 = GoodsStatus,
            NewGoodsInfo = GoodsInfo#goods{player_id=GoodsStatus#goods_status.player_id, location=Location, num=OldNum},
            [_, NewGoodsStatus] = goods_logic:add_goods(NewGoodsInfo, NewGoodsStatus1);
        _ ->
            NewGoodsStatus = GoodsStatus
    end,
    [NewNum, [NewGoodsStatus, GoodsInfo, Location, MaxOverlap]].

%%----------------------------------------------------------------------------------------
%% @doc 添加新的不可叠加物品
add_nonlap_goods(_, [GoodsStatus, GoodsInfo, Location]) ->
    NewGoodsInfo3 = GoodsInfo#goods{
        player_id = GoodsStatus#goods_status.player_id,
        num = 1
    },
    [_, NewGoodsStatus] = goods_logic:add_goods(NewGoodsInfo3, GoodsStatus),
    [NewGoodsStatus, GoodsInfo, Location].

%%-----------------------------------------------------------------------------
%% @doc 加钱
%% -spec add_money(PlayerStatus, Amount, Type) -> NewPlayerStatus
%% PlayerStatus | NewPlayerStatus   ::  玩家记录
%% Amount                           ::  增加的数量
%% Type                             ::  要加的类型
%%     coin:铜钱 rcoin:铜钱 一般不用 bgold:绑定元宝 gold:元宝 bcoin:绑定铜币，不用
%% @end
%% ---------------------------------------------------------------------------------
add_money(PlayerStatus, Amount, Type) ->
    add_money(PlayerStatus, Amount, Type, none, "").

%% ---------------------------------------------------------------------------
%% @doc 加钱
-spec add_money(PlayerStatus, Amount, MoneyType, Type, About) ->  Return when
    PlayerStatus   :: #player_status{},   %% #player_status{}
    Amount         :: integer(),          %% 数量
    MoneyType      :: non_neg_integer(),  %% 货币类型
    Type           :: atom(),             %% 货币产出类型
    About          :: string(),           %% 产出描述
    Return         :: #player_status{}.   %% #player_status{}
%% ---------------------------------------------------------------------------
add_money(PlayerStatus, Amount, MoneyType, ProduceType, About) ->
    case MoneyType of
        ?TYPE_COIN ->
            Total = PlayerStatus#player_status.coin + Amount,
            NewPlayerStatus1 = PlayerStatus#player_status{coin =Total};
        ?TYPE_BGOLD ->
            lib_daily:plus_count(PlayerStatus#player_status.id, ?MOD_GOODS, ?GOODS_COUNTER_DAILY_BGOLD_GET, Amount),
            case lib_vip_api:check_daily_cur_limit(PlayerStatus#player_status.id, PlayerStatus#player_status.vip_status#vip_status.lv, ?TYPE_BGOLD, Amount) of
                true ->
                    NewPlayerStatus1 = PlayerStatus#player_status{bgold = (PlayerStatus#player_status.bgold + Amount)};
                _ ->
                    NewPlayerStatus1 = PlayerStatus
            end;
        ?TYPE_FGOLD ->
            NewPlayerStatus1 = PlayerStatus#player_status{fgold = (PlayerStatus#player_status.fgold + Amount)};
        ?TYPE_GOLD ->
            case lib_vip_api:check_daily_cur_limit(PlayerStatus#player_status.id, PlayerStatus#player_status.vip_status#vip_status.lv, ?TYPE_GOLD, Amount) of
                true ->
                    NewPlayerStatus1 = PlayerStatus#player_status{gold = (PlayerStatus#player_status.gold + Amount)};
                _ ->
                    NewPlayerStatus1 = PlayerStatus
            end;
        ?TYPE_GFAME ->
            lib_achievement_api:guild_donate_get_event(PlayerStatus, Amount),
            NewPlayerStatus1 = PlayerStatus#player_status{gfame = (PlayerStatus#player_status.gfame + Amount)};
		%% 运营投放类名望礼包([{4, GoodsId, Num}]格式的)不限制使用
		?TYPE_HONOUR when ProduceType == honour_not_limit ->
            lib_daily:plus_count(PlayerStatus#player_status.id, ?MOD_GOODS, ?GOODS_COUNTER_DAILY_HONOUR_GET, Amount),
            lib_achievement_api:honour_get_event(PlayerStatus, Amount),
            lib_activitycalen_api:honour_get_event(PlayerStatus, Amount),
            custom_act_contract:do_trigger_contract_task(PlayerStatus, fame, ?CONTRACT_FIN_TYPE_1, [Amount]),
			NewPlayerStatus1 = PlayerStatus#player_status{honour = (PlayerStatus#player_status.honour + Amount)};
        ?TYPE_HONOUR when ProduceType =:= guild_support->
            %% 公会徽章加成
            Effect = lib_guild_badge:badge_ability_for_repution(PlayerStatus#player_status.guild#status_guild.badges),
            NewAmount = util:ceil(Amount*(1+Effect/100)),
	        case lib_player:check_daily_can_get_honour(PlayerStatus, NewAmount) of
                {true, AddAmount} ->
                    lib_daily:plus_count(PlayerStatus#player_status.id, ?MOD_GOODS, ?GOODS_COUNTER_DAILY_HONOUR_GET, Amount),
                    lib_daily:plus_count(PlayerStatus#player_status.id, ?MOD_GOODS, 2, AddAmount),
                    %% 推送最新日名望券
                    TodayNum = lib_daily:get_count(PlayerStatus#player_status.id, ?MOD_GOODS, 2),
                    {ok, BinData} = pt_150:write(15088, [TodayNum]),
                    lib_server_send:send_to_sid(PlayerStatus#player_status.sid, BinData),
                    lib_achievement_api:honour_get_event(PlayerStatus, AddAmount),
                    lib_activitycalen_api:honour_get_event(PlayerStatus, AddAmount),
                    custom_act_contract:do_trigger_contract_task(PlayerStatus, fame, ?CONTRACT_FIN_TYPE_1, [AddAmount]),
					NewPlayerStatus1 = PlayerStatus#player_status{honour = (PlayerStatus#player_status.honour + AddAmount)};
				_ ->
					NewPlayerStatus1 = PlayerStatus
	        end;
        ?TYPE_HONOUR ->
	        case lib_player:check_daily_can_get_honour(PlayerStatus, Amount) of
                {true, AddAmount} ->
                    lib_daily:plus_count(PlayerStatus#player_status.id, ?MOD_GOODS, ?GOODS_COUNTER_DAILY_HONOUR_GET, Amount),
                    lib_daily:plus_count(PlayerStatus#player_status.id, ?MOD_GOODS, 2, AddAmount),
                    %% 推送最新日名望券
                    TodayNum = lib_daily:get_count(PlayerStatus#player_status.id, ?MOD_GOODS, 2),
                    {ok, BinData} = pt_150:write(15088, [TodayNum]),
                    lib_server_send:send_to_sid(PlayerStatus#player_status.sid, BinData),
                    lib_achievement_api:honour_get_event(PlayerStatus, AddAmount),
                    lib_activitycalen_api:honour_get_event(PlayerStatus, AddAmount),
                    custom_act_contract:do_trigger_contract_task(PlayerStatus, fame, ?CONTRACT_FIN_TYPE_1, [AddAmount]),
                    NewPlayerStatus1 = PlayerStatus#player_status{honour = (PlayerStatus#player_status.honour + AddAmount)};
		        _ ->
			        NewPlayerStatus1 = PlayerStatus
	        end
    end,
    if
        MoneyType == ?TYPE_COIN andalso ProduceType =/= auction_fail ->
            %% 触发交易券成就
            {ok, NewPlayerStatus} = lib_achievement_api:coin_get_event(NewPlayerStatus1, Amount),
            lib_activitycalen_api:coin_get_event(NewPlayerStatus, Amount);
        true ->
            NewPlayerStatus = NewPlayerStatus1
    end,
    lib_player:update_player_money(PlayerStatus, NewPlayerStatus),
    lib_player:send_attribute_change_notify(NewPlayerStatus, ?NOTIFY_MONEY),
    % lib_goods_api:send_tv_tip(PlayerStatus#player_status.id, [{MoneyType, 2, Amount}]),
    if
        ProduceType=/=none -> lib_log_api:log_produce(ProduceType, MoneyType, PlayerStatus, NewPlayerStatus, About);
        true -> skip
    end,
    NewPlayerStatus.

add_currency(PS, CurrencyId, Value) ->
    #player_status{currency_map = CMap, id = PlayerId} = PS,
    case maps:find(CurrencyId, CMap) of
        {ok, Old} ->
            New = Old + Value,
            SQL = io_lib:format(?SQL_UPDATE_CURRENCY, [New, PlayerId, CurrencyId]);
        _ ->
            New = Value,
            SQL = io_lib:format(?SQL_INSERT_CURRENCY, [PlayerId, CurrencyId, New])
    end,
    db:execute(SQL),
    NewPS = PS#player_status{currency_map = CMap#{CurrencyId => New}},
    goods_logic:send_update_currency(NewPS, CurrencyId),
    NewPS.

updata_dict_add(_Produce,[])->
    ok;
updata_dict_add(Produce,[H|T])->
    #produce{type=PType}=Produce,
    case PType of
        nucleon_rock->
            BasrItem = data_nucleon:get(nucleon_equip_lv),
            #goods{id=Uid,type=Type,goods_id=ItemID}=H,
            case Type of
                ?GOODS_TYPE_X3 when ItemID /= BasrItem->
                    Backs = get_dict_goods(),
                    NList = [{ItemID,Uid,1}|Backs],
                    save_dict_goods(NList),
                    updata_dict_add(Produce,T);
                _->
                    updata_dict_add(Produce,T)
            end;
        _->
            updata_dict_add(Produce,T)
    end.

get_dict_goods()->
    case get(goods_create_back) of
        Backs when is_list(Backs)->
            Backs;
        _->
            []
    end.
save_dict_goods(Backs)->
    put(goods_create_back,Backs).
