%%%-------------------------------------------------------------------
%%% @author : tyl
%%% @doc    时装工坊逻辑模块
%%% Module  : lib_workroom.erl
%%% Created : 2021-05-21
%%% @end
%%%-------------------------------------------------------------------
-module(lib_workroom).

-include("fashion_workroom.hrl").
-include("def_module.hrl").
-include("def_goods.hrl").
-include("def_fun.hrl").
-include("common.hrl").
-include("server.hrl").
-include("goods.hrl").
-include("fashion.hrl").
-include("predefine.hrl").
-include("figure.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").
-include("errcode.hrl").

%% API
-export([
	login/1
	,handle_event/2             %% 4点刷新次数
	,daily_timer/0

	,forge_target_info/1        %% 打造目标信息
	,change_forge_target/2      %% 更换打造目标
	,delete_forge_target/2      %% 删除打造目标
	,replace_forge_target/2     %% 一键替换打造目标
	,forge_goods/2              %% 打造时装

	,suit_info/1                %% 套装信息
	,active_suit/2              %% 激活套装
	,calc_each_suit_own_num/2   %% 统计套装件数(时装激活时调用)
	,calc_each_suit_sum_star/2  %% 统计套装星数(时装升星时调用)

	,shop_info/1                %% 商城商品信息
	,buy_good/2                 %% 购买商品
	,refresh_goods/1            %% 刷新商品

	,get_attr/1
	,send_error/2
]).

%% ------------------------------------------------------------------------
%% @doc
%%      4点补满3次次数(在清理日计数器同时进行)
%% @end
%% ------------------------------------------------------------------------
handle_event(Player, #event_callback{type_id = ?EVENT_FOUR_CLOCK}) ->
	#player_status{sid = Sid, status_workroom = StatusWorkroom} = Player,
	MaxCount = case data_fashion_workroom:get_kv_cfg(store_refresh_num) of
		           TCount when is_integer(TCount) -> TCount;
		           _ -> 3
	           end,
	#status_workroom{shop = ShopInfo} = StatusWorkroom,
	#workroom_shop{
		shop_goods = ShopGoods
	} = ShopInfo,
	NewLastRefresh  = utime:unixtime(),
	NewRefreshCount = MaxCount,
	NewShopInfo = ShopInfo#workroom_shop{
		 refresh_count = NewRefreshCount
		,last_refresh  = NewLastRefresh
	},
	NewStatusWorkroom = StatusWorkroom#status_workroom{
		shop = NewShopInfo
	},
	PackGoodsList = pack_shop_list(ShopGoods, []),
	lib_server_send:send_to_sid(Sid, pt_421, 42106,
		[PackGoodsList, NewRefreshCount, NewLastRefresh, 0]
	),
	{ok, Player#player_status{status_workroom = NewStatusWorkroom}}.

daily_timer() ->
	MaxCount = case data_fashion_workroom:get_kv_cfg(store_refresh_num) of
				   TCount when is_integer(TCount) -> TCount;
				   _ -> 3
	           end,
	NowTime = utime:unixtime(),
	Sql = io_lib:format(<<"update workroom_shop set `last_refresh` = ~p, `refresh_count` = ~p">>,
		[NowTime, MaxCount]
	),
	db:execute(Sql),
	ok.

%% ------------------------------------------------------------------------
%% @doc
%%      登录加载数据
%% @end
%% ------------------------------------------------------------------------
login(Player) ->
	RoleId = Player#player_status.id,
	case db_select_forge_target(RoleId) of
		[_|_] = ForgeInfos ->
			F = fun([Pos, GoodsId], TList) ->
				[{Pos, GoodsId}|TList]
			end,
			ForgeList = lists:foldl(F, [], ForgeInfos);
		_ ->
			ForgeList = []
	end,
	case db_select_workroom_suit(RoleId) of
		[_|_] = SuitInfos ->
			F1 = fun([SuitId, OwnNum, ActiveNum, SumStar], TList) ->
				SuitInfo = #suit_info{
					suit_id     = SuitId
					,own_num    = OwnNum
					,active_num = ActiveNum
					,sum_star   = SumStar
				},
				[SuitInfo|TList]
			    end,
			SuitList = lists:foldl(F1, [], SuitInfos);
		_ ->
			SuitList = []
	end,
	case db_select_workroom_shop(RoleId) of
		[LastRefresh, RefreshCount] -> ok;
		_ ->  LastRefresh = 0, RefreshCount= 0
	end,
	case db_select_shop_goods(RoleId) of
		[_|_] = GoodsInfos ->
			F2 = fun([Pos, SGoodId, HasBuy], TList) ->
				ShopGood = #shop_good{
					pos       = Pos
					,goods_id = SGoodId
					,has_buy  = HasBuy
				},
				[ShopGood|TList]
			end,
			ShopGoods = lists:foldl(F2, [], GoodsInfos);
		_ ->
			ShopGoods = []
	end,
	WorkRoomShop = #workroom_shop{
		shop_goods     = ShopGoods
		,last_refresh  = LastRefresh
		,refresh_count = RefreshCount
	},
	StatusWorkroom = #status_workroom{
		forge_targets  = ForgeList
		,suit_list     = SuitList
		,shop          = WorkRoomShop
	},
	AttrStatusWorkroom = calc_attr(StatusWorkroom),
	Player#player_status{status_workroom = AttrStatusWorkroom}.

%% ------------------------------------------------------------------------
%% @doc
%%      打造目标信息
%% @end
%% ------------------------------------------------------------------------
forge_target_info(Player) ->
	#player_status{sid = Sid, status_workroom = StatusWorkroom} = Player,
	#status_workroom{forge_targets = TargetList} = StatusWorkroom,
	lib_server_send:send_to_sid(Sid, pt_421, 42101, [TargetList]),
	ok.

