%%-----------------------------------------------------------------------------
%% @Module  :       lib_lucky_flop
%% @Author  :       WJQ
%% @Created :       2019-07-16
%% @Description:    幸运翻牌
%%-----------------------------------------------------------------------------
-module(lib_lucky_flop).

-include("common.hrl").
-include("server.hrl").
-include("figure.hrl").
-include("lucky_flop.hrl").
-include("custom_act.hrl").
-include("errcode.hrl").
-include("def_fun.hrl").
-include("goods.hrl").
-include("def_module.hrl").
-include("predefine.hrl").

-export([
	login/1					%% 登录
	,send_info/2			%% 主界面信息
	,select_reward/3		%% 选择奖励
	,draw/2					%% 抽奖
	,recieve_reward/3		%% 领取累计奖励
	,reset_reward/2			%% 重置奖励
	,act_end/2				%% 活动结束
	,clear_act_data_and_send_reward/4		%% 清除活动数据并发送未领取累计奖励
	,clear_act_data/2						%% 仅清除活动数据
	]).

%% DB数据转内存记录
make_record(SubType, NowTimes, TotalTimes, Turn, SDrawRewards, SReceiveRewards, Time) ->
	DrawRewards = util:bitstring_to_term(SDrawRewards),
	ReceiveRewards = util:bitstring_to_term(SReceiveRewards),
	#lucky_flop_data{
		sub_type = SubType, now_times = NowTimes, total_times = TotalTimes, turn = Turn,
		draw_rewards = DrawRewards, receive_rewards = ReceiveRewards, time = Time
	}.

