%% ---------------------------------------------------------------------------
%% @doc 交易市场
%% @author zhaoyu
%% @since  2019-12-26
%% ---------------------------------------------------------------------------
-module(market).

-include("market.hrl").
-include("common.hrl").
-include("server.hrl").
-include("goods.hrl").
-include("def_goods.hrl").
-include("def_fun.hrl").
-include("errcode.hrl").
-include("figure.hrl").
-include("predefine.hrl").
-include("guild.hrl").
-include("def_id_create.hrl").
-include("equipment.hrl").
-include("def_module.hrl").
-include("language.hrl").
-include_lib("stdlib/include/ms_transform.hrl").

%% 降序
-define(DESC, 0).
%% 顺序
-define(ASC, 1).

%% 时间排序
-define(TIME_SORT, 1).
%% 当前竞拍价排序
-define(PRICE_SORT, 2).
%% 一口价排序
-define(BUYOUT_PRICE_SORT, 3).

-export([
    put_on_goods/6                      %% 物品上架
    ,select_goods/11                    %% 筛选市场商品
    ,send_market_goods/11               %% 发送市场商品列表
    ,bid_for_goods/3                    %% 竞拍物品
    ,bid_for_goods_callback/6           %% 竞拍物品（交易市场进程回调）
    ,send_role_bid_goods_info/1         %% 发送玩家正在竞价的商品列表
    ,send_role_market_goods/1           %% 发送玩家已上架物品列表
    ,put_goods_down/2                   %% 物品下架
    ,put_goods_down_callback/2          %% 物品下架-交易市场进程回调
    ,log_market_operation/3             %% 交易市场日志
    ,log_market_race/2
    ,put_on_goods_again/4               %% 商品再上架
    ,batch_put_goods_on_again/1         %% 商品一键再上架

    , make_market_goods/8

    ,role_update_market_deal_log/4      %% 商品成交处理
    ,update_role_deal/4                 %% 完成交易后：更新个人玩家成交记录

    ,login/1                            %% 角色登录
    ,send_deal_log/2                    %% 发送交易市场成交记录
    ,send_role_like_market_goods/1      %% 发送玩家关注的商品列表
    ,like_market_goods/2                %% 关注物品
    ,del_like_market_goods/2            %% 取消关注物品
    ,del_like_market_goods_callback/2   %% 关注取消关注物品物品
    ,add_reminder/2                     %% 添加入库提醒
    ,remove_reminder/2                  %% 取消入库提醒
    ,remove_reminder_by_type/3          %% 取消入库提醒
    ,sys_put_on_goods/2                 %% 系统对物品奖励进行公会上架
    ,sys_put_on_goods/3                 %% 系统对物品奖励进行公会上架
    ,notice_goods_overdue/1             %% 通知玩家有商品过期
    ,reminder_others/4                  %% 提醒上架
    ,reminder_me/4
    ,get_market_tex/1                   %% 获得交易税

    ,rest_show_time/2                   %% 商品公示剩余时间
    ,extra_data_pack/1                  %% 打包物品额外数据
    ,split_goods_list/1                 %% 分拆物品

    ,timer_hour/1                       %% 定时判断
]).


%% ---------------------------------------------------------------------------
%% @doc 物品上架
-spec put_on_goods(Role, GoodsId, GoodsTypeId, Num, SellType, IsToWorld) ->
    {false, ErrCode} | {true, MarketGoods, NewRole} when
    Role        :: #player_status{},
    GoodsId     :: integer(),
    GoodsTypeId :: integer(),
    Num         :: integer(),
    SellType    :: integer(),
    IsToWorld   :: integer(),
    ErrCode     :: integer(),
    MarketGoods :: #market_goods{},
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
put_on_goods(Role, GoodsId, _GoodsTypeId, Num, SellType, IsToWorld) ->
    GoodsStatus = lib_goods_do:get_goods_status(),
    GoodsInfo = lib_goods_util:get_goods(GoodsId, GoodsStatus#goods_status.dict),
    case check_put_on_goods(Role, GoodsInfo, Num, SellType, GoodsStatus, IsToWorld) of
        {false, Reason} ->
            {false, Reason};
        true ->
            put_on_goods_do(Role, GoodsStatus, GoodsInfo, Num, SellType, IsToWorld)
    end.

put_on_goods_do(Role, GoodsStatus, GoodsInfo, GoodsNum, SellType, IsToWorld) ->
    #player_status{id = RoleId} = Role,
    #goods{id = GoodsId, goods_id = GTypeId} = GoodsInfo,
    MarketGoods = goods_to_market_goods(Role, GoodsInfo, GoodsNum, SellType, IsToWorld),
    F = fun() ->
        ok = lib_goods_dict:start_dict(),
        {
            ok, NewStatus = #goods_status{dict = Dict}
        } = goods_del:delete_goods_list(GoodsStatus, [{GoodsInfo, GoodsNum}]),
        {Dict2, GoodsL} = lib_goods_dict:handle_dict_and_notify(Dict),
        NewStatus2 = NewStatus#goods_status{dict = Dict2},
        %% 删除角色背包中的物品后，马上写到交易市场出售的商品数据库中
        SaveSql = market_db:format_save_market_goods_sql(MarketGoods),
        goods_transaction:add_sql(SaveSql),
        {ok, NewStatus2, GoodsL}
    end,
    case goods_transaction:transaction(F) of
        {ok, NewStatus, UpdateGoodsList} ->
            lib_goods_do:set_goods_status(NewStatus),
            lib_goods_api:notify_client_num(RoleId, UpdateGoodsList),
            lib_log_api:log_throw(sell_up, RoleId, GoodsId, GTypeId, GoodsNum, 0, 0),
            NewRole = do_put_on_after(Role, MarketGoods, GTypeId, GoodsNum),
            {true, MarketGoods, NewRole};
        Error ->
            ?ERR("put_on_goods_do error:~p", [Error]),
            {false, ?ERRCODE(fail)}
    end.

do_put_on_after(Role, MarketGoods, _GoodsTypeId, _GoodsNum) ->
    {ok, Role2} = lib_seven_days_goal:do_finish_task(Role, ?MOD_MARKET, 4),
    ets:insert(?MARKET_GOODS_ETS, MarketGoods),
    market_mgr:cast({mgr_put_on_goods, MarketGoods}),
    log_market_operation(?MARKET_OPERATION_MANUAL_ON, MarketGoods, 0),
    Role2.


%% 检查物品是否可以上架
check_put_on_goods(Role, GoodsInfo, GoodsNum, SellType, GoodsStatus, IsToWorld) ->
    #player_status{id = RoleId} = Role,
    GoodsCheck = check_goods_base(RoleId, GoodsInfo, GoodsNum, GoodsStatus),
    SellTypeCheck = case GoodsCheck of
        true ->
            check_sell_type(Role, SellType, GoodsInfo#goods.goods_id, IsToWorld);
        _ ->
            GoodsCheck
    end,
    MaxNumCheck = case SellTypeCheck of
        true ->
            check_role_shelf_num(RoleId);
        _ ->
            SellTypeCheck
    end,
    MaxNumCheck.

%% 检查物品是否可以上架
check_goods_base(RoleId, GoodsInfo, GoodsNum, _GoodsStatus) ->
    if
        %% 物品不存在
        is_record(GoodsInfo, goods) =:= false orelse GoodsInfo#goods.id < 0 ->
            {false, ?ERRCODE(err150_no_goods)};
        %% 物品不属于你所有
        GoodsInfo#goods.player_id =/= RoleId ->
            {false, ?ERRCODE(err150_palyer_err)};
        %% 物品不在背包
        GoodsInfo#goods.location =/= ?GOODS_LOC_BAG ->
            {false, ?ERRCODE(err150_location_err)};
        %% 物品数量不正确
        GoodsNum < 1 orelse GoodsInfo#goods.num < GoodsNum ->
            {false, ?ERRCODE(err150_num_err)};
        %% 已过有效期
        GoodsInfo#goods.expire_time > 0 ->
            {false, ?ERRCODE(err155_expire_time)};
        %% 绑定物品不可交易
        GoodsInfo#goods.bind == ?BIND ->
            {false, ?ERRCODE(err155_bind_cannot_sell)};
        true ->
            case data_goods_type:get(GoodsInfo#goods.goods_id) of
                #ets_goods_type{
                    sell_category = Category, sell_subcategory = SubCategory, bind = ?UNBIND
                } when Category > 0 andalso SubCategory > 0 ->
                    case data_market:get_sub_type_list(Category) of
                        List when List =/= [] ->
                            case lists:member(SubCategory, List) of
                                true ->
                                    true;
                                false ->
                                    {false, ?ERRCODE(err155_not_sell_category)}
                            end;
                        _ ->
                            {false, ?ERRCODE(err155_not_sell_category)}
                    end;
                _ ->
                    {false, ?ERRCODE(err155_cannot_trade)}
            end
    end.

%% 检查是否还有上架的格子
check_role_shelf_num(RoleId) ->
    NowNum = length(select_role_put_on_goods(RoleId)),
    MaxNum = data_market:get_config(booth_number),
    case NowNum >= MaxNum of
        true ->
            {false, ?ERRCODE(err155_shelves_are_full)};
        false ->
            true
    end.

%% 检查是否可以上架
check_sell_type(_Role, ?MARKET_SELL_TYPE_WORLD, GoodTypeId, _IsToWorld) ->
    case data_goods_type:get(GoodTypeId) of
        #ets_goods_type{
            market_range = Range
        } when Range =:= ?MARKET_RANGE_ALL orelse Range =:= ?MARKET_RANGE_WORLD ->
            true;
        _ ->
            {false, ?ERRCODE(err155_cannot_sell_to_world)}
    end;
check_sell_type(Role, ?MARKET_SELL_TYPE_GUILD, GoodTypeId, IsToWorld) ->
    RangeCheck = case data_goods_type:get(GoodTypeId) of
        #ets_goods_type{
            market_range = Range
        } when IsToWorld =:= 1 andalso Range =/= ?MARKET_RANGE_ALL ->
            {false, ?ERRCODE(err155_cannot_sell_to_world)};
        #ets_goods_type{
            market_range = Range
        } when Range =/= ?MARKET_RANGE_ALL andalso Range =/= ?MARKET_RANGE_GUILD ->
            {false, ?ERRCODE(err155_cannot_sell_to_guild)};
        _ ->
            true
    end,
    case RangeCheck of
        true ->
            #player_status{guild = GuildInfo} = Role,
            case GuildInfo of
                #status_guild{id = GId} when GId > 0 -> true;
                _ -> {false, ?ERRCODE(err155_no_guild_for_guild_sell_type)}
            end;
        _ ->
            RangeCheck
    end;