%% ------------------------------------------------------------------------
%% @doc 替换打造目标 @end
change_forge_target(Player, GoodsId) ->
	#player_status{sid = Sid, id = RoleId, status_workroom = StatusWorkroom} = Player,
	#status_workroom{forge_targets = TargetList} = StatusWorkroom,
	CheckList = [{check_cfg, GoodsId}, {has_on_pos, TargetList, GoodsId}],
	case lib_workroom_check:check_change_forge_target(CheckList) of
		true ->
			%% 根据pos排序
			TargetList1 = lists:keysort(1, TargetList),
			OldLen = erlang:length(TargetList1),
			NewTargetList = case OldLen of
				                0 ->
					                [{1, GoodsId}];
				                _ when OldLen < ?DEFAULT_TARGET_LEN ->
					                Pos = OldLen + 1,
					                [{Pos, GoodsId}|TargetList1];
				                _ ->
					                [{_, _}|LeftTargetList] = TargetList1,
					                LeftTargetList1 = push_target_pos(LeftTargetList),
					                [{OldLen, GoodsId}|LeftTargetList1]
			                end,
			NewStatusWorkroom = StatusWorkroom#status_workroom{
				forge_targets = NewTargetList
			},
			NewPlayer = Player#player_status{status_workroom = NewStatusWorkroom},
			DbF = fun() ->
				db_delete_forge_target(RoleId),
				db_save_forge_target(RoleId, NewTargetList),
				ok
			end,
			case catch db:transaction(DbF) of
				ok ->
					lib_server_send:send_to_sid(Sid, pt_421, 42102, [0, NewTargetList]),
					{ok, NewPlayer};
				_ ->
					send_error(Player, ?FAIL),
					{ok, Player}
			end;
		{false, Err} ->
			send_error(Player, Err),
			{ok, Player}
	end.

%% @doc 删除打造目标 @end
delete_forge_target(#player_status{
	sid = Sid, id = RoleId,status_workroom = StatusWorkroom} = Player, GoodsList
) ->
	#status_workroom{forge_targets = TargetList} = StatusWorkroom,
	F = fun(GoodsId, TList) ->
		lists:keydelete(GoodsId, 2, TList)
	end,
	LeftTargetList = lists:foldl(F, TargetList, GoodsList),
	LeftTargetList1 = lists:keysort(1, LeftTargetList),
	NewTargetList = push_target_pos(LeftTargetList1),
	NewStatusWorkRoom =
	StatusWorkroom#status_workroom{forge_targets = NewTargetList},
	DbF = fun() ->
		db_delete_forge_target(RoleId),
		db_save_forge_target(RoleId, NewTargetList),
		ok
	end,
	case catch db:transaction(DbF) of
		ok ->
			lib_server_send:send_to_sid(Sid, pt_421, 42102, [1, NewTargetList]),
			{ok, Player#player_status{status_workroom = NewStatusWorkRoom}};
		_ ->
			send_error(Player, ?FAIL),
			{ok, Player}
	end.

%% @doc 一键替换打造目标 @end
replace_forge_target(#player_status{
	sid = Sid, id = RoleId, status_workroom = StatusWorkroom} = Player, GoodsList
) ->
	#status_workroom{forge_targets = TargetList} = StatusWorkroom,
	SuitLen = erlang:length(GoodsList),
	case SuitLen =< ?DEFAULT_TARGET_LEN of
		true ->
			TargetList1 = lists:reverse(lists:keysort(1, TargetList)),
			LeftPos = ?DEFAULT_TARGET_LEN - SuitLen,
			case LeftPos > 0 of
				true ->
					%% 先删除旧列表中原有的
					F = fun(TGoodsId, TList) ->
						lists:keydelete(TGoodsId, 2, TList)
					end,
					TargetList2  = lists:foldl(F, TargetList1, GoodsList),
					{LeftList, _} = ulists:split(LeftPos, TargetList2),
					NewGoodsList  = [GoodsId||{_, GoodsId}<-lists:reverse(LeftList)] ++ GoodsList,
					NewTargetList = push_target_pos(NewGoodsList);
				_ ->
					NewTargetList = push_target_pos(GoodsList)
			end,
			NewStatusWorkRoom = StatusWorkroom#status_workroom{
				forge_targets = NewTargetList
			},
			DbF = fun() ->
				db_delete_forge_target(RoleId),
				db_save_forge_target(RoleId, NewTargetList),
				ok
			end,
			case catch db:transaction(DbF) of
				ok ->
					lib_server_send:send_to_sid(Sid, pt_421, 42102, [2, NewTargetList]),
					{ok, Player#player_status{status_workroom = NewStatusWorkRoom}};
				_ ->
					send_error(Player, ?FAIL),
					{ok, Player}
			end;
		_ ->
			send_error(Player, ?ERRCODE(err421_goods_list_err)),
			{ok, Player}
	end.

push_target_pos(TargetList) ->
	{_, NewTargetList} =
	case TargetList of
		[{_, _}|_] ->
			lists:foldl(fun push_fun1/2, {0, []}, TargetList);
		_ ->
			lists:foldl(fun push_fun2/2, {0, []}, TargetList)
	end,
	NewTargetList.

push_fun1({_TPos, TGoodId}, {TNum, TList}) ->
	TList1 = [{TNum + 1, TGoodId}|TList],
	{TNum + 1, TList1}.

push_fun2(TGoodId, {TNum, TList}) ->
	TList1 = [{TNum + 1, TGoodId}|TList],
	{TNum + 1, TList1}.


%% ------------------------------------------------------------------------
%% @doc
%%      打造物品
%% @end
%% ------------------------------------------------------------------------
forge_goods(#player_status{sid = Sid, id = RoleId, figure = Figure} = Player, GoodsId
) ->
	CheckList = [forge_good, forge_material, material_enough],
	case lib_workroom_check:check_forge_good(CheckList, Player, GoodsId) of
		true ->
			#base_workroom_forge{cost = CostList} = data_fashion_workroom:get_forge_goods(GoodsId),
			case lib_goods_api:cost_object_list_with_check(Player, CostList, forge_goods, "") of
				{true, Player1, _}  ->
					Produce = #produce{reward = [{?TYPE_GOODS, GoodsId, 1}], type = forge_goods},
					{ok, NewPlayer} = lib_goods_api:send_reward_with_mail(Player1, Produce, ?MOD_WORKROOM),
					lib_chat:send_TV({all}, ?MOD_WORKROOM, 2, [Figure#figure.name, RoleId, GoodsId]),
					lib_server_send:send_to_sid(Sid, pt_421, 42103, [GoodsId]),
					{ok, NewPlayer};
				{false, ErrCode, _Player1}  ->
					lib_server_send:send_to_sid(Sid, pt_421, 42100, [ErrCode]),
					{ok, Player}
			end;
		{false, Err} ->
			lib_server_send:send_to_sid(Sid, pt_421, 42100, [Err]),
			{ok, Player}
	end.

%% ------------------------------------------------------------------------
%% @doc
%%      套装信息
%% @end
%% ------------------------------------------------------------------------
suit_info(#player_status{
	sid = Sid, status_workroom = StatusWorkroom}
) ->
	#status_workroom{suit_list = SuitList} = StatusWorkroom,
	PackList = pack_suit_info(SuitList),
	lib_server_send:send_to_sid(Sid, pt_421, 42104, [PackList]),
	ok.

pack_suit_info(SuitList) ->
	F = fun(SuitInfo, TList) ->
		#suit_info{
			suit_id   = SuitId
			,sum_star = SumStar
			,own_num  = OwnNum
			,active_num = ActiveNum
		} = SuitInfo,
		[{SuitId, SumStar, OwnNum, ActiveNum}|TList]
	    end,
	PackList = lists:foldl(F, [], SuitList),
	PackList.

%% @doc  统计件数  @end
calc_each_suit_own_num(Player, AllFashions) ->
	#player_status{id = RoleId, status_workroom = StatusWorkroom} = Player,
	#status_workroom{suit_list = SuitList} = StatusWorkroom,
	AllSuit = data_fashion_workroom:get_all_suit(),
	HasAllFashion = calc_has_fashion(AllFashions),
	F = fun(SuitId, TSuitList) ->
		calc_suit_num(SuitId, TSuitList, HasAllFashion)
		end,
	NewSuitList = lists:foldl(F, SuitList, AllSuit),
	db_save_workroom_suit(RoleId, NewSuitList),
	NewStatusWorkroom = StatusWorkroom#status_workroom{suit_list = NewSuitList},
	PackList = pack_suit_info(NewSuitList),
	lib_server_send:send_to_uid(RoleId, pt_421, 42104, [PackList]),
	Player#player_status{status_workroom = NewStatusWorkroom}.

calc_suit_num(SuitId, SuitList, HasAllFashion) ->
	case data_fashion_workroom:get_suit(SuitId) of
		#base_workroom_suit{goods_list = GoodsList} ->
			CfgFashionIds = case GoodsList of
								[{_, _}|_] ->
									[TGoodsId||{_, TGoodsId}<-GoodsList];
								_ -> []
			                end,
			SuitGoods =
			[
				HasGoodsId||HasGoodsId<-HasAllFashion,
				lists:member(HasGoodsId, CfgFashionIds)
			],
			SuitKey = #suit_info.suit_id,
			case lists:keyfind(SuitId, SuitKey, SuitList) of
				#suit_info{} = SuitInfo ->
					NewSuitInfo = SuitInfo#suit_info{
						own_num = erlang:length(SuitGoods)
					};
				_ ->
					NewSuitInfo = #suit_info{
						suit_id = SuitId,
						own_num = erlang:length(SuitGoods)
					}
			end,
			lists:keystore(SuitId, SuitKey, SuitList, NewSuitInfo);
		_ ->
			SuitList
	end.