%% 登录
login(RoleId) ->
	DBList = db_get_lucky_flop(RoleId),
	OpenInfos = lib_custom_act_api:get_open_custom_act_infos(?CUSTOM_ACT_TYPE_LUCAY_FLOP),
	F = fun([SubType, NowTimes, TotalTimes, Turn, SDrawRewards, SReceiveRewards, Time], {ValidList, DropList}) ->
		case lists:keyfind({?CUSTOM_ACT_TYPE_LUCAY_FLOP, SubType}, #act_info.key, OpenInfos) of
			#act_info{stime = Stime, etime = Etime} when Time >= Stime, Time < Etime ->
				LuckyFlopData = make_record(SubType, NowTimes, TotalTimes, Turn, SDrawRewards, SReceiveRewards, Time),
				{[{SubType, LuckyFlopData}|ValidList], DropList};
			_ ->
				{ValidList, [SubType|DropList]}
		end
	end,
	{ValidList, DropList} = lists:foldl(F, {[], []}, DBList),
	case DropList =:= [] of
		true  -> skip;
		false -> delete_lucky_flop_role(RoleId, DropList)
	end,
	DataMaps = maps:from_list(ValidList),
	#lucky_flop{data_map = DataMaps}.

%% ===========
%% 主界面信息
%% ===========
send_info(PS, SubType) ->
	#player_status{id = RoleId, figure = Figure} = PS,
	Lv = Figure#figure.lv,
	case check_act_open(SubType) of
		{true, _ActInfo} ->
			{Result, Args} = get_act_args_with_check(SubType, [role_lv]),
			CheckList =[{check_act_condition, Result}, {check_lv, proplists:get_value(role_lv, Args), Lv}],
			case check(CheckList) of
				true ->
					do_send_info(PS, SubType);
				{false, ErrCode} ->
					lib_custom_act:send_error_code(RoleId, ErrCode)
			end;
		{false, ErrCode} ->
			lib_custom_act:send_error_code(RoleId, ErrCode)
	end.

do_send_info(PS, SubType) ->
	#player_status{sid = Sid, lucky_flop = LuckyFlop} = PS,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	LuckyFlopData = maps:get(SubType, DataMap, #lucky_flop_data{}),
	#lucky_flop_data{draw_rewards = DrawRewards, receive_rewards = ReceiveRewards, now_times = NowTimes, turn = Turn} = LuckyFlopData,
	{ok, Bin} = pt_332:write(33214, [SubType, DrawRewards, ReceiveRewards, NowTimes, Turn]),
	lib_server_send:send_to_sid(Sid, Bin).

%% ===========
%% 选择奖励
%% ===========
select_reward(PS, SubType, GradeIds) ->
	#player_status{id = RoleId, figure = Figure, lucky_flop = LuckyFlop} = PS,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	Lv = Figure#figure.lv,
	case check_act_open(SubType) of
		{true, ActInfo} ->
			LuckyFlopData = get_player_data(DataMap, ActInfo),
			#lucky_flop_data{draw_rewards = DrawRewards} = LuckyFlopData,
			{Result, Args} = get_act_args_with_check(SubType, [role_lv, select_limit]),
			RewardInfos = get_reward_infos(SubType, GradeIds),
			RewardLvCount = count_reward_lv(RewardInfos),
			CheckList =[
				{check_act_condition, Result}, {check_lv, proplists:get_value(role_lv, Args), Lv},
				{check_can_select, DrawRewards}, {check_reward_repeat, GradeIds},
				{check_reward_info, RewardInfos},
				{check_reward_nums, proplists:get_value(select_limit, Args), RewardLvCount}
			],
			case check(CheckList) of
				true ->
					do_select_reward(PS, SubType, GradeIds);
				{false, ErrCode} ->
					lib_custom_act:send_error_code(RoleId, ErrCode)
			end;
		{false, ErrCode} ->
			lib_custom_act:send_error_code(RoleId, ErrCode)
	end.

do_select_reward(PS, SubType, GradeIds) ->
	#player_status{id = RoleId, sid = Sid, lucky_flop = LuckyFlop} = PS,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	LuckyFlopData = maps:get(SubType, DataMap, #lucky_flop_data{sub_type = SubType}),
	DrawRewards = [{GradeId, ?NOT_GET} || GradeId <- GradeIds],
	NewLuckyFlopData = LuckyFlopData#lucky_flop_data{draw_rewards = DrawRewards, time = utime:unixtime()},
	save_lucky_flop_data(RoleId, NewLuckyFlopData),
	NewDataMap = maps:put(SubType, NewLuckyFlopData, DataMap),
	NewLuckyFlop = LuckyFlop#lucky_flop{data_map = NewDataMap},
	NewPS = PS#player_status{lucky_flop = NewLuckyFlop},
	{ok, Bin} = pt_332:write(33215, [SubType, GradeIds]),
	lib_server_send:send_to_sid(Sid, Bin),
	{ok, NewPS}.

get_reward_infos(_SubType, []) -> [];
get_reward_infos(SubType, GradeIds) -> get_reward_infos(SubType, GradeIds, []).

get_reward_infos(_SubType, [], AccList) -> AccList;
get_reward_infos(SubType, [GradeId|T], AccList) ->
	case get_reward_condition(SubType, GradeId, false) of
		Condition when is_list(Condition) ->
			get_reward_infos(SubType, T, [Condition|AccList]);
		_ ->
			false
	end.

count_reward_lv(RewardInfos) when not is_list(RewardInfos) -> [];
count_reward_lv([]) -> [];
count_reward_lv(RewardInfos) -> count_reward_lv(RewardInfos, []).

count_reward_lv([RewardInfo|T], AccList) ->
	case lib_custom_act_check:check_act_condtion([reward_lv], RewardInfo) of
		[RewardLv] ->
			case lists:keyfind(RewardLv, 1, AccList) of
				{RewardLv, Count} ->
					NewAccList = lists:keyreplace(RewardLv, 1, AccList, {RewardLv, Count + 1});
				_ ->
					NewAccList = [{RewardLv, 1}|AccList]
			end,
			count_reward_lv(T, NewAccList);
		_ ->
			[]
	end;
count_reward_lv([], AccList) -> AccList.

%% ===========
%% 抽奖
%% ===========
draw(PS, SubType) ->
	#player_status{id = RoleId, figure = Figure, lucky_flop = LuckyFlop} = PS,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	Lv = Figure#figure.lv,
	case check_act_open(SubType) of
		{true, ActInfo} ->
			LuckyFlopData = get_player_data(DataMap, ActInfo),
			#lucky_flop_data{draw_rewards = DrawRewards, now_times = NowTimes} = LuckyFlopData,
			{Result, Args} = get_act_args_with_check(SubType, [role_lv, draw_cost, draw_gid]),
			CostList = get_cost_list(proplists:get_value(draw_cost, Args), proplists:get_value(draw_gid, Args), NowTimes + 1),
			CheckList =[
				{check_act_condition, Result}, {check_lv, proplists:get_value(role_lv, Args), Lv},
				{check_draw_rewards, DrawRewards}, {check_null_cell, PS},
				{check_cost, CostList, PS}
			],
			case check(CheckList) of
				true ->
					do_draw(PS, ActInfo, SubType, CostList);
				{false, ErrCode} ->
					lib_custom_act:send_error_code(RoleId, ErrCode)
			end;
		{false, ErrCode} ->
			lib_custom_act:send_error_code(RoleId, ErrCode)
	end.

do_draw(PS, ActInfo, SubType, CostList) ->
	#player_status{id = RoleId, sid = Sid, figure = Figure, lucky_flop = LuckyFlop} = PS,
	#figure{name = Name} = Figure,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	LuckyFlopData = maps:get(SubType, DataMap),
	#lucky_flop_data{draw_rewards = DrawRewards, now_times = NowTimes, total_times = TotalTimes, turn = Turn} = LuckyFlopData,
	DrawList = get_draw_list(PS, ActInfo, DrawRewards),
	[{GradeId, RewardList}] = urand:list_rand_by_weight(DrawList, 1),
	case lib_goods_api:can_give_goods(PS, RewardList) of
		true ->
			About = lists:concat(["Times:", NowTimes + 1]),
			{true, NewPS1, _RealCost} = lib_goods_api:cost_objects_with_auto_buy(PS, CostList, lucky_flop_cost, About),
			NewDrawRewards = lists:keyreplace(GradeId, 1, DrawRewards, {GradeId, ?HAS_GET}),
			NewLuckyFlopData = LuckyFlopData#lucky_flop_data{
				draw_rewards = NewDrawRewards, now_times = NowTimes + 1, total_times = TotalTimes + 1
			},
			save_lucky_flop_data(RoleId, NewLuckyFlopData),
			Produce = #produce{type = lucky_flop, reward = RewardList, show_tips = ?SHOW_TIPS_1, remark = About},
			NewPS2 = lib_goods_api:send_reward(NewPS1, Produce),
			check_send_tv(ActInfo, Name, GradeId, RewardList),
			NewDataMap = maps:put(SubType, NewLuckyFlopData, DataMap),
			NewLuckyFlop = LuckyFlop#lucky_flop{data_map = NewDataMap},
			LastPS = NewPS2#player_status{lucky_flop = NewLuckyFlop},
			{ok, Bin} = pt_332:write(33216, [SubType, NewDrawRewards, NowTimes + 1, Turn]),
			lib_server_send:send_to_sid(Sid, Bin),
			lib_log_api:log_lucky_flop_draw(RoleId, SubType, CostList, RewardList, NowTimes+1, Turn+1, TotalTimes+1);
		false ->
			LastPS = PS,
			lib_custom_act:send_error_code(RoleId, ?ERRCODE(err150_no_cell))
	end,
	{ok, LastPS}.

