%% ---------------------------------------------------------
%% Author:  xyj
%% Email:   156702030@qq.com
%% Created: 2011-12-13
%% Description: 物品实用工具类
%% --------------------------------------------------------
-module(lib_goods_util).

-include("common.hrl").
-include("goods.hrl").
-include("def_goods.hrl").
-include("sql_goods.hrl").
-include("server.hrl").
-include("sell.hrl").
-include("sql_player.hrl").
-include("predefine.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("def_fun.hrl").
-include("def_id_create.hrl").
-include("attr.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("def_module.hrl").
-include("skill.hrl").
-include("rec_achievement.hrl").

-export([
    is_random_goods_rewards/1     %% 奖励配置是否随机（物品id随机, 数量随机）
    , calc_random_goods_rewards/1 %% 根据随机配置计算出要发的奖励 可能返回[] 需要上层自己兼容
    , get_random_goods_rewards/1  %% 获取随机配置奖励（先判断是否随机）
]).

-compile(export_all).

%% 扣除消耗列表object_list (物品模块内部使用)
cost_object_list(PS, [], _Type, _About, GoodsStatus) ->
    {true, GoodsStatus, PS};
cost_object_list(PS, [H|T], Type, About, GoodsStatus) ->
    case cost_object(PS, H, Type, About, GoodsStatus) of
        {true, NewStatus, NewPS} ->
            cost_object_list(NewPS, T, Type, About, NewStatus);
        {false, Res} ->
            {false, Res, GoodsStatus, PS}
    end.

cost_object(PS, {?TYPE_GOODS, GoodsId, GoodsNum}, Type, _About, GoodsStatus) ->
    case goods_del:delete_type_list_goods({GoodsId,GoodsNum}, GoodsStatus) of
        {ok, NewStatus} ->
            lib_log_api:log_throw(Type, PS#player_status.id, 0, GoodsId, GoodsNum, 0, 0),
            {true, NewStatus, PS};
        _ -> {false, ?GOODS_NOT_ENOUGH}
    end;
cost_object(PS, {CostType, _GoodsId, GoodsNum}, Type, About, GoodsStatus) ->
    NewPS = goods_del:cost_money(PS, GoodsNum, CostType, Type, About),
    {true, GoodsStatus, NewPS}.

%%------------------------------------------------
%% @doc 获得某个背包的所有物品
%% 以字典形式返回
get_location_goods(GoodsMaps, Location) ->
    case maps:find(Location, GoodsMaps) of
        {ok, Value} -> Value;
        _ -> dict:new()
    end.

%%------------------------------------------------
%% @doc 保存某个背包的所有物品
%% 返回物品maps
put_location_goods(GoodsMaps, Location, GoodsDict) ->
    maps:put(Location, GoodsDict, GoodsMaps).

%%---------------------------------------------------
%% @doc  基于背包物品id读取物品
%%---------------------------------------------------
get_goods(GoodsId, Maps) ->
    get_goods(GoodsId, Maps, ?GOODS_LOC_BAG).
%%get_goods_info(GoodsId, Maps, Location) ->
%%    get_goods(GoodsId, Maps, Location).
get_goods(GoodsId, Maps, Location) ->
    Dict = get_location_goods(Maps, Location),
    case dict:find(GoodsId, Dict) of
        {ok, GoodsInfo} -> GoodsInfo;
        error -> []
    end.

%%-----------------------------------------------------
%% @doc 取对应位置的物品列表
%% -spec get_goods_list(PlayerId, Location, Dict, Lv) -> List
%% when
%% PlayerId     :: integer() 玩家Id
%% Location     :: integer() 位置
%% Dict         :: dict()   物品字典
%% Lv           :: integer() 玩家等级
%% List         :: list()   物品列表[#goods{}]
%% @end
%%-----------------------------------------------------
get_goods_list(_PlayerId, Location, GoodsMaps) ->
    get_goods_list(GoodsMaps, Location).
get_goods_list(_PlayerId, Location, GoodsMaps, _Lv) ->
    get_goods_list(GoodsMaps, Location).
get_goods_list(GoodsMaps, Location) ->
    Dict = get_location_goods(GoodsMaps, Location),
    lib_goods_dict:fetch_values(Dict).
%%-----------------------------------------------------
%% @doc 取对应位置的物品列表
%% -spec get_goods_list_by_type(PlayerId, Pos, Type, Dict, Lv) -> List
%% when
%% PlayerId     :: integer() 玩家Id
%% Pos          :: integer() 位置
%% Type         :: integer() 物品类型
%% Dict         :: dict()   物品字典
%% Lv           :: integer() 玩家等级
%% List         :: list()   物品列表[#goods{}]
%% @end
%%-----------------------------------------------------
get_goods_list_by_type(PlayerId, Location, Type, Maps) ->
    get_goods_list_by_type(PlayerId, Location, Type, Maps, 0).
get_goods_list_by_type(_PlayerId, Location, Type, Maps, _Lv) ->
    Dict = get_location_goods(Maps, Location),
    Dict1 = dict:filter(fun(_Key, Value) -> Value#goods.type == Type end, Dict),
    lib_goods_dict:fetch_values(Dict1).

%%----------------------------------------------------------------------------
%% @doc 获取同类物品列表
%%----------------------------------------------------------------------------
%% 增加的时候判断
get_same_goods_list(GoodsInfo, GoodsMaps) ->
    Dict = get_location_goods(GoodsMaps, GoodsInfo#goods.location),
    Dict1 = dict:filter(fun(_Key, Value) ->
        Value#goods.goods_id =:= GoodsInfo#goods.goods_id
            andalso Value#goods.bind =:= GoodsInfo#goods.bind
            andalso Value#goods.expire_time =:= GoodsInfo#goods.expire_time end, Dict),
    lib_goods_dict:fetch_values(Dict1).
get_type_goods_list(_PlayerId, GoodsTypeId, Bind, Location, GoodsMaps, _Lv) ->
    Dict = get_location_goods(GoodsMaps, Location),
    Dict1 = dict:filter(fun(_Key, Value) ->
        Value#goods.goods_id =:= GoodsTypeId
            andalso Value#goods.bind =:= Bind end, Dict),
    lib_goods_dict:fetch_values(Dict1).

%% 扣除的时候调用
get_type_goods_list(GoodsTypeId, Location, GoodsMaps) ->
    get_type_goods_list(0, GoodsTypeId, Location, GoodsMaps, 0).
get_type_goods_list(_PlayerId, GoodsTypeId, Location, GoodsMaps, _Lv) ->
    NowTime = utime:unixtime(),
    Dict = get_location_goods(GoodsMaps, Location),
    Dict1 = dict:filter(fun(_Key, Value) ->
        Value#goods.goods_id =:= GoodsTypeId andalso
            (Value#goods.expire_time =:= 0 orelse Value#goods.expire_time > NowTime) end, Dict),
    lib_goods_dict:fetch_values(Dict1).

%% 获取背包限时有效的物品
get_type_goods_list_expire_time(_PlayerId, GoodsTypeId, Location, NowTime, GoodsMaps) ->
    Dict = get_location_goods(GoodsMaps, Location),
    Dict1 = dict:filter(fun(_Key, Value) ->  Value#goods.goods_id =:= GoodsTypeId andalso Value#goods.expire_time > NowTime end, Dict),
    lib_goods_dict:fetch_values(Dict1).

%% 获取背包非限时的物品
get_type_goods_list_without_expire_time(GoodsTypeId, Location, GoodsMaps) ->
    Dict = get_location_goods(GoodsMaps, Location),
    Dict1 = dict:filter(fun(_Key, Value) -> Value#goods.goods_id =:= GoodsTypeId andalso Value#goods.expire_time == 0 end, Dict),
    lib_goods_dict:fetch_values(Dict1).

%% GM扣除时调用 指定背包和仓库的
get_all_type_goods_list(GoodsTypeId, GoodsMaps, Location) ->
    get_type_goods_list(GoodsTypeId, ?GOODS_LOC_STORAGE, GoodsMaps)
    ++ get_type_goods_list(GoodsTypeId, Location, GoodsMaps).
%%-------------------------------------------------
%% @doc 根据物品位置取物品
%% -spec get_goods_by_cell(_PlayerId, Location, Cell, Dict) -> GoodsInfo | []
%% when
%%      PlayerId    :: integer()    玩家Id
%%      Location    :: integer()    装备位置
%%      Cell        :: integer()    装备格子
%%      Dict        :: dict()       物品字典
%%      GoodsInfo   :: #goods{}     物品信息
%% @end
%%--------------------------------------------------
get_goods_by_cell(Location, Cell) ->
    GoodsStatus = lib_goods_do:get_goods_status(),
    get_goods_by_cell(GoodsStatus#goods_status.dict, Location, Cell).
get_goods_by_cell(_PlayerId, Location, Cell, GoodsMaps) ->
    get_goods_by_cell(GoodsMaps, Location, Cell).
get_goods_by_cell(GoodsMaps, Location, Cell) ->
    lists:keyfind(Cell, #goods.cell, get_goods_list(GoodsMaps, Location)).

%%-----------------------------------------------------------
%% @doc 增加同类型的物品还需要多少格子数
%%-----------------------------------------------------------
get_goods_need_cell_num(GoodsList, GoodsInfo, MaxNum, GoodsNum) when MaxNum > 1 ->
    F = fun(BagGoodsInfo, AccList) ->
        Same = can_overlap_same_cell(GoodsInfo, BagGoodsInfo),
        ?IF(Same == true, [BagGoodsInfo|AccList], AccList)
        end,
    SameGoodsList = lists:foldl(F, [], GoodsList),
    case SameGoodsList=/=[] of
        true -> calc_null_cell_num(SameGoodsList, MaxNum, GoodsNum);
        _ -> calc_null_cell_num(MaxNum, GoodsNum)
    end;
get_goods_need_cell_num(_GoodsList, _GoodsInfo, _MaxNum, GoodsNum) ->
    GoodsNum.

calc_null_cell_num(SameGoodsList, MaxNum, GoodsNum) ->
    TotalNum = lists:foldl(fun(X, Sum) -> X#goods.num + Sum end, 0, SameGoodsList),
    CellNum = util:ceil((TotalNum+GoodsNum)/MaxNum),
    (CellNum - length(SameGoodsList)).

calc_null_cell_num(MaxNum, GoodsNum) ->
    util:ceil(GoodsNum/MaxNum).

%%----------------------------------------------------
%% @doc 是否叠加在同一个背包格子
%% 绑定、有效时间、锁定时间一致才叠加在同一格子，不然另外新起格子
%% OldGoodsInfo 已有物品记录
%% AddGoodsInfo 新增物品记录
%%----------------------------------------------------
can_overlap_same_cell(OldGoodsInfo, AddGoodsInfo) ->
    #goods{bind = Bind, expire_time = ExpireTime, goods_id = GoodsId} = OldGoodsInfo,
    case AddGoodsInfo of
        #goods{bind = Bind, expire_time = ExpireTime, goods_id = GoodsId}  -> true;
        _ -> false
    end.
%%----------------------------------------------------
%% @doc 取物品总数
%%----------------------------------------------------
get_goods_total_num(GoodsList) ->
    NowTime = utime:unixtime(),
    lists:foldl(
        %% 已过有效期的排除
        fun(X, Sum) when X#goods.expire_time > 0 andalso X#goods.expire_time =< NowTime  -> Sum;
            (X, Sum) -> X#goods.num + Sum
        end, 0, GoodsList).

%%--------------------------------------------
%% 更改物品格子位置
%%--------------------------------------------
change_goods_location_cell(GoodsInfo, Location, Cell) ->
    case is_integer(GoodsInfo#goods.id) of
        true ->
            Sql = io_lib:format(?SQL_GOODS_UPDATE_LOCATION_CELL, [Location, Cell, util:term_to_bitstring(GoodsInfo#goods.extra_data), GoodsInfo#goods.id, GoodsInfo#goods.player_id]),
            goods_transaction:add_sql(Sql),
            GoodsInfo#goods{location=Location, cell=Cell};
        false ->
            GoodsInfo
    end.

%%--------------------------------------------
%% 更改物品格子位置
%%--------------------------------------------
change_goods_location_cell_bind(GoodsInfo) ->
    #goods{id = Id, player_id = RoleId, location = Location, cell = Cell, bind = Bind, extra_data = ExtraData} = GoodsInfo,
    case is_integer(GoodsInfo#goods.id) of
        true ->
            Sql = io_lib:format(?SQL_GOODS_UPDATE_LOCATION_CELL_BIND, [Location, Cell, Bind, util:term_to_bitstring(ExtraData), Id, RoleId]),
            goods_transaction:add_sql(Sql);
        false ->
            GoodsInfo
    end.
%%--------------------------------------------
%% 更新物品额外数据
%%--------------------------------------------
change_goods_extra(GoodsId, RoleId, ExtraData) ->
    DataStr = util:term_to_string(ExtraData),
    SQL = io_lib:format(?SQL_GOODS_UPDATE_OTHER, [DataStr, GoodsId, RoleId]),
    goods_transaction:add_sql(SQL).

%%--------------------------------------------
%% 更改物品类型ID
%%--------------------------------------------
update_goods_info(GoodsInfo) ->
    #goods{type = Type, subtype = SubType, extra_data = ExtraData, id = Id, goods_id = GoodsId, player_id = RoleId} = GoodsInfo,
    Sql = io_lib:format(?SQL_GOODS_UPDATE_INFO, [GoodsId, Type, SubType, util:term_to_bitstring(ExtraData), Id, RoleId]),
    goods_transaction:add_sql(Sql),
    GoodsInfo.
%%--------------------------------------------
%%取多条记录
get_list(Table, Sql, Lv) ->
	List = get_list(Sql),
	lists:flatmap(fun(GoodsInfo) -> make_info(Table, GoodsInfo, Lv) end, List).

get_list(Sql) ->
	List = (catch db:get_all(Sql)),
	case is_list(List) of
		true ->
			List;
		false ->
			[]
	end.

get_lv_model(PlayerId, Career, Sex, Turn, Lv) ->
    [
        {?MODEL_PART_WEAPON, get_model_id(?MODEL_PART_WEAPON, PlayerId, Career, Sex, Turn, Lv)},
        {?MODEL_PART_CLOTH, get_model_id(?MODEL_PART_CLOTH, PlayerId, Career, Sex, Turn, Lv)},
        {?MODEL_PART_HEAD, get_model_id(?MODEL_PART_HEAD, PlayerId, Career, Sex, Turn, Lv)}
    ].

get_model_id(Part, _PlayerId, Career, Sex, Turn, Lv) ->
    lib_player:get_model_id(?LV_MODEL, Part, Career, Sex, Turn, Lv, 0).

get_model_id_by_goods_id(GoodsTypeId, Part, Career, Sex, Turn, Lv) ->
    case data_goods_type:get(GoodsTypeId) of
        #ets_goods_type{model_id = ModelId}
            when GoodsTypeId=/=0 andalso ModelId =/= 0 -> ModelId;
        _ -> lib_player:get_model_id(?LV_MODEL, Part, Career, Sex, Turn, Lv, 0)
    end.

%%-----------------------------------------------------
%% @doc  增加物品
add_goods(GoodsInfo) ->
    GoodsId = mod_id_create:get_new_id(?GOODS_ID_CREATE),
    SQL = io_lib:format(?SQL_GOODS_INSERT, [
        GoodsId
        , GoodsInfo#goods.player_id
        , GoodsInfo#goods.goods_id
        , GoodsInfo#goods.type
        , GoodsInfo#goods.subtype
        , GoodsInfo#goods.location
        , GoodsInfo#goods.cell
        , GoodsInfo#goods.num
        , GoodsInfo#goods.expire_time
        , GoodsInfo#goods.bind
        , util:term_to_bitstring(GoodsInfo#goods.extra_data)]),
    goods_transaction:add_sql(SQL),
    goods_extra_data:extra_data_save(GoodsInfo),
    GoodsInfo#goods{id = GoodsId}.
%%-----------------------------------------------------
%% @doc  删除物品
delete_goods(GoodsId, RoleId) ->
    Sql = io_lib:format(?SQL_GOODS_DELETE_BY_ID, [GoodsId, RoleId]),
    goods_transaction:add_sql(Sql).
%%-----------------------------------------------------
%% @doc 更改物品数量
%% -spec change_goods_num(GoodsInfo, Num) -> NewGoodsInfo
%% GoodsInfo | NewGoodsInfo     :: #goods{}     物品记录
%% Num                          :: integer()    物品新数量
%% @end
%% -------------------------------------------------------
change_goods_num(GoodsInfo, Num) ->
	Sql = io_lib:format(?SQL_GOODS_UPDATE_NUM, [Num, GoodsInfo#goods.id, GoodsInfo#goods.player_id]),
    goods_transaction:add_sql(Sql),
	GoodsInfo#goods{num = Num}.

%%-----------------------------------------------------
%% @doc 更改物品有效期
%% -spec change_goods_num(GoodsInfo, Num) -> NewGoodsInfo
%% GoodsInfo | NewGoodsInfo     :: #goods{}     物品记录
%% Num                          :: integer()    物品新数量
%% @end
%% -------------------------------------------------------
change_goods_expire_time(GoodsInfo, Time) ->
	Sql = io_lib:format(?SQL_GOODS_UPDATE_EXPIRE, [Time, GoodsInfo#goods.id, GoodsInfo#goods.player_id]),
    goods_transaction:add_sql(Sql),
    GoodsInfo#goods{expire_time = Time}.

%%------------------------------------------------------
%% @doc 扩充背包
%% -spce extend_bag(PlayerStatus, CellNum) -> NewPlayerStatus
%%  when
%%      PlayerStatus|NewPlayerStatus    :: #player_status{} 玩家记录
%%      CellNum                         :: 要增加的背包栏
%% @end
%% -----------------------------------------------------------
extend_bag(PlayerStatus, CellNum) ->
	NewCellNum = PlayerStatus#player_status.cell_num + CellNum,
	Sql = io_lib:format(?SQL_PLAYER_UPDATE_CELL, [NewCellNum, PlayerStatus#player_status.id]),
    goods_transaction:add_sql(Sql),
	PlayerStatus#player_status{cell_num = NewCellNum}.

%%------------------------------------------------
to_term(BinString) ->
	case util:bitstring_to_term(BinString) of
		undefined ->
			[];
		Term ->
			Term
	end.

%%------------------------------------------------
%% 排序
%%------------------------------------------------
sort(GoodsList, Type) ->
	case Type of
		id -> F = fun(G1, G2) -> G1#goods.id < G2#goods.id end;
		bind -> F = fun(G1, G2) -> G1#goods.bind > G2#goods.bind end;
        goods_id -> F = fun(G1, G2) -> G1#goods.goods_id < G2#goods.goods_id end;
        bind_id ->
            F = fun(G1, G2) ->
                if
                    G1#goods.goods_id =:= G2#goods.goods_id ->
                        G1#goods.bind < G2#goods.bind;
                    true ->
                        G1#goods.goods_id < G2#goods.goods_id
                end
        end;
        expire_time -> F = fun(G1, G2) -> G1#goods.expire_time < G2#goods.expire_time end;
		_ -> F = fun(G1, G2) -> G1#goods.goods_id < G2#goods.goods_id end
		end,
	lists:sort(F, GoodsList).

%%--------------------------------------------------
%% 奖励多倍化
goods_object_multiply_by(GoodsList, 1) -> GoodsList;
goods_object_multiply_by(GoodsList, X) ->
    [
        begin
            case Object of
                {Type, GTypeId, Num} ->
                    {Type, GTypeId, trunc(Num*X)};
                {GTypeId, Num} ->
                    {GTypeId, trunc(Num*X)}
            end
        end || Object <- GoodsList
    ].

%%--------------------------------------------------
%% 获取空格子的数量
%% @param  NullCellsMap #{location => NullCellsList}
%% @param  Location     description
%% @return              {NullCell, NewNullCellsMap}
%%--------------------------------------------------
get_null_cell_num(Pos) ->
    GoodsStatus = lib_goods_do:get_goods_status(),
    get_null_cell_num(GoodsStatus, Pos).
get_null_cell_num(GoodsStatus, Pos) ->
    GoodsDict = get_location_goods(GoodsStatus#goods_status.dict, Pos),
    Has = dict:size(GoodsDict),
    case Pos of
        ?GOODS_LOC_BAG ->
            GoodsStatus#goods_status.cell_num - Has;
        ?GOODS_LOC_STORAGE ->
            GoodsStatus#goods_status.storage_num - Has;
        _ ->
            data_goods:get_location_size(Pos) - Has
    end.

%%--------------------------------------------------
%% 获取空格子的数量列表
%% @param  Player #player_status{}
%% @return        [{location, NullCell}]
%%--------------------------------------------------
get_null_cell_num_list(Player) when is_record(Player, player_status) ->
    GoodsStatus = lib_goods_do:get_goods_status(),
    get_null_cell_num_list(GoodsStatus);
get_null_cell_num_list(GoodsStatus) ->
    [{Pos, get_null_cell_num(GoodsStatus, Pos)} || Pos <- ?GOODS_LOC_BAG_TYPES].

get_null_cell_num_maps(Player) when is_record(Player, player_status) ->
    GoodsStatus = lib_goods_do:get_goods_status(),
    get_null_cell_num_maps(GoodsStatus);
get_null_cell_num_maps(GoodsStatus) ->
    NullCellList = get_null_cell_num_list(GoodsStatus),
    maps:from_list(NullCellList).

%%--------------------------------------------------
%% 增加一个空的格子
%% @param  NullCellsMap #{location => NullCellsList}
%% @param  Location     description
%% @param  Cell         description
%% @return              NewNullCellsMap
%%--------------------------------------------------
add_null_cell(NullCellsMap, Location, Cell) when is_integer(Cell) ->
    add_null_cell(NullCellsMap, Location, [Cell]);
add_null_cell(NullCellsMap, Location, CellList) when is_list(CellList) ->
    L = maps:get(Location, NullCellsMap, []),
    SortL = lists:sort(CellList ++ L),
    maps:put(Location, SortL, NullCellsMap).

%%--------------------------------------------------
%% 合并相同物品
%% GoodsList = [{GoodsId, GoodsNum }, ...]
%% GoodsList = [{GoodsId, GoodsTypeId, GoodsNum }, ...]
%% GoodsList = [{Type, GoodsTypeId, GoodsNum }, ...]
%% GoodsList = [{?TYPE_GOODS, GoodsTypeId, GoodsNum }, ...]
%% GoodsList = [{?TYPE_BIND_GOODS, GoodsTypeId, GoodsNum }, ...]
%% GoodsList = [{?TYPE_ATTR_GOODS, GoodsTypeId, GoodsNum, AttrList}, ...]
merge_same_goods([]) -> [];
merge_same_goods(GoodsList) ->
    merge_same_goods(GoodsList, []).
merge_same_goods([H|T], List) ->
    NewList =
    case H of
        {GoodsId, GoodsNum} ->
            case lists:keyfind(GoodsId, 1, List) of
                {GoodsId, Num} ->
                    lists:keystore(GoodsId, 1, List, {GoodsId, Num+GoodsNum});
                _ ->
                    [{GoodsId, GoodsNum}|List]
            end;
        {GoodsType, GoodsTypeId, GoodsNum} ->
            case lists:keyfind({GoodsType, GoodsTypeId}, 1, List) of
                {{GoodsType, GoodsTypeId}, Num} ->
                    lists:keystore({GoodsType, GoodsTypeId}, 1, List, {{GoodsType, GoodsTypeId}, Num+GoodsNum});
                _ ->
                    [{{GoodsType, GoodsTypeId}, GoodsNum}|List]
            end;
        {?TYPE_ATTR_GOODS, GoodsTypeId, GoodsNum, AttrList} ->
            case lists:keyfind({?TYPE_GOODS, GoodsTypeId}, 1, List) of
                {{?TYPE_ATTR_GOODS, GoodsTypeId}, Num, AttrList} ->
                    lists:keystore({?TYPE_GOODS, GoodsTypeId}, 1, List, {{?TYPE_GOODS, GoodsTypeId}, Num+GoodsNum, AttrList});
                _ ->
                    [{{?TYPE_GOODS, GoodsTypeId}, GoodsNum, AttrList}|List]
            end;
        _ ->
            [H|List]
    end,
    merge_same_goods(T, NewList);
merge_same_goods([], List) ->
    [
        case One of
            {{GoodsType, GoodsTypeId}, GoodsNum} ->
                {GoodsType, GoodsTypeId, GoodsNum};
            {GoodsId, GoodsNum} ->
                {GoodsId, GoodsNum};
            {{?TYPE_GOODS, GoodsTypeId}, GoodsNum, AttrList} ->
                {?TYPE_ATTR_GOODS, GoodsTypeId, GoodsNum, AttrList};
            _ ->
                One
        end
        || One <- List
    ].

%%--------------------------------------------------
%% 分离可重叠和不可重叠的物品
%% @return {CanOverLapGoodsList, CannotOverLapGoodsList}
%%  CanOverLapGoodsList    可重叠列表   [{Type, GoodsTypeId, Num}|...]
%%  CannotOverLapGoodsList 不可重叠列表 [{?GOODS, GoodsTypeId, Num}|...]
%%  CommonList 通用列表
split_can_overlap(GoodsList) ->
    F = fun
            ({Type, GoodsTypeId, _Num} = GoodsInfo, {CanOverLapGoodsList, CannotOverLapGoodsList}) ->
                case Type =/= ?TYPE_GOODS orelse Type =/= ?TYPE_BIND_GOODS
                 orelse Type =/= ?TYPE_DESIGN orelse Type =/= ?TYPE_FASHION of %% 如果不是物品类型，则默认可叠加
                    true ->
                        {[GoodsInfo | CanOverLapGoodsList], CannotOverLapGoodsList};
                    _ ->
                        case data_goods_type:get(GoodsTypeId) of
                            #ets_goods_type{max_overlap = 0} -> %% max_overlap == 0 表示不可叠加
                                {CanOverLapGoodsList, [GoodsInfo|CannotOverLapGoodsList]};
                            _ ->
                                {[GoodsInfo | CanOverLapGoodsList], CannotOverLapGoodsList}
                        end
                end;
            %% 带额外属性的道具默认
            ({?TYPE_ATTR_GOODS, _GoodsTypeId, _Num, _ExtraAttr} = GoodsInfo, {CanOverLapGoodsList, CannotOverLapGoodsList}) ->
                case check_attr_goods_can_overlap(GoodsInfo) of
                    true  -> {[GoodsInfo | CanOverLapGoodsList], CannotOverLapGoodsList};
                    false -> {CanOverLapGoodsList, [GoodsInfo|CannotOverLapGoodsList]}
                end;
            ({?TYPE_DESIGN, GoodsTypeId} = GoodsInfo, {CanOverLapGoodsList, CannotOverLapGoodsList}) ->
                case data_goods_type:get(GoodsTypeId) of
                    #ets_goods_type{max_overlap = 0} ->
                        {CanOverLapGoodsList, [GoodsInfo|CannotOverLapGoodsList]};
                    _ ->
                        {[GoodsInfo|CanOverLapGoodsList], CannotOverLapGoodsList}
                end;
            ({?TYPE_FASHION, GoodsTypeId} = GoodsInfo, {CanOverLapGoodsList, CannotOverLapGoodsList}) ->
                case data_goods_type:get(GoodsTypeId) of
                    #ets_goods_type{max_overlap = 0} ->
                        {CanOverLapGoodsList, [GoodsInfo|CannotOverLapGoodsList]};
                    _ ->
                        {[GoodsInfo|CanOverLapGoodsList], CannotOverLapGoodsList}
                end
        end,
    lists:foldl(F, {[], []}, GoodsList).

%% 【注】叠加类型的物品不能动态赋予绑定属性、过期时间之外的字段
check_attr_goods_can_overlap({_, GoodsTypeId, _Num, DyAttrList}) ->
    LeftAttr = lists:foldl(
        fun({Key, _}, Temp) ->
            case Key of
                bind -> Temp;
                expire -> Temp;
                {expire, _} -> Temp;
                {expire_type,_}->Temp;
                _ -> [Key|Temp]
            end
        end, [], DyAttrList),
    case LeftAttr of
        [] ->
            case data_goods_type:get(GoodsTypeId) of
                #ets_goods_type{max_overlap = 0} ->
                    false;
                _ -> true
            end;
        _ ->
            false
    end.
%%----------------------------------------------------------------------------------------------------
%% 把数据库里取出来的数据构建成物品记录
%%----------------------------------------------------------------------------------------------------
make_info(Table, Info, _Lv) ->
    case Table of
        goods when Info =/= [] ->
            [
                Id, RoleId, GoodsId, Type, Subtype, Location, Cell, Num, CTime, ExpireTime, Bind, ExtraData
            ] = Info,
            case data_goods_type:get(GoodsId) of
                GoodsTypeR when is_record(GoodsTypeR, ets_goods_type) ->
                    %% 物品记录
                    Goods = make(goods, [GoodsTypeR, Id, RoleId, GoodsId, Type, Subtype, Location, Cell, Num, CTime, ExpireTime, Bind, ExtraData]),
                    [Goods];
                _ -> %% 没有该物品配置
                    catch ?ERR("no such a goods config, goods_id=~p, RoleId=~p~n", [GoodsId, RoleId]),
                    []
            end;
        _ -> []
    end.

make(goods, [GoodsTypeR, Id, RoleId, GoodsId, Type, Subtype, Location, Cell, Num, CTime, ExpireTime, Bind, OtherData]) ->
    GoodsInfo = #goods{
        id = Id, player_id = RoleId, goods_id = GoodsId, type = Type,
        subtype = Subtype, color = GoodsTypeR#ets_goods_type.color,
        bind = Bind, location = Location,
        cell = Cell, num = Num, expire_time = ExpireTime,
        bag_location = GoodsTypeR#ets_goods_type.bag_location,
        level = GoodsTypeR#ets_goods_type.level,
        extra_data = to_term(OtherData), ctime = CTime
    },
    goods_extra_data:extra_data_load(GoodsInfo).

%%==========================================
%% 物品信息构造
%%=========================================
pack_goods_list(GoodsList) ->
    ConvertList = data_goods_convert:get_all_goods_type_id(),
    F = fun
            (GoodsInfo, AccList) when is_record(GoodsInfo, goods) =:= true
                andalso is_integer(GoodsInfo#goods.id) =:= true ->
                #goods{
                    id = GoodsId,
                    goods_id = TypeId,
                    cell = Cell,
                    num = Num,
                    bind = Bind,
                    expire_time = ExpireTime
                }=GoodsInfo,
                ConvertTime = mod_convert_mgr:get_time_for_goods_id(TypeId, ConvertList),
                Elem = {
                    GoodsId, TypeId, Cell, Num, Bind, ExpireTime, ConvertTime, goods_extra_data:extra_data_pack(GoodsInfo)},
                [Elem|AccList];
            (_GoodsInfo, AccList) ->
                AccList
        end,
    lists:foldl(F, [], GoodsList).

pack_equipment_list(GoodsList) ->
    F = fun
            (GoodsInfo, AccList) when is_record(GoodsInfo, goods) =:= true
                andalso is_integer(GoodsInfo#goods.id) =:= true ->
                #goods{
                    id = GoodsId,
                    goods_id = TypeId,
                    cell = Cell
                }=GoodsInfo,
                Elem = {
                    GoodsId, TypeId, Cell, goods_extra_data:extra_data_pack(GoodsInfo)},
                [Elem|AccList];
            (_GoodsInfo, AccList) ->
                AccList
        end,
    lists:foldl(F, [], GoodsList).
%%==========================================
%% 执行F函数N次
%%=========================================
deeploop(F, N, Data) ->
    case N > 0 of
        true ->
            [N1, Data1] = F(N, Data),
            deeploop(F, N1, Data1);
        false ->
            Data
    end.

%%----------------------------------------------------
%% @doc 通过物品类型信息构建物品记录
%% -spec get_new_goods(GoodsTypeInfo) -> Goods
%% GoodsTypeInfo    :: #ets_goods_type{}    物品类型记录
%% Goods            :: #goods{}             物品记录
%% @end
%%-----------------------------------------------------
get_new_goods(RoleId, GoodsTypeInfo, ExtraArgs) ->
    GoodsInfo = get_new_goods(GoodsTypeInfo),
    set_goods_info(ExtraArgs, GoodsInfo#goods{player_id = RoleId}).

get_new_goods(GoodsTypeInfo, ExtraArgs) ->
    GoodsInfo = get_new_goods(GoodsTypeInfo),
    set_goods_info(ExtraArgs, GoodsInfo).

get_new_goods(GoodsTypeInfo) ->
    NowTime = utime:unixtime(),
    GoodsTypeId = GoodsTypeInfo#ets_goods_type.goods_id,
    ExpireTime = get_expire_time(GoodsTypeInfo#ets_goods_type.expire_type, GoodsTypeInfo#ets_goods_type.expire_time, NowTime),
    GoodsInfo = #goods{
        goods_id = GoodsTypeId,
        type = GoodsTypeInfo#ets_goods_type.type,
        subtype = GoodsTypeInfo#ets_goods_type.subtype,
        bind = GoodsTypeInfo#ets_goods_type.bind,
        expire_time = ExpireTime,
        level = GoodsTypeInfo#ets_goods_type.level,
        color = GoodsTypeInfo#ets_goods_type.color,
        bag_location = GoodsTypeInfo#ets_goods_type.bag_location,
        location = GoodsTypeInfo#ets_goods_type.bag_location,
        ctime = NowTime %% 物品创建时间
    },
    %% 不同的物品类型 可能有不同的extra_data
    NewGoodsInfo = goods_extra_data:extra_data_init(GoodsInfo),
    NewGoodsInfo.

%% 设置物品属性
set_goods_info([], GoodsInfo)->
    GoodsInfo;
set_goods_info([{K, V}|DyAttrList], GoodsInfo)->
    NewGoodsInfo = case K of
                        bind    ->
                            GoodsInfo#goods{bind = V};
                       expire ->
                            GoodsInfo#goods{expire_time = V+ utime:unixtime()};
                       {expire_type, Type} ->
                           GoodsInfo#goods{expire_time = get_expire_time(Type, V, utime:unixtime())};
                        equipment_lv ->
                            goods_extra_data:extra_data_update(GoodsInfo, [{?GOODS_INFO_EQUIPMENT_LV, V}]);
                        equipment_great ->
                            goods_extra_data:extra_data_update(GoodsInfo, [{?GOODS_OTHER_KEY_EQUIPMENT_GREAT_LV, V}]);
                        _R ->
                            GoodsInfo
                    end,
    set_goods_info(DyAttrList, NewGoodsInfo).

%% 过期类型：1有效时间(秒数)/2时间戳/3开服天数/4合服天数/5当天0点开始计算的有效时间(秒数)
get_expire_time(ExpireType, Time, NowTime) ->
    case ExpireType of
        ?GOODS_EXPIRE_TYPE_VALID_TIME ->
            NowTime + Time;
        ?GOODS_EXPIRE_TYPE_TIMESTAMP ->
            Time;
        ?GOODS_EXPIRE_TYPE_OPEN_DAY ->
            OpenDay = util:get_open_day(),
            (Time - OpenDay + 1)*86400 + utime:unixdate() - 1;
        ?GOODS_EXPIRE_TYPE_MERGE_DAY ->
            MergeDay = util:get_merge_day(),
            (Time - MergeDay + 1)*86400 + utime:unixdate() - 1;
        ?GOODS_EXPIRE_TYPE_FROM_NIGHT ->
            utime:unixdate(NowTime) + Time;
        _  ->
            0
    end.

calc_random_rewards(Rewards) ->
     F = fun
        ({Key, Id, NumFormat}) ->
            Num = calc_random_num(NumFormat),
            {Key, Id, Num}
    end,
    [Item || {_Key, _Id, Num} = Item <- [F(R) || R <- Rewards], Num > 0].

is_random_rewards(Rewards) ->
    lists:any(fun
        ({_, _, Num}) ->
            not is_number(Num)
    end, Rewards).

get_random_goods_rewards(Rewards) ->
    case is_random_goods_rewards(Rewards) of
        false ->
            Rewards;
        _ ->
            calc_random_goods_rewards(Rewards)
    end.

calc_random_goods_rewards(Rewards) ->
     F = fun
        ({Key, GoodIdFormat, NumFormat}) ->
            GoodId = calc_random_good_id(GoodIdFormat),
            Num = calc_random_num(NumFormat),
            {Key, GoodId, Num}
    end,
    [Item || {_Key, _Id, Num} = Item <- [F(R) || R <- Rewards], Num > 0].

is_random_goods_rewards(Rewards) ->
    lists:any(fun
        ({_, GoodId, Num}) ->
            not is_number(GoodId) orelse not is_number(Num)
    end, Rewards).

%%计算随机数量
%%支持格式（数量| [最小值,最大值] |[{权重,数量}]）
calc_random_num(NumFormat) ->
    case NumFormat of
        Value when is_integer(Value) ->
            Value;
        [Min, Max] when is_integer(Min) andalso is_integer(Max) ->
            if
                Min > Max ->
                    urand:rand(Max, Min);
                true ->
                    urand:rand(Min, Max)
            end;
        _ ->
            urand:rand_with_weight(NumFormat)
    end.

%%计算随机物品id
%%支持格式 （物品id| [{权重,物品id}]）
calc_random_good_id(GoodIdFormat) ->
    case GoodIdFormat of
        Value when is_integer(Value) ->
            Value;
        _ ->
            urand:rand_with_weight(GoodIdFormat)
    end.

goods_to_bind_goods(ObjectList) ->
    [begin
        case Object of
            {?TYPE_GOODS, Id, Num} ->
                {?TYPE_BIND_GOODS, Id, Num};
            {Id, Num} when is_integer(Id) ->
                {?TYPE_BIND_GOODS, Id, Num};
            _ ->
                Object
        end
     end || Object <- ObjectList].

%% goods_low表的other_data字段
format_other_data(GoodsInfo) ->
    case GoodsInfo#goods.type of
        _ -> []
    end.

%% goods_extra表dstone、stren_expend字段
format_extra_data(_GoodsInfo) ->
    [[], 0].

calc_diffrence_goods(GoodsList, []) -> GoodsList;

calc_diffrence_goods(GoodsList1, GoodsList2) ->
    List1 = lib_goods_api:trans_object_list(GoodsList1),
    List2 = lib_goods_api:trans_object_list(GoodsList2),
    calc_diffrence_objects(List1, List2).

calc_diffrence_objects(List, [{Key, Num}|T]) ->
    case lists:keytake(Key, 1, List) of
        {value, {_, Num1}, Others} ->
            if
                Num1 > Num ->
                    calc_diffrence_objects([{Key, Num1 - Num}|Others], T);
                true ->
                    calc_diffrence_objects(Others, T)
            end;
        _ ->
            calc_diffrence_objects(List, T)
    end;

calc_diffrence_objects(List, []) ->
    [{Type, Id, Num} || {{Type, Id}, Num} <- List].

is_enough_currency(PS, CurrencyId, Num) ->
    case maps:find(CurrencyId, PS#player_status.currency_map) of
        {ok, Value} ->
            Value >= Num;
        _ ->
            false
    end.

load_currency(RoleId) ->
    SQL = io_lib:format(?LOAD_CURRENCY, [RoleId]),
    All = db:get_all(SQL),
    maps:from_list([{CurrencyId, Value} || [CurrencyId, Value|_] <- All]).

count_role_equip_attribute(NewPlayerStatus) ->
    {ok, NewPlayerStatus}.