%% @doc  统计星级  @end
calc_each_suit_sum_star(Player, AllFashions) ->
	#player_status{id = RoleId, status_workroom = StatusWorkroom} = Player,
	#status_workroom{suit_list = SuitList} = StatusWorkroom,
	AllSuit = data_fashion_workroom:get_all_suit(),
	F = fun(SuitId, TSuitList) ->
		calc_suit_star(SuitId, TSuitList, AllFashions)
	end,
	NewSuitList = lists:foldl(F, SuitList, AllSuit),
	db_save_workroom_suit(RoleId, NewSuitList),
	NewStatusWorkroom = StatusWorkroom#status_workroom{suit_list = NewSuitList},
	NewPlayer = Player#player_status{status_workroom = NewStatusWorkroom},
	refresh_power(NewPlayer).

calc_suit_star(SuitId, SuitList, AllFashions) ->
	HasAllFashion = calc_has_fashion(AllFashions),
	case data_fashion_workroom:get_suit(SuitId) of
		#base_workroom_suit{goods_list = GoodsList} ->
			CfgFashionIds = case GoodsList of
				                [{_, _}|_] ->
					                [TGoodsId||{_, TGoodsId}<-GoodsList];
				                _ -> []
			                end,
			SuitGoods =
				[
					HasGoodsId||HasGoodsId<-HasAllFashion,
					lists:member(HasGoodsId, CfgFashionIds)
				],
			SuitKey = #suit_info.suit_id,
			%% 整套激活才记录星数
			SuitSumStar1 = case erlang:length(CfgFashionIds) == erlang:length(SuitGoods) of
							   true ->
								   FashionStarL = calc_fashions_sum_star(AllFashions),
								   F = fun(SuitGoodId, TNum) ->
									   case lists:keyfind(SuitGoodId, 1, FashionStarL) of
										   {_, Num} -> TNum + Num;
										   _ -> TNum
									   end end,
								   SuitSumStar = lists:foldl(F, 0, SuitGoods),
								   SuitSumStar;
							   _ ->
								   0
			               end,
			case lists:keyfind(SuitId, SuitKey, SuitList) of
				#suit_info{} = SuitInfo ->
					NewSuitInfo = SuitInfo#suit_info{
						sum_star = SuitSumStar1
					};
				_ ->
					%% 保底逻辑，一般不会走这里
					NewSuitInfo = #suit_info{
						suit_id  = SuitId,
						sum_star = SuitSumStar1,
						own_num  = erlang:length(SuitGoods)
					}
			end,
			lists:keystore(SuitId, SuitKey, SuitList, NewSuitInfo);
		_ ->
			SuitList
	end.