get_draw_list(PS, ActInfo, DrawRewards) -> get_draw_list(PS, ActInfo, DrawRewards, []).

get_draw_list(_PS, _ActInfo, [], AccList) -> AccList;
get_draw_list(PS, ActInfo, [{_GradeId, ?HAS_GET}|T], AccList) ->
	get_draw_list(PS, ActInfo, T, AccList);
get_draw_list(PS, ActInfo, [{GradeId, ?NOT_GET}|T], AccList) ->
	#act_info{key = {_Type, SubType}} = ActInfo,
	case lib_custom_act_util:get_act_reward_cfg_info(?CUSTOM_ACT_TYPE_LUCAY_FLOP, SubType, GradeId) of
		#custom_act_reward_cfg{condition = Condition} = RewardCfg ->
			case lib_custom_act_check:check_act_condtion([weight], Condition) of
				[Weight] ->
					RewardList = lib_custom_act_util:count_act_reward(PS, ActInfo, RewardCfg),
					get_draw_list(PS, ActInfo, T, [{Weight, {GradeId, RewardList}}|AccList]);
				_ ->
					get_draw_list(PS, ActInfo, T, AccList)
			end;
		_ ->
			get_draw_list(PS, ActInfo, T, AccList)
	end.

get_cost_list(undefine, _DrawGoodsId, _Times) -> [];
get_cost_list(_DrawCost, undefine, _Times) -> [];
get_cost_list([{Min, Max, Num}|T], GoodsId, Times) ->
	case Times >= Min andalso Times =< Max of
		true ->
			[{GoodsId, Num}];
		false ->
			get_cost_list(T, GoodsId, Times)
	end;
get_cost_list(_DrawCost, _DrawGoodsId, _Times) -> [].

