%%%-------------------------------------------------------------------
%%% @doc     宠物副本-排行榜管理进程
%%% Module  : mod_dun_pet_rank.erl
%%% Created : 2020-07-06
%%% @Author : tyl
%%%-------------------------------------------------------------------
-module(mod_dun_pet_rank).

-include("common.hrl").
-include("server.hrl").
-include("figure.hrl").
-include("role.hrl").
-include("errcode.hrl").
-include("goods.hrl").
-include("dungeon_pet.hrl").
-include("def_module.hrl").
-include("def_goods.hrl").
-include("predefine.hrl").

-behaviour(gen_server).

%% API
-export([
	get_score_rank/2             %% 查询积分和排行榜
	,get_real_time_score_rank/1  %% 查询实时排行(前后三名)
	,update_score_record/5       %% 更新积分和排行记录
	,get_daily_reward_status/1   %% 查询每日奖励领取状态
	,receive_daily_reward/1      %% 领取每日奖励
	,update_dungeon_rank/2
	,update_figure/2             %% 更新玩家形象数据
	,midnight_refresh/0          %% 零点更新日奖励领取状态
]).


-export([start_link/0, cast/1 ,call/1 ,info/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

%% API
%% 查询积分和排行榜
get_score_rank(RoleId, Type) ->
	cast({get_score_rank, RoleId, Type}).

%% 查询实时排行(前后三名)
get_real_time_score_rank(RoleId) ->
	cast({get_real_time_score_rank, RoleId}).

%% 更新积分记录和排行榜
update_score_record(RoleId, RoleInfo, DunId, Wave, Score) ->
	cast({update_score_record, RoleId, RoleInfo, DunId, Wave, Score}).

%% 零点更新日奖励领取状态 & 昨日积分排行
midnight_refresh() ->
	cast({midnight_refresh}).

%% 查询每日奖励领取状态
get_daily_reward_status(RoleId) ->
	cast({get_daily_reward_status, RoleId}).

%% 领取每日奖励(根据昨日排名)
receive_daily_reward(RoleId) ->
	cast({receive_daily_reward, RoleId}).

%% 更新副本中玩家数据的排行
update_dungeon_rank(RoleId, CopyId) ->
	cast({update_dungeon_rank, RoleId, CopyId}).

%% 玩家角色数据改变,修改排行榜数据
update_figure(RoleId, FigureData) ->
	cast({update_figure, RoleId, FigureData}).


%% ---------------------------------------------------------------------------
start_link() ->
	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

cast(Msg) ->
	gen_server:cast(?MODULE, Msg).

call(Msg) ->
	gen_server:call(?MODULE, Msg).

info(Msg) ->
	erlang:send(?MODULE, Msg).

init([]) ->
	process_flag(trap_exit, true),
	State = init_dun_pet_rank(),
	{ok, State}.

handle_call(Msg, From, State) ->
	try do_handle_call(Msg, From, State) of
		Reply ->
			Reply
	catch ?EXCEPTION(Class, Reason, StackTrace) ->
		Error = {Class, Reason, StackTrace},
		?ERR("~w:handle_call error:~p~n", [?MODULE, Error]),
		{error, Reason}
	end.

handle_cast(Msg, State) ->
	try do_handle_cast(Msg, State) of
		Result ->
			Result
	catch ?EXCEPTION(Class, Reason, StackTrace) ->
		Error = {Class, Reason, StackTrace},
		?ERR("~w:handle_cast error:~p~n", [?MODULE, Error]),
		{noreply, State}
	end.

handle_info(Info, State) ->
	try do_handle_info(Info, State) of
		Result ->
			Result
	catch ?EXCEPTION(Class, Reason, StackTrace) ->
		Error = {Class, Reason, StackTrace},
		?ERR("~w:handle_info error:~p~n", [?MODULE, Error]),
		{noreply, State}
	end.

terminate(_Reason, _State) ->
	ok.

code_change(_OldVsn, State, _Extra) ->
	{ok, State}.

%% ---------------------------------------------------------------------------
do_handle_call(_Msg, _From, State) ->
	{reply, ok, State}.

%% ---------------------------------------------------------------------------
do_handle_cast({get_score_rank, RoleId, Type}, State) ->
	#dun_pet_rank_state{rank_list = RankList} = State,
	case Type of
		1 ->
			LimitRankList =
			lists:filter(fun(TDunRank) ->
				TDunRank#dun_pet_rank.rank =< data_dungeon_pet:get_dun_pet_kv_cfg(send_tv_rank)
			             end, RankList);
		_ ->
			LimitRankList =
			lists:filter(fun(TDunRank) ->
				TDunRank#dun_pet_rank.old_rank =< data_dungeon_pet:get_dun_pet_kv_cfg(last_day_rank_length)
			             end, RankList)
	end,
	PackRankList = pack_rank_list(LimitRankList, [], Type),
	case lists:keyfind(RoleId, #dun_pet_rank.role_id, RankList) of
		#dun_pet_rank{rank = Rank, score = Score, dun_wave = DunWave, old_rank = OldRank, old_score = OldScore, old_dun_wave = OldDunWave} ->
			case Type of
				1 ->
					MyRank = Rank, MyScore = Score,
					{MyDunId, MyWave} = re_calc_dun_wave(DunWave),
					MyDunNum = calc_dun_num(MyDunId);
				_ ->
					MyRank = OldRank, MyScore = OldScore,
					{MyDunId, MyWave} = re_calc_dun_wave(OldDunWave),
					MyDunNum = calc_dun_num(MyDunId)
			end;
		_ ->  MyRank = 0, MyScore = 0, MyDunNum = 0, MyWave = 0
	end,
	lib_server_send:send_to_uid(RoleId, pt_610, 61075, [Type, PackRankList, MyRank, MyScore, MyDunNum, MyWave]),
	{noreply, State};

do_handle_cast({get_real_time_score_rank, RoleId}, State) ->
	#dun_pet_rank_state{rank_list = RankList} = State,
	%% 推送排名(我附近三名的排行和积分)
	case lists:keyfind(RoleId, #dun_pet_rank.role_id, RankList) of
		#dun_pet_rank{rank = MyRank, score = MyScore, dun_wave = DunWave, role_name = MyName, photo_id = MyPhotoId} ->
			{DunId, MyWave} = re_calc_dun_wave(DunWave),
			MyDunNum = calc_dun_num(DunId);
		_ ->
			MyRank = 0, MyScore = 0, MyDunNum = 0, MyWave = 0, MyName = "", MyPhotoId = 0
	end,
	PackSendList = send_rank_info(lists:max(data_dungeon_pet:get_all_dun_pet_rank_cfg()), MyRank, RankList),
	lib_server_send:send_to_uid(RoleId, pt_610, 61076, [MyRank, MyScore, MyDunNum, MyWave, MyName, MyPhotoId, PackSendList]),
	{noreply, State};

do_handle_cast({update_score_record, RoleId, RoleInfo, DunId, Wave, Score}, State) ->
	#dun_pet_rank_state{rank_list = RankList} = State,
	%% 需要达到积分要求才更新榜单
	case get_rank_by_dun_wave(DunId, Wave) > 0 of
		true ->
			DunWave = calc_dun_wave(DunId, Wave),
			{Res, NewRankList} =
			case lists:keyfind(RoleId, #dun_pet_rank.role_id, RankList) of
				#dun_pet_rank{dun_wave = OldDunWave} = OldPetRank ->
					case DunWave =< OldDunWave of
						true -> {no_change, RankList};
						_ ->
							ReachTime = utime:unixtime(),
							update_db_score(RoleId, Score, DunWave, ReachTime),
							{change, lists:keystore(RoleId, #dun_pet_rank.role_id, RankList,
								OldPetRank#dun_pet_rank{score = Score, dun_wave = DunWave, reach_time = ReachTime}
							)}
					end;
				_ ->
					case lib_role:get_role_show(RoleId) of
						#ets_role_show{figure = Figure} ->
							RoleName = Figure#figure.name;
						_ -> RoleName = ""
					end,
					[Sex, PhotoId, VipLv, SupVip] = RoleInfo,
					ReachTime = utime:unixtime(),
					DunPetRank = #dun_pet_rank{
						role_id     = RoleId
						,role_name  = RoleName
						,sex        = Sex
						,vip        = VipLv
						,sup_vip    = SupVip
						,photo_id   = PhotoId
						,score      = Score
						,dun_wave   = DunWave
						,reach_time = ReachTime
					},
					save_db_dun_pet_rank(RoleId, Score, DunWave, ReachTime),
					{change, lists:keystore(RoleId, #dun_pet_rank.role_id, RankList, DunPetRank)}
			end,
			case {Res, NewRankList} of
				{change, _} ->
					SortRankList = sort_rank_list(1, NewRankList, 1),
					LimitRankList =
					lists:filter(fun(TDunRank) ->
									TDunRank#dun_pet_rank.rank =< get_max_rank()
					                end, SortRankList),
					%% 推送排名(玩家自己的和前10名的排行和积分)
					case lists:keyfind(RoleId, #dun_pet_rank.role_id, LimitRankList) of
						#dun_pet_rank{rank = MyRank, score = MyScore, role_name = MyName, photo_id = MyPhotoId} ->
							%% 缓存中没加载过，需要拉一次
							case MyPhotoId of
								0 ->
									case lib_role:get_role_show(RoleId) of
										#ets_role_show{figure = TFigure} ->
											MyName1 = TFigure#figure.name, MyPhotoId1 = TFigure#figure.profile_photo_id;
										_ ->
											MyName1 = MyName, MyPhotoId1 = MyPhotoId
									end;
								_ ->
									MyName1 = MyName, MyPhotoId1 = MyPhotoId
							end,
							%% 如果排名小于15推送传闻
							#dun_pet_rank{rank = MyOldRank} = lists:keyfind(RoleId, #dun_pet_rank.role_id, NewRankList),
							case MyRank =< ?DUN_PET_SEND_TV_RANK andalso MyRank =/= MyOldRank of
								true -> lib_chat:send_TV({all}, ?MOD_DUNGEON, 7, [MyName1, RoleId, MyRank]);
								_ -> ignore
							end;
						_ -> MyRank = 0, MyScore = 0, MyName1 = "", MyPhotoId1 = 0
					end,
					%% 以发送给前端的排行为准
					LastRank = data_dungeon_pet:get_dun_pet_kv_cfg(send_tv_rank),
					PackSendList = send_rank_info(LastRank, MyRank, LimitRankList),
					%% 推送实时积分
					lib_server_send:send_to_uid(RoleId, pt_610, 61086, [MyRank, Score, calc_dun_num(DunId), Wave]),
					lib_server_send:send_to_uid(RoleId, pt_610, 61076, [MyRank, MyScore, calc_dun_num(DunId), Wave, MyName1, MyPhotoId1, PackSendList]),
					{noreply, State#dun_pet_rank_state{rank_list = LimitRankList}};
				_ ->
					%% 更新副本中我的排名,用于结算推送
					case lists:keyfind(RoleId, #dun_pet_rank.role_id, RankList) of
						#dun_pet_rank{rank = MyRank} -> skip;
						_ -> MyRank = 0
					end,
					%% 推送实时积分
					lib_server_send:send_to_uid(RoleId, pt_610, 61086, [MyRank, Score, calc_dun_num(DunId), Wave]),
					{noreply, State}
			end;
		_ ->
			%% 还没达到上榜要求
			%% 也需要推送实时积分,数据库里面就暂时不存数据了(包括积分)
			lib_server_send:send_to_uid(RoleId, pt_610, 61086, [0, Score, calc_dun_num(DunId), Wave]),
			{noreply, State}
	end;

do_handle_cast({midnight_refresh}, State) ->
	#dun_pet_rank_state{rank_list = RankList} = State,
	%% TODO:加日志,优化sql语句
	F = fun(#dun_pet_rank{role_id = RoleId, is_received = ?DAILY_REWARD_HAS_RECEIVED, rank = Rank, score = Score, dun_wave = DunWave, reach_time = ReachTime
				} = DunPetRank, TRankList) ->
				update_db_reward_status(RoleId, ?DAILY_REWARD_NOT_RECEIVE),
				update_db_old_score(RoleId, Score, DunWave, ReachTime),
				%% 日志
				lib_log_api:log_dun_pet_rank_reward(RoleId, Score, Rank),
				NewDunPetRank = DunPetRank#dun_pet_rank{is_received = ?DAILY_REWARD_NOT_RECEIVE, old_rank = Rank, old_dun_wave = DunWave, old_score = Score},
				lib_server_send:send_to_uid(RoleId, pt_610, 61098, [1, ?DAILY_REWARD_NOT_RECEIVE, ?SUCCESS]),
				lists:keystore(RoleId, #dun_pet_rank.role_id, TRankList, NewDunPetRank);
			(#dun_pet_rank{role_id = RoleId, rank = Rank, score = Score, dun_wave = DunWave, reach_time = ReachTime} = DunPetRank, TRankList) ->
				update_db_old_score(RoleId, Score, DunWave, ReachTime),
				%% 日志
				lib_log_api:log_dun_pet_rank_reward(RoleId, Score, Rank),
				lists:keystore(RoleId, #dun_pet_rank.role_id, TRankList, DunPetRank#dun_pet_rank{old_rank = Rank, old_dun_wave = DunWave, old_score = Score});
			(_, TRankList) ->
				TRankList
	end,
	NewRankList = lists:foldl(F, RankList, RankList),
	{noreply, State#dun_pet_rank_state{rank_list = NewRankList}};

do_handle_cast({get_daily_reward_status, RoleId}, State) ->
	#dun_pet_rank_state{rank_list = RankList} = State,
	case lists:keyfind(RoleId, #dun_pet_rank.role_id, RankList) of
		#dun_pet_rank{old_rank = OldRank, is_received = IsReceived} when OldRank > 0 ->
			lib_server_send:send_to_uid(RoleId, pt_610, 61098, [1, IsReceived, ?SUCCESS]);
		_ ->
			%% 未有排行
			lib_server_send:send_to_uid(RoleId, pt_610, 61098, [1, 0, 0])
	end,
	{noreply, State};

do_handle_cast({receive_daily_reward, RoleId}, State) ->
	#dun_pet_rank_state{rank_list = RankList} = State,
	case lists:keyfind(RoleId, #dun_pet_rank.role_id, RankList) of
		#dun_pet_rank{old_rank = Rank, is_received = ?DAILY_REWARD_NOT_RECEIVE} = DunPetRank when Rank > 0 ->
			case data_dungeon_pet:get_dun_pet_rank_reward_cfg(Rank) of
				[{_, _RewardId, _}|_] = Reward ->
					Product = #produce{reward = Reward, type = dun_pet},
					lib_goods_api:send_reward_with_mail(RoleId, Product, ?MOD_DUNGEON),
					update_db_reward_status(RoleId, ?DAILY_REWARD_HAS_RECEIVED),             %% 存库
					NewDunPetRank = DunPetRank#dun_pet_rank{is_received = ?DAILY_REWARD_HAS_RECEIVED},
					lib_server_send:send_to_uid(RoleId, pt_610, 61098, [2, ?DAILY_REWARD_HAS_RECEIVED, ?SUCCESS]),
					NewRankList = lists:keystore(RoleId, #dun_pet_rank.role_id, RankList, NewDunPetRank);
				_ ->
					lib_server_send:send_to_uid(RoleId, pt_610, 61098, [2, ?DAILY_REWARD_NOT_RECEIVE, ?ERRCODE(err610_dun_pet_rank_reward_err)]),
					NewRankList = RankList
			end;
		#dun_pet_rank{is_received = ?DAILY_REWARD_HAS_RECEIVED} ->
			lib_server_send:send_to_uid(RoleId, pt_610, 61098, [2, ?DAILY_REWARD_HAS_RECEIVED, ?ERRCODE(err610_dun_pet_rank_has_received)]),
			NewRankList = RankList;
		_ ->
			%% 未有排行
			lib_server_send:send_to_uid(RoleId, pt_610, 61098, [2, 0, ?ERRCODE(err610_dun_pet_rank_none)]),
			NewRankList = RankList
	end,
	{noreply, State#dun_pet_rank_state{rank_list = NewRankList}};

do_handle_cast({update_dungeon_rank, RoleId, CopyId}, State) ->
	#dun_pet_rank_state{rank_list = RankList} = State,
	case lists:keyfind(RoleId, #dun_pet_rank.role_id, RankList) of
		#dun_pet_rank{rank = MyRank, score = MyScore} -> skip;
		_ -> MyRank = 0, MyScore = 0
	end,
	mod_dungeon:apply(CopyId, lib_dungeon_pet, update_rank, [MyRank, MyScore]),
	{noreply, State};

do_handle_cast({update_figure, RoleId, FigureData}, State) ->
	#dun_pet_rank_state{rank_list = RankList} = State,
	case lists:keyfind(RoleId, #dun_pet_rank.role_id, RankList) of
		#dun_pet_rank{} = DunPetRank ->
			NewDunPetRank = update_figure_data(FigureData, DunPetRank),
			NewRankList = lists:keystore(RoleId, #dun_pet_rank.role_id, RankList, NewDunPetRank),
			NewState = State#dun_pet_rank_state{rank_list = NewRankList};
		_ ->
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast(_Msg, State) ->
	?ERR("Handle unknow msg[~w]~n", [_Msg]),
	{noreply, State}.

%% ---------------------------------------------------------------------------
do_handle_info(_Info, State) ->
	?ERR("Handle unknow msg[~w]~n", [_Info]),
	{noreply, State}.


%% ---------------------- builtin function -------------------------------------------
%% 排行榜进程初始化数据
init_dun_pet_rank() ->
	case get_db_dun_pet_rank(get_max_rank()) of
		[_|_] = RankListInfo ->
			RankList = make_rank_list(RankListInfo, []);
		_ ->
			RankList = []
	end,
	%% 计算今天榜单
	SortRankList  = sort_rank_list(1, RankList, 1),
	%% 计算昨日榜单
	case get_db_dun_pet_yest_rank(?DUN_PET_YEST_MAX_RANK) of
		[_|_] = YestRankListInfo ->
			YestRankList = make_yest_rank_list(YestRankListInfo, []);
		_ ->
			YestRankList = []
	end,
	%% 旧排行数据赋值
	SortYestRankList = sort_yest_rank_list(YestRankList),
	SortRankList1 = assign_old_rank(SortRankList, SortYestRankList),
	% LimitRankList =
	% lists:filter(fun(TDunRank) ->
	% 				TDunRank#dun_pet_rank.rank =< get_max_rank()
	%              end, SortRankList1),
	DunPetRankState = #dun_pet_rank_state{
		rank_list      = SortRankList1
		,reward_status = 0
	},
	DunPetRankState.

%% 根据积分获得排行上下限区段
%%get_rank_range(RankListMap, Score) ->
%%	RankKeys = maps:keys(RankListMap),
%%	case data_dungeon_pet:get_dun_pet_rank_cfg(Score) of
%%		#dun_pet_rank_reward_cfg{rank_low = ScoreLow, rank_height = ScoreHeight} ->
%%			ok;
%%		_ ->
%%			ScoreLow = 0, ScoreHeight = 0
%%	end,
%%	F = fun({Low, Height}, Return) ->
%%		case ScoreLow == Low andalso ScoreHeight == Height of
%%			true -> {Low, Height};
%%			_ -> Return
%%		end end,
%%	lists:foldl(F, {}, RankKeys).

%% old: 获取我前后两名的积分和排行
%% new: 2021-04-22 获取前10名的积分和排行
send_rank_info(_LastRank, _MyRank, AllRankList) ->
	%% old: 第一名拿后面两名的数据,最后一名拿前两名数据,没有排行拿最后三名数据
	% if
	%	MyRank == 0 -> PreRank = LastRank - 1, NextRank = LastRank;
	%	MyRank == 1 -> PreRank = 2, NextRank = 3;
	%	MyRank == LastRank ->
	%		PreRank = LastRank - 2, NextRank = LastRank - 1;
	%	true ->
	%		PreRank = MyRank - 1, NextRank = MyRank + 1
	% end,
	%% 重启之后这里没有玩家数据,需要读取role_show的数据
	F = fun(DunRank, TList) ->
		case DunRank of
			#dun_pet_rank{score = Score, dun_wave = DunWave, role_id = RoleId, rank = Rank} when Rank =< 10 ->
				case lib_role:get_role_show(RoleId) of
					#ets_role_show{figure = Figure} -> ok;
					_ -> Figure = #figure{}
				end,
				{DunId, Wave} = re_calc_dun_wave(DunWave),
				[{Rank, Score, calc_dun_num(DunId), Wave, Figure#figure.name, Figure#figure.profile_photo_id}|TList];
			_ ->
				TList
		end end,
	lists:foldl(F, [], AllRankList).
	% case MyRank of
	% 	0 -> lists:foldl(F, [], [PreRank - 1, PreRank, NextRank]);
	%	_ ->
	%		lists:foldl(F, [], [PreRank, NextRank])
	% end.

%% 最大排行
get_max_rank() ->
	case data_dungeon_pet:get_max_rank() of
		MaxRank when is_integer(MaxRank) -> MaxRank;
		_ -> ?DUN_PET_DEFAULT_MAX_RANK
	end.

%% 获取排行榜最后一名(实际配置决定)
%%get_last_rank([], Return) -> Return;
%%get_last_rank([{_Low, High}|L], TReturn) ->
%%	case High > TReturn of
%%		true ->
%%			get_last_rank(L, High);
%%		_ ->
%%			get_last_rank(L, TReturn)
%%	end;
%%get_last_rank([_|L], TReturn) ->
%%	get_last_rank(L, TReturn).

%% 删除旧的排行
%%delete_old_rank(RoleId, RankList) ->
%%	case lists:keyfind(RoleId, #dun_pet_rank.role_id, RankList) of
%%		#dun_pet_rank{rank = _Rank} ->
%%			%% 删除旧数据,重新组装排行榜区段
%%			NewRankList = lists:keydelete(RoleId, #dun_pet_rank.role_id, RankList),
%%			delete_db_dun_pet_rank(RoleId),
%%			NewRankList;
%%		_ ->
%%			RankList
%%	end.

%%init_rank_list_map([], Map) -> Map;
%%init_rank_list_map([Id|L], TMap) ->
%%	NewMap =
%%	case data_dungeon_pet:get_dun_pet_rank_cfg0(Id) of
%%		#dun_pet_rank_reward_cfg{rank_low = Low, rank_height = Height} ->
%%			maps:put({Low, Height}, [], TMap);
%%		_ ->
%%			TMap
%%	end,
%%	init_rank_list_map(L, NewMap).

make_rank_list([], RankList) -> RankList;
make_rank_list([[RoleId, Score, DunWave, ReachTime, OldScore, OldDunWave, IsReceived]|L], TRankList) ->
	% case lib_role:get_role_show(RoleId) of
	%	#ets_role_show{figure = TFigure} ->
	%		case is_record(TFigure, figure) of
	%			true -> Figure = TFigure;
	%			_ -> Figure = #figure{}
	%		end;
	%	_ ->
	%		Figure = #figure{}
	%end,
	DunPetRank = #dun_pet_rank{
		role_id    = RoleId
		%,role_name  = Figure#figure.name
		%,sex        = Figure#figure.sex
		%,vip        = Figure#figure.vip
		%,sup_vip    = Figure#figure.sup_vip
		%,photo_id   = Figure#figure.profile_photo_id
		,score      = Score
		,dun_wave   = DunWave
		,reach_time = ReachTime
		,old_score  = OldScore
		,old_dun_wave = OldDunWave
		,is_received  = IsReceived
	},
	make_rank_list(L, [DunPetRank|TRankList]);
make_rank_list([_|L], TRankList) ->
	make_rank_list(L, TRankList).

make_yest_rank_list([], RankList) -> RankList;
make_yest_rank_list([[RoleId, OldScore, OldDunWave, OldReachTime]|L], TRankList) ->
	DunPetYestRank = #dun_pet_yest_rank{
		role_id    = RoleId
		,old_score = OldScore
		,old_dun_wave   = OldDunWave
		,old_reach_time = OldReachTime
	},
	make_yest_rank_list(L, [DunPetYestRank|TRankList]);
make_yest_rank_list([_|L], TRankList) ->
	make_yest_rank_list(L, TRankList).


pack_rank_list([], PackList, _Type) -> PackList;
pack_rank_list([PetRank|L], TPackList, Type) when is_record(PetRank, dun_pet_rank) ->
	#dun_pet_rank{
		rank       = TodayRank
		,role_id   = RoleId
		%,role_name = RoleName
		,score     = TodayScore
		%,vip       = VipLv
		%,sup_vip   = SupVip
		%,sex       = Sex
		,old_score = OldScore
		,old_rank  = OldRank
		,dun_wave  = DunWave
		,old_dun_wave = OldDunWave
	} = PetRank,
	case Type of
		1 ->
			Rank = TodayRank, Score = TodayScore,
			{DunId, Wave} = re_calc_dun_wave(DunWave),
			DunNum = calc_dun_num(DunId);
		_ ->
			Rank = OldRank, Score = OldScore,
			{DunId, Wave} = re_calc_dun_wave(OldDunWave),
			DunNum = calc_dun_num(DunId)
	end,
	%% 第一天旧榜数据为0,需要过滤
	case Rank == 0 of
		true ->
			pack_rank_list(L, TPackList, Type);
		_ ->
			%% 玩家信息
			case lib_role:get_role_show(RoleId) of
				#ets_role_show{figure = Figure} -> ok;
				_ -> Figure = #figure{}
			end,
			case Figure#figure.dress_list of
				[_|_] = DressList -> case lists:keyfind(2, 1, DressList) of
					                     {_, FrameId} when is_integer(FrameId) -> ok;
										 _ -> FrameId = 420001
				                     end;
				_ -> FrameId = 420001 %% 给默认头框
			end,
			#figure{lv = RoleLv, sex = Sex, name = RoleName, profile_photo_id = PhotoId, vip = VipLv, sup_vip = SupVip} = Figure,
			pack_rank_list(L, [{Rank, RoleId, RoleName, Score, DunNum, Wave, VipLv, SupVip, Sex, FrameId, PhotoId, RoleLv}|TPackList], Type)
	end;
pack_rank_list([_|L], TPackList, Type) ->
	pack_rank_list(L, TPackList, Type).

%% 排序 Type 1-今日排名 2-昨日排名(昨日榜计算已废弃)
sort_rank_list(_BeginRank, RankList, Type) ->
	F = fun(RankA, RankB) ->
		case Type of
			1 ->
				#dun_pet_rank{dun_wave = DunWaveA, reach_time = TimeA} = RankA,
				#dun_pet_rank{dun_wave = DunWaveB, reach_time = TimeB} = RankB;
			_ ->
				#dun_pet_rank{dun_wave = DunWaveA, reach_time = TimeA} = RankA,
				#dun_pet_rank{dun_wave = DunWaveB, reach_time = TimeB} = RankB
		end,
		if
			DunWaveA == DunWaveB ->
				case TimeA < TimeB of
					true -> true;
					_    -> false
				end;
			DunWaveA > DunWaveB ->
				true;
			true ->
				false
		end
	end,
	SortList = lists:sort(F, RankList),
	F1 = fun(RankInfo, TRankList) ->
		case Type of
			1 ->
				#dun_pet_rank{dun_wave = DunWave} = RankInfo;
			_ ->
				#dun_pet_rank{old_dun_wave = DunWave} = RankInfo
		end,
		Rank = get_rank_by_dun_wave(DunWave),
		TRolePetRank =
		case Type of
			1 -> lists:keyfind(Rank, #dun_pet_rank.rank, TRankList);
			_ -> lists:keyfind(Rank, #dun_pet_rank.old_rank, TRankList)
		end,
		case TRolePetRank of
			#dun_pet_rank{} ->
				MaxRank = data_dungeon_pet:get_max_rank(),
				%% 如果当前排名已经有玩家,则继续往下排
				TrueRank = get_true_rank(Type, Rank, TRankList, MaxRank),
				case Type of
					1 ->
						NewRankInfo = RankInfo#dun_pet_rank{rank = TrueRank};
					_ ->
						NewRankInfo = RankInfo#dun_pet_rank{old_rank = TrueRank}
				end,
				[NewRankInfo|TRankList];
			_ ->
				case Type of
					1 ->
						NewRankInfo = RankInfo#dun_pet_rank{rank = Rank};
					_ ->
						NewRankInfo = RankInfo#dun_pet_rank{old_rank = Rank}
				end,
				[NewRankInfo|TRankList]
		end
	end,
	NewRankList = lists:foldl(F1, [], SortList),
	lists:reverse(NewRankList).

sort_yest_rank_list(RankList) ->
	F = fun(RankA, RankB) ->
		#dun_pet_yest_rank{old_dun_wave = OldWaveA, old_reach_time = ReachTimeA} = RankA,
		#dun_pet_yest_rank{old_dun_wave = OldWaveB, old_reach_time = ReachTimeB} = RankB,
		%% 如果关卡相同,先达成者靠前
		if
			OldWaveA == OldWaveB ->
				ReachTimeA < ReachTimeB;
			true ->
				OldWaveA > OldWaveB
		end end,
	SortList = lists:sort(F, RankList),
	RankF = fun(RankInfo, TRankList) ->
		#dun_pet_yest_rank{old_score = Score, old_dun_wave = OldDunWave, role_id = RoleId} = RankInfo,
		%% 因为是昨日榜，必定有排行
		Rank = get_rank_by_dun_wave(OldDunWave),
		TRolePetRank = lists:keyfind(Rank, #dun_pet_yest_rank.old_rank, TRankList),
		case TRolePetRank of
			#dun_pet_yest_rank{} ->
				MaxRank = ?DUN_PET_YEST_MAX_RANK,
				%% 如果当前排名已经有玩家,则继续往下排
				TrueRank = case Rank =/= 0 of
					           true -> get_true_rank(2, Rank, TRankList, MaxRank);
					           _ -> Rank
				           end,
				YestRankInfo = #dun_pet_yest_rank{role_id = RoleId, old_score = Score, old_dun_wave = OldDunWave, old_rank = TrueRank},
				[YestRankInfo|TRankList];
			_ ->
				YestRankInfo = #dun_pet_yest_rank{role_id = RoleId, old_score = Score, old_dun_wave = OldDunWave, old_rank = Rank},
				[YestRankInfo|TRankList]
		end end,
	NewRankList = lists:foldl(RankF, [], SortList),
	lists:reverse(NewRankList).

%% DunWave 计算公式： DunId * 1000 + Wave 便于存储数据库以及排序
%% 获取排行配置时需要逆运算
calc_dun_wave(DunId, Wave) ->
	DunWave = DunId * 1000 + Wave,
	DunWave.

re_calc_dun_wave(DunWave) ->
	DunId = DunWave div 1000,
	Wave  = DunWave rem 1000,
	{DunId, Wave}.

get_rank_by_dun_wave(DunWave) ->
	DunId = DunWave div 1000,
	Wave  = DunWave rem 1000,
	get_rank_by_dun_wave(DunId, Wave).
get_rank_by_dun_wave(DunId, Wave) ->
	%% 排名需要从大到小开始筛选
	RankList = data_dungeon_pet:get_all_dun_pet_rank_cfg(),
	F = fun(RankId, TUpRank) ->
		case data_dungeon_pet:get_dun_pet_rank_cfg0(RankId) of
			#dun_pet_rank_reward_cfg{rank_low = Rank, need_dun = NeedDun, need_wave = NeedWave} ->
				if
					DunId > NeedDun -> Rank;
					DunId == NeedDun andalso Wave >= NeedWave -> Rank;
					true -> TUpRank
				end;
			_ ->
				TUpRank
		end end,
	UpRank = lists:foldr(F, 0, RankList),
	UpRank.

%% 副本Id转成成关卡数推送给前端
calc_dun_num(DunId) ->
	DunNum = DunId rem 12000,
	DunNum.

assign_old_rank(SortRankList, SortYestRankList) ->
	ForF = fun(#dun_pet_yest_rank{role_id = RoleId, old_score = OldScore, old_dun_wave = OldDunWave, old_rank = OldRank}, TList) ->
		case lists:keyfind(RoleId, #dun_pet_rank.role_id, SortRankList) of
			#dun_pet_rank{} = DunPetRank ->
				lists:keystore(RoleId, #dun_pet_rank.role_id, TList,
					DunPetRank#dun_pet_rank{old_score = OldScore, old_dun_wave = OldDunWave, old_rank = OldRank});
			_ ->
				lists:keystore(RoleId, #dun_pet_rank.role_id, TList,
					#dun_pet_rank{role_id = RoleId, old_score = OldScore, old_dun_wave = OldDunWave, old_rank = OldRank})
		end end,
	lists:foldl(ForF, SortRankList, SortYestRankList).

get_true_rank(Type, Rank, RankList, MaxRank) when Rank =< MaxRank ->
	TRankInfo = case Type of
					1 -> lists:keyfind(Rank, #dun_pet_rank.rank, RankList);
					_ -> lists:keyfind(Rank, #dun_pet_yest_rank.old_rank, RankList)
	            end,
	case TRankInfo of
		#dun_pet_rank{} when Type == 1 ->
			get_true_rank(Type, Rank+1, RankList, MaxRank);
		#dun_pet_yest_rank{} when Type == 2 ->
			get_true_rank(Type, Rank+1, RankList, MaxRank);
		_ ->
			Rank
	end;
get_true_rank(_Type, Rank, _RankList, _MaxRank) -> Rank.

update_figure_data([], DunPetRank) ->
	DunPetRank;
update_figure_data([{vip, NewVip}|Rest], DunPetRank) ->
	update_figure_data(Rest, DunPetRank#dun_pet_rank{vip = NewVip});
update_figure_data([{name, NewName}|Rest], DunPetRank) ->
	update_figure_data(Rest, DunPetRank#dun_pet_rank{role_name = NewName});
update_figure_data([{sup_vip, NewSupVip}|Rest], DunPetRank) ->
	update_figure_data(Rest, DunPetRank#dun_pet_rank{sup_vip = NewSupVip});
update_figure_data([{photo_id, NewPhotId}|Rest], DunPetRank) ->
	update_figure_data(Rest, DunPetRank#dun_pet_rank{photo_id = NewPhotId});
update_figure_data([_|Rest], DunPetRank) ->
	update_figure_data(Rest, DunPetRank).

%% ----------------- 数据库操作 -------------------------------
get_db_dun_pet_rank(MaxRank) ->
	Sql = io_lib:format(?SQL_SELECT_DUN_PET_RANK, [MaxRank]),
	db:get_all(Sql).

get_db_dun_pet_yest_rank(MaxRank) ->
	db:get_all(io_lib:format(?SQL_SELECT_DUN_PET_YEST_RANK, [MaxRank])).

save_db_dun_pet_rank(RoleId, Score, DunWave, ReachTime) ->
	Sql = io_lib:format(?SQL_INSERT_DUN_PET_RANK, [RoleId, Score, DunWave, ReachTime]),
	db:execute(Sql).

%%delete_db_dun_pet_rank(RoleId) ->
%%	Sql = io_lib:format(?SQL_DELETE_DUN_PET_RANK, [RoleId]),
%%	db:execute(Sql).

update_db_reward_status(RoleId, Status) ->
	Sql = io_lib:format(?SQL_UPDATE_DUN_PET_RANK_REWARD_STATUS, [Status, RoleId]),
	db:execute(Sql).

update_db_old_score(RoleId, OldScore, OldDunWave, OldReachTime) ->
	Sql = io_lib:format(?SQL_UPDATE_DUN_PET_RANK_OLD_SCORE, [OldScore, OldDunWave, OldReachTime, RoleId]),
	db:execute(Sql).

update_db_score(RoleId, Score, DunWave, ReachTime) ->
	Sql = io_lib:format(?SQL_UPDATE_DUN_PET_RANK_SCORE, [Score, DunWave, ReachTime, RoleId]),
	db:execute(Sql).