%%%-------------------------------------------------------------------
%%% @doc     拼图-逻辑文件
%%% Module  : puzzle.erl
%%% Created : 2020-12-07
%%% @author : tyl
%%% @end
%%%-------------------------------------------------------------------
-module(puzzle).

-include("puzzle.hrl").
-include("custom_act.hrl").
-include("server.hrl").
-include("common.hrl").
-include("def_module.hrl").
-include("predefine.hrl").
-include("def_goods.hrl").
-include("goods.hrl").
-include("figure.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").
-include("errcode.hrl").

%% API
-export([
	puzzle_base_info/2      %% 拼图基础信息
	,puzzle_card_info/2     %% 拼图信息
	,reward_info/2          %% 奖励信息
	,start_puzzle/2         %% 开始拼图
	,change_card/3          %% 交换位置
	,end_puzzle/3           %% 结束拼图
	,handle_event/2         %% 上线检测
	,notify_other_module/5  %% 活动结束数据处理&结算
	,notify_start/3         %% 推送开始
]).

%% ------------------------------------------------------------------------------
%% @doc 上线切换完场景之后检测 @end
%% ------------------------------------------------------------------------------
handle_event(Player, #event_callback{type_id = ?EVENT_CLIENT_LOGIN_SUCCESS}) ->
	Type = ?CUSTOM_ACT_TYPE_PUZZLE,
	case lib_custom_act_api:get_open_subtype_ids(Type) of
		[_|_] = SubTypeList ->
			F = fun(SubType) ->
				login_continue(Player, SubType)
			    end,
			lists:map(F, SubTypeList);
		_ ->
			ignore
	end,
	{ok, Player};
handle_event(Player, _) ->
	{ok, Player}.

login_continue(#player_status{id = RoleId} = Player, SubType) ->
	Type = ?CUSTOM_ACT_TYPE_PUZZLE,
	case lib_custom_act_api:is_open_act(Type, SubType) of
		true ->
			#custom_act_data{act_data = ActData} = CustomActData =
				lib_custom_act_data:custom_act_data(Type, SubType),
			ActData1 = trans_puzzle_data(ActData),
			case ActData1 of
				#puzzle_data{card_list = CardList, end_time = EndTimeMs, stage_list = _StageList} when CardList =/= [] ->
					NowTimeMs = utime:unixtime(ms),
					% Stage = erlang:length(StageList),
					case NowTimeMs >= EndTimeMs of
						true ->
							end_puzzle(Player, SubType, 1);
						_ ->
							Ref = util:send_after([], (EndTimeMs-NowTimeMs), self(), {'mod', ?MODULE, end_puzzle, [SubType, 1]}),
							NewCustomActData = CustomActData#custom_act_data{time = utime:unixtime()},
							%% Ref只存内存
							lib_custom_act_data:put_custom_data(NewCustomActData#custom_act_data{act_data = ActData1#puzzle_data{ref = Ref}}),
							lib_custom_act_data:db_save_custom_act_data(RoleId, NewCustomActData)
					end;
				_ -> ignore
			end;
		_ -> ignore
	end.

%% ------------------------------------------------------------------------------
%% @doc 活动结束清理数据 @end
%% ------------------------------------------------------------------------------
notify_other_module(ActStatus, _IsKfActType, #act_info{key = {ActType, SubType}} = ActInfo, _Stime, _Etime)
	when ActStatus =:= ?CUSTOM_ACT_STATUS_CLOSE orelse ActStatus =:= ?CUSTOM_ACT_STATUS_MANUAL_CLOSE ->
	lib_custom_act:broadcast_act_info(ActStatus, [ActInfo]),
	case lib_custom_act_util:get_act_cfg_info(ActType, SubType) of
		#custom_act_cfg{clear_type = ?CUSTOM_ACT_CLEAR_CLOSE} ->
			%% 活动结束清理数据的才需要
			lib_custom_act_data:db_delete_custom_act_data(ActType, SubType),
			%% 玩家身上清除活动数据% (活动结束{round,stage}设为{99,99})
			lib_player:cast_online_role_apply(
				?APPLY_CAST_STATUS, ?MODULE, end_puzzle, [SubType, 1]
			);
		_ ->
			ok
	end;
notify_other_module(_, _, _, _, _) -> ok.

%% ------------------------------------------------------------------------------
%% @doc 拼图基础信息 @end
%% ------------------------------------------------------------------------------
puzzle_base_info(#player_status{id = RoleId} = Player, SubType) ->
	case lib_custom_act_api:is_open_act(?CUSTOM_ACT_TYPE_PUZZLE, SubType) of
		true ->
			#custom_act_data{act_data = ActData} =
				lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_PUZZLE, SubType),
			PlayTimes = lib_daily:get_count(RoleId, ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_PUZZLE, 1),
			case trans_puzzle_data(ActData) of
				#puzzle_data{stage_list = StageList} ->
					Stage = erlang:length(StageList);
				_  ->
					Stage = 0
			end,
			lib_server_send:send_to_sid(
				Player#player_status.sid, pt_332, 33271, [SubType, Stage, PlayTimes]
			);
		false ->
			lib_server_send:send_to_sid(
				Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_act_closed)]
			)
	end,
	ok.

%% ------------------------------------------------------------------------------
%% @doc 拼图信息 @end
%% ------------------------------------------------------------------------------
puzzle_card_info(Player, SubType) ->
	case lib_custom_act_api:is_open_act(?CUSTOM_ACT_TYPE_PUZZLE, SubType) of
		true ->
			#custom_act_data{act_data = ActData} =
				lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_PUZZLE, SubType),
			case ActData of
				#puzzle_data{card_list = CardList, picture = Picture, start_time = StartTime, end_time = EndTime} ->
					PackCardList = pack_puzzle_card(CardList);
				_  ->
					PackCardList = [], Picture = 0, StartTime = 0, EndTime = 0
			end,
			lib_server_send:send_to_sid(
				Player#player_status.sid, pt_332, 33272, [SubType, 1, Picture, PackCardList, StartTime, EndTime]
			);
		false ->
			lib_server_send:send_to_sid(
				Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_act_closed)]
			)
	end,
	ok.