%% ------------------------------------------------------------------------
%% 拥有的时装总星级
calc_fashions_sum_star(AllFashions) ->
	PosF = fun(FashionPos, TList) ->
		case FashionPos of
			#fashion_pos{fashion_list = FashionList} ->
				FashionF = fun(FashionInfo, TList1) ->
					case FashionInfo of
						#fashion_info{fashion_id = FashionId, color_list = ColorList} ->
							FashionStar = calc_sum_star(FashionId, ColorList),
							FashionStar ++ TList1;
						_ ->
							TList1
					end end,
				lists:foldl(FashionF, TList, FashionList);
			_ ->
				TList
		end end,
	lists:foldl(PosF, [], AllFashions).

calc_sum_star(FashionId, ColorList) ->
	F = fun(ColorInfo, TList) ->
		case ColorInfo of
			{Color, Star, Time} when Time =< 0 ->
				case data_fashion:get_fashion_con(FashionId, Color, 0) of
					#base_fashion_star_up{cost_goods = [{_, GoodsId, _}]} ->
						[{GoodsId, Star}|TList];
					_ -> TList
				end;
			_ -> TList
		end end,
	lists:foldl(F, [], ColorList).

%% ------------------------------------------------------------------------
%% 拥有的时装物品Id
calc_has_fashion(AllFashions) ->
	F = fun(FashionPos, TList) ->
		case FashionPos of
			#fashion_pos{fashion_list = FashionList} ->
				lists:foldl(fun calc_fashions_goods/2, TList, FashionList);
			_ ->
				TList
		end end,
	lists:foldl(F, [], AllFashions).

calc_fashions_goods(FashionInfo, GoodsList) ->
	case FashionInfo of
		#fashion_info{fashion_id = FashionId, color_list = ColorList} ->
			F = fun(ColorInfo, TList) ->
				case ColorInfo of
					{Color, _, Time} when Time =< 0 ->
						case data_fashion:get_fashion_con(FashionId, Color, 0) of
							#base_fashion_star_up{cost_goods = [{_, GoodsId, _}]} ->
								[GoodsId|TList];
							_ -> TList
						end;
					_ -> TList
				end end,
			lists:foldl(F, [], ColorList) ++ GoodsList;
		_ ->
			GoodsList
	end.