check_send_tv(_ActInfo, _Name, _GradeId, []) -> skip;
check_send_tv(#act_info{key={_Type, SubType}}, _Name, GradeId, _RewardList) ->
	RewardInfos = get_reward_infos(SubType, [GradeId]),
	RewardLvCount = count_reward_lv(RewardInfos),
	case RewardLvCount of
		[{1, _}] -> %%
			% case lib_custom_act_util:get_act_cfg_info(Type, SubType) of
			%	#custom_act_cfg{name = ActName} ->
			%		ok;
			%	_ ->
			%		ActName = <<>>
			% end,
			% [{_GoodsType, GoodsTypeId, GoodsNum}|_] = RewardList,
			% GoodsColor = data_goods:get_goods_color(GoodsTypeId);
			% lib_chat:send_TV({all}, ?MOD_AC_CUSTOM, 23, [Name, ActName, GoodsColor, GoodsTypeId, GoodsNum]);
			ok;
		_ ->
			skip
	end.

%% ===========
%% 领取累计奖励
%% ===========
recieve_reward(PS, SubType, Times) ->
	#player_status{id = RoleId, figure = Figure, lucky_flop = LuckyFlop} = PS,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	Lv = Figure#figure.lv,
	case check_act_open(SubType) of
		{true, ActInfo} ->
			LuckyFlopData = get_player_data(DataMap, ActInfo),
			#lucky_flop_data{now_times = NowTimes, receive_rewards = ReceiveRewards} = LuckyFlopData,
			{Result, Args} = get_act_args_with_check(SubType, [role_lv]),
			RewardList = get_receive_reward(PS, ActInfo, Times),
			CheckList =[
				{check_act_condition, Result}, {check_lv, proplists:get_value(role_lv, Args), Lv},
				{check_receive_reward, RewardList}, {check_times, Times, NowTimes},
				{check_not_get, Times, ReceiveRewards}, {check_can_give_goods, PS, RewardList}
			],
			case check(CheckList) of
				true ->
					do_receive_reward(PS, SubType, Times, RewardList);
				{false, ErrCode} ->
					lib_custom_act:send_error_code(RoleId, ErrCode)
			end;
		{false, ErrCode} ->
			lib_custom_act:send_error_code(RoleId, ErrCode)
	end.

do_receive_reward(PS, SubType, Times, RewardList) ->
	#player_status{id = RoleId, sid = Sid, lucky_flop = LuckyFlop} = PS,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	LuckyFlopData = maps:get(SubType, DataMap),
	#lucky_flop_data{receive_rewards = ReceiveRewards} = LuckyFlopData,
	NewReceiveRewards = [Times|lists:delete(Times, ReceiveRewards)],
	NewLuckyFlopData = LuckyFlopData#lucky_flop_data{receive_rewards = NewReceiveRewards},
	save_lucky_flop_data(RoleId, NewLuckyFlopData),
	NewDataMap = maps:put(SubType, NewLuckyFlopData, DataMap),
	NewLuckyFlop = LuckyFlop#lucky_flop{data_map = NewDataMap},
	Remark = lists:concat(["Times:", Times]),
	Produce = #produce{type = lucky_flop_receive_reward, reward = RewardList, show_tips = ?SHOW_TIPS_1, remark = Remark},
	NewPS = lib_goods_api:send_reward(PS, Produce),
	LastPS = NewPS#player_status{lucky_flop = NewLuckyFlop},
	{ok, Bin} = pt_332:write(33217, [SubType, Times]),
	lib_server_send:send_to_sid(Sid, Bin),
	lib_log_api:log_lucky_flop_receive(RoleId, SubType, RewardList, Times),
	{ok, LastPS}.

%% @return [{Times, RewardList},...]
get_all_receive_reward(PS, ActInfo) ->
	#act_info{key = {_Type, SubType}} = ActInfo,
	GradeIds = lib_custom_act_util:get_act_reward_grade_list(?CUSTOM_ACT_TYPE_LUCAY_FLOP, SubType),
	F = fun(GradeId, AccList) ->
		case lib_custom_act_util:get_act_reward_cfg_info(?CUSTOM_ACT_TYPE_LUCAY_FLOP, SubType, GradeId) of
			#custom_act_reward_cfg{condition = Condition} = RewardCfg ->
				case lib_custom_act_check:check_act_condtion([times_limit], Condition) of
					[Times] ->
						RewardList = lib_custom_act_util:count_act_reward(PS, ActInfo, RewardCfg),
						lists:keystore(Times, 1, AccList, {Times, RewardList});
					false ->
						AccList
				end;
			_ ->
				AccList
		end
	end,
	lists:foldl(F, [], GradeIds).

%% @retrun [{GoodsType, GoodsId, Num},...]
get_receive_reward(PS, ActInfo, Times) ->
	AllRewardList = get_all_receive_reward(PS, ActInfo),
	case lists:keyfind(Times, 1, AllRewardList) of
		{Times, RewardList} ->
			RewardList;
		_ ->
			[]
	end.

%% ===========
%% 重置奖励
%% ===========
reset_reward(PS, SubType) ->
	#player_status{id = RoleId, figure = Figure, lucky_flop = LuckyFlop} = PS,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	Lv = Figure#figure.lv,
	case check_act_open(SubType) of
		{true, ActInfo} ->
			LuckyFlopData = get_player_data(DataMap, ActInfo),
			#lucky_flop_data{turn = Turn, now_times = NowTimes, draw_rewards = DrawRewards, receive_rewards = ReceiveRewards} = LuckyFlopData,
			{Result, Args} = get_act_args_with_check(SubType, [role_lv, reset_cost, reset_gid]),
			CostList = get_reset_cost(proplists:get_value(reset_cost, Args), proplists:get_value(reset_gid, Args), Turn + 1),
			AllRewardList = get_all_receive_reward(PS, ActInfo),
			ReceiveTimes = [NeedTimes || {NeedTimes, _Reward} <- AllRewardList, NowTimes >= NeedTimes],
			CheckList =[
				{check_act_condition, Result}, {check_lv, proplists:get_value(role_lv, Args), Lv},
				{check_need_reset, DrawRewards}, {check_can_reset, ReceiveTimes, ReceiveRewards},
				{check_reset_cost, PS, CostList}
			],
			case check(CheckList) of
				true ->
					do_reset_reward(PS, ActInfo, SubType, CostList);
				{false, ErrCode} ->
					lib_custom_act:send_error_code(RoleId, ErrCode)
			end;
		{false, ErrCode} ->
			lib_custom_act:send_error_code(RoleId, ErrCode)
	end.

do_reset_reward(PS, _ActInfo, SubType, CostList) ->
	#player_status{id = RoleId, sid = Sid, lucky_flop = LuckyFlop} = PS,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	LuckyFlopData = maps:get(SubType, DataMap, #lucky_flop_data{sub_type = SubType, time = utime:unixtime()}),
	#lucky_flop_data{turn = Turn, receive_rewards = _ReceiveRewards, now_times = _NowTimes} = LuckyFlopData,
	NewLuckyFlopData = LuckyFlopData#lucky_flop_data{now_times = 0, turn = Turn + 1, draw_rewards = [], receive_rewards = []},
	save_lucky_flop_data(RoleId, NewLuckyFlopData),
	NewDataMap = maps:put(SubType, NewLuckyFlopData, DataMap),
	NewLuckyFlop = LuckyFlop#lucky_flop{data_map = NewDataMap},
	About = lists:concat(["Turn", Turn + 1]),
	case CostList of
		[{_GoodsId, 0}] ->
			Type = 0,
			LogCost = [],
			NewPS = PS;
		_ ->
			Type = 1,
			LogCost = CostList,
			{true, NewPS, _RealCost} = lib_goods_api:cost_objects_with_auto_buy(PS, CostList, lucky_flop_refresh, About)
	end,
	LastPS = NewPS#player_status{lucky_flop = NewLuckyFlop},
	% try_to_send_receive_reward(LastPS, ActInfo, ReceiveRewards, NowTimes),		% 邮件形式发送未领取累计奖励
	{ok, Bin} = pt_332:write(33218, [SubType, Turn + 1]),
	lib_server_send:send_to_sid(Sid, Bin),
	lib_log_api:log_lucky_flop_reset(RoleId, Type, LogCost, Turn+1),
	{ok, LastPS}.

get_reset_cost(undefine, _ResetGoodsId, _Turn) -> [];
get_reset_cost(_ResetCost, undefine, _Turn) -> [];
get_reset_cost([{Min, Max, Num}|T], GoodsId, Turn) ->
	case Turn >= Min andalso Turn =< Max of
		true ->
			[{GoodsId, Num}];
		false ->
			get_reset_cost(T, GoodsId, Turn)
	end;
get_reset_cost(_ResetCost, _ResetGoodsId, _Turn) -> [].

try_to_send_receive_reward(PS, ActInfo, ReceiveRewards, NowTimes) ->
	#player_status{id = RoleId} = PS,
	#act_info{key = {_Type, SubType}} = ActInfo,
	AllRewardList = get_all_receive_reward(PS, ActInfo),
	F = fun
		({Times, RewardList}, {AccReward, AccList}) when NowTimes >= Times ->
			case lists:member(Times, ReceiveRewards) of
				true  -> {AccReward, AccList};
				false -> {[RewardList|AccReward], [{Times, RewardList}|AccList]}
			end;
		({_Times, _RewardList}, {AccReward, AccList}) ->
			{AccReward, AccList}
	end,
	{TRewardList, LogList} = lists:foldl(F, {[], []}, AllRewardList),
	LastRewardList = lib_goods_api:make_reward_unique(lists:flatten(TRewardList)),
	case LastRewardList =/= [] of
		true ->
			Title = utext:get(3310051),
			Content = utext:get(3310052),
			lib_mail_api:send_sys_mail([RoleId], ?MOD_AC_CUSTOM_EXTRA, ?CUSTOM_ACT_TYPE_LUCAY_FLOP, Title, Content, LastRewardList),
			add_log_lucky_flop_receive(RoleId, SubType, LogList);
		false ->
			skip
	end.

%% ===========
%% 活动结束
%% ===========
% 邮件形式发送未领取累计奖励，清空所有同子类型数据
act_end(?CUSTOM_ACT_STATUS_CLOSE, #act_info{key = {_Type, SubType}} = ActInfo) ->
	UserDatas = db_get_all_lucky_flop(SubType),
	F = fun([RoleId, NowTimes, SReceiveRewards]) ->
		case NowTimes > 0 of
			true ->
				Args = [ActInfo, util:bitstring_to_term(SReceiveRewards), NowTimes],
				lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?HAND_OFFLINE, ?MODULE, clear_act_data_and_send_reward, Args);
			false ->
				lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?MODULE, clear_act_data, [SubType])
		end
	end,
	lists:foreach(F, UserDatas),
	delete_lucky_flop_by_sub_type(SubType);