pack_puzzle_card(CardList) ->
	F = fun({{X, Y}, Num}, TList) ->
		[{X, Y, Num}|TList]
	end,
	PackCardList = lists:foldl(F, [], CardList),
	PackCardList.

%% ------------------------------------------------------------------------------
%% @doc 奖励信息 @end
%% ------------------------------------------------------------------------------
reward_info(#player_status{} = Player, SubType) ->
	Type = ?CUSTOM_ACT_TYPE_PUZZLE,
	GradeIdList = lib_custom_act_util:get_act_reward_grade_list(Type, SubType),
	ForFun = fun(GradeId, TList) ->
		case lib_custom_act_util:get_act_reward_cfg_info(Type, SubType, GradeId) of
			#custom_act_reward_cfg{reward = Reward} ->
				Reward ++ TList;
			_ -> TList
		end end,
	PackList = lists:foldl(ForFun, [], GradeIdList),
	lib_server_send:send_to_sid(
		Player#player_status.sid, pt_332, 33278, [SubType, PackList]
	),
	ok.

%% ------------------------------------------------------------------------------
%% @doc 开启拼图 @end
%% ------------------------------------------------------------------------------
start_puzzle(#player_status{id = RoleId} = Player, SubType) ->
	Type = ?CUSTOM_ACT_TYPE_PUZZLE,
	case lib_custom_act_api:is_open_act(Type, SubType) of
		true ->
			#custom_act_data{act_data = ActData} = CustomActData =
				lib_custom_act_data:custom_act_data(Type, SubType),
			ActCfg = case lib_custom_act_util:get_act_cfg_info(Type, SubType) of
				         #custom_act_cfg{} = TActCfg -> TActCfg;
				         _ -> #custom_act_cfg{}
			         end,
			ActData1 = trans_puzzle_data(ActData),
			case ActData1 of
				#puzzle_data{stage_list = StageList, end_time = EndTime, pic_list = OldPicList, ref = OldRef} ->
					PlayTimes = lib_daily:get_count(RoleId, ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_PUZZLE, 1),
					%% 秒有时会有延时，直接使用ms
					NowTimeMs = utime:unixtime(ms),
					#custom_act_cfg{condition = Condition} = ActCfg,
					LimitTimes = case lists:keyfind(everyday_time, 1, Condition) of
						             {_, TLimit} -> TLimit;
									 _ -> 0
					             end,
					if
						PlayTimes >= LimitTimes ->
							lib_server_send:send_to_sid(
								Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_puzzle_times_limit)]
							);
						NowTimeMs < EndTime ->
							lib_server_send:send_to_sid(
								Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_puzzle_not_finish)]
							);
						true ->
							%% 根据拼图阶段获取阶数
							Order = get_order(StageList, Condition),
							%% 创建乱序卡片
							CardList  = create_card(Order),
							CardList1 = check_order_card(CardList, Order),
							%% 图片Id
							Picture = rand_picture(Condition, OldPicList),
							%% 计算开始结束时间
							{NewStartTime, NewEndTime} = get_start_end_time(NowTimeMs, Order),
							%% 推送开启时间
							util:send_after([], (NewStartTime-NowTimeMs), self(), {'mod', ?MODULE, notify_start, [SubType, erlang:length(StageList)+1]}),
							Ref = util:send_after(OldRef, (NewEndTime-NowTimeMs), self(), {'mod', ?MODULE, end_puzzle, [SubType, 1]}),
							NewActData = ActData1#puzzle_data{
								card_list   = CardList1
								,picture    = Picture
								,pic_list   = [Picture|OldPicList]
								,start_time = NewStartTime
								,end_time   = NewEndTime
							},
							NewCustomActData = CustomActData#custom_act_data{time = utime:unixtime(), act_data = NewActData},
							lib_custom_act_data:put_custom_data(NewCustomActData#custom_act_data{act_data = NewActData#puzzle_data{ref = Ref}}),
							lib_custom_act_data:db_save_custom_act_data(RoleId, NewCustomActData),
							lib_server_send:send_to_sid(
								Player#player_status.sid, pt_332, 33272, [SubType, 2, Picture, pack_puzzle_card(CardList1), NewStartTime, NewEndTime]
							)
					end;
				_  ->
					lib_server_send:send_to_sid(
						Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_act_closed)]
					)
			end;
		false ->
			lib_server_send:send_to_sid(
				Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_act_closed)]
			)
	end,
	ok.