%% ------------------------------------------------------------------------
%% @doc
%%      激活套装
%% @end
%% ------------------------------------------------------------------------
active_suit(#player_status{id = RoleId,
	sid = Sid, status_workroom = StatusWorkroom, figure = Figure} = Player, SuitId
) ->
	#status_workroom{suit_list = SuitList} = StatusWorkroom,
	SuitKey = #suit_info.suit_id,
	case lists:keyfind(SuitId, SuitKey, SuitList) of
		#suit_info{
			suit_id = SuitId, own_num = OwnNum, active_num = OldActiveNum, sum_star = SumStar
		} = SuitInfo ->
			ActiveNum = OldActiveNum + 1,
			CheckList = [{check_cfg, SuitId, ActiveNum}, {can_active, OwnNum, ActiveNum}],
			case lib_workroom_check:check_active_suit(CheckList) of
				true ->
					#base_workroom_suit{goods_list = GoodsList, suit_name = SuitName} =
						data_fashion_workroom:get_suit(SuitId),
					NewSuitInfo = SuitInfo#suit_info{active_num = ActiveNum},
					NewSuitList = lists:keystore(SuitId, SuitKey, SuitList, NewSuitInfo),
					NewStatusWorkroom = StatusWorkroom#status_workroom{
						suit_list = NewSuitList
					},
					db_save_workroom_suit(RoleId, NewSuitList),
					lib_server_send:send_to_sid(Sid, pt_421, 42105, [SuitId, SumStar, OwnNum, ActiveNum]),
					Player1   = Player#player_status{status_workroom = NewStatusWorkroom},
					NewPlayer  = check_calc_star(Player1),
					AttrPlayer = refresh_power(NewPlayer),
					ActiveNum == erlang:length(GoodsList) andalso
						lib_chat:send_TV({all}, ?MOD_WORKROOM, 1, [Figure#figure.name, RoleId, SuitName]),
					{ok, AttrPlayer};
				{false, ErrCode} ->
					send_error(Player, ErrCode),
					{ok, Player}
			end;
		_ ->
			send_error(Player, ?ERRCODE(err421_cannot_active)),
			{ok, Player}
	end.

%% 检查统计星级
check_calc_star(Player) ->
	#player_status{role_fashion = RoleFashion} = Player,
	#role_fashion{all_fashions = AllFashions} = RoleFashion,
	NewPlayer = calc_each_suit_sum_star(Player, AllFashions),
	NewPlayer.

%% ------------------------------------------------------------------------
%% @doc
%%      商店信息
%% @end
%% ------------------------------------------------------------------------
shop_info(#player_status{
	sid = Sid, id = RoleId, status_workroom = StatusWorkroom} = Player
) ->
	#status_workroom{shop = ShopInfo} = StatusWorkroom,
	#workroom_shop{
		shop_goods     = ShopGoods
		,refresh_count = RefreshCount
		,last_refresh  = LastRefresh
	} = ShopInfo,
	%% 查看信息要考虑是否补充次数
	%% 计算可以补充的次数，以及剩余的倒计时。
	NowTime = utime:unixtime(),
	TimeGap = data_fashion_workroom:get_kv_cfg(store_refresh_time),
	MaxCount= data_fashion_workroom:get_kv_cfg(store_refresh_num),
	%% 先判断是否需要补次数
	case RefreshCount >= MaxCount of
		true ->
			PackGoodsList = pack_shop_list(ShopGoods, []),
			lib_server_send:send_to_sid(Sid, pt_421, 42106,
				[PackGoodsList, RefreshCount, LastRefresh, 0]
			),
			{ok, Player};
		_ ->
			%% 第一次打开界面需要刷新数据
			{
				TimeCount, NewShopGoods, NewLastRefresh
			} =
			case LastRefresh of
				0 ->
					{MaxCount, refresh_goods_core(), NowTime};
				_ ->
					TTimeCount = max(0, (NowTime - LastRefresh) div TimeGap),
					TNewLastRefresh = LastRefresh + TTimeCount * TimeGap,
					{TTimeCount, ShopGoods, TNewLastRefresh}
			end,
			%% 刷新次数上限
			DailyCount = lib_daily:get_count(RoleId, ?MOD_WORKROOM, 1),
			DailyCfgCount  = data_fashion_workroom:get_kv_cfg(store_recovery_num),
			LeftDailyCount = max(0, DailyCfgCount - DailyCount),
			%% 最多可以刷新次数
			CanRefreshCount = min(TimeCount, LeftDailyCount),
			%% 有剩余补足次数，并且刷新次数小于配置才补足
			case CanRefreshCount > 0 of
				true when RefreshCount =< MaxCount ->
					NewRefreshCount = min(RefreshCount + CanRefreshCount, MaxCount),
					NewShopInfo = ShopInfo#workroom_shop{
						shop_goods     = NewShopGoods
						,refresh_count = NewRefreshCount
						,last_refresh  = NewLastRefresh
					},
					NewStatusWorkroom = StatusWorkroom#status_workroom{
						shop = NewShopInfo
					},
					AddRefreshCount = NewRefreshCount - RefreshCount,
					Result =
					case LastRefresh of
						0 ->
							DbF = fun() ->
								db_save_shop_goods(RoleId, NewShopGoods),
								db_save_workroom_shop(RoleId, NewLastRefresh, NewRefreshCount),
								ok
							end,
							case catch db:transaction(DbF) of
								ok -> true;
								_ -> false
							end;
						_ ->
							db_save_workroom_shop(RoleId, NewLastRefresh, NewRefreshCount),
							true
					end,
					case Result of
						true ->
							AddRefreshCount > 0 andalso LastRefresh =/= 0 andalso
								lib_daily:plus_count(RoleId, ?MOD_WORKROOM, 1, AddRefreshCount),
							PackGoodsList = pack_shop_list(NewShopGoods, []),
							NeedCountDown = case (DailyCount + AddRefreshCount) >= DailyCfgCount of
								                true -> 0;
								                _ when NewRefreshCount >= MaxCount -> 0;
								                _ -> 1
							                end,
							lib_server_send:send_to_sid(Sid, pt_421, 42106,
								[PackGoodsList, NewRefreshCount, NewLastRefresh, NeedCountDown]
							),
							{ok, Player#player_status{status_workroom = NewStatusWorkroom}};
						_ ->
							send_error(Player, ?FAIL),
							{ok, Player}
					end;
				_ ->
					%% 前端是否需要倒计时，根据是否有剩余补充次数来判断
					NeedCountDown = case LeftDailyCount > 0 of
						                true -> 1;
						                _ -> 0
					                end,
					PackGoodsList = pack_shop_list(ShopGoods, []),
					lib_server_send:send_to_sid(Sid, pt_421, 42106,
						[PackGoodsList, RefreshCount, LastRefresh, NeedCountDown]
					),
					{ok, Player}
			end
	end.

pack_shop_list([], PackList) -> PackList;
pack_shop_list([ShopInfo|T], PackList) ->
	case ShopInfo of
		#shop_good{
			pos       = Pos
			,goods_id = GoodsId
			,has_buy  = HasBuy
		} ->
			PackList1 = [{Pos, GoodsId, HasBuy}|PackList],
			pack_shop_list(T, PackList1);
		_ ->
			pack_shop_list(T, PackList)
	end.

%% ------------------------------------------------------------------------
%% @doc
%%      购买商品
%% @end
%% ------------------------------------------------------------------------
buy_good(#player_status{
	sid = Sid, id = RoleId, status_workroom = StatusWorkroom} = Player, Pos
) ->
	#status_workroom{shop = ShopInfo} = StatusWorkroom,
	#workroom_shop{
		shop_goods = ShopGoods
	} = ShopInfo,
	PosKey = #shop_good.pos,
	CheckList = [{has_buy, ShopGoods, Pos}, {price_cfg, ShopGoods, Pos}],
	case lib_workroom_check:check_buy_good(CheckList) of
		true ->
			#shop_good{goods_id = GoodsId} = ShopGood = lists:keyfind(Pos, PosKey, ShopGoods),
			#base_workroom_store{price_list = PriceList} = data_fashion_workroom:get_good_cfg(GoodsId),
			case lib_goods_api:cost_object_list_with_check(
				Player, PriceList, buy_workroom_good, ""
			) of
				{true, Player1, _} ->
					Produce = #produce{
						reward = [{?TYPE_GOODS, GoodsId, 1}], type = buy_workroom_good
					},
					{ok, Player2} = lib_goods_api:send_reward_with_mail(Player1, Produce, ?MOD_WORKROOM),
					NewShopGood  = ShopGood#shop_good{has_buy = 1},
					db_update_shop_good(RoleId, GoodsId, 1),
					NewShopGoods = lists:keystore(Pos, PosKey, ShopGoods, NewShopGood),
					NewShopInfo = ShopInfo#workroom_shop{shop_goods = NewShopGoods},
					NewStatusWorkroom = StatusWorkroom#status_workroom{shop = NewShopInfo},
					NewPlayer = Player2#player_status{status_workroom = NewStatusWorkroom},
					lib_seven_days_goal:finish_task(RoleId, ?MOD_WORKROOM, 2, 1),
					PackList  = pack_shop_list(NewShopGoods, []),
					lib_server_send:send_to_sid(Sid, pt_421, 42107, [PackList]),
					{ok, NewPlayer};
				{false, ErrCode, _}  ->
					send_error(Player, ErrCode),
					{ok,Player}
			end;
		{false, ErrCode} ->
			send_error(Player, ErrCode),
			{ok, Player}
	end.