act_end(?CUSTOM_ACT_STATUS_MANUAL_CLOSE, #act_info{key = {_Type, SubType}} = _ActInfo) ->
	UserDatas = db_get_all_lucky_flop(SubType),
	F = fun([RoleId, _NowTimes, _SReceiveRewards]) ->
		lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?MODULE, clear_act_data, [SubType])
	end,
	lists:foreach(F, UserDatas),
	delete_lucky_flop_by_sub_type(SubType).

% 清除活动数据并发送未领取累计奖励
% @return skip | #player_status{}
clear_act_data_and_send_reward(PS, ActInfo, ReceiveRewards, NowTimes) ->
	#player_status{id = RoleId} = PS,
	#act_info{key = {_Type, SubType}} = ActInfo,
	try_to_send_receive_reward(PS, ActInfo, ReceiveRewards, NowTimes),
	case lib_player:is_online_global(RoleId) of
		true ->
			clear_act_data(PS, SubType);
		false ->
			ok
	end.

% 仅清除活动数据
% @return #player_status{}
clear_act_data(PS, SubType) ->
	#player_status{lucky_flop = LuckyFlop} = PS,
	#lucky_flop{data_map = DataMap} = LuckyFlop,
	NewDataMap = maps:remove(SubType, DataMap),
	NewLuckyFlop = LuckyFlop#lucky_flop{data_map = NewDataMap},
	PS#player_status{lucky_flop = NewLuckyFlop}.