%% 创建乱序卡片
create_card(Stage) ->
	%% 构建坐标
	ForFun = fun(X, TList) ->
		F = fun(Y, TList1) ->
			[{X, Y}|TList1]
		end,
		lists:foldl(F, TList, lists:seq(1, Stage))
	end,
	PosList = lists:foldl(ForFun, [], lists:seq(1, Stage)),
	%% 构建顺序
	NumList = lists:seq(1, Stage*Stage),
	ShuffleNumList = urand:list_shuffle(NumList),
	%% 将顺序为1放到阶数{N,N}位置,保证是乱序
	FixPosNumList = [{{Stage, Stage}, 1}],
	ShuffleNumList1 = ShuffleNumList -- [1],
	PosList1 = PosList -- [{Stage, Stage}],
	%% 将数字放进坐标
	PutFun = fun({TX, TY}, {TSfList, TPosNumList}) ->
		case TSfList of
			[TNum|TSfList1] ->
				TPosNumList1 = [{{TX, TY}, TNum}|TPosNumList],
				{TSfList1, TPosNumList1};
			_ ->
				{TSfList, TPosNumList}
		end end,
	{_, PosNumList} = lists:foldl(PutFun, {ShuffleNumList1, []}, PosList1),
	NewPosNumList = PosNumList ++ FixPosNumList,
	NewPosNumList.

%% 随机图片Id
rand_picture(Condition, OldPicList) ->
	Picture = case lists:keyfind(picture, 1, Condition) of
		          {_, [Min, Max]} ->
			          case lists:seq(max(1, Min), Max) -- OldPicList of
				          [_|_] = RandList -> urand:list_rand(RandList);
						  _ -> urand:list_rand(lists:seq(max(1, Min), Max))
			          end;
		          _ -> 0
	          end,
	Picture.

%% 开始和结束时间
get_start_end_time(NowTime, Stage) ->
	StartTime = NowTime + 5*1000, %% 5秒记忆时间
	CapTime = case Stage of
				  ?PUZZLE_2 -> ?PUZZLE_2_TIME;
				  ?PUZZLE_3 -> ?PUZZLE_3_TIME;
				  ?PUZZLE_4 -> ?PUZZLE_4_TIME;
				  ?PUZZLE_5 -> ?PUZZLE_5_TIME;
				  _ -> ?PUZZLE_5_TIME
	          end,
	EndTime = StartTime + CapTime*1000,
	{StartTime, EndTime}.

%% 只检查2阶的
check_order_card(CardList, Order) ->
	CardList1 = case Order of
		            2 -> case if_all_fix(CardList, Order)of
			                 true -> create_card(Order);
			                 _ -> CardList
		                 end;
		            _ -> CardList
	            end,
	CardList1.

%% 推送开始
notify_start(Player, SubType, Stage) ->
	lib_server_send:send_to_sid(
		Player#player_status.sid, pt_332, 33279, [SubType, Stage]
	),
	{ok, Player}.

%% ------------------------------------------------------------------------------
%% @doc 替换卡片 @end
%% ------------------------------------------------------------------------------
change_card(#player_status{id = RoleId} = Player, SubType, ChangeList) ->
	Type = ?CUSTOM_ACT_TYPE_PUZZLE,
	case lib_custom_act_api:is_open_act(Type, SubType) of
		true ->
			ActCfg = case lib_custom_act_util:get_act_cfg_info(Type, SubType) of
				         #custom_act_cfg{} = TActCfg -> TActCfg;
				         _ -> #custom_act_cfg{}
			         end,
			#custom_act_data{act_data = ActData} = CustomActData =
				lib_custom_act_data:custom_act_data(Type, SubType),
			case ActData of
				#puzzle_data{card_list = CardList, stage_list = StageList, start_time = StartTime} ->
					#custom_act_cfg{condition = Condition} = ActCfg,
					Order = get_order(StageList, Condition),
					CheckCard = check_change_card(ChangeList, Order),
					NowTime = utime:unixtime(ms),
					case CheckCard of
						false ->
							lib_server_send:send_to_sid(
								Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_puzzle_change_err)]
							);
						_ when CardList == [] ->
							lib_server_send:send_to_sid(
								Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_puzzle_not_start)]
							);
						_ when NowTime =< StartTime ->
							lib_server_send:send_to_sid(
								Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_puzzle_remember_time)]
							);
						_ ->
							NewCardList = change_card_core(CardList, ChangeList),
							CheckAllFix = if_all_fix(NewCardList, Order),
							% PlayTimes = lib_daily:get_count(RoleId, ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_PUZZLE, 1),
							case CheckAllFix of
								true ->
									{ok, Player1} = end_puzzle(Player, SubType, 0),
									{ok, Player1};
								_ ->
									NewActData = ActData#puzzle_data{
										card_list   = NewCardList
									},
									NewCustomActData = CustomActData#custom_act_data{time = utime:unixtime(),act_data = NewActData},
									lib_custom_act_data:put_custom_data(NewCustomActData),
									lib_custom_act_data:db_save_custom_act_data(
										RoleId, NewCustomActData#custom_act_data{act_data = NewActData#puzzle_data{ref = []}}
									),
									lib_server_send:send_to_sid(
										Player#player_status.sid, pt_332, 33273, [SubType, pack_puzzle_card(NewCardList)]
									),
									ok
							end
					end;
				_  ->
					lib_server_send:send_to_sid(
						Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_act_closed)]
					),
					ok
			end;
		false ->
			lib_server_send:send_to_sid(
				Player#player_status.sid, pt_331, 33100, [?ERRCODE(err331_act_closed)]
			),
			ok
	end.