%% ------------------------------------------------------------------------
%% @doc
%%      刷新商城物品
%% @end
%% ------------------------------------------------------------------------
refresh_goods(#player_status{
	id = RoleId, status_workroom = StatusWorkroom} = Player
) ->
	#status_workroom{shop = ShopInfo} = StatusWorkroom,
	DailyCount = lib_daily:get_count(RoleId, ?MOD_WORKROOM, 1),
	DailyCfgCount = data_fashion_workroom:get_kv_cfg(store_recovery_num),
	{
		NewLastRefresh, NewRefreshCount
	} = calc_new_refresh(RoleId, ShopInfo),
	%% 1. 有免费次数时用免费次数  2. 没有免费次数使用道具  3. 没有道具付费
	case NewRefreshCount > 0 of
		true ->
			refresh_by_free(Player, NewLastRefresh, NewRefreshCount);
		_ ->
			NeedCountDown = case DailyCount >= DailyCfgCount of
				                true -> 0;
				                _ -> 1
			                end,
			refresh_with_material(Player, NeedCountDown)
	end.

calc_new_refresh(RoleId, ShopInfo) ->
	#workroom_shop{
		refresh_count  = RefreshCount
		,last_refresh  = LastRefresh
	} = ShopInfo,
	NowTime = utime:unixtime(),
	TimeGap  = data_fashion_workroom:get_kv_cfg(store_refresh_time),
	MaxCount = data_fashion_workroom:get_kv_cfg(store_refresh_num),
	%% 计算可以补充的次数，以及剩余的倒计时。
	TimeCount = max(0, (NowTime - LastRefresh) div TimeGap),
	NewLastRefresh = LastRefresh + TimeCount * TimeGap,
	%% 刷新次数上限
	DailyCount = lib_daily:get_count(RoleId, ?MOD_WORKROOM, 1),
	DailyCfgCount  = data_fashion_workroom:get_kv_cfg(store_recovery_num),
	LeftDailyCount = max(0, DailyCfgCount - DailyCount),
	%% 最多可以刷新次数
	CanRefreshCount = min(TimeCount, LeftDailyCount),
	NewRefreshCount = min(RefreshCount + CanRefreshCount, MaxCount),
	{NewLastRefresh, NewRefreshCount}.