check_sell_type(_Role, _SellType, _GoodTypeId, _IsToWorld) ->
    {false, ?ERRCODE(fail)}.


%% ---------------------------------------------------------------------------
%% @doc select玩家已上架物品
-spec select_role_put_on_goods(RoleId) -> Return when
    RoleId      :: integer(),
    Return      :: [#market_goods{}].
%% ---------------------------------------------------------------------------
select_role_put_on_goods(RoleId) ->
    MatchSpec = ets:fun2ms(
        fun(#market_goods{role_id = PlayerId} = MarketGoods)
            when PlayerId =:= RoleId -> MarketGoods
        end
    ),
    ets:select(?MARKET_GOODS_ETS, MatchSpec).


%% ---------------------------------------------------------------------------
%% @doc 物品转换为交易市场物品
-spec goods_to_market_goods(Role, GoodsInfo, Num, SellType, IsToWorld) -> MarketGoods when
    Role        :: #player_status{},
    GoodsInfo   :: #goods{},
    Num         :: integer(),
    SellType    :: integer(),
    IsToWorld   :: integer(),
    MarketGoods :: #market_goods{}.
%% ---------------------------------------------------------------------------
goods_to_market_goods(Role, GoodsInfo, Num, SellType, IsToWorld) ->
    #player_status{id = RoleId, guild = GuildInfo} = Role,
    #goods{id = GoodsId, goods_id = GoodsTypeId} = GoodsInfo,
    %% 构造基本信息
    MarketGoods = make_market_goods(RoleId, GoodsId, GoodsTypeId, Num, SellType, GuildInfo#status_guild.id, IsToWorld, true),
    Id = mod_id_create:get_new_id(?SELL_ID_CREATE),
    MarketGoods#market_goods{
        id = Id
    }.

%% 计算系统回购时间戳
calc_sys_buyback_time(SellType, GoodsTypeId, SellingTime, ShowTime, OffTime, NowTime) ->
    StartSellingTime = NowTime + ShowTime,
    OpenDay = util:get_open_day(),
    case data_market:get_market_sys_buyback(GoodsTypeId, OpenDay) of
        #market_buyback_cfg{buyback_time = {BackMin, BackMax}} when SellType =:= ?MARKET_SELL_TYPE_WORLD ->
            BackTimeMin = StartSellingTime + BackMin,
            BackTimeMax = StartSellingTime + BackMax,
            {BuyBackTimeMin, BuyBackTimeMax} = if
                BackTimeMin >= OffTime ->
                    {NowTime + round((SellingTime - ShowTime) * 0.5), OffTime - 1};
                BackTimeMax >= OffTime ->
                    {BackTimeMin, OffTime - 1};
                true ->
                    {BackTimeMin, BackTimeMax}
            end,
            urand:rand(BuyBackTimeMin, BuyBackTimeMax);
        _ ->
            0
    end.

%% 构造商品
make_market_goods(RoleId, GoodsId, GoodsTypeId, Num, SellType, RoleGuildId, IsToWorld, NeedBuyBack) ->
    case data_goods_type:get(GoodsTypeId) of
        #ets_goods_type{
            sell_category = SellCategory, sell_subcategory = SellSubCategory
            ,trade_price = Price, selling_time = SellingTime, color = Color, level = Lev
            ,buyout_price = BuyoutPrice, sex = Sex, market_show_time = ShowTime
        } ->
            NowTime = utime:unixtime(),
            GuildId = case SellType of
                          ?MARKET_SELL_TYPE_GUILD when RoleGuildId > 0 ->
                              RoleGuildId;
                          _ ->
                              0
                      end,
            Series  = equipment_api:get_equipment_series(GoodsTypeId),
            Careers = equipment_api:get_equipment_careers(GoodsTypeId),
            OffTime = NowTime + SellingTime,
            BuyBackTime =
                case NeedBuyBack of
                    true  ->
                        calc_sys_buyback_time(
                            SellType, GoodsTypeId, SellingTime, ShowTime, OffTime, NowTime
                        );
                    false ->
                        0
                end,
            #market_goods{
                role_id = RoleId, goods_id = GoodsId, goods_type_id = GoodsTypeId
                ,num = Num, lev = Lev, color = Color, price = Price*Num, sell_type = SellType
                ,guild_id = GuildId, time = NowTime, off_time = OffTime
                ,is_to_world = IsToWorld, type = SellCategory
                ,sub_type = SellSubCategory, buyout_price = BuyoutPrice*Num, career = Careers
                ,sex = Sex, series = Series, sys_buyback_time = BuyBackTime
            };
        _ ->
            false
    end.

%% ---------------------------------------------------------------------------
%% @doc 发送市场商品列表
%% 选择的主规则，如果相同则根据剩余时间排序.
-spec send_market_goods(
    Role, SellType, Type, SubType, Series, Color, Career, PageSize, PageNo, SortId, SortOrder
) -> ok when
    Role        :: #player_status{},
    SellType    :: integer(),   %% 拍卖类型 1:世界拍卖 2:公会拍卖
    Type        :: integer(),   %% 商品大类 0:为推荐商品需要做特殊处理
    SubType     :: integer(),   %% 商品子类
    Series      :: integer(),   %% 套数-发0表示不筛选这一项
    Color       :: integer(),   %% 品质颜色-发99表示不筛选这一项
    Career      :: integer(),   %% 职业-发0表示不筛选这一项
    PageSize    :: integer(),   %% 页大小
    PageNo      :: integer(),   %% 页号
    SortId      :: integer(),   %% 排序规则id 1:时间排序 2:当前竞拍价排序 3:一口价排序 4：品质排序
    SortOrder   :: integer().   %% 0:表示顺序（从大到小） 1:表示倒序（从小到大）
%% ---------------------------------------------------------------------------
send_market_goods(
    Role, SellType, Type, SubType, Series, Color, Career, PageSize, PageNo, SortId, SortOrder
) ->
    {PageTotal, GoodsList} = select_goods(
        Role, SellType, Type, SubType, Series, Color, Career, PageSize, PageNo, SortId, SortOrder
    ),
    #player_status{market_data = MarketData} = Role,
    #market_data{like_list = LikeList} = MarketData,
    MyBidList = role_bid_info_list(Role),
    GoodsInfoList = pack_market_goods(LikeList, MyBidList, GoodsList, []),
    Data = [
        SellType, Type, SubType, Career, PageTotal, PageNo, SortId, SortOrder, GoodsInfoList
    ],
    {ok, Bin} = pt_155:write(15501, Data),
    lib_server_send:send_to_sid(Role#player_status.sid, Bin),
    ok.

pack_market_goods(_LikeList, _MyBidList, [], GoodsInfoList) ->
    GoodsInfoList;
pack_market_goods(LikeList, MyBidList, [MarketGoods | T], GoodsInfoList) ->
    #market_goods{
        id = Id, role_id = RoleId, goods_type_id = GoodsTypeId, num = Num, price = Price
        ,sell_type = SellType, off_time = OffTime, extra_attr = ExtraAttr
        ,buyout_price = BuyoutPrice, time = Time, bidder_id = BidderId
    } = MarketGoods,
    IsLike = case lists:keyfind(Id, #market_like.id, LikeList) of
        _Like = #market_like{} -> 1;
        _ -> 0
    end,
    MyPrice = case lists:keyfind(Id, #market_bid.id, MyBidList) of
        #market_bid{price = ThePrice} ->
            MyBid = 1,
            ThePrice;
        _ ->
            MyBid = 0,
            0
    end,
    IsThereABid = case BidderId =/= 0 of
        true -> 1;
        false -> 0
    end,
    Info = {
        Id, RoleId, GoodsTypeId, IsLike, Time, OffTime, Price, MyPrice, BuyoutPrice
        ,SellType, Num, IsThereABid, MyBid, extra_data_pack(ExtraAttr)
    },
    GoodsInfoList2 = [Info | GoodsInfoList],
    pack_market_goods(LikeList, MyBidList, T, GoodsInfoList2).

%% 打包额外数据
extra_data_pack([]) ->
    [];
extra_data_pack([{extra_data, ExtraAttr} | _T]) ->
    [{K, util:term_to_bitstring(V)} || {K, V} <- ExtraAttr];
extra_data_pack([_ | T]) ->
    extra_data_pack(T).


%% ---------------------------------------------------------------------------
%% @doc 筛选市场商品.
%% 选择的主规则，如果相同则根据剩余时间排序.
-spec select_goods(
    Role, SellType, Type, SubType, Series, Color, Career, PageSize, PageNo, SortId, SortOrder
) ->
    {PageTotal :: integer(), [#market_goods{}]} when
    Role        :: #player_status{},
    SellType    :: integer(),   %% 拍卖类型 1:世界拍卖 2:公会拍卖
    Type        :: integer(),   %% 商品大类 0:为推荐商品需要做特殊处理
    SubType     :: integer(),   %% 商品子类
    Series      :: integer(),   %% 套数-发0表示不筛选这一项
    Color       :: integer(),   %% 品质颜色-发99表示不筛选这一项
    Career      :: integer(),   %% 职业-发0表示不筛选这一项
    PageSize    :: integer(),   %% 页大小
    PageNo      :: integer(),   %% 页号
    SortId      :: integer(),   %% 排序规则id 1:时间排序 2:当前竞拍价排序 3:一口价排序
    SortOrder   :: integer().   %% 0:表示顺序（从大到小） 1:表示倒序（从小到大）
%% ---------------------------------------------------------------------------
select_goods(
    Role, SellType, Type, SubType, Series, Color, Career, PageSize, PageNo, SortId, SortOrder
) ->
    #player_status{
        guild = GuildInfo,
        figure = #figure{career = MyCareer}
    } = Role,
    GuildId = case GuildInfo of
        #status_guild{id = GId} when GId > 0 -> GId;
        _ -> 0
    end,
    SelectCondition = #market_select{
        sell_type = SellType, type = Type, sub_type = SubType, series = Series
        ,color = {Color, Color}, career = Career, guild_id = GuildId
    },
    SelectConditionList = make_select_conditon_list(Type, SubType, SelectCondition),
    Now = utime:unixtime(),
    %% 筛选满足条件的物品
    {SelectConditionList, _Now2, GoodsList} = ets:foldl(
        fun select_fun/2, {SelectConditionList, Now, []}, ?MARKET_GOODS_ETS
    ),
    %% 如果商品大类是0，即是推荐类，需要再根据等级和品质再筛选一次，只显示自己职业的。
    GoodsList2 = select_by_condition(SelectCondition#market_select{career = MyCareer}, GoodsList),
    %% 排序
    SortFun = make_sort_fun(Type, SortId, SortOrder, Now),
    GoodsListSorted = lists:sort(SortFun, GoodsList2),
    StartPos = (PageNo - 1) * PageSize + 1,
    GoodsLen = length(GoodsListSorted),
    PageGoodsList = case GoodsLen < StartPos of
        true -> %% 数据不足
            [];
        false -> %% 数据充足
            lists:sublist(GoodsListSorted, StartPos, PageSize)
    end,
    PageTotal = util:ceil(GoodsLen / PageSize),
    {PageTotal, lists:reverse(PageGoodsList)}.

%% 遍历函数
select_fun(MarketGoods, {SelectConditionList, Now, GoodsList}) ->
    GoodsList2 = case is_match(MarketGoods, Now, SelectConditionList) of
        true -> [MarketGoods | GoodsList];
        false -> GoodsList
    end,
    {SelectConditionList, Now, GoodsList2}.

%% 物品是否满足筛选条件
is_match(_MarketGoods, _Now, []) ->
    false;
is_match(MarketGoods, Now, [SelectCondition | T]) ->
    #market_goods{
        sell_type = SellType, series = Series, color = Color, type = Type
        ,sub_type = SubType, career = Careers, sex = Sex, off_time = OffTime
        ,guild_id = GuildId
    } = MarketGoods,
    #market_select{
        series = SSeries, color = {MinColor, MaxColor}, type = SType
        ,sub_type = SSubType, career = SCareer, sex = SSex, sell_type = SSellType
        ,guild_id = SGuildId
    } = SelectCondition,
    IsMatch = OffTime > Now %% 还在上架中
        andalso (SellType =:= SSellType ) %% 上架类型一样
        andalso ( %% 公会上架的话，相同的公会才可以看到
            (SellType =:= ?MARKET_SELL_TYPE_GUILD andalso GuildId =:= SGuildId)
            orelse (SellType =:= ?MARKET_SELL_TYPE_WORLD)
        )
        andalso (
            Type =:= SType andalso (SubType =:= SSubType orelse SSubType =:= 0) %% 商品类型一样
        )
        andalso (SSeries =:= 0 orelse SSeries =:= Series)   %% 满足套数条件
        %% 满足品质要求
        andalso ((MinColor =< Color andalso Color =< MaxColor) orelse MinColor =:= 99)
        andalso (Careers =:= [] orelse SCareer =:= 0 orelse lists:member(SCareer, Careers))    %% 满足职业要求
        andalso (Sex =:= 0 orelse Sex =:= SSex),    %% 满足性别要求
    case IsMatch of
        true ->
            true;
        _ ->
            is_match(MarketGoods, Now, T)
    end.

%% 推荐类的商品需要更新筛选条件,
make_select_conditon_list(0, SubType, SelectCondition) ->
    CondList = case SubType of
        0 ->
            data_market:get_recommend_cfg_list();
        _ ->
            data_market:get_recommend_cfg(SubType)
    end,
    [
        SelectCondition#market_select{
            type = SType, sub_type = SSubType, color = {ColorMin, ColorMax}
        } || {SType, SSubType, {ColorMin, ColorMax}} <- CondList
    ];
make_select_conditon_list(_Type, _SubType, SelectCondition) ->
    [SelectCondition].

%% 如果商品大类是0，即是推荐类，需要再根据等级和品质再筛选一次。
select_by_condition(Condition = #market_select{type = 0, sell_type = ?MARKET_SELL_TYPE_WORLD}, GoodsList) ->
    #market_select{color = {MinColor, MaxColor}, career = MyCareer} = Condition,
    Fun = fun(MarketGoods) ->
        #market_goods{color = Color, career = Career} = MarketGoods,
        ((MinColor =< Color andalso Color =< MaxColor) orelse MinColor =:= 99)
          andalso (Career =:= [] orelse lists:member(MyCareer, Career))
         %% 满足品质要求
    end,
    [Goods || Goods <- GoodsList, Fun(Goods)];
select_by_condition(_SelectCondition, GoodsList) ->
    GoodsList.

%% 推荐类商品排序规则
make_sort_fun(_Type = 0, _SortId, _SortOrder, Now) ->
    %% 推荐类商品默认按品质排序
    format_recommend_sort_fun(4, ?DESC, Now);
make_sort_fun(_Type, SortId, SortOrder, _Now) ->
    format_sort_fun(SortId, SortOrder).

%% 时间排序
format_sort_fun(_SortId = ?TIME_SORT, _SortOrder = ?DESC) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{price = Price, off_time = OffTime} = MarketGoods,
        #market_goods{price = Price2, off_time = OffTime2} = MarketGoods2,
        if
            OffTime > OffTime2 -> true;
            OffTime =:= OffTime2 andalso Price > Price2 -> true;
            true -> false
        end
    end;
format_sort_fun(_SortId = ?TIME_SORT, _SortOrder = ?ASC) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{price = Price, off_time = OffTime} = MarketGoods,
        #market_goods{price = Price2, off_time = OffTime2} = MarketGoods2,
        if
            OffTime < OffTime2 -> true;
            OffTime =:= OffTime2 andalso Price > Price2 -> true;
            true -> false
        end
    end;
%% 当前竞拍价排序
format_sort_fun(_SortId = ?PRICE_SORT, _SortOrder = ?DESC) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{price = Price, off_time = OffTime} = MarketGoods,
        #market_goods{price = Price2, off_time = OffTime2} = MarketGoods2,
        if
            Price > Price2 -> true;
            Price =:= Price2 andalso OffTime > OffTime2 -> true;
            true -> false
        end
    end;
format_sort_fun(_SortId = ?PRICE_SORT, _SortOrder = ?ASC) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{price = Price, off_time = OffTime} = MarketGoods,
        #market_goods{price = Price2, off_time = OffTime2} = MarketGoods2,
        if
            Price < Price2 -> true;
            Price =:= Price2 andalso OffTime > OffTime2 -> true;
            true -> false
        end
    end;
%% 一口价排序
format_sort_fun(_SortId = ?BUYOUT_PRICE_SORT, _SortOrder = ?DESC) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{off_time = OffTime, buyout_price = BPrice} = MarketGoods,
        #market_goods{off_time = OffTime2, buyout_price = BPrice2} = MarketGoods2,
        if
            BPrice > BPrice2 -> true;
            BPrice =:= BPrice2 andalso OffTime > OffTime2 -> true;
            true -> false
        end
    end;
format_sort_fun(_SortId = ?BUYOUT_PRICE_SORT, _SortOrder = ?ASC) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{off_time = OffTime, buyout_price = BPrice} = MarketGoods,
        #market_goods{off_time = OffTime2, buyout_price = BPrice2} = MarketGoods2,
        if
            BPrice < BPrice2 -> true;
            BPrice =:= BPrice2 andalso OffTime > OffTime2 -> true;
            true -> false
        end
    end.

%% 时间排序
format_recommend_sort_fun(_SortId = ?TIME_SORT, _SortOrder = ?DESC, Now) ->
    fun(MarketGoods, MarketGoods2) ->
        sort_by_time_desc(MarketGoods, MarketGoods2, Now)
    end;
%% 时间升序
format_recommend_sort_fun(_SortId = ?TIME_SORT, _SortOrder = ?ASC, Now) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{price = Price, off_time = OffTime} = MarketGoods,
        #market_goods{price = Price2, off_time = OffTime2} = MarketGoods2,
        RestShowTime = rest_show_time(MarketGoods, Now),
        RestShowTime2 = rest_show_time(MarketGoods2, Now),
        if
            RestShowTime > RestShowTime2 andalso RestShowTime > 0 -> true;
            RestShowTime < RestShowTime2 andalso RestShowTime2 > 0 -> false;
            OffTime < OffTime2 -> true;
            OffTime =:= OffTime2 andalso Price > Price2 -> true;
            true -> false
        end
    end;
%% 当前竞拍价排序
format_recommend_sort_fun(_SortId = ?PRICE_SORT, _SortOrder = ?DESC, Now) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{price = Price, off_time = OffTime} = MarketGoods,
        #market_goods{price = Price2, off_time = OffTime2} = MarketGoods2,
        RestShowTime = rest_show_time(MarketGoods, Now),
        RestShowTime2 = rest_show_time(MarketGoods2, Now),
        if
            RestShowTime > RestShowTime2 andalso RestShowTime > 0 -> true;
            RestShowTime < RestShowTime2 andalso RestShowTime2 > 0 -> false;
            Price > Price2 -> true;
            Price =:= Price2 andalso OffTime > OffTime2 -> true;
            true -> false
        end
    end;
format_recommend_sort_fun(_SortId = ?PRICE_SORT, _SortOrder = ?ASC, Now) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{price = Price, off_time = OffTime} = MarketGoods,
        #market_goods{price = Price2, off_time = OffTime2} = MarketGoods2,
        RestShowTime = rest_show_time(MarketGoods, Now),
        RestShowTime2 = rest_show_time(MarketGoods2, Now),
        if
            RestShowTime > RestShowTime2 andalso RestShowTime > 0 -> true;
            RestShowTime < RestShowTime2 andalso RestShowTime2 > 0 -> false;
            Price < Price2 -> true;
            Price =:= Price2 andalso OffTime > OffTime2 -> true;
            true -> false
        end
    end;
%% 一口价排序
format_recommend_sort_fun(_SortId = ?BUYOUT_PRICE_SORT, _SortOrder = ?DESC, Now) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{off_time = OffTime, buyout_price = BPrice} = MarketGoods,
        #market_goods{off_time = OffTime2, buyout_price = BPrice2} = MarketGoods2,
        RestShowTime = rest_show_time(MarketGoods, Now),
        RestShowTime2 = rest_show_time(MarketGoods2, Now),
        if
            RestShowTime > RestShowTime2 andalso RestShowTime > 0 -> true;
            RestShowTime < RestShowTime2 andalso RestShowTime2 > 0 -> false;
            BPrice > BPrice2 -> true;
            BPrice =:= BPrice2 andalso OffTime > OffTime2 -> true;
            true -> false
        end
    end;
format_recommend_sort_fun(_SortId = ?BUYOUT_PRICE_SORT, _SortOrder = ?ASC, Now) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{off_time = OffTime, buyout_price = BPrice} = MarketGoods,
        #market_goods{off_time = OffTime2, buyout_price = BPrice2} = MarketGoods2,
        RestShowTime = rest_show_time(MarketGoods, Now),
        RestShowTime2 = rest_show_time(MarketGoods2, Now),
        if
            RestShowTime > RestShowTime2 andalso RestShowTime > 0 -> true;
            RestShowTime < RestShowTime2 andalso RestShowTime2 > 0 -> false;
            BPrice < BPrice2 -> true;
            BPrice =:= BPrice2 andalso OffTime > OffTime2 -> true;
            true -> false
        end
    end;
%% 按物品品质排序
format_recommend_sort_fun(_SortId = 4, _SortOrder = ?DESC, Now) ->
    fun(MarketGoods, MarketGoods2) ->
        #market_goods{color = Color1} = MarketGoods,
        #market_goods{color = Color2} = MarketGoods2,
        if
            Color1 > Color2 -> true;
            Color1 < Color2 -> false;
            true ->
                %% 品质相同，按时间排序
                sort_by_time_asc(MarketGoods, MarketGoods2, Now)
        end
    end;
format_recommend_sort_fun(_, _, _) -> true.

%% 时间降序
sort_by_time_desc(MarketGoods, MarketGoods2, Now) ->
    #market_goods{price = Price, off_time = OffTime} = MarketGoods,
    #market_goods{price = Price2, off_time = OffTime2} = MarketGoods2,
    RestShowTime = rest_show_time(MarketGoods, Now),
    RestShowTime2 = rest_show_time(MarketGoods2, Now),
    if
        RestShowTime > RestShowTime2 andalso RestShowTime > 0 -> true;
        RestShowTime < RestShowTime2 andalso RestShowTime2 > 0 -> false;
        Price > Price2 -> true;
        Price =:= Price2 andalso OffTime > OffTime2 -> true;
        true -> false
    end.

%% 时间升序
%% 可以购买的放前面，展示期的放后面
%% 可以购买的off_time小放前面，展示期内展示时间短的放前面
sort_by_time_asc(MarketGoods, MarketGoods2, Now) ->
    #market_goods{off_time = OffTime} = MarketGoods,
    #market_goods{off_time = OffTime2} = MarketGoods2,
    RestShowTime = rest_show_time(MarketGoods, Now),
    RestShowTime2 = rest_show_time(MarketGoods2, Now),
    if
        RestShowTime < 0 andalso RestShowTime2 < 0 ->
            %% 可以直接购买
            OffTime < OffTime2;
        RestShowTime > 0 andalso RestShowTime2 > 0->
            %% 都在展示期内
            RestShowTime < RestShowTime2;
        RestShowTime > 0 ->
            %% 展示期内，放后面
            false;
        RestShowTime < 0 ->
            %% 可以购买，放后面
            true;
        true -> false
    end.
%% ---------------------------------------------------------------------------
%% @doc 物品剩余展示时间
-spec rest_show_time(MarketGoods, Now) -> RestShowTime when
    MarketGoods :: #market_goods{},
    Now         :: integer(),
    RestShowTime:: integer().
%% ---------------------------------------------------------------------------
rest_show_time(#market_goods{goods_type_id = GoodsTypeId, time = Time}, Now) ->
    #ets_goods_type{market_show_time = ShowTime} = data_goods_type:get(GoodsTypeId),
    Time + ShowTime - Now;
rest_show_time(_, _) -> false.


%% ---------------------------------------------------------------------------
%% @doc 竞拍物品
-spec bid_for_goods(Role, MarketId, Price) -> {false, Reason} | ok when
    Role        :: #player_status{},
    MarketId    :: integer(),
    Price       :: integer(),
    Reason      :: integer().
%% ---------------------------------------------------------------------------
bid_for_goods(Role, MarketId, Price) ->
    #player_status{
        id = RoleId, coin = Coin, figure = #figure{name = Name}, guild = GuildInfo
    } = Role,
    MarketGoods = case ets:lookup(?MARKET_GOODS_ETS, MarketId) of
        [MG = #market_goods{} | _] -> MG;
        _ -> false
    end,
    Now = utime:unixtime(),
    GuildId = case GuildInfo of
        #status_guild{id = GId} -> GId;
        _ -> 0
    end,
    RestTime = rest_show_time(MarketGoods, Now),
    if
        not (is_record(MarketGoods, market_goods)) ->
            {false, ?ERRCODE(err155_market_goods_not_exist)};
        MarketGoods#market_goods.off_time =< Now ->
            {false, ?ERRCODE(err155_market_goods_off)};
        %% 不能买自己的商品
        MarketGoods#market_goods.role_id =:= RoleId ->
            {false, ?ERRCODE(err155_market_goods_yourself)};
        %% 价格不能低于现在的竞拍价格
        (
            MarketGoods#market_goods.bidder_id =/= 0 andalso
            Price =< MarketGoods#market_goods.price
        ) orelse (
            MarketGoods#market_goods.bidder_id =:= 0 andalso
            Price < MarketGoods#market_goods.price
        ) ->
            {false, ?ERRCODE(err155_market_goods_price_error)};
        %% 自己已经是最高出价了，不能再出, 除非一口价
        (
            MarketGoods#market_goods.bidder_id =:= RoleId
                andalso
                (
                    (
                        MarketGoods#market_goods.buyout_price > 0
                        andalso Price < MarketGoods#market_goods.buyout_price
                    )
                    orelse
                        (MarketGoods#market_goods.buyout_price =< 0)
                )
        ) ->
            {false, ?ERRCODE(err155_market_goods_your_price_max)};
        %% 玩家手动上架到公会的物品除了没有配置一口价的物品可以竞拍，其他的只能用一口价购买
        (
            MarketGoods#market_goods.sell_type =:= ?MARKET_SELL_TYPE_GUILD
                andalso MarketGoods#market_goods.role_id > ?MARKET_SYS_MAX_ROLE_ID
                andalso MarketGoods#market_goods.buyout_price > 0
                andalso Price < MarketGoods#market_goods.buyout_price
        ) ->
            {false, ?ERRCODE(err155_market_guild_cannot_bid)};
        %% 上架到公会的物品，本公会的人才可以购买
        (
            MarketGoods#market_goods.sell_type =:= ?MARKET_SELL_TYPE_GUILD
            andalso MarketGoods#market_goods.guild_id =/= GuildId
        ) ->
            {false, ?ERRCODE(err155_market_goods_not_your_guild)};
        %% 公示期间不能竞拍
        RestTime > 0 ->
            {false, ?ERRCODE(err155_market_goods_is_showing)};
        %% 判断自己钱够不够, 上次出价是自己，那只能是一口价
        (
            MarketGoods#market_goods.bidder_id =:= RoleId
            andalso MarketGoods#market_goods.buyout_price - MarketGoods#market_goods.price > Coin
        )
            orelse
        (
            MarketGoods#market_goods.bidder_id =/= RoleId
                andalso Price > Coin
        )->
            {false, ?ERRCODE(coin_not_enough)};
        true ->
            market_mgr:cast(
                {mgr_bid_for_goods, RoleId, Name, self(), MarketId, Price}
            ),
            ok
    end.


%% ---------------------------------------------------------------------------
%% @doc 竞拍物品（交易市场进程回调）
%% 注意！！需要调用gen_server:reply/2
-spec bid_for_goods_callback(Role, From, MarketId, Price, BidInfo, MarketGoods) ->
    ok | {ok, NewRole} when
    Role        :: #player_status{},
    From        :: tuple(),
    MarketId    :: integer(),
    Price       :: integer(),
    BidInfo     :: #market_bid{},
    MarketGoods :: #market_goods{},
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
bid_for_goods_callback(Role, From, MarketId, Price, _BidInfo, MarketGoods) ->
    CostList = [{?TYPE_COIN, 0, Price}],
    #market_goods{goods_type_id = GoodsTypeId} = MarketGoods,
    About = io_lib:format("~w:~w", [GoodsTypeId, MarketId]),
    case lib_goods_api:cost_object_list_with_check(Role, CostList, buy_market_goods, About) of
        {false, ErrorCode, _Role2} ->
            gen_server:reply(From, {false, ErrorCode}),
            ok;
        {true, Role2, _ColstList2} ->
            gen_server:reply(From, {ok}),
            lib_player:send_attribute_change_notify(Role2, ?NOTIFY_MONEY),
            %% 写日志
            {ok, Role2}
    end.


%% ---------------------------------------------------------------------------
%% @doc 发送玩家正在竞价的商品列表
-spec send_role_bid_goods_info(Role) -> ok when
    Role        :: #player_status{}.
%% ---------------------------------------------------------------------------
send_role_bid_goods_info(Role) ->
    BidInfoList = role_bid_info_list(Role),
    GoodsList = pack_bid_info_list(BidInfoList, []),
    #player_status{market_data = MarketData} = Role,
    #market_data{like_list = LikeList} = MarketData,
    GoodsInfoList = pack_market_goods(LikeList, BidInfoList, GoodsList, []),
    {ok, Bin} = pt_155:write(15502, [GoodsInfoList]),
    lib_server_send:send_to_sid(Role#player_status.sid, Bin),
    ok.

pack_bid_info_list([], GoodsList) ->
    GoodsList;
pack_bid_info_list([MarketBid | T], GoodsList) ->
    #market_bid{id = MarketId} = MarketBid,
    GoodsList2 = case ets:lookup(?MARKET_GOODS_ETS, MarketId) of
        [MG = #market_goods{} | _] -> [MG | GoodsList];
        _ -> GoodsList
    end,
    pack_bid_info_list(T, GoodsList2).


%% ---------------------------------------------------------------------------
%% @doc 玩家出价数据
-spec role_bid_info_list(Role) -> MarketBidList when
    Role            :: #player_status{},
    MarketBidList   :: [#market_bid{}].
%% ---------------------------------------------------------------------------
role_bid_info_list(Role) ->
    #player_status{id = RoleId} = Role,
    MatchSpec = ets:fun2ms(
        fun(#market_bid{role_id = PlayerId} = MarketBid)
            when PlayerId =:= RoleId -> MarketBid
        end
    ),
    BidInfoList = ets:select(?MARKET_BID_ETS, MatchSpec),
    BidInfoList.


%% ---------------------------------------------------------------------------
%% @doc 发送玩家已上架物品列表
-spec send_role_market_goods(Role) -> ok when
    Role        :: #player_status{}.
%% ---------------------------------------------------------------------------
send_role_market_goods(Role) ->
    #player_status{id = RoleId} = Role,
    MatchSpec = ets:fun2ms(
        fun(#market_goods{role_id = PlayerId} = MarketGoods)
            when PlayerId =:= RoleId -> MarketGoods
        end
    ),
    GoodsList = ets:select(?MARKET_GOODS_ETS, MatchSpec),
    GoodsInfoList = pack_role_market_goods(GoodsList, []),
    {ok, Bin} = pt_155:write(15503, [GoodsInfoList]),
    lib_server_send:send_to_sid(Role#player_status.sid, Bin),
    ok.

pack_role_market_goods([], GoodsInfoList) ->
    GoodsInfoList;
pack_role_market_goods([MarketGoods | T], GoodsInfoList) ->
    #market_goods{
        id = Id, goods_type_id = GoodsTypeId, num = Num, price = Price
        ,sell_type = SellType, off_time = OffTime, extra_attr = ExtraAttr
        ,buyout_price = BuyoutPrice, time = Time, bidder_id = BidderId
    } = MarketGoods,
    IsThereABid = case BidderId =/= 0 of
                      true -> 1;
                      false -> 0
                  end,
    Info = {
        Id, GoodsTypeId, Time, OffTime, Price, BuyoutPrice, SellType
        ,Num, IsThereABid, extra_data_pack(ExtraAttr)
    },
    GoodsInfoList2 = [Info | GoodsInfoList],
    pack_role_market_goods(T, GoodsInfoList2).

%% ---------------------------------------------------------------------------
%% @doc 发送玩家关注的商品列表
-spec send_role_like_market_goods(Role) -> ok when
    Role        :: #player_status{}.
%% ---------------------------------------------------------------------------
%% 3商品过期|4未过期手动下架5|成交
send_role_like_market_goods(Role) ->
    %% 玩家正在竞价的物品
    BidInfoList = role_bid_info_list(Role),
    #player_status{market_data = MarketData} = Role,
    #market_data{like_list = LikeList} = MarketData,
    GoodsList = pack_like_goods_list(BidInfoList ++ LikeList, []),
    GoodsInfoList = pack_market_goods(LikeList, BidInfoList, GoodsList, []),
    {ok, Bin} = pt_155:write(15512, [GoodsInfoList]),
    lib_server_send:send_to_sid(Role#player_status.sid, Bin),
    ok.

pack_like_goods_list([], GoodsList) ->
    GoodsList;
pack_like_goods_list([MarketIdInfo | T], GoodsList) ->
    MarketId = case MarketIdInfo of
        #market_bid{id = MId} -> MId;
        #market_like{id = MId} -> MId;
        _ -> false
    end,
    GoodsList2 = case lists:keymember(MarketId, #market_goods.id, GoodsList) of
        false when is_integer(MarketId) ->
            case ets:lookup(?MARKET_GOODS_ETS, MarketId) of
                [MG = #market_goods{} | _] -> [MG | GoodsList];
                _ -> GoodsList
            end;
        _ ->
            GoodsList
    end,
    pack_like_goods_list(T, GoodsList2).



%% ---------------------------------------------------------------------------
%% @doc 物品下架
-spec put_goods_down(Role, MarketId) ->
    {false, ErrCode} | {true, NewRole} when
    Role        :: #player_status{},
    MarketId    :: integer(),
    ErrCode     :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
put_goods_down(Role, MarketId) ->
    #player_status{id = RoleId} = Role,
    MarketGoods = case ets:lookup(?MARKET_GOODS_ETS, MarketId) of
        [MG = #market_goods{} | _] -> MG;
        _ -> false
    end,
    if
        not is_record(MarketGoods, market_goods) ->
            {false, ?ERRCODE(err155_market_goods_not_exist)};
        MarketGoods#market_goods.role_id =/= RoleId ->
            {false, ?ERRCODE(err155_market_goods_not_yours)};
        MarketGoods#market_goods.bidder_id =/= 0 ->
            {false, ?ERRCODE(err155_market_goods_is_offer)};
        true ->
            #market_goods{
                goods_type_id = GoodsTypeId, num = Num, extra_attr = ExtraAttr
            } = MarketGoods,
            Award =
                case ExtraAttr == [] of
                    true  ->
                        [{?TYPE_GOODS, GoodsTypeId, Num}];
                    false ->
                        [{?TYPE_ATTR_GOODS, GoodsTypeId, Num, ExtraAttr}]
                end,
            case lib_goods_api:can_give_goods(Role, Award) of
                true ->
                    market_mgr:cast({mrg_put_goods_down, RoleId, self(), MarketId}),
                    {true, Role};
                false ->
                    {false, ?ERRCODE(err150_no_cell)}
            end
    end.


%% ---------------------------------------------------------------------------
%% @doc 物品下架-交易市场进程回调
-spec put_goods_down_callback(Role, MarketGoods) -> {ok, NewRole} when
    Role        :: #player_status{},
    MarketGoods :: #market_goods{},
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
put_goods_down_callback(Role, MarketGoods) ->
    #player_status{sid = Sid} = Role,
    #market_goods{
        goods_type_id = GoodsTypeId, num = Num, extra_attr = ExtraAttr
    } = MarketGoods,
    market_db:del_market_goods(MarketGoods),
    Award = [{?TYPE_ATTR_GOODS, GoodsTypeId, Num, ExtraAttr}],
    lib_goods_api:send_reward(Role, Award, market_down),
    {ok, Bin} = pt_155:write(15508, [?SUCCESS]),
    lib_server_send:send_to_sid(Sid, Bin),
    log_market_operation(?MARKET_OPERATION_MANUAL_OFF, MarketGoods, 0),
    Now = utime:unixtime(),
    MarketGoods#market_goods.off_time > Now andalso
        ta_agent_fire:auction_state(Role, {MarketGoods, 2, 0}),
    {ok, Role}.

%% ---------------------------------------------------------------------------
%% @doc 交易市场日志
-spec log_market_operation(OperationType, MarketGoods, GetRoleId) -> ok when
    OperationType   :: integer(),
    %% 操作类型:1:手动上架|2:手动下架|3:成交|4:系统删除|5:系统上架|6:系统下架|7:下架流拍至世界
    MarketGoods     :: #market_goods{},
    GetRoleId       :: integer().   %% 获得玩家Id
%% ---------------------------------------------------------------------------
log_market_operation(OperationType, MarketGoods, GetRoleId) ->
    #market_goods{
        id = Id, role_id = RoleId, goods_type_id = GoodsTypeId, num = Num
        ,lev = Lev, color = Color, price = Price, sell_type = SellType
        ,guild_id = GuildId, time = Time,extra_attr = ExtraAttr
        ,buyout_price = BuyoutPrice
    } = MarketGoods,
    case OperationType of
        ?MARKET_OPERATION_SETTLEMENT ->
            Tax = get_market_tex(Price),
            DealType = case Price >= BuyoutPrice andalso BuyoutPrice > 0 of
                true -> 2;
                _ -> 1
            end;
        _ ->
            Tax = 0,
            DealType = 0
    end,
    LogTime = utime:unixtime(),
    Figure = lib_role:get_role_figure(RoleId),
    GetFigure = lib_role:get_role_figure(GetRoleId),
    LogData = [
        Id, RoleId, Figure#figure.name, Figure#figure.vip, GetRoleId, GetFigure#figure.name, GetFigure#figure.vip, GoodsTypeId, Num,
        Lev, Color, Price, Tax, SellType, GuildId, LogTime, DealType, OperationType, util:term_to_string(ExtraAttr), Time
    ],
    lib_log_api:log_market_operation(LogData),
    ok.

%% 竞价日志
log_market_race(MarketGoodsNew, MarketGoodsOld) ->
    #market_goods{
        price = Price, bidder_id = RoleId, goods_type_id = GoodsTypeId
        ,id = MarketId
    } = MarketGoodsNew,
    #market_goods{
        price = LastPrice, off_time = OffTime, bidder_id = LastRole
    } = MarketGoodsOld,
    #ets_goods_type{trade_price = TradePrice,buyout_price = BuyoutPrice} = data_goods_type:get(GoodsTypeId),
    OriginPrice = case TradePrice > 0 of
        true  -> TradePrice;
        false -> BuyoutPrice
    end,
    Figure = lib_role:get_role_figure(RoleId),
    LastRoleFigure = lib_role:get_role_figure(LastRole),
    Now = utime:unixtime(),
    LogData = [
        MarketId, RoleId, Figure#figure.name, Figure#figure.vip, Price, LastRole, LastRoleFigure#figure.name, LastRoleFigure#figure.vip,
        LastPrice, OriginPrice, GoodsTypeId, OffTime - Now, Now
    ],
    lib_log_api:log_market_race(LogData),
    ok.

%% 获得交易税
get_market_tex(Price) ->
    TaxRates = case data_market:get_config(tax_rates) of
                   Rates when is_float(Rates) -> Rates;
                   _ -> 1
               end,
    util:floor(Price * TaxRates).
%% ---------------------------------------------------------------------------
%% @doc 商品再上架
-spec put_on_goods_again(Role, MarketId, SellType, IsToWorld) ->
    {false, ErrCode} | {true, MarketGoods, NewRole} when
    Role        :: #player_status{},
    MarketId    :: integer(),
    SellType    :: integer(),
    IsToWorld   :: integer(),
    ErrCode     :: integer(),
    MarketGoods :: #market_goods{},
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
put_on_goods_again(Role, MarketId, SellType, _IsToWorldDis) ->
    #player_status{id = RoleId, guild = GuildInfo} = Role,
    MarketGoods = case ets:lookup(?MARKET_GOODS_ETS, MarketId) of
        [MG = #market_goods{} | _] -> MG;
        _ -> false
    end,
    Now = utime:unixtime(),
    if
        not (is_record(MarketGoods, market_goods)) ->
            {false, ?ERRCODE(err155_market_goods_not_exist)};
        MarketGoods#market_goods.off_time > Now ->
            {false, ?ERRCODE(err155_market_goods_not_off)};
        MarketGoods#market_goods.role_id =/= RoleId ->
            {false, ?ERRCODE(err155_market_goods_not_yours)};
        true ->
            #market_goods{id = Id, goods_id = GoodsId, extra_attr = ExtraAttr,
                goods_type_id = GoodsTypeId, num = Num, is_to_world = IsToWorld} = MarketGoods,
            case make_market_goods(RoleId, GoodsId, GoodsTypeId, Num, SellType, GuildInfo#status_guild.id, IsToWorld, true) of
                false ->
                    {false, ?MISSING_CONFIG};
                MarketGoods1 ->
                    %% 复用之前的id
                    MarketGoods2 = MarketGoods1#market_goods{id = Id, extra_attr = ExtraAttr},
                    NewRole = do_put_on_after(Role, MarketGoods2, GoodsTypeId, Num),
                    {true, MarketGoods2, NewRole}
            end
    end.

%% 一键再上架
batch_put_goods_on_again(Role) ->
    #player_status{id = RoleId} = Role,
    Now = utime:unixtime(),
    MatchSpec = ets:fun2ms(
        fun(#market_goods{role_id = PlayerId, off_time = OffTime} = MarketGoods)
            when PlayerId =:= RoleId andalso OffTime < Now -> MarketGoods
        end
    ),
    case ets:select(?MARKET_GOODS_ETS, MatchSpec) of
        [] ->
            {false, ?ERRCODE(err155_no_goods_put)};
        List ->
            batch_put_goods_on_again_each(Role, List)
    end.

batch_put_goods_on_again_each(Role, []) ->
    {true, Role};
batch_put_goods_on_again_each(Role, [MarketGoods|List]) ->
    #market_goods{id = MarketId, sell_type = SellType, is_to_world = IsToWorld} = MarketGoods,
    case put_on_goods_again(Role, MarketId, SellType, IsToWorld) of
        {false, Error} ->
            {false, Error};
        {true, _MarketGoods2, NewRole} ->
            batch_put_goods_on_again_each(NewRole, List)
    end.

%% ---------------------------------------------------------------------------
%% @doc 角色更新成交记录: 在线玩家处理，卖家或者买家
-spec role_update_market_deal_log(Role, RoleDealLog, BuyType, WhoAmI) -> {ok, NewRole} when
    Role        :: #player_status{},
    RoleDealLog :: #market_role_deal_log{},
    BuyType     :: integer(),
    WhoAmI      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
role_update_market_deal_log(Role, RoleDealLog, BuyType, WhoAmI) ->
    #player_status{market_data = MarketData} = Role,
    #market_data{deal_log_list = DealLogList} = MarketData,
    DealLogList2 = [RoleDealLog | DealLogList],
    MaxLog = data_market:get_log_type_cfg(3, 0),
    DealLogList3 = case length(DealLogList2) > MaxLog of
        true -> lists:sublist(DealLogList2, MaxLog);
        false -> DealLogList2
    end,
    MarketData2 = MarketData#market_data{deal_log_list = DealLogList3},
    Role2 = Role#player_status{market_data = MarketData2},
    send_deal_log(Role2, ?MARKET_DEAL_LOG_ROLE),
    
    %% 通知卖家，物品卖出去了
    WhoAmI == ?MARKET_SELLER andalso market:send_role_market_goods(Role2),
    %% 一些任务类的行为
    TriggerRole = trigger_others(Role2, RoleDealLog, BuyType, WhoAmI),
    
    {ok, TriggerRole}.

%% 卖家卖出物品，那就跟买家的身份和方式无关了
trigger_others(Role, RoleDealLog, _, ?MARKET_SELLER) ->
    RoleThen =
    case data_goods_type:get(RoleDealLog#market_role_deal_log.goods_type_id) of
        #ets_goods_type{type = ?GOODS_TYPE_EQUIP}->
            %% 触发装备任务:出售或者购买都算一次
            Role1 = equipment_task:event_market_buy_check(Role, RoleDealLog#market_role_deal_log.sell_type, RoleDealLog#market_role_deal_log.goods_type_id),
            %% 装备出售成就
            {ok, Role2} = lib_achievement_api:shop_sell_equip_event(Role1, 1),
            Role2;
        _ ->
            Role
    end,
    RoleEnd = RoleThen,
    RoleEnd;
%% 购买了物品
trigger_others(Role, RoleDealLog, _, ?MARKET_BUYER) ->
    RoleThen =
    case data_goods_type:get(RoleDealLog#market_role_deal_log.goods_type_id) of
        #ets_goods_type{type = ?GOODS_TYPE_EQUIP} ->
            %% 触发装备任务:出售或者购买都算一次
            Role1 = equipment_task:event_market_buy_check(Role, RoleDealLog#market_role_deal_log.sell_type, RoleDealLog#market_role_deal_log.goods_type_id),
            %% 装备购买成就
            % {ok, Role2} = lib_achievement_api:shop_buy_equip_event(Role1, 1),
            {ok, Role3} = lib_seven_days_goal:do_finish_task(Role1, ?MOD_MARKET, 1),
            Role3;
        _ ->
            Role
    end,
    %% 社团竞拍成就
    RoleThen1 =
    case RoleDealLog#market_role_deal_log.sell_type =:= ?MARKET_SELL_TYPE_GUILD of
        true  ->
            {ok, RoleThenNew} = lib_achievement_api:guild_auction_event(RoleThen, 1),
            RoleThenNew;
        false ->
            RoleThen
    end,
    RoleEnd = RoleThen1,
    RoleEnd;
trigger_others(Role, _RoleDealLog, _, _) ->
    Role.

%% 离线玩家出售了物品/离线的玩家竞拍到物品。一般是竞拍后，超时结算，买家不在线/或者卖家不在线
offline_update_market_deal_log(RoleId, RoleDealLog, BuyType, WhoAmI) ->
    %% 触发装备任务
    case {BuyType, WhoAmI} of
        {_, ?MARKET_BUYER}  ->  %% 普通购买
            %% 触发装备任务:出售或者购买都算一次-离线处理
            equipment_task:event_market_buy_check(RoleId, RoleDealLog#market_role_deal_log.sell_type, RoleDealLog#market_role_deal_log.goods_type_id),
            lib_seven_days_goal:offline_do_finish_task(RoleId, ?MOD_MARKET, 1, 1);
        {_, ?MARKET_SELLER} ->  %% 卖出都可以的
            %% 触发装备任务:出售或者购买都算一次-离线处理
            equipment_task:event_market_buy_check(RoleId, RoleDealLog#market_role_deal_log.sell_type, RoleDealLog#market_role_deal_log.goods_type_id);
        _ ->
            ignore
    end.

%% 完成交易后：更新个人玩家成交记录
update_role_deal(RoleId, RoleDealLog, BuyType, Who) ->
    case lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, role_update_market_deal_log, [RoleDealLog, BuyType, Who]) of
        skip ->
            spawn(
                fun() ->
                    offline_update_market_deal_log(RoleId, RoleDealLog, BuyType, Who)
                end
            );
        _ ->
            ok
    end.
%% ---------------------------------------------------------------------------
%% @doc 角色登录
-spec login(Role) -> NewRole when
    Role        :: #player_status{},
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
login(Role) ->
    #player_status{id = RoleId} = Role,
    DealLogList = market_db:load_role_deal_log(RoleId),
    DealLogList2 = lists:keysort(#market_role_deal_log.deal_time, DealLogList),
    DealLogList3 = lists:reverse(DealLogList2),
    Len = length(DealLogList3),
    MaxLog = data_market:get_log_type_cfg(3, 0),
    NewDealLogList = case Len > MaxLog of
        true ->
            DealLogList4 = lists:sublist(DealLogList3, MaxLog),
            #market_role_deal_log{deal_time = DealTime} = lists:last(DealLogList4),
            %% 删除多余的记录
            market_db:del_role_deal_log(RoleId, DealTime),
            DealLogList4;
        _ ->
            DealLogList3
    end,
    LikeList = market_db:load_market_like_infos(RoleId),
    LikeNum = length(LikeList),
    ReminderList = market_db:load_reminder_info(RoleId),
    MarketData = #market_data{
        deal_log_list = NewDealLogList, like_list = LikeList, like_num = LikeNum
        ,reminder_list = ReminderList
    },
    Role#player_status{market_data = MarketData}.


%% ---------------------------------------------------------------------------
%% @doc 发送交易市场成交记录
-spec send_deal_log(Role, LogType) -> ok when
    Role        :: #player_status{},
    LogType     :: integer().
%% ---------------------------------------------------------------------------
send_deal_log(Role, LogType = ?MARKET_DEAL_LOG_WORLD) ->
    market_mgr:cast({mgr_send_deal_log, LogType, Role#player_status.sid});
send_deal_log(Role, LogType = ?MARKET_DEAL_LOG_GUILD) ->
    market_mgr:cast({mgr_send_deal_log, LogType, Role#player_status.sid});
send_deal_log(Role, LogType = ?MARKET_DEAL_LOG_ROLE) ->
    #player_status{market_data = MarketData, sid = Sid} = Role,
    #market_data{deal_log_list = DealLogList} = MarketData,
    LogList = pack_role_deal_log_list(DealLogList, []),
    {ok, Bin} = pt_155:write(15504, [LogType, LogList]),
    lib_server_send:send_to_sid(Sid, Bin),
    ok;
send_deal_log(_Role, _LogType) ->
    ?ERR("send_deal_log: ~w", [_LogType]),
    ok.

pack_role_deal_log_list([], LogInfoList) ->
    LogInfoList;
pack_role_deal_log_list([DealLog | T], LogInfoList) ->
    #market_role_deal_log{
        id = Id, goods_type_id = GoodsTypeId, num = Num, price = Price
        ,deal_time = DealTime, deal_type = DealType, extra_attr = ExtraAttr
    } = DealLog,
    LogInfo = {
        Id, GoodsTypeId, DealTime, Price, DealType, Num, <<>>
        ,extra_data_pack(ExtraAttr)
    },
    pack_role_deal_log_list(T, [LogInfo | LogInfoList]).



%% ---------------------------------------------------------------------------
%% @doc 关注物品
-spec like_market_goods(Role, MarketId) -> {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    MarketId    :: integer(),
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
like_market_goods(Role, MarketId) ->
    #player_status{id = RoleId, market_data = MarketData} = Role,
    MarketGoods = case ets:lookup(?MARKET_GOODS_ETS, MarketId) of
        [MG = #market_goods{} | _] -> MG;
        _ -> false
    end,
    #market_data{like_list = LikeList, like_num = LikeNum} = MarketData,
    Now = utime:unixtime(),
    IsMember = lists:keymember(MarketId, #market_like.id, LikeList),
    %% 关注上限
    LikeMaxNum = case data_market:get_config(focus_max) of
        FocusMax when is_integer(FocusMax) -> FocusMax;
        _ -> 10
    end,
    if
        not (is_record(MarketGoods, market_goods)) ->
            {false, ?ERRCODE(err155_market_goods_not_exist)};
        MarketGoods#market_goods.off_time =< Now ->
            {false, ?ERRCODE(err155_market_goods_off)};
        MarketGoods#market_goods.role_id =:= RoleId ->
            {false, ?ERRCODE(err155_cannot_like_yourself)};
        IsMember ->
            {false, ?ERRCODE(err155_already_like)};
        LikeNum >= LikeMaxNum ->
            {false, ?ERRCODE(err155_like_num_max)};
        true ->
            LikeInfo = #market_like{id = MarketId, role_id = RoleId, time = Now},
            market_db:save_market_like_info(LikeInfo),
            LikeList2 = [LikeInfo | LikeList],
            MarketData2 = MarketData#market_data{
                like_list = LikeList2, like_num = LikeNum + 1
            },
            Role2 = Role#player_status{market_data = MarketData2},
            {ok, Role2}
    end.


%% ---------------------------------------------------------------------------
%% @doc 取消关注物品
-spec del_like_market_goods(Role, MarketId) -> {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    MarketId    :: integer(),
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
del_like_market_goods(Role, MarketId) ->
    #player_status{market_data = MarketData} = Role,
    #market_data{like_list = LikeList, like_num = LikeNum} = MarketData,
    IsMember = lists:keymember(MarketId, #market_like.id, LikeList),
    if
        not IsMember ->
            {false, ?SUCCESS};
        true ->
            {
                value, LikeInfo, LikeList2
            } = lists:keytake(MarketId, #market_like.id, LikeList),
            market_db:del_market_like_info(LikeInfo),
            MarketData2 = MarketData#market_data{
                like_list = LikeList2, like_num = LikeNum - 1
            },
            Role2 = Role#player_status{market_data = MarketData2},
            {ok, Role2}
    end.


%% ---------------------------------------------------------------------------
%% @doc 取消关注物品
-spec del_like_market_goods_callback(Role, MarketId) -> {ok, NewRole} when
    Role        :: #player_status{},
    MarketId    :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
del_like_market_goods_callback(Role, MarketId) ->
    #player_status{market_data = MarketData} = Role,
    #market_data{like_list = LikeList, like_num = LikeNum} = MarketData,
    IsMember = lists:keymember(MarketId, #market_like.id, LikeList),
    if
        not IsMember ->
            {ok, Role};
        true ->
            LikeList2 = lists:keydelete(MarketId, #market_like.id, LikeList),
            MarketData2 = MarketData#market_data{
                like_list = LikeList2, like_num = LikeNum - 1
            },
            Role2 = Role#player_status{market_data = MarketData2},
            market:send_role_like_market_goods(Role2),
            {ok, Role2}
    end.



%% ---------------------------------------------------------------------------
%% @doc 添加入库提醒
-spec add_reminder(Role, GoodsTypeId) -> {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    GoodsTypeId :: integer(),
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
add_reminder(Role, GoodsTypeId) ->
    #player_status{id = RoleId, market_data = MarketData} = Role,
    #market_data{reminder_list = ReminderList} = MarketData,
    IsMember = lists:member(GoodsTypeId, ReminderList),
    GoodsCfg = data_goods_type:get(GoodsTypeId),
    Length = length(ReminderList),
    if
        IsMember ->
            {ok, Role};
        not (is_record(GoodsCfg, ets_goods_type)) ->
            {false, ?ERRCODE(err155_goods_not_exist)};
        Length >= 50 ->
            {false, ?ERRCODE(err155_reminder_limit)};
        true ->
            ReminderList2 = [GoodsTypeId | ReminderList],
            MarketData2 = MarketData#market_data{reminder_list = ReminderList2},
            Role2 = Role#player_status{market_data = MarketData2},
            Reminder = #market_reminder{
                key = {RoleId, GoodsTypeId}, role_id = RoleId
                ,goods_type_id = GoodsTypeId
            },
            ets:insert(?MARKET_REMINDER_ETS, Reminder),
            market_db:save_reminder_info(RoleId, ReminderList2),
            {ok, Role2}
    end.


%% ---------------------------------------------------------------------------
%% @doc 取消入库提醒
-spec remove_reminder(Role, GoodsTypeId) -> {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    GoodsTypeId :: integer(),
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
remove_reminder(Role, GoodsTypeId) ->
    #player_status{id = RoleId, market_data = MarketData} = Role,
    #market_data{reminder_list = ReminderList} = MarketData,
    IsMember = lists:member(GoodsTypeId, ReminderList),
    GoodsCfg = data_goods_type:get(GoodsTypeId),
    if
        not IsMember ->
            {ok, Role};
        not (is_record(GoodsCfg, ets_goods_type)) ->
            {false, ?ERRCODE(err155_goods_not_exist)};
        true ->
            ReminderList2 = lists:delete(GoodsTypeId, ReminderList),
            MarketData2 = MarketData#market_data{reminder_list = ReminderList2},
            Role2 = Role#player_status{market_data = MarketData2},
            market_db:save_reminder_info(RoleId, ReminderList2),
            ets:delete(?MARKET_REMINDER_ETS, {RoleId, GoodsTypeId}),
            {ok, Role2}
    end.


%% ---------------------------------------------------------------------------
%% @doc 取消入库提醒
-spec remove_reminder_by_type(Role, Type, SubType) ->
    {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    Type        :: integer(),
    SubType     :: integer(),
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
remove_reminder_by_type(Role, Type, SubType) ->
    #player_status{id = RoleId, market_data = MarketData} = Role,
    #market_data{reminder_list = ReminderList} = MarketData,
    if
        Type =:= 0 ->
            {false, ?FAIL};
        true ->
            ReminderList2 = remove_reminder_by_type_do(
                RoleId, Type, SubType, ReminderList, []
            ),
            MarketData2 = MarketData#market_data{reminder_list = ReminderList2},
            Role2 = Role#player_status{market_data = MarketData2},
            case ReminderList2 of
                ReminderList ->
                    ok;
                _ ->
                    market_db:save_reminder_info(RoleId, ReminderList2)
            end,
            {ok, Role2}
    end.

remove_reminder_by_type_do(_RoleId, _Type, _SubType, [], ReminderList) ->
    ReminderList;
remove_reminder_by_type_do(RoleId, Type, SubType, [GoodsTypeId | T], ReminderList) ->
    ReminderList2 = case data_goods_type:get(GoodsTypeId) of
        #ets_goods_type{
            sell_category = Type, sell_subcategory = GSubType
        } when (GSubType =:= SubType orelse SubType =:= 0) -> %% 取消入库提醒
            ets:delete(?MARKET_REMINDER_ETS, {RoleId, GoodsTypeId}),
            ReminderList;
        _ ->
            [GoodsTypeId | ReminderList]
    end,
    remove_reminder_by_type_do(RoleId, Type, SubType, T, ReminderList2).




%% ---------------------------------------------------------------------------
%% @doc 系统对物品奖励进行公会上架。
-spec sys_put_on_goods(GuildId, AwardList) -> ok when
    GuildId     :: integer(),
    AwardList   :: list(). %% 目前物品格式只有以下几种
    %% RewardList = [{?TYPE_GOODS, GoodsTypeId, GoodsNum }, ...]
    %% RewardList = [{?TYPE_ATTR_GOODS, GoodsTypeId, GoodsNum, AttrList}, ...]
    %% Mod 上架模块
%% ---------------------------------------------------------------------------
sys_put_on_goods(GuildId,AwardList)->
    sys_put_on_goods(GuildId, 1, AwardList).

sys_put_on_goods(_GuildId, _Mod,[]) ->
    ok;
sys_put_on_goods(GuildId, Mod, AwardListArgs) ->
    AwardList = merge_goods_list(AwardListArgs),
    SellType = case GuildId > 0 of
        true  -> ?MARKET_SELL_TYPE_GUILD;
        false -> ?MARKET_SELL_TYPE_WORLD
    end,
    Now = utime:unixtime(),
    MarketGoodsList = format_market_goods(GuildId, Mod, SellType, Now, AwardList, []),
    market_db:batch_save_market_goods(MarketGoodsList),
    ets:insert(?MARKET_GOODS_ETS, MarketGoodsList),
    market_mgr:cast({mgr_put_on_goods_list, MarketGoodsList}),
    [log_market_operation(?MARKET_OPERATION_SYS_ON, MarketGoods, 0) || MarketGoods <- MarketGoodsList],
    ok.

format_market_goods(_GuildId, _Mod, _SellType, _Now, [], GoodsList) ->
    GoodsList;
format_market_goods(GuildId,Mod,SellType, Now, [GoodsInfo | T], GoodsList) ->
    MarketGoods = case GoodsInfo of
        {?TYPE_GOODS, GoodsTypeId, Num} ->
            to_market_goods(GuildId, Mod, SellType, Now, GoodsTypeId, Num, []);
        {?TYPE_ATTR_GOODS, GoodsTypeId, Num, AttrList} ->
            to_market_goods(GuildId, Mod, SellType, Now, GoodsTypeId, Num, AttrList);
        _ ->
            false
    end,
    GoodsList2 = case MarketGoods of
        #market_goods{} -> [MarketGoods | GoodsList];
        _ -> GoodsList
    end,
    format_market_goods(GuildId, Mod, SellType, Now, T, GoodsList2).

to_market_goods(GuildId, Mod, SellType, _Now, GoodsTypeId, Num, ExtraData) ->
    case data_goods_type:get(GoodsTypeId) of
        #ets_goods_type{is_to_market = IsToWorld} ->
            MarketGoods = market:make_market_goods(Mod, 0, GoodsTypeId, Num, SellType, GuildId, IsToWorld, false),
            Id = mod_id_create:get_new_id(?SELL_ID_CREATE),
            MarketGoods#market_goods{id = Id, extra_attr = ExtraData};
        _ ->
            false
    end.

%% 根据堆叠数判断物品是否可以合并
merge_goods_list(AwardList) ->
    %% 无脑合并
    MergeList = lib_goods_util:merge_same_goods(AwardList),
    %% 分拆成数量不超过堆叠数的列表
    split_goods_list(MergeList).


%% ---------------------------------------------------------------------------
%% @doc 分拆物品。
-spec split_goods_list(GoodsList) -> GoodsList2 when
    GoodsList   :: list(), %% 目前物品格式只有以下几种
    GoodsList2  :: list(). %% 目前物品格式只有以下几种
    %% RewardList = [{?TYPE_GOODS, GoodsTypeId, GoodsNum }, ...]
    %% RewardList = [{?TYPE_ATTR_GOODS, GoodsTypeId, GoodsNum, AttrList}, ...]
%% ---------------------------------------------------------------------------
split_goods_list(GoodsList) ->
    split_goods_list(GoodsList, []).

split_goods_list([], AwardList) ->
    AwardList;
split_goods_list([GoodsInfo | T], AwardList) ->
    {GoodsTypeId, GoodsNum} = case GoodsInfo of
        {?TYPE_GOODS, GTypeId, Num} ->
            {GTypeId, Num};
        {?TYPE_ATTR_GOODS, GTypeId, Num, _AttrList} ->
            {GTypeId, Num};
        _ ->
            {false, 0}
    end,
    case data_goods_type:get(GoodsTypeId) of
        #ets_goods_type{max_overlap = MaxOverlap} when GoodsNum =< MaxOverlap ->
            split_goods_list(T, [GoodsInfo | AwardList]);
        #ets_goods_type{
            max_overlap = MaxOverlap
        } when GoodsNum > MaxOverlap andalso is_integer(MaxOverlap) ->
            NewMaxOverlap = max(1, MaxOverlap),
            DuplicateNum = GoodsNum div NewMaxOverlap,
            GoodsInfoElem = erlang:setelement(3, GoodsInfo, NewMaxOverlap),
            DupList = lists:duplicate(DuplicateNum, GoodsInfoElem),
            LessNum = GoodsNum - DuplicateNum * NewMaxOverlap,
            GoodsList = case LessNum > 0 of
                true ->
                    [erlang:setelement(3, GoodsInfo, LessNum) | DupList];
                false ->
                    DupList
            end,
            AwardList2 = GoodsList ++ AwardList,
            split_goods_list(T, AwardList2);
        _ ->
            split_goods_list(T, AwardList)
    end.

%% 通知玩家有商品过期
notice_goods_overdue(Player) ->
    send_role_market_goods(Player),
    Player.

%% ---------------------------------------------------------------------------
%% 提醒商品上架
reminder_others(RoleId, GoodsTypeId, SellType, GuildId) ->
    case lib_player:apply_cast(RoleId, ?APPLY_CAST, ?MODULE, reminder_me, [RoleId, GoodsTypeId, SellType, GuildId]) of
        skip ->
            reminder_me(RoleId, GoodsTypeId, SellType, GuildId);
        _ ->
            ok
    end.

reminder_me(RoleId, GoodsTypeId, SellType, GuildId) ->
    NeedSend = SellType =:= ?MARKET_SELL_TYPE_WORLD orelse lib_role:get_role_guild(RoleId) =:= GuildId,
    case NeedSend of
        true  ->
            DailyReminderMax = lib_daily:get_count_limit(?MOD_MARKET, ?MARKET_COUNTER_DAILY_REMINDER),
            DailyReminder = lib_daily:get_count_offline(RoleId, ?MOD_MARKET, ?MARKET_COUNTER_DAILY_REMINDER),
            case DailyReminder < DailyReminderMax of
                true  ->
                    lib_daily:increment_offline(RoleId, ?MOD_MARKET, ?MARKET_COUNTER_DAILY_REMINDER),
                    Title = ?LAN_MSG(1550005),
                    Content = uio:format(?LAN_MSG(1550006), [GoodsTypeId]),
                    lib_mail_api:send_auction_mail([RoleId], ?MOD_MARKET, Title, Content, []);
                false ->
                    ignore
            end;
        false ->
            false
    end.
%% ---------------------------------------------------------------------------
%% @doc 每天6点将过期7天以上的商品用邮件返回给玩家
timer_hour(6) ->
    market_mgr:cast(mgr_clean_offtime_goods);
timer_hour(_) -> ok.