check_change_card(ChangeList, Order) ->
	Length = erlang:length(ChangeList),
	[{X1, Y1}, {X2, Y2}|_] = ChangeList,
	CheckList = [{TX, TY}||{TX, TY}<-ChangeList, TX =< Order, TX > 0, TY =< Order, TY > 0],
	Length =:= 2 andalso {X1, Y1} =/= {X2, Y2} andalso erlang:length(CheckList) =:= 2.

change_card_core(CardList, ChangeList) ->
	F = fun({TX, TY}, TList) ->
		case lists:keyfind({TX, TY}, 1, CardList) of
			{_, _Num} = Value -> [Value|TList];
			_ -> TList
		end end,
	OldPosNum = lists:foldl(F, [], ChangeList),
	[{{X1, Y1}, Num1}, {{X2, Y2}, Num2}] = OldPosNum,
	NewPosNum = [{{X1, Y1}, Num2}, {{X2, Y2}, Num1}],
	PutFun = fun({{TX1, TY1}, _TNum1} = TValue, TList1) ->
			lists:keystore({TX1, TY1}, 1, TList1, TValue)
		end,
	NewCardList = lists:foldl(PutFun, CardList, NewPosNum),
	NewCardList.

%% 是否所有顺序归位
if_all_fix(CardList, Order) ->
	%% 这里直接用Order,不需要开根号了
	Length = erlang:length(CardList),
	ForFun = fun({{X, Y}, Num}, TList) ->
		TrueNum = max(0, X-1)*Order + Y,
		case TrueNum =:= Num of
			true -> [1|TList];
			_ -> [0|TList]
		end end,
	CheckList = lists:foldl(ForFun, [], CardList),
	Length =:= lists:sum(CheckList).