%% 1> 免费刷新
refresh_by_free(#player_status{
	sid = Sid, id = RoleId, status_workroom = StatusWorkroom} = Player,
		NewLastRefresh, NewRefreshCount
) ->
	#status_workroom{shop = ShopInfo} = StatusWorkroom,
	#workroom_shop{
		refresh_count  = RefreshCount
	} = ShopInfo,
	ShopGoods = refresh_goods_core(),
	%% 计数器记录自动补足免费次数
	AddRefreshCount = NewRefreshCount - RefreshCount,
	AddRefreshCount > 0 andalso lib_daily:plus_count(RoleId, ?MOD_WORKROOM, 1, AddRefreshCount),
	%% 如果原来是免费次数上限的，更新倒计时为当前时间
	MaxCount = data_fashion_workroom:get_kv_cfg(store_refresh_num),
	NewLastRefresh1 =
	case RefreshCount >= MaxCount of
		true -> utime:unixtime();
		_ -> NewLastRefresh
	end,
	%% 是否需要倒计时
	DailyCfgCount = data_fashion_workroom:get_kv_cfg(store_recovery_num),
	DailyCount = lib_daily:get_count(RoleId, ?MOD_WORKROOM, 1), %% 加上次数之后的日次数
	NeedCountDown =
	case DailyCount >= DailyCfgCount of
		true -> 0;
		_ when NewRefreshCount - 1 >= MaxCount -> 0;
		_ -> 1
	end,
	NewShopInfo = ShopInfo#workroom_shop{
		shop_goods     = ShopGoods
		,refresh_count = NewRefreshCount - 1
		,last_refresh  = NewLastRefresh1
	},
	NewStatusWorkroom = StatusWorkroom#status_workroom{
		shop = NewShopInfo
	},
	F = fun() ->
		db_delete_shop_goods(RoleId),
		db_save_workroom_shop(RoleId, NewLastRefresh1, NewRefreshCount - 1),
		db_save_shop_goods(RoleId, ShopGoods),
		ok
	    end,
	case catch db:transaction(F) of
		ok ->
			PackList = pack_shop_list(ShopGoods, []),
			lib_server_send:send_to_sid(Sid, pt_421, 42108,
				[PackList, NewRefreshCount - 1, NewLastRefresh1, NeedCountDown]
			),
			{ok, Player#player_status{status_workroom = NewStatusWorkroom}};
		_ ->
			send_error(Player, ?FAIL),
			{ok, Player}
	end.

%% 2> 用材料或者付费刷新
refresh_with_material(#player_status{
	sid = Sid, id = RoleId, status_workroom = StatusWorkroom} = Player, NeedCountDown
) ->
	#status_workroom{shop = ShopInfo} = StatusWorkroom,
	#workroom_shop{
		refresh_count  = RefreshCount
		,last_refresh  = LastRefresh
	} = ShopInfo,
	CostList = data_fashion_workroom:get_kv_cfg(store_refresh_goods),
	case lib_goods_api:cost_objects_with_auto_buy(Player, CostList, refresh_shop, "") of
		{true, Player1, _} ->
			ShopGoods = refresh_goods_core(),
			NewShopInfo = ShopInfo#workroom_shop{
				shop_goods = ShopGoods
			},
			NewStatusWorkroom = StatusWorkroom#status_workroom{
				shop = NewShopInfo
			},
			F = fun() ->
				db_delete_shop_goods(RoleId),
				db_save_shop_goods(RoleId, ShopGoods),
				ok
			    end,
			case catch db:transaction(F) of
				ok ->
					PackList = pack_shop_list(ShopGoods, []),
					lib_server_send:send_to_sid(Sid, pt_421, 42108,
						[PackList, RefreshCount, LastRefresh, NeedCountDown]
					),
					{ok, Player1#player_status{status_workroom = NewStatusWorkroom}};
				_ ->
					send_error(Player, ?FAIL),
					{ok, Player}
			end;
		{false, ErrorCode, _} ->
			send_error(Player, ErrorCode),
			{ok, Player}
	end.

refresh_goods_core() ->
	AllGoods = data_fashion_workroom:get_all_goods(),
	GoodsLimit  = get_goods_limit(AllGoods),
	TypeNeedNum = case data_fashion_workroom:get_kv_cfg(store_goodstype) of
					  TNum when is_integer(TNum), TNum > 0 -> TNum;
					  _ -> ?DEFAULT_TYPE_NEED
	              end,
	%% 开始检查类型补足次数
	StarCheckTimes = max(0, ?DEFAULT_STORE_LEN - TypeNeedNum + 1),
	RandGoodIds = rand_goods(1, ?DEFAULT_STORE_LEN, AllGoods, [], GoodsLimit, TypeNeedNum, StarCheckTimes),
	%% 把顺序重新逆过来
	{_, ShopGoods} = lists:foldr(fun tran_goods_info/2, {0, []}, RandGoodIds),
	ShopGoods.

rand_goods(RandTimes, Times, _AllGoods, RandGoods, _GoodsLimit, _TypeNeedNum, _StarCheckTimes) when RandTimes == Times + 1 ->
	RandGoods;
rand_goods(RandTimes, Times, AllGoods, RandGoods, GoodsLimit, TypeNeedNum, StarCheckTimes) ->
	FilterGoods = filter_goods(AllGoods, RandGoods, GoodsLimit),
	GoodsTypeList = goods_type(RandGoods),
	GoodsTypeNum  = erlang:length(GoodsTypeList),
	case RandTimes >= StarCheckTimes of
		true when GoodsTypeNum =< TypeNeedNum ->
			FilterGoods1 = filter_type_goods(FilterGoods, GoodsTypeList);
		_ ->
			FilterGoods1 = FilterGoods
	end,
	case FilterGoods1 =/= [] of
		true ->
			GoodsWeightList = get_goods_weight(FilterGoods1),
			RandGoodsId = urand:rand_with_weight(GoodsWeightList),
			rand_goods(RandTimes+1, Times, AllGoods, [RandGoodsId|RandGoods], GoodsLimit, TypeNeedNum, StarCheckTimes);
		_ ->
			rand_goods(RandTimes+1, Times, AllGoods, RandGoods, GoodsLimit, TypeNeedNum, StarCheckTimes)
	end.

filter_goods(AllGoods, RandGoods, GoodsLimit) ->
	GoodsNumL = lists:foldl(fun calc_goods_num/2, [], RandGoods),
	F = fun({GoodsId, Num}, TList) ->
		case lists:keyfind(GoodsId, 1, GoodsLimit) of
			{_, LimitNum} when Num >= LimitNum -> [GoodsId|TList];
			_ -> TList
		end end,
	OutLimitGoods = lists:foldl(F, [], GoodsNumL),
	FilterGoods = AllGoods -- OutLimitGoods,
	FilterGoods.

goods_type(RandGoods) ->
	F = fun(GoodsId, TList) ->
		case data_fashion_workroom:get_good_cfg(GoodsId) of
			#base_workroom_store{tag = Tag} -> [Tag|lists:delete(Tag, TList)];
			_ -> TList
		end end,
	lists:foldl(F, [], RandGoods).

filter_type_goods(FilterGoods, GoodsTypeList) ->
	F = fun(GoodsId, TList) ->
		case data_fashion_workroom:get_good_cfg(GoodsId) of
			#base_workroom_store{tag = Tag} ->
				case lists:member(Tag, GoodsTypeList) of
					true -> TList;
					_ -> [GoodsId|TList]
				end;
			_ -> TList
		end end,
	lists:foldl(F, [], FilterGoods).

get_goods_limit(AllGoods) ->
	F = fun(GoodId, TList) ->
		case data_fashion_workroom:get_good_cfg(GoodId) of
			#base_workroom_store{limit = LimitNum} ->
				[{GoodId, LimitNum}|TList];
			_ ->
				TList
		end end,
	lists:foldl(F, [], AllGoods).

get_goods_weight(GoodIdList) ->
	F = fun(GoodId, TList) ->
		case data_fashion_workroom:get_good_cfg(GoodId) of
			#base_workroom_store{weight = Weight} ->
				[{Weight, GoodId}|TList];
			_ ->
				TList
		end end,
	lists:foldl(F, [], GoodIdList).

%% ------------------------ local function -------------------------------------------
tran_goods_info(GoodsId, {Pos, GoodInfoL}) ->
	GoodsInfo = #shop_good{
		pos       = Pos + 1
		,goods_id = GoodsId
	},
	{Pos + 1, [GoodsInfo|GoodInfoL]}.

calc_goods_num(GoodsId, GoodsList) ->
	case lists:keyfind(GoodsId, 1, GoodsList) of
		{_, Num} -> lists:keystore(GoodsId, 1, GoodsList, {GoodsId, Num + 1});
		_ -> lists:keystore(GoodsId, 1, GoodsList, {GoodsId, 1})
	end.

%% --------------------------------------------------------------------------------------
send_error(Player, ErrCode) ->
	#player_status{sid = Sid} = Player,
	{ok, Bin} = pt_421:write(42100, [ErrCode]),
	lib_server_send:send_to_sid(Sid, Bin).

%% ------------------------ builtin function -------------------------------------------
refresh_power(Player) ->
	#player_status{status_workroom = StatusWorkroom} = Player,
	NewStatusWorkroom = calc_attr(StatusWorkroom),
	NewPlayer = Player#player_status{status_workroom = NewStatusWorkroom},
	RefreshPlayer = lib_player:count_player_attribute(NewPlayer, ?MOD_WORKROOM, star_up),
	lib_player:send_attribute_change_notify(RefreshPlayer, ?NOTIFY_ATTR),
	mod_scene_agent:update(RefreshPlayer, [{battle_attr, RefreshPlayer#player_status.battle_attr}]),
	RefreshPlayer.

calc_attr(StatusWorkroom) ->
	#status_workroom{suit_list = SuitList} = StatusWorkroom,
	F = fun(SuitInfo, TList) ->
		#suit_info{
			suit_id     = SuitId
			,active_num = ActiveNum
			,sum_star   = SumStar
		} = SuitInfo,
		{ActiveAttr, GoodsLen} =
			case data_fashion_workroom:get_suit(SuitId) of
				#base_workroom_suit{attr_list = AttrList, goods_list = GoodsList} ->
					F = fun(TNum, TAttrList) ->
						case lists:keyfind(TNum, 1, AttrList) of
							{_, TAttr} -> TAttr ++ TAttrList;
							_ -> TAttrList
						end end,
					AAttr = lists:foldl(F, [], lists:seq(1, ActiveNum)),
					{AAttr, erlang:length(GoodsList)};
				_ ->
					{[], 0}
			end,
		%% 星级属性需要激活所有套件才计算
		StarAttr =
		case ActiveNum >= GoodsLen of
			true ->
				case data_fashion_workroom:get_suit_star(SuitId, SumStar) of
					#base_workroom_suit_star{attr_list = SAttr} -> SAttr;
					_ -> []
				end;
			_ -> []
		end,
		ActiveAttr ++ StarAttr ++ TList
	    end,
	SumAttrList = lists:foldl(F, [], SuitList),
	NewStatusWorkroom = StatusWorkroom#status_workroom{attr_list = SumAttrList},
	NewStatusWorkroom.

get_attr(Player) ->
	#player_status{status_workroom = StatusWorkroom} = Player,
	#status_workroom{attr_list = AttrList} = StatusWorkroom,
	AttrList.

%% ----------------------------- db ----------------------------------
db_save_forge_target(RoleId, ForgeList) ->
	F = fun(ForgeInfo, TList) ->
		case ForgeInfo of
			{Pos, GoodsId} ->
				Sql = io_lib:format(?SQL_INSERT_FORGET_TARGET_VALUE, [RoleId, Pos, GoodsId]),
				[Sql|TList]
		end end,
	case lists:foldl(F, [], ForgeList) of
		[_|_] = SQLs ->
			NewValues = util:join_bitstring(SQLs, <<", ">>),
			FinalSql = io_lib:format(?SQL_INSERT_FORGET_TARGET, [NewValues]),
			db:execute(FinalSql);
		_ ->
			ignore
	end.

db_select_forge_target(RoleId) ->
	Sql = io_lib:format(?SQL_SELECT_FORGET_TARGET, [RoleId]),
	db:get_all(Sql).

db_delete_forge_target(RoleId) ->
	Sql = io_lib:format(?SQL_DELETE_FORGET_TARGET, [RoleId]),
	db:execute(Sql).

db_save_workroom_suit(RoleId, SuitList) ->
	F = fun(SuitInfo, TList) ->
		case SuitInfo of
			#suit_info{
				suit_id        = SuitId
				,own_num       = OwnNum
				,active_num    = ActiveNum
				,sum_star      = SumStar
			} ->
				Sql = io_lib:format(?SQL_INSERT_WORKROOM_SUIT_VALUE,
					[RoleId, SuitId, OwnNum, ActiveNum, SumStar]
				),
				[Sql|TList]
		end end,
	case lists:foldl(F, [], SuitList) of
		[_|_] = SQLs ->
			NewValues = util:join_bitstring(SQLs, <<", ">>),
			FinalSql = io_lib:format(?SQL_INSERT_WORKROOM_SUIT, [NewValues]),
			db:execute(FinalSql);
		_ ->
			ignore
	end,
	ok.

db_select_workroom_suit(RoleId) ->
	Sql = io_lib:format(?SQL_SELECT_WORKROOM_SUIT, [RoleId]),
	db:get_all(Sql).

db_save_workroom_shop(RoleId, LastRefresh, RefreshCount) ->
	Sql = io_lib:format(?SQL_INSERT_WORKROOM_SHOP, [RoleId, LastRefresh, RefreshCount]),
	db:execute(Sql),
	ok.

db_select_workroom_shop(RoleId) ->
	Sql = io_lib:format(?SQL_SELECT_WORKROOM_SHOP, [RoleId]),
	db:get_row(Sql).

db_save_shop_goods(RoleId, ShopGoods) ->
	F = fun(ShopInfo, TList) ->
		case ShopInfo of
			#shop_good{
				 pos      = Pos
				,goods_id = GoodsId
				,has_buy  = HasBuy
			} ->
			Sql = io_lib:format(?SQL_INSERT_SHOP_GOODS_VALUE, [RoleId, Pos, GoodsId, HasBuy]),
			[Sql|TList]
		end end,
	case lists:foldl(F, [], ShopGoods) of
		[_|_] = SQLs ->
			NewValues = util:join_bitstring(SQLs, <<", ">>),
			FinalSql  = io_lib:format(?SQL_INSERT_SHOP_GOODS, [NewValues]),
			db:execute(FinalSql);
		_ ->
			ignore
	end,
	ok.

db_select_shop_goods(RoleId) ->
	Sql = io_lib:format(?SQL_SELECT_SHOP_GOODS, [RoleId]),
	db:get_all(Sql).

db_update_shop_good(RoleId, GoodsId, HasBuy) ->
	Sql = io_lib:format(?SQL_UPDATE_SHOP_GOODS, [HasBuy, RoleId, GoodsId]),
	db:execute(Sql).

db_delete_shop_goods(RoleId) ->
	Sql = io_lib:format(?SQL_DELETE_SHOP_GOODS, [RoleId]),
	db:execute(Sql).