%% ========================================== Util Function ==========================================
%% 获取定制活动条件
get_act_condition(SubType, Default) ->
	case lib_custom_act_util:get_act_cfg_info(?CUSTOM_ACT_TYPE_LUCAY_FLOP, SubType) of
		#custom_act_cfg{condition = Condition} ->
			Condition;
		_ ->
			Default
	end.

%% 获取定制活动奖励条件
get_reward_condition(SubType, GradeId, Default) ->
	case lib_custom_act_util:get_act_reward_cfg_info(?CUSTOM_ACT_TYPE_LUCAY_FLOP, SubType, GradeId) of
		#custom_act_reward_cfg{condition = Condition} ->
			Condition;
		_ ->
			Default
	end.

%% 获取活动常量配置，返回参数带检测结果
%% @return {true, [{ConditionName, Value},...]} | {false, []}
get_act_args_with_check(_SubType, []) -> {true, []};
get_act_args_with_check(SubType, List) ->
	Condition = get_act_condition(SubType, []),
	Values = lib_custom_act_check:check_act_condtion(List, Condition),
	get_act_args_with_check(Values, List, []).

get_act_args_with_check(false, _List, _AccList) -> {false, []};
get_act_args_with_check(_Values, [], AccList) -> {true, AccList};
get_act_args_with_check([Value|LessValues], [Term|LessTerms], AccList) ->
	case check([{act_condition, Term, Value}]) of
		true ->
			NewAccList = [{Term, Value}|AccList],
			get_act_args_with_check(LessValues, LessTerms, NewAccList);
		{false, _ErrCode} ->
			{false, []}
	end.

%% 检测活动是否开启
%% @return {true, #act_info}|{false, ErrCode}
check_act_open(SubType) ->
	NowTime = utime:unixtime(),
	case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_LUCAY_FLOP, SubType) of
		#act_info{etime = Etime} = T when Etime > NowTime ->
			{true, T};
		_ ->
			{false, ?ERRCODE(err331_act_closed)}
	end.

%% 批量写入累计奖励日志
%% @param List :: [{Times, RewardList},...]
add_log_lucky_flop_receive(RoleId, SubType, List) ->
	F = fun({Times, RewardList}) ->
		lib_log_api:log_lucky_flop_receive(RoleId, SubType, RewardList, Times)
	end,
	lists:foreach(F, List).

get_player_data(DataMap, ActInfo) ->
	get_player_data(DataMap, ActInfo, #lucky_flop_data{}).

%% 获取有效的玩家活动数据
get_player_data(DataMap, #act_info{key = {_Type, SubType}, stime = Stime, etime = Etime} = _ActInfo, Default) ->
	case maps:get(SubType, DataMap, false) of
		#lucky_flop_data{time = Time} = LuckyFlopData when Time >= Stime, Time < Etime ->
			LuckyFlopData;
		_ ->
			Default
	end.


%% ========================================== Check Function ==========================================
check([]) ->
	true;
check([H|T]) ->
	case do_check(H) of
		true ->
			check(T);
		{false, ErrCode} ->
			?PRINT(lists:concat(["\e[1;36m", "H:~W~n", "\e[0;38m"]), [H,20]),
			{false, ErrCode};
		_Other ->
			?PRINT(lists:concat(["\e[1;36m", "H:~W~n", "\e[0;38m"]), [H,20]),
			?ERR("bad return value:~p~n", [_Other]),
			{false, ?FAIL}
	end.

do_check([]) ->
	true;

do_check({check_act_condition, Result}) ->
	?IF(Result, true, {false, ?ERRCODE(err_config)});

%% 检测玩家等级
do_check({check_lv, OpenLv, RoleLv}) ->
	case RoleLv >= OpenLv of
		true  -> true;
		false -> {false, ?LEVEL_LIMIT}
	end;

%% 检测能否选择奖励
do_check({check_can_select, DrawRewards}) ->
	case DrawRewards =:= [] of
		true  -> true;
		false -> {false, ?ERRCODE(err331_lucky_flop_cant_select)}
	end;

%% 检测选择的奖励是否重复
do_check({check_reward_repeat, GradeIds}) when GradeIds =:= [] ->
	true;
do_check({check_reward_repeat, [H|T]}) ->
	case not lists:member(H, T) of
		true  -> do_check({check_reward_repeat, T});
		false -> {false, ?ERRCODE(err331_lucky_flop_reward_repeat)}
	end;

%% 检测奖励信息
do_check({check_reward_info, RewardInfos}) ->
	case is_list(RewardInfos) of
		true  -> true;
		false -> {false, ?ERRCODE(err331_lucky_flop_no_reward)}
	end;

%% 检测选择奖励是否正确
do_check({check_reward_nums, SelectLimit, RewardLvCount}) ->
	S1 = lists:sort(SelectLimit),
	S2 = lists:sort(RewardLvCount),
	case S1 =:= S2 of
		true  -> true;
		false -> {false, ?ERRCODE(err331_lucky_flop_select_error)}
	end;

%% 检测奖池是否还有未抽取奖励
do_check({check_draw_rewards, DrawRewards}) ->
	case DrawRewards =/= [] of
		true ->
			F = fun({_GradeId, HasDraw}) -> HasDraw =:= ?NOT_GET end,
			case lists:any(F, DrawRewards) of
				true  -> true;
				false -> {false, ?ERRCODE(err331_lucky_flop_need_reset)}
			end;
		false ->
			{false, ?ERRCODE(err331_lucky_flop_no_select)}
	end;

%% 检测是否有空格子
do_check({check_null_cell, PS}) ->
	CellNum = lib_goods_api:get_cell_num(PS),
	case CellNum > 0 of
		true  -> true;
		false -> {false, ?ERRCODE(err150_no_cell)}
	end;

%% 检测玩家消耗
do_check({check_cost, CostList, PS}) ->
	case lib_goods_api:check_object_list_with_auto_buy(PS, CostList) of
		true ->
			true;
		{false, ErrCode} ->
			{false, ErrCode}
	end;

%% 检测次数是否足够
do_check({check_times, Times, NowTimes}) ->
	case NowTimes >= Times of
		true  -> true;
		false -> {false, ?ERRCODE(err331_act_can_not_get)}
	end;

%% 检测是否已经领取
do_check({check_not_get, Times, ReceiveRewards}) ->
	case not lists:member(Times, ReceiveRewards) of
		true  -> true;
		false -> {false, ?ERRCODE(err331_already_get_reward)}
	end;

%% 检测是否有累计奖励
do_check({check_receive_reward, RewardList}) ->
	case RewardList =/= [] of
		true  -> true;
		false -> {false, ?ERRCODE(err331_no_act_reward_cfg)}
	end;

%% 检测背包空间是否足够
do_check({check_can_give_goods, PS, RewardList}) ->
	case lib_goods_api:can_give_goods(PS, RewardList) of
		true  -> true;
		false -> {false, ?ERRCODE(err150_no_cell)}
	end;

%% 检测重置消耗
do_check({check_reset_cost, PS, CostList}) ->
	case CostList of
		[] ->
			{false, ?ERRCODE(err_config)};
		[{_GoodsId, 0}] ->
			true;
		_ ->
			case lib_goods_api:check_object_list_with_auto_buy(PS, CostList) of
				true ->
					true;
				{false, ErrCode} ->
					{false, ErrCode}
			end
	end;

%% 检测是否需要重置
do_check({check_need_reset, DrawRewards}) ->
	case DrawRewards =/= [] of
		true  -> true;
		false -> {false, ?ERRCODE(err331_lucky_flop_no_select)}
	end;

%% 检测能否重置
do_check({check_can_reset, ReceiveTimes, ReceiveRewards}) ->
	case length(ReceiveTimes) =:= length(ReceiveRewards) of
		true  -> true;
		false -> {false, ?ERRCODE(err331_lucky_flop_cant_reset)}
	end;

%% 玩家等级
do_check({act_condition, role_lv, RoleLv}) ->
	case is_integer(RoleLv) of
		true  -> true;
		false -> {false, ?ERRCODE(err_config)}
	end;

%% 抽奖消耗
do_check({act_condition, draw_cost, DrawCost}) ->
	case is_list(DrawCost) of
		true ->
			F = fun(Element) ->
				case Element of
					{Min, Max, Cost} ->
						is_integer(Min) andalso is_integer(Max) andalso is_integer(Cost);
					_ ->
						false
				end
			end,
			Res = lists:all(F, DrawCost),
			?IF(Res, true, {false, ?ERRCODE(err_config)});
		false ->
			{false, ?ERRCODE(err_config)}
	end;

%% 重置消耗
do_check({act_condition, reset_cost, ResetCost}) ->
	case is_list(ResetCost) of
		true ->
			F = fun(Element) ->
				case Element of
					{Min, Max, Cost} ->
						is_integer(Min) andalso is_integer(Max) andalso is_integer(Cost);
					_ ->
						false
				end
			end,
			Res = lists:all(F, ResetCost),
			?IF(Res, true, {false, ?ERRCODE(err_config)});
		false ->
			{false, ?ERRCODE(err_config)}
	end;

%% 抽奖物品id
do_check({act_condition, draw_gid, GoodsId}) ->
	case is_integer(GoodsId) andalso GoodsId > 0 of
		true  -> true;
		false -> {false, ?ERRCODE(err_config)}
	end;

%% 重置物品id
do_check({act_condition, reset_gid, GoodsId}) ->
	case is_integer(GoodsId) andalso GoodsId > 0 of
		true  -> true;
		false -> {false, ?ERRCODE(err_config)}
	end;

%% 重置次数
do_check({act_condition, reset_times, Times}) ->
	case is_integer(Times) andalso Times > 0 of
		true  -> true;
		false -> {false, ?ERRCODE(err_config)}
	end;

%% 奖励等级选择限制
do_check({act_condition, select_limit, SelectLimit}) ->
	case is_list(SelectLimit) of
		true ->
			F = fun(Element) ->
				case Element of
					{RewardLv, Limit} ->
						is_integer(RewardLv) andalso is_integer(Limit);
					_ ->
						false
				end
			end,
			Res = lists:all(F, SelectLimit),
			?IF(Res, true, {false, ?ERRCODE(err_config)});
		false ->
			{false, ?ERRCODE(err_config)}
	end;

do_check({reward_condition, reward_lv, Condition}) ->
	case lib_custom_act_check:check_act_condtion([reward_lv], Condition) of
		[RewardLv] when is_integer(RewardLv)->
			true;
		_ ->
			{false, ?ERRCODE(err_config)}
	end;

do_check({reward_condition, times_limit, Condition}) ->
	case lib_custom_act_check:check_act_condtion([times_limit], Condition) of
		[TimesLimit] when is_integer(TimesLimit)->
			true;
		_ ->
			{false, ?ERRCODE(err_config)}
	end;

do_check({reward_condition, weight, Condition}) ->
	case lib_custom_act_check:check_act_condtion([weight], Condition) of
		[Weight]when is_integer(Weight)->
			true;
		_ ->
			{false, ?ERRCODE(err_config)}
	end;

do_check(_Condition) ->
	?ERR("unknow check condition:~p~n", [_Condition]),
	{false, ?FAIL}.

%% ================================================= SQL =================================================
save_lucky_flop_data(RoleId, LuckyFlopData) ->
	#lucky_flop_data{
		sub_type = SubType, now_times = NowTimes, total_times = TotalTimes, turn = Turn,
		draw_rewards = DrawRewards, receive_rewards = ReceiveRewards, time = Time
	} = LuckyFlopData,
	SDrawRewards = util:term_to_bitstring(DrawRewards),
	SReceiveRewards = util:term_to_bitstring(ReceiveRewards),
	ReSql = io_lib:format(?REPLACE_LUCKY_FLOP, [RoleId, SubType, NowTimes, TotalTimes, Turn, SDrawRewards, SReceiveRewards, Time]),
	db:execute(ReSql).

db_get_lucky_flop(RoleId) ->
	ReSql = io_lib:format(?SELECT_LUCKY_FLOP, [RoleId]),
	db:get_all(ReSql).

db_get_all_lucky_flop(SubType) ->
	ReSql = io_lib:format(?SELECT_ALL_LUCKY_FLOP, [SubType]),
	db:get_all(ReSql).

delete_lucky_flop_role(RoleId, SubTypeList) ->
	ReSql = io_lib:format(?DELETE_LUCKY_FLOP_ROLE, [RoleId, util:link_list(SubTypeList)]),
	db:execute(ReSql).

delete_lucky_flop_by_sub_type(SubType) ->
	ReSql = io_lib:format(?DELETE_LUCKY_FLOP_BY_SUB_TYPE, [SubType]),
	db:execute(ReSql).