%% ------------------------------------------------------------------------------
%% @doc 时间结束 @end
%% ------------------------------------------------------------------------------
end_puzzle(#player_status{id = RoleId, figure = Figure} = Player, SubType, IsTimeOut) ->
	Type = ?CUSTOM_ACT_TYPE_PUZZLE,
	Condition = case lib_custom_act_util:get_act_cfg_info(Type, SubType) of
		            #custom_act_cfg{condition = TCondition} -> TCondition;
		            _ -> []
	            end,
	#custom_act_data{act_data = ActData} = CustomActData =
		lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_PUZZLE, SubType),
	ActData1 = trans_puzzle_data(ActData),
	#puzzle_data{card_list = OldCardList, stage_list = OldStageList, pic_list = OldPicList,
		continue = OldContinue, ref = OldRef} = ActData1,
	util:cancel_timer(OldRef),
	% OldStage1 = erlang:length(OldStageList),
	Order = get_order(OldStageList, Condition),
	%% 增加次数
	PlayerTimes = lib_daily:get_count(RoleId, ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_PUZZLE, 1),
	case IsTimeOut of
		0 ->
			NewStageList = [Order|OldStageList],
			MaxBarrier = max_barrier(Condition),
			case erlang:length(NewStageList) >= MaxBarrier of
				true ->
					%% 一轮结束增加次数 & 发奖励
					NewPlayerTimes = PlayerTimes + 1,
					NewStageList1  = [],
					NewPicList     = [],
					NewContinue    = 0,
					lib_daily:set_count(RoleId, ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_PUZZLE, 1, NewPlayerTimes),
					{SendReward, ShowGrade} = calc_reward(Type, SubType, IsTimeOut),
					Produce = #produce{reward = SendReward, type = puzzle},
					{ok, NewPlayer} = lib_goods_api:send_reward_with_mail(Player, Produce, ?MOD_AC_CUSTOM),
					lib_custom_act_slices:task_event(RoleId, ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_PUZZLE),
					log_send_goods(NewPlayer, Type, SubType, SendReward),
					send_tv(Figure#figure.name, SubType, ShowGrade);
				_ ->
					NewPlayerTimes = PlayerTimes,
					NewStageList1  = NewStageList,
					NewPicList     = OldPicList,
					NewContinue    = OldContinue + 1,
					SendReward     = [],
					NewPlayer = Player
			end,
			NewActData = ActData1#puzzle_data{
				card_list   = []
				,stage_list = NewStageList1
				,continue   = NewContinue
				,picture    = 0
				,pic_list   = NewPicList
				,start_time = 0
				,end_time   = 0
				,ref        = []
			},
			NewCustomActData = CustomActData#custom_act_data{time = utime:unixtime(),act_data = NewActData},
			lib_custom_act_data:save_custom_data(RoleId, NewCustomActData),
			lib_server_send:send_to_sid(
				NewPlayer#player_status.sid, pt_332, 33274,
				[SubType, ?SUCCESS, SendReward, erlang:length(NewStageList1), NewPlayerTimes]
			),
			{ok, NewPlayer};
		_ ->
			%% 成功通关的直接跳过
			case OldCardList of
				[] -> {ok, Player};
				_ ->
					%% 失败重新新的一轮
					NewActData = ActData1#puzzle_data{
						card_list   = []
						,stage_list = []
						,continue   = 0
						,picture    = 0
						,pic_list   = []
						,start_time = 0
						,end_time   = 0
						,ref        = []
					},
					lib_daily:increment(RoleId, ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_PUZZLE, 1),
					NewCustomActData = CustomActData#custom_act_data{time = utime:unixtime(),act_data = NewActData},
					lib_custom_act_data:save_custom_data(RoleId, NewCustomActData),
					{SendReward, _ShowGrade} = calc_reward(Type, SubType, IsTimeOut),
					Produce = #produce{reward = SendReward, type = puzzle},
					{ok, NewPlayer} = lib_goods_api:send_reward_with_mail(Player, Produce, ?MOD_AC_CUSTOM),
					lib_custom_act_slices:task_event(RoleId, ?MOD_AC_CUSTOM, ?CUSTOM_ACT_TYPE_PUZZLE),
					log_send_goods(NewPlayer, Type, SubType, SendReward),
					lib_server_send:send_to_sid(
						NewPlayer#player_status.sid, pt_332, 33274,
						[SubType, ?ERRCODE(err331_puzzle_over_time), SendReward, 0, PlayerTimes + 1]
					),
					{ok, NewPlayer}
			end
	end.

max_barrier(Condition) ->
	case lists:keyfind(stage_total, 1, Condition) of
		{_, Barrier} -> Barrier;
		_ ->1
	end.

calc_reward(Type, SubType, IsTimeOut) ->
	GradeIdList = lib_custom_act_util:get_act_reward_grade_list(Type, SubType),
	ForFun = fun(GradeId, {TWeightList, TShowList}) ->
		case lib_custom_act_util:get_act_reward_cfg_info(Type, SubType, GradeId) of
			#custom_act_reward_cfg{condition = Condition} ->
				Weight = case lists:keyfind(weight, 1, Condition) of
					         {_, TWeight} -> TWeight;
					         _ -> 0
				         end,
				IsShow = case lists:keyfind(tv_show, 1, Condition) of
					         {_, TShow} -> TShow;
					         _ -> 0
				         end,
				case IsShow of
					1 ->
						TWeightList1 = [{Weight, GradeId}|TWeightList],
						TShowList1   = [GradeId|TShowList];
					_ ->
						TWeightList1 = [{Weight, GradeId}|TWeightList],
						TShowList1   = TShowList
				end,
				{TWeightList1, TShowList1}
		end end,
	{WeightList, ShowList} = lists:foldl(ForFun, {[], []}, GradeIdList),
	%% 超时1份奖励,非超时3份奖励
	case IsTimeOut of
		1 ->
			RandGrade = urand:rand_with_weight(WeightList),
			ShowGrade = case lists:member(RandGrade, ShowList) of
				             true -> [RandGrade];
				             _ -> []
			             end,
			SendReward = case lib_custom_act_util:get_act_reward_cfg_info(Type, SubType, RandGrade) of
				             #custom_act_reward_cfg{reward = TReward} -> TReward;
				             _ -> []
			             end,
			{SendReward, ShowGrade};
		_ ->
			RewardFun = fun(_Times, {TGradeList, TGradeShow}) ->
							RandGrade = urand:rand_with_weight(WeightList),
							TGradeShow1 = case lists:member(RandGrade, ShowList) of
								              true -> [RandGrade|TGradeShow];
								              _ -> TGradeShow
							              end,
							{[RandGrade|TGradeList], TGradeShow1}
			            end,
			{RewardGradeList, ShowGrade} = lists:foldl(RewardFun, {[], []}, lists:seq(1, 3)),
			SendFun = fun(TRandGrade, TList) ->
				case lib_custom_act_util:get_act_reward_cfg_info(Type, SubType, TRandGrade) of
					#custom_act_reward_cfg{reward = TReward} -> TReward ++ TList;
					_ -> TList
				end end,
			SendReward = lists:foldl(SendFun, [], RewardGradeList),
			{SendReward, ShowGrade}
	end.

send_tv(PlayerName, SubType, GradeList) ->
	Type = ?CUSTOM_ACT_TYPE_PUZZLE,
	F = fun(GradeId) ->
		case lib_custom_act_util:get_act_reward_cfg_info(Type, SubType, GradeId) of
			#custom_act_reward_cfg{reward = [{_, GoodsId, Num}]} ->
				lib_chat:send_TV({all}, ?MOD_AC_CUSTOM, 12, [PlayerName, GoodsId, Num]);
			_ -> ignore
		end end,
	lists:map(F, GradeList),
	ok.

%% 记录日志
log_send_goods(Player, Type, SubType, Reward) ->
	#player_status{id = RoleId, figure = Figure, gold = Gold, bgold = BGold} = Player,
	#figure{vip = Vip, lv = Lv} = Figure,
	WorldLv = util:get_world_lv(),
	lib_log_api:log_custom_act_reward(RoleId, Type, SubType,
		0, WorldLv, Reward, [], Vip, Lv, Gold, Gold, BGold, BGold, utime:unixtime()
	),
	ok.

%% -------------------------------- builtin_function ----------------------------------
trans_puzzle_data(#puzzle_data{} = PuzzleData) -> PuzzleData;
trans_puzzle_data(_) -> #puzzle_data{}.

get_order(StageList, Condition) ->
	OldStage = erlang:length(StageList),
	NewStage = OldStage + 1,
	%% 最小为2阶
	StageConfig = case lists:keyfind(stage_config, 1, Condition) of
		              {_, TStageConfig} -> TStageConfig;
		              _ -> [?PUZZLE_DEFAULT_ORDER]
	              end,
	AllStage = erlang:length(StageConfig),
	MaxStage = lists:max(StageConfig),
	%% Order为数学上的阶数
	Order = case NewStage > AllStage of
		        true -> MaxStage;
		        _ -> lists:nth(NewStage, StageConfig)
	        end,
	Order.