%%%-------------------------------------------------------------------
%%% @doc    公会竞速-排行榜管理进程
%%% 备注： 本活动为开服前8天活动
%%%         1. 前七天为数据统计时间，包括个人和社团数据统计
%%%         2. 第八天为数据展示时间和奖励领取时间(称号发放除外)
%%%         3. 第九天0点会将邮件发放未领取的奖励
%%% 竞榜一小时结算一次
%%% (定时器由管理进程内部定时器控制,
%%% 超过活动天数后定时器不再发挥作用)
%%% @end
%%% Module  : mod_guild_race_rank.erl
%%% Created : 2020-07-16
%%% @author : tyl
%%%-------------------------------------------------------------------
-module(mod_guild_race_rank).

-include("common.hrl").
-include("server.hrl").
-include("figure.hrl").
-include("role.hrl").
-include("errcode.hrl").
-include("goods.hrl").
-include("guild.hrl").
-include("guild_race_rank.hrl").
-include("def_module.hrl").
-include("def_goods.hrl").
-include("def_fun.hrl").
-include("server_kv.hrl").
-include("custom_act.hrl").
-include("predefine.hrl").

-behaviour(gen_server).

-export([
	act_start/0
	,act_end/0

	,hour_check/0               %% 第一天结算前每一小时检测一次

	,get_guild_rank/2           %% 查询社团战力排行榜
	,get_member_rank/2          %% 查询成员战力排行榜
	,get_guild_live_rank/2      %% 查询社团活跃排行榜
	,get_member_rank_dsg/2      %% 上线查询个人排行和称号
	,sync_guild_msg/2           %% 社团数据同步到竞榜进程

	%% 第八天领取奖励
	,reward_receive_status/3            %% 奖励领取情况
	,receive_power_reward/2             %% 领取战力榜奖励
	,receive_live_reward/2              %% 领取活跃榜奖励
	,receive_captain_power_reward/2     %% 领取战力榜团长专属奖励
	,receive_captain_live_reward/2      %% 领取活跃榜团长专属奖励

	%% 社团数据变更,刷新排行榜数据
	,member_change/3
	,position_change/4
	,power_change/3
	,live_change/3
	,figure_change/4

	,guild_msg_change/4
	,guild_disband/1

	,init_race_rank_state/0
	,sort_guild_rank/1
	,sort_guild_live_rank/1
	,sort_member_rank/1
	,update_guild_limit_num/1
	,cancel_dsg_after_leave_guild/3
	,apply_cast/3
	,apply_cast1/3

	,timer_check/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]).


%% 测试使用
timer_check() ->
	cast({timer_check}).

%% 活动开启(已废弃)
act_start() ->
	cast({act_start}).

%% 活动结束
act_end() ->
	gen_server:cast(?MODULE, {act_end}).

%% 每小时更新一次战力榜
hour_check() ->
	gen_server:cast(?MODULE, {hour_check}).

%% 查询社团战力排行榜
get_guild_rank(RoleId, GuildId) ->
	cast1({get_guild_rank, RoleId, GuildId}).

%% 查询成员排行榜
get_member_rank(GuildId, RoleId) ->
	cast1({get_member_rank, GuildId, RoleId}).

%% 查询个人排行和称号
get_member_rank_dsg(GuildId, RoleId) ->
	cast({get_member_rank_dsg, GuildId, RoleId}).

%% 查询社团活跃排行榜
get_guild_live_rank(RoleId, GuildId) ->
	cast1({get_guild_live_rank, RoleId, GuildId}).

%% 奖励领取情况
reward_receive_status(RoleId, GuildId, RewardType) ->
	gen_server:cast(?MODULE, {reward_receive_status, RoleId, GuildId, RewardType}).

%% 领取战力榜奖励
receive_power_reward(RoleId, GuildId) ->
	gen_server:cast(?MODULE, {receive_power_reward, RoleId, GuildId}).

%% 领取活跃榜奖励
receive_live_reward(RoleId, GuildId) ->
	gen_server:cast(?MODULE, {receive_live_reward, RoleId, GuildId}).

%% 领取战力榜团长专属奖励
receive_captain_power_reward(RoleId, GuildId) ->
	gen_server:cast(?MODULE, {receive_captain_power_reward, RoleId, GuildId}).

%% 领取战力榜团长专属奖励
receive_captain_live_reward(RoleId, GuildId) ->
	gen_server:cast(?MODULE, {receive_captain_live_reward, RoleId, GuildId}).

%% 服务器启动同步社团成员数据到竞榜进程(测试的时候使用)
%% 数据格式：
%% {RoleId, RoleName, VipLv, SupVip, GuildId, Position, Power} = RoleInfo,
%% {GuildId, GuildName, GuildLv} = GuildInfo,
sync_guild_msg(SynMemberMsg, SynGuildMsg) ->
	gen_server:cast(?MODULE, {sync_guild_member, SynMemberMsg, SynGuildMsg}).

%% 成员人数发生变更 ChangeType:0-退出社团 1-进入社团 2-创建社团
member_change(GuildInfo, RoleInfo, ChangeType) ->
	cast({member_change, GuildInfo, RoleInfo, ChangeType}).

%% 成员职位发生变更
position_change(GuildId, RoleId, RoleName, Position) ->
	cast({position_change, GuildId, RoleId, RoleName, Position}).

%% 成员战力发生变更
power_change(GuildId, RoleId, Power) ->
	cast({power_change, GuildId, RoleId, Power}).

%% 成员活跃值变更
live_change(GuildId, RoleId, RoleLive) ->
	cast({live_change, GuildId, RoleId, RoleLive}).

%% 成员形象发生变更(vip等级/特权贵族/名称)
figure_change(GuildId, RoleId, FigureList, Type) ->
	cast({figure_change, GuildId, RoleId, FigureList, Type}).

%% 社团信息发生变化(等级/名称）
guild_msg_change(GuildId, GuildName, GuildLv, ChangeType) ->
	cast({guild_msg_change, GuildId, GuildName, GuildLv, ChangeType}).

%% 社团解散
guild_disband(GuildId) ->
	cast({guild_disband, GuildId}).

%% 社团合并
%%guild_merge(TargetId, GuildId) ->
%%	cast({guild_merge, TargetId, GuildId}).

%% 异步消息(前7天)
apply_cast(Module, Function, Args) ->
	cast({'apply_cast', Module, Function, Args}).
%% 异步消息(前8天)
apply_cast1(Module, Function, Args) ->
	cast1({'apply_cast', Module, Function, Args}).

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

cast(Msg) ->
	case lib_guild_race_rank:is_open_act() of
		true ->
			gen_server:cast(?MODULE, Msg);
		_ ->
			skip
	end.

%% 前八天均可查询排行榜数据
cast1(Msg) ->
	OpenDay = util:get_open_day(),
	case OpenDay =< ?REWARD_RECEIVE_DAY of
		true ->
			gen_server:cast(?MODULE, Msg);
		_ ->
			skip
	end.

call(Msg) ->
	case lib_guild_race_rank:is_open_act() of
		true ->
			gen_server:call(?MODULE, Msg);
		_ ->
			skip
	end.

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

init([]) ->
	process_flag(trap_exit, true),
	State = init_race_rank_state(),
	% State = #guild_race_rank_state{},
	%% 20后同步排行到公会进程,理论上公会进程已经完成初始化
	erlang:send_after(20*1000, self(), {sync_race_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) ->
	case lib_guild_race_rank:is_open_act() of
		true ->
			%% 第一天11点之前不发奖励,只做数据更新和存储
			case lib_guild_race_rank:is_first_reward() of
				false ->
					State0 = lib_guild_race_rank:deal_with_guild_disband_or_mem_leave(State),
					State1 = lib_guild_race_rank:timer_recalc_rank(State0),
					lib_guild_race_rank:move_rank(State1);
				_ ->
					%% 排序并保存数据
					State0 = lib_guild_race_rank:deal_with_guild_disband_or_mem_leave(State),
					State1 = lib_guild_race_rank:timer_recalc_rank(State0),
					lib_guild_race_rank:timer_send_reward(State1, ?FIRST_SEND_REWARD_NO),
					lib_guild_race_rank:move_rank(State1)
			end;
		_ -> ignore
	end,
	ok.

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

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

%% ---------------------------------------------------------------------------
do_handle_cast({timer_check}, State) ->
	NewState = lib_guild_race_rank:timer_check(State, ?FIRST_SEND_REWARD_NO),
	{noreply, NewState};

do_handle_cast({act_start}, State) ->
	NewState = lib_guild_race_rank:check_start_send_reward(State),
	{noreply, NewState};

do_handle_cast({act_end}, State) ->
	NewState = lib_guild_race_rank:act_end(State),
	{noreply, NewState};

do_handle_cast({hour_check}, State) ->
	#guild_race_rank_state{start_reward = RewardStatus} = State,
	case lib_guild_race_rank:is_open_act() of
		true when RewardStatus =:= ?START_REWARD_NO ->
			State0 = lib_guild_race_rank:deal_with_guild_disband_or_mem_leave(State),
			State1 = lib_guild_race_rank:timer_recalc_rank(State0),
			NewState = lib_guild_race_rank:move_rank(State1);
		_ ->
			NewState = State
	end,
	{noreply, NewState};

%% 奖励领取情况
do_handle_cast({reward_receive_status, RoleId, GuildId, RewardType}, State) ->
	OpenDay = util:get_open_day(),
	case OpenDay =< ?REWARD_RECEIVE_DAY of
		true ->
			lib_guild_race_rank:reward_receive_status(RoleId, GuildId, RewardType, State);
		_ ->
			ignore
	end,
	{noreply, State};

%% 领取奖励(第八天全天可领；如果没有领取，邮件发放)
do_handle_cast({receive_power_reward, RoleId, GuildId}, State) ->
	OpenDay = util:get_open_day(),
	case OpenDay of
		?REWARD_RECEIVE_DAY ->
			NewState = lib_guild_race_rank:receive_power_reward(RoleId, GuildId, State);
		_ ->
			lib_server_send:send_to_uid(RoleId, pt_407, 40705, [?ERRCODE(err407_not_receive_time), 1]),
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast({receive_live_reward, RoleId, GuildId}, State) ->
	OpenDay = util:get_open_day(),
	case OpenDay of
		?REWARD_RECEIVE_DAY ->
			NewState = lib_guild_race_rank:receive_live_reward(RoleId, GuildId, State);
		_ ->
			lib_server_send:send_to_uid(RoleId, pt_407, 40705, [?ERRCODE(err407_not_receive_time), 2]),
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast({receive_captain_power_reward, RoleId, GuildId}, State) ->
	OpenDay = util:get_open_day(),
	case OpenDay of
		?REWARD_RECEIVE_DAY ->
			NewState = lib_guild_race_rank:receive_captain_power_reward(RoleId, GuildId, State);
		_ ->
			lib_server_send:send_to_uid(RoleId, pt_407, 40705, [?ERRCODE(err407_not_receive_time), 3]),
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast({receive_captain_live_reward, RoleId, GuildId}, State) ->
	OpenDay = util:get_open_day(),
	case OpenDay of
		?REWARD_RECEIVE_DAY ->
			NewState = lib_guild_race_rank:receive_captain_live_reward(RoleId, GuildId, State);
		_ ->
			lib_server_send:send_to_uid(RoleId, pt_407, 40705, [?ERRCODE(err407_not_receive_time), 4]),
			NewState = State
	end,
	{noreply, NewState};

%% 个人榜 & 公会榜(都只展示旧的数据，old_rank=0的数据为最新数据，不发送)
do_handle_cast({get_member_rank, MyGuildId, RoleId}, State) ->
	#guild_race_rank_state{member_rank_map = MemberRankMap, guild_rank_list = GuildRankList
		,role_guild_map = RoleGuildMap
	} = State,
	RealGuildId = lib_guild_race_rank:get_real_guild_id(RoleId, MyGuildId, RoleGuildMap),
	BoolFun = fun(TGuildRank) -> TGuildRank#guild_rank.old_rank =/= 0 end,
	HasFirstRefresh = lists:any(BoolFun, GuildRankList),
	case HasFirstRefresh of
		true -> SMemberRankMap = MemberRankMap;
		_ -> SMemberRankMap = #{}
	end,
	case maps:get(RealGuildId, SMemberRankMap, []) of
		[_|_] = MemberRankList ->
			F = fun(MemberRank, TList) ->
				#member_rank{
					role_id = MemberId, role_name = RoleName, old_power = OldPower,
					old_rank = OldRank, position = Position, vip_lv = VipLv, sup_vip = SupVip
				} = MemberRank,
				Rank = OldRank, Power = OldPower,
				case Rank of
					0 -> TList;
					_ -> [{Rank, MemberId, RoleName, Power, Position, VipLv, SupVip}|TList]
				end end,
			PackList = lists:foldl(F, [], MemberRankList),
			case lists:keyfind(RoleId, #member_rank.role_id, MemberRankList) of
				#member_rank{old_rank = MyOldRank} ->
					MyRank = MyOldRank;
				_ ->
					MyRank = 0
			end,
			lib_server_send:send_to_uid(RoleId, pt_407, 40701, [PackList, MyRank]);
		_ ->
			lib_server_send:send_to_uid(RoleId, pt_407, 40701, [[], 0])
	end,
	{noreply, State};

do_handle_cast({get_guild_rank, RoleId, MyGuildId}, State) ->
	#guild_race_rank_state{
		guild_rank_list = GuildRankList, member_rank_map = MemberRankMap
		,start_reward = StartReward, role_guild_map = RoleGuildMap
	} = State,
	RealGuildId = lib_guild_race_rank:get_real_guild_id(RoleId, MyGuildId, RoleGuildMap),
	BoolFun = fun(TGuildRank) -> TGuildRank#guild_rank.old_rank =/= 0 end,
	HasFirstRefresh = lists:any(BoolFun, GuildRankList),
	{SGuildRankList, SMemberRankMap} =
	case HasFirstRefresh of
		true -> {GuildRankList, MemberRankMap};
		_ -> {[], #{}}
	end,
	F = fun(GuildRank, TList) ->
		#guild_rank{old_rank = OldRank, old_power = OldPower, guild_id = GuildId, guild_lv = GuildLv,
			guild_name = GuildName, captain_name = CaptainName, limit_num = LimitNum
		} = GuildRank,
		MemberList = maps:get(GuildId, SMemberRankMap, []),
		MemberList1 = [1 || #member_rank{old_rank = MRank}<-MemberList, MRank =/= 0],
		MemberNum = length(MemberList1),
		case StartReward == 1 of
			true -> LimitNum1 = LimitNum;
			_ -> LimitNum1 = data_guild_m:get_guild_member_capacity(GuildLv)
		end,
		case OldRank of
			0 -> TList;
			_ -> [{OldRank, GuildId, GuildName, CaptainName, MemberNum, LimitNum1, OldPower, OldRank}|TList]
		end
	end,
	case HasFirstRefresh of
		true -> PackList = lists:foldl(F, [], SGuildRankList);
		_    -> PackList = []
	end,
	case lists:keyfind(RealGuildId, #guild_rank.guild_id, SGuildRankList) of
		#guild_rank{old_rank = OldGuildRank} ->
			GuildRank = OldGuildRank;
		_ ->
			GuildRank = 0
	end,
	lib_server_send:send_to_uid(RoleId, pt_407, 40700, [PackList, GuildRank]),
	{noreply, State};

do_handle_cast({get_guild_live_rank, RoleId, MyGuildId}, State) ->
	#guild_race_rank_state{
		guild_rank_list = GuildRankList, member_rank_map = MemberRankMap
		,start_reward = StartReward, role_guild_map = RoleGuildMap
	} = State,
	RealGuildId = lib_guild_race_rank:get_real_guild_id(RoleId, MyGuildId, RoleGuildMap),
	BoolFun = fun(TGuildRank) -> TGuildRank#guild_rank.old_rank =/= 0 end,
	HasFirstRefresh = lists:any(BoolFun, GuildRankList),
	{SGuildRankList, SMemberRankMap} =
		case HasFirstRefresh of
			true -> {GuildRankList, MemberRankMap};
			_ -> {[], #{}}
		end,
	F = fun(GuildRank, TList) ->
		#guild_rank{
			old_live_rank = OldRank, old_live = OldLive, guild_id = GuildId, guild_lv = GuildLv, guild_name = GuildName,
			captain_name = CaptainName, limit_num = LimitNum
		} = GuildRank,
		MemberList = maps:get(GuildId, SMemberRankMap, []),
		MemberList1 = [1 || #member_rank{old_rank = MRank}<-MemberList, MRank =/= 0],
		MemberNum = length(MemberList1),
		case StartReward == 1 of
			true -> LimitNum1 = LimitNum;
			_ -> LimitNum1 = data_guild_m:get_guild_member_capacity(GuildLv)
		end,
		case OldRank of
			0 -> TList;
			_ -> [{OldRank, GuildId, GuildName, CaptainName, MemberNum, LimitNum1, OldLive}|TList]
		end end,
	case HasFirstRefresh of
		true -> PackList = lists:foldl(F, [], SGuildRankList);
		_    -> PackList = []
	end,
	GuildMemberList = maps:get(RealGuildId, SMemberRankMap, []),
	case lists:keyfind(RoleId, #member_rank.role_id, GuildMemberList) of
		#member_rank{old_live = MyLive} -> ok;
		_ -> MyLive = 0
	end,
	lib_server_send:send_to_uid(RoleId, pt_407, 40703, [PackList, MyLive]),
	{noreply, State};

do_handle_cast({get_member_rank_dsg, GuildId, RoleId}, State) ->
	#guild_race_rank_state{member_rank_map = MemberRankMap, guild_rank_list = GuildRankList
		,start_reward = StartStatus} = State,
	%% 开始结算之后才能查询
	case StartStatus == ?START_REWARD_YES of
		true ->
			case lists:keyfind(GuildId, #guild_rank.guild_id, GuildRankList) of
				#guild_rank{old_rank = GuildRank} when GuildRank > 0 -> skip;
				_ -> GuildRank = 0
			end,
			case maps:get(GuildId, MemberRankMap, []) of
				[_|_] = MemberRankList ->
					case lists:keyfind(RoleId, #member_rank.role_id, MemberRankList) of
						#member_rank{old_rank = MyRank} ->
							case data_guild_race_rank:get_guild_race_rank_reward_cfg(GuildRank, MyRank) of
								#guild_race_rank_reward_cfg{reward = [{_, DsgId, _}]} -> skip;
								_ -> DsgId = 0
							end;
						_ -> DsgId = 0, MyRank = 0
					end,
					lib_server_send:send_to_uid(RoleId, pt_407, 40702, [MyRank, DsgId]);
				_ -> skip
			end;
		_ -> skip
	end,
	{noreply, State};

%% 社团进程同步数据
do_handle_cast({sync_guild_member, SynMemberMsg, SynGuildMsg}, State) ->
	#guild_race_rank_state{guild_rank_list = GuildRankList, member_rank_map = MemberRankMap} = State,
	F = fun(RoleInfo, TMemberRankMap) ->
		{RoleId, RoleName, VipLv, SupVip, GuildId, Position, Power, Live} = RoleInfo,
		MemberRankList  = maps:get(GuildId, TMemberRankMap, []),
		case lists:keyfind(RoleId, #member_rank.role_id, MemberRankList) of
			#member_rank{} ->
				TMemberRankMap;
			_ ->
				MemberRank = #member_rank{
					role_id      = RoleId
					,role_name   = RoleName
					,guild_id    = GuildId
					,position    = Position
					,power       = Power
					,vip_lv      = VipLv
					,sup_vip     = SupVip
					,live        = Live
					,old_rank    = 0
				},
				lib_guild_race_rank:db_save_guild_race_rank_member(
					RoleId, GuildId, Position, Power, 0, Live, 0, 0, 0, 0
				),
				NewMemberRankList = lists:keystore(RoleId, #member_rank.role_id, MemberRankList, MemberRank),
				maps:put(GuildId, NewMemberRankList, TMemberRankMap)
		end end,
	MemberRankMap1 = lists:foldl(F, MemberRankMap, SynMemberMsg),
	F1 = fun(TGuildId, TMemberRanks, TMap1) ->
		NewMemberRanks = sort_member_rank(TMemberRanks),
		maps:put(TGuildId, NewMemberRanks, TMap1)
	end,
	NewMemberRankMap = maps:fold(F1, #{}, MemberRankMap1),
	F2 = fun(GuildInfo, TGuildRankList) ->
		{TGuildId, GuildName, GuildLv} = GuildInfo,
		case lists:keyfind(TGuildId, #guild_rank.guild_id, TGuildRankList) of
			#guild_rank{} = GuildRank ->
				TMemberRankList = maps:get(TGuildId, NewMemberRankMap, []),
				LimitMemberRank = lists:filter(fun(TMemberRank) -> TMemberRank#member_rank.rank =< 10 end, TMemberRankList),
				NewSumPower = lists:sum([TPower||#member_rank{power = TPower}<-LimitMemberRank]),
				lib_guild_race_rank:db_update_guild_race_rank_power(TGuildId, NewSumPower),
				lists:keystore(TGuildId, #guild_rank.guild_id, TGuildRankList, GuildRank#guild_rank{sum_power = NewSumPower});
			_ ->
				TMemberRankList = maps:get(TGuildId, NewMemberRankMap, []),
				LimitMemberRank = lists:filter(fun(TMemberRank) -> TMemberRank#member_rank.rank =< 10 end, TMemberRankList),
				NewSumPower = lists:sum([TPower||#member_rank{power = TPower}<-LimitMemberRank]),
				NewSumLive  = lists:sum([TLive||#member_rank{live = TLive}<-TMemberRankList]),
				CaptainRank =
					case [TMemberRank1||#member_rank{position = Position} = TMemberRank1 <- TMemberRankList, Position == ?POS_CHIEF] of
						[TCaptainRank|_] -> TCaptainRank;
						_ -> #member_rank{}
					end,
				GuildRank = #guild_rank{
					guild_id  = TGuildId
					,guild_name = GuildName
					,guild_lv   = GuildLv
					,captain_id = CaptainRank#member_rank.role_id
					,captain_name = CaptainRank#member_rank.role_name
					,sum_power = NewSumPower
					,sum_live = NewSumLive
				},
				lib_guild_race_rank:db_save_guild_race_rank(
					TGuildId, GuildName, GuildLv, CaptainRank#member_rank.role_id, NewSumPower, NewSumPower, 0,
					NewSumLive, CaptainRank#member_rank.role_id
				),
				lists:keystore(TGuildId, #guild_rank.guild_id, TGuildRankList, GuildRank)
		end end,
	GuildRankList1 = lists:foldl(F2, GuildRankList, SynGuildMsg),
	State0 = State#guild_race_rank_state{guild_rank_list = GuildRankList1, member_rank_map = NewMemberRankMap},
	State1 = lib_guild_race_rank:timer_recalc_rank(State0),
	NewState = lib_guild_race_rank:move_rank(State1),
	{noreply, NewState};

%% 战力前10名才参与总战力计算
%% @doc  注意: 人员和战力变更只存数据，1小时后再统计
do_handle_cast({member_change, GuildInfo, RoleInfo, ChangeType}, State) ->
	#guild_race_rank_state{guild_rank_list = GuildRankList, member_rank_map = MemberRankMap
		,disband_rank_list = DisbandGuildList, leave_member_map = LeaveMemberMap
	} = State,
	case ChangeType of
		%% 1>进入公会
		?CHANGE_TYPE_IN ->
			{GuildId, GuildName, GuildLv} = GuildInfo,
			{RoleId, RoleName, VipLv, SupVip, Position, Power, Live} = RoleInfo,
			%% 更新成员榜
			MemberRankList  = maps:get(GuildId, MemberRankMap, []),
			MemberRank = #member_rank{
				role_id      = RoleId
				,role_name   = RoleName
				,guild_id    = GuildId
				,position    = Position
				,old_power   = Power
				,power       = Power
				,live        = Live
				,old_live    = Live
				,vip_lv      = VipLv
				,sup_vip     = SupVip
				,old_rank    = 0
			},
			lib_guild_race_rank:db_save_guild_race_rank_member(
				RoleId, GuildId, Position, Power, 0, Live, 0, 0, 0, 0
			),
			MemberRankList1  = lists:keystore(RoleId, #member_rank.role_id, MemberRankList, MemberRank),
			NewMemberRankMap = maps:put(GuildId, MemberRankList1, MemberRankMap),
			NewGuildRankList =
			case lists:keyfind(GuildId, #guild_rank.guild_id, GuildRankList) of
				#guild_rank{} -> GuildRankList;
				%% 创建公会,第一个进入
				_ ->
					case Position of
						?POS_CHIEF ->
							CaptainId   = RoleId,
							CaptainName = RoleName;
						_ ->
							CaptainId   = 0,
							CaptainName = ""
					end,
					%% 全服第一个创建的社团打上标签
					%% 第一次刷新排行前只展示这个社团数据
					IsFirst = case erlang:length(GuildRankList) of
								  0 -> 1;
								  _ -> 0
					          end,
					GuildRank = #guild_rank{
						guild_id      = GuildId
						,guild_lv     = GuildLv
						,guild_name   = GuildName
						,captain_id   = CaptainId
						,captain_name = CaptainName
						,sum_power    = Power
						,sum_live     = Live
						,is_first     = IsFirst
						,create_id    = CaptainId
					},
					lib_guild_race_rank:db_save_guild_race_rank(
						GuildId, GuildName, GuildLv, CaptainId, Power, Power, 0, Live, CaptainId
					),
					GuildRankList1 = lists:keystore(GuildId, #guild_rank.guild_id, GuildRankList, GuildRank),
					GuildRankList1
			end,
			%% 重新进入社团后需要删除退出缓存
			LeaveMemList = maps:get(GuildId, LeaveMemberMap, []),
			LeaveMemList1 = lists:delete(RoleId, LeaveMemList),
			LeaveMemberMap1 = maps:put(GuildId, LeaveMemList1, LeaveMemberMap),
			NewState = State#guild_race_rank_state{
				guild_rank_list = NewGuildRankList, member_rank_map = NewMemberRankMap
				,leave_member_map = LeaveMemberMap1
			};
		_ ->
			%% 2>退出公会
			%% 这里只做数据预处理判断，实际上并不删除数据。但称号会及时去除
			GuildId = GuildInfo, RoleId = RoleInfo,
			MemberRankList  = maps:get(GuildId, MemberRankMap, []),
			MemberRankList1 = lists:keydelete(RoleId, #member_rank.role_id, MemberRankList),
			%% 删除原有称号
			cancel_dsg_after_leave_guild(State, RoleId, GuildId),
			case MemberRankList1 == [] of
				false ->
					NewMemberRankMap  = MemberRankMap,
					%% 更新社团榜
					NewGuildRankList  = GuildRankList,
					LeaveMemberList   = maps:get(GuildId, LeaveMemberMap, []),
					LeaveMemberList1  = [RoleId|lists:delete(RoleId, LeaveMemberList)],
					LeaveMemberMap1   = maps:put(GuildId, LeaveMemberList1, LeaveMemberMap),
					DisbandGuildList1 = DisbandGuildList;
				_ ->
					%% 社团解散延后操作
					% NewMemberRankMap = maps:remove(GuildId, MemberRankMap),
					% lib_guild_race_rank:db_delete_guild_race_rank(GuildId),
					% NewGuildRankList = lists:keydelete(GuildId, #guild_rank.guild_id, GuildRankList)
					NewMemberRankMap  = MemberRankMap,
					NewGuildRankList  = GuildRankList,
					LeaveMemberMap1   = LeaveMemberMap,
					DisbandGuildList1 = [GuildId|lists:delete(GuildId, DisbandGuildList)]
			end,
			NewState = State#guild_race_rank_state{
				guild_rank_list = NewGuildRankList, member_rank_map = NewMemberRankMap
				,disband_rank_list = DisbandGuildList1, leave_member_map = LeaveMemberMap1
			}
	end,
	{noreply, NewState};

do_handle_cast({position_change, GuildId, RoleId, RoleName, Position}, State) ->
	#guild_race_rank_state{guild_rank_list = GuildRankList, member_rank_map = MemberRankMap} = State,
	%% 修改公会榜(新职位是团长才修改)
	case Position of
		?POS_CHIEF ->
			NewGuildRankList =
			case lists:keyfind(GuildId, #guild_rank.guild_id, GuildRankList) of
				#guild_rank{} = GuildRank ->
					NewGuildRank = GuildRank#guild_rank{captain_id = RoleId, captain_name = RoleName},
					lib_guild_race_rank:db_update_guild_race_rank_captain(GuildId, RoleId),
					lists:keystore(GuildId, #guild_rank.guild_id, GuildRankList, NewGuildRank);
				_ ->
					GuildRankList
			end;
		_ ->
			NewGuildRankList = GuildRankList
	end,
	%% 修改个人榜
	NewMemberRankMap =
	case maps:get(GuildId, MemberRankMap, []) of
		[_|_] = MemberRankList ->
			case lists:keyfind(RoleId, #member_rank.role_id, MemberRankList) of
				#member_rank{} = MemberRank ->
					NewMemberRank = MemberRank#member_rank{position = Position},
					lib_guild_race_rank:db_update_guild_race_rank_member_position(RoleId, Position),
					NewMemberRankList = lists:keystore(RoleId, #member_rank.role_id, MemberRankList, NewMemberRank);
				_ ->
					NewMemberRankList = MemberRankList
			end,
			maps:put(GuildId, NewMemberRankList, MemberRankMap);
		_ ->
			MemberRankMap
	end,
	NewState = State#guild_race_rank_state{guild_rank_list = NewGuildRankList, member_rank_map = NewMemberRankMap},
	{noreply, NewState};

%% ----------------------------------------------------------------------------------
%% @doc 这里不实时刷新总战力，一小时之后/进程terminate再重新排序、存库 @end
%% ----------------------------------------------------------------------------------
do_handle_cast({power_change, GuildId, RoleId, Power}, State) ->
	#guild_race_rank_state{member_rank_map = MemberRankMap} = State,
	%% 修改个人榜 & 社团榜
	case maps:get(GuildId, MemberRankMap, []) of
		[_|_] = MemberRankList ->
			case lists:keyfind(RoleId, #member_rank.role_id, MemberRankList) of
				#member_rank{} = MemberRank ->
					NewMemberRank = MemberRank#member_rank{power = Power},
					MemberRankList1  = lists:keystore(RoleId, #member_rank.role_id, MemberRankList, NewMemberRank),
					NewMemberRankMap = maps:put(GuildId, MemberRankList1, MemberRankMap),
					NewState = State#guild_race_rank_state{member_rank_map = NewMemberRankMap};
				_ ->
					NewState = State
			end;
		_ ->
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast({live_change, GuildId, RoleId, RoleLive}, State) ->
	#guild_race_rank_state{member_rank_map = MemberRankMap, guild_rank_list = GuildRankList} = State,
	%% 修改个人榜 & 社团榜
	case maps:get(GuildId, MemberRankMap, []) of
		[_|_] = MemberRankList ->
			case lists:keyfind(RoleId, #member_rank.role_id, MemberRankList) of
				#member_rank{live = OldRoleLive} = MemberRank ->
					NewMemberRank = MemberRank#member_rank{live = RoleLive},
					MemberRankList1  = lists:keystore(RoleId, #member_rank.role_id, MemberRankList, NewMemberRank),
					NewMemberRankMap = maps:put(GuildId, MemberRankList1, MemberRankMap),
					%% 因为需要保留离开玩家的活跃值，玩家更新的时候就需要加到总进度里面
					NewGuildRankList =
					case lists:keyfind(GuildId, #guild_rank.guild_id, GuildRankList) of
						#guild_rank{sum_live = SumLive} = GuildRank ->
							AddRoleLive = max(0, RoleLive - OldRoleLive),
							NewSumLive  = SumLive + AddRoleLive,
							lib_guild_race_rank:db_update_guild_race_rank_old_live(GuildId, NewSumLive),
							lib_guild_race_rank_task:trigger_live_up_task(GuildId, NewSumLive),
							lists:keystore(GuildId, #guild_rank.guild_id, GuildRankList,
								GuildRank#guild_rank{sum_live = NewSumLive});
						_ ->
							GuildRankList
					end,
					NewState = State#guild_race_rank_state{
						member_rank_map = NewMemberRankMap, guild_rank_list = NewGuildRankList
					};
				_ ->
					NewState = State
			end;
		_ ->
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast({figure_change, GuildId, RoleId, FigureList, Type}, State) ->
	case FigureList of
		[Name, Vip, SupVip]  -> ok;
		_ -> Name = "", Vip = 0, SupVip = 0
	end,
	#guild_race_rank_state{member_rank_map = MemberRankMap, guild_rank_list = GuildRankList} = State,
	case maps:get(GuildId, MemberRankMap, []) of
		[_|_] = MemberRankList ->
			case lists:keyfind(RoleId, #member_rank.role_id, MemberRankList) of
				#member_rank{} = MemberRank ->
					NewMemberRank = MemberRank#member_rank{
						role_name = Name
						,vip_lv   = Vip
						,sup_vip  = SupVip
					},
					NewMemberRankList = lists:keystore(RoleId, #member_rank.role_id, MemberRankList, NewMemberRank);
				_ ->
					NewMemberRankList = MemberRankList
			end,
			NewMemberRankMap = maps:put(GuildId, NewMemberRankList, MemberRankMap);
		_ ->
			NewMemberRankMap = MemberRankMap
	end,
	NewGuildRankList =
	case Type of
		?FIGURE_CHANGE_NAME ->
			case lists:keyfind(GuildId, #guild_rank.guild_id, GuildRankList) of
				#guild_rank{captain_id = RoleId} = GuildRank -> lists:keystore(GuildId, #guild_rank.guild_id, GuildRankList, GuildRank#guild_rank{captain_name = Name});
				_ -> GuildRankList
			end;
		_ -> GuildRankList
	end,
	{noreply, State#guild_race_rank_state{member_rank_map = NewMemberRankMap, guild_rank_list = NewGuildRankList}};

do_handle_cast({guild_msg_change, GuildId, GuildName, GuildLv, ChangeType}, State) ->
	#guild_race_rank_state{guild_rank_list = GuildRakList} = State,
	NewGuildRankList =
	case lists:keyfind(GuildId, #guild_rank.guild_id, GuildRakList) of
		#guild_rank{guild_lv = OldGuildLv, guild_name = OldGuildName} = GuildRank ->
			case ChangeType of
				%% 改名
				1 when GuildName =/= OldGuildName ->
					lib_guild_race_rank:db_update_guild_name(GuildId, GuildName),
					NewGuildRank = GuildRank#guild_rank{guild_name = GuildName};
				%% 提升等级
				2 when GuildLv =/= OldGuildLv ->
					GuildRank1 = GuildRank#guild_rank{guild_lv = GuildLv},
					lib_guild_race_rank:db_update_guild_lv(GuildId, GuildLv),
					NewGuildRank = update_one_guild_limit_num(GuildRank1);
				_ ->
					NewGuildRank = GuildRank
			end,
			lists:keystore(GuildId, #guild_rank.guild_id, GuildRakList, NewGuildRank);
		_ ->
			GuildRakList
	end,
	{noreply, State#guild_race_rank_state{guild_rank_list = NewGuildRankList}};

%% 社团解散，不马上处理，一小时定时刷新或者服务器重启时刷新
do_handle_cast({guild_disband, GuildId}, State) ->
	#guild_race_rank_state{
		disband_rank_list = DisbandGuildList
	} = State,
	DisbandGuildList1 = [GuildId|lists:delete(GuildId, DisbandGuildList)],
	{noreply, State#guild_race_rank_state{disband_rank_list = DisbandGuildList1}};

%% 合并社团
%%do_handle_cast({guild_merge, TargetId, GuildId}, State) ->
%%	%% 活动期间,需要重新排序
%%	#guild_race_rank_state{guild_rank_list = GuildRakList, member_rank_map = MemberRankMap} = State,
%%	ok;

%% 异步消息
do_handle_cast({'apply_cast', Module, Function, Args}, State) ->
	case erlang:apply(Module, Function, [State|Args]) of
		{ok, NewState} -> ok;
		_ -> NewState = State
	end,
	{noreply, NewState};

do_handle_cast(_Msg, State) ->
	{noreply, State}.

%% ---------------------------------------------------------------------------
do_handle_info({sync_race_rank}, State) ->
	lib_guild_race_rank:sync_race_rank(State),
	{noreply, State};

do_handle_info({start_reward}, State) ->
	Now = utime:unixtime(),
	ServerKv = #server_kv{key = ?SERVER_KV_GUILD_RACE_RANK_START_REWARD, value = 1, time = Now},
	lib_server_kv:update_server_kv(ServerKv),
	State1 = State#guild_race_rank_state{start_reward = 1},
	NewState = lib_guild_race_rank:timer_check(State1, ?FIRST_SEND_REWARD_YES),
	{noreply, NewState};

do_handle_info({timer_check}, State) ->
	%% 第八天结算
	OpenDay = util:get_open_day(),
	case lib_guild_race_rank:get_end_day() of
		EndDay when OpenDay > EndDay ->
			mod_guild_race_rank:act_end(),
			NewState = State;
		_ ->
			NewState = lib_guild_race_rank:timer_check(State, ?FIRST_SEND_REWARD_NO)
	end,
	{noreply, NewState};

do_handle_info({send_not_received_reward}, State) ->
	#guild_race_rank_state{send_ref = SendRef} = State,
	util:cancel_timer(SendRef),
	Now = utime:unixtime(),
	ServerKv = #server_kv{key = ?SERVER_KV_GUILD_RACE_RANK_SEND_REWARD, value = 1, time = Now},
	lib_server_kv:update_server_kv(ServerKv),
	NewState = lib_guild_race_rank:send_not_received_reward(State),
	{noreply, NewState#guild_race_rank_state{send_ref = []}};

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


%% ---------------------- builtin function -------------------------------------------
init_race_rank_state() ->
	case lib_guild_race_rank:db_select_guild_race_rank() of
		[_|_] = RaceRankInfos ->
			RaceRank = make_list(guild_rank, RaceRankInfos);
		_ ->
			RaceRank = []
	end,
	%% 如果活动已经结束，仍然需要将member数据加载进来，因有可能发放邮件奖励
	%% 第九天开始才不需要加载数据(7天活动时间，1天展示时间)
	OpenDay = util:get_open_day(),
	EndDay  = lib_guild_race_rank:get_end_day(),
	case OpenDay > (EndDay+1) of
		true ->
			MemberRankMap = #{},
			RoleMap = #{},
			GuildTaskMap = #{};
		_ ->
			case lib_guild_race_rank:db_select_guild_race_rank_member() of
				[_|_] = MemberInfos ->
					MemberRankMap = make_list(member_rank, MemberInfos);
				_ ->
					MemberRankMap = #{}
			end,
			case lib_guild_race_rank:db_select_guild_race_role() of
				[_|_] = RoleInfos ->
					RoleMap = make_role_map(RoleInfos, #{});
				_ ->
					RoleMap = #{}
			end,
			case lib_guild_race_rank_task:db_select_race_rank_task() of
				[_|_] = TaskInfos ->
					GuildTaskMap = make_task_map(TaskInfos, #{});
				_ ->
					GuildTaskMap = #{}
			end
	end,
	StartStatus = lib_server_kv:get_value(?SERVER_KV_GUILD_RACE_RANK_START_REWARD, 0),
	%% 过滤合服情况(StartStatus可能为0)
	NewRef = case lib_guild_race_rank:is_open_act() of
		         true -> check_has_start_reward(StartStatus);
		         _ -> []
	         end,
	%% 检查是否已经发送过邮件奖励
	SendStatus = lib_server_kv:get_value(?SERVER_KV_GUILD_RACE_RANK_SEND_REWARD, 0),
	SendRef = check_has_send_mail_reward(SendStatus),
	#guild_race_rank_state{
		guild_rank_list   = RaceRank
		,member_rank_map  = MemberRankMap
		,role_guild_map   = RoleMap
		,start_reward     = StartStatus
		,ref              = NewRef
		,send_ref         = SendRef
		,guild_task       = GuildTaskMap
	}.

make_list(guild_rank, RaceRankInfos) ->
	F = fun([GuildId, GuildName, GuildLv, CaptainId, SumPower, OldPower,
				OldRank, OldSumLive, CreateId, Time
			], TList) ->
		case lib_role:get_role_show(CaptainId) of
			#ets_role_show{figure = Figure} ->
				CaptainName = Figure#figure.name;
			_ ->
				CaptainName = ""
		end,
		GuildRank = #guild_rank{
			guild_id    = GuildId
			,guild_name = GuildName
			,guild_lv   = GuildLv
			,captain_id = CaptainId
			,captain_name = CaptainName
			,sum_power  = SumPower
			,rank       = OldRank
			,old_power  = OldPower
			,old_rank   = OldRank
			,sum_live   = OldSumLive
			,old_live   = OldSumLive
			,create_id  = CreateId
			,time       = Time
		},
		[GuildRank|TList]
	end,
	GuildRankList0 = lists:foldl(F, [], RaceRankInfos),
	%% 需要计算一次活跃排行
	GuildRankList = sort_guild_live_rank(GuildRankList0),
	NewGuildRankList = move_live_rank(GuildRankList),
	%% 此处不进行重新排行,因有可能是合服数据(第一天11点之前除外)
	case lib_guild_race_rank:is_first_reward() of
		false ->
			NewGuildRankList1 = sort_guild_rank_before_first_reward(NewGuildRankList);
		_ ->
			NewGuildRankList1 = NewGuildRankList
	end,
	update_guild_limit_num(NewGuildRankList1);
make_list(member_rank, MemberInfos) -> %% todo:vip等数据读取优化
	F = fun([RoleId, GuildId, Position, OldPower, OldRank, OldLive,
		ReceivePower, ReceiveLive, ReceiveCaptainPower, ReceiveCaptainLive
			], TList) ->
		case lib_role:get_role_show(RoleId) of
			#ets_role_show{figure = Figure} ->
				%% 需要拿存库的战力(减去称号战力)
				Power = OldPower,
				#figure{name = RoleName, vip = VipLv, sup_vip = SupVip} = Figure;
			_ ->
				RoleName = "", Power = 0, VipLv = 0, SupVip = 0
		end,
		MemberRank = #member_rank{
			role_id      = RoleId
			,role_name   = RoleName
			,guild_id    = GuildId
			,position    = Position
			,power       = Power
			,vip_lv      = VipLv
			,sup_vip     = SupVip
			,old_power   = OldPower
			,rank        = OldRank        %% 会重新计算一次，也可不赋值
			,old_rank    = OldRank
			,live        = OldLive
			,old_live    = OldLive
			,received_power   = ReceivePower
			,received_live    = ReceiveLive
			,received_captain_power = ReceiveCaptainPower
			,received_captain_live  = ReceiveCaptainLive
		},
		[MemberRank|TList]
	end,
	MemberRankList = lists:foldl(F, [], MemberInfos),
	make_member_rank_map(MemberRankList).

make_member_rank_map(MemberRankList) ->
	F = fun(MemberRank, TMap) ->
		#member_rank{role_id = RoleId, guild_id = GuildId} = MemberRank,
		case maps:get(GuildId, TMap, []) of
			[_Rank|_] = MemberRanks ->
				MemberRanks1 = lists:keystore(RoleId, #member_rank.role_id, MemberRanks, MemberRank),
				maps:put(GuildId, MemberRanks1, TMap);
			_ ->
				MemberRanks1 = lists:keystore(RoleId, #member_rank.role_id, [], MemberRank),
				maps:put(GuildId, MemberRanks1, TMap)
		end
	end,
	MemberRankMap = lists:foldl(F, #{}, MemberRankList),
	F1 = fun(TGuildId, TMemberRanks, TMap1) ->
		NewMemberRanks = sort_member_rank(TMemberRanks),
		maps:put(TGuildId, NewMemberRanks, TMap1)
	end,
	maps:fold(F1, #{}, MemberRankMap).

make_role_map([], RoleMap) -> RoleMap;
make_role_map([[RoleId, GuildId]|T], RoleMap) ->
	NewRoleMap = maps:put(RoleId, GuildId, RoleMap),
	make_role_map(T, NewRoleMap);
make_role_map([_|T], RoleMap) ->
	make_role_map(T, RoleMap).

make_task_map([], TaskMap) -> TaskMap;
make_task_map([[GuildId, TaskId, Progress, Status]|T], TaskMap) ->
	GuildTask = #guild_race_rank_task{task_id = TaskId, progress = Progress, status = Status},
	TaskList  = maps:get(GuildId, TaskMap, []),
	NewTaskList = lists:keystore(TaskId, #guild_race_rank_task.task_id, TaskList, GuildTask),
	NewTaskMap  = maps:put(GuildId, NewTaskList, TaskMap),
	make_task_map(T, NewTaskMap);
make_task_map([_|T], TaskMap) ->
	make_task_map(T, TaskMap).

sort_guild_rank_before_first_reward(GuildRankList) ->
	TimeList = [Time|| #guild_rank{time = Time} <- GuildRankList],
	MinTime = case TimeList of
		          [_|_] ->
			          lists:min(TimeList);
		          _ ->
			          0
	          end,
	F = fun(RankA, RankB) ->
		#guild_rank{sum_power = SumPowerA} = RankA,
		#guild_rank{sum_power = SumPowerB} = RankB,
		if
			SumPowerA >= SumPowerB ->
				true;
			true ->
				false
		end end,
	GuildRankList1 = lists:sort(F, GuildRankList),
	F1 = fun(RankInfo, {TRank, TRankList}) ->
		#guild_rank{guild_id = GuildId, time = Time} = RankInfo,
		case Time == MinTime of
			true -> IsFirst = 1;
			_ -> IsFirst = 0
		end,
		TRankList1 = lists:keystore(GuildId, #guild_rank.guild_id, TRankList, RankInfo#guild_rank{rank = TRank, is_first = IsFirst}),
		{TRank + 1, TRankList1}
	end,
	{_, GuildRankList2} = lists:foldl(F1, {1, []}, GuildRankList1),
	lists:reverse(GuildRankList2).

sort_guild_rank(GuildRankList) ->
	F = fun(RankA, RankB) ->
		#guild_rank{sum_power = SumPowerA} = RankA,
		#guild_rank{sum_power = SumPowerB} = RankB,
		if
			SumPowerA >= SumPowerB ->
				true;
			true ->
				false
		end end,
	GuildRankList1 = lists:sort(F, GuildRankList),
	F1 = fun(RankInfo, {TRank, TRankList}) ->
		#guild_rank{guild_id = GuildId} = RankInfo,
		{TRank + 1, lists:keystore(GuildId, #guild_rank.guild_id, TRankList, RankInfo#guild_rank{rank = TRank})}
	end,
	{_, GuildRankList2} = lists:foldl(F1, {1, []}, GuildRankList1),
	lists:reverse(GuildRankList2).

sort_guild_live_rank(GuildRankList) ->
	F = fun(RankA, RankB) ->
		#guild_rank{sum_live = SumLiveA} = RankA,
		#guild_rank{sum_live = SumLiveB} = RankB,
		if
			SumLiveA >= SumLiveB ->
				true;
			true ->
				false
		end end,
	GuildRankList1 = lists:sort(F, GuildRankList),
	F1 = fun(RankInfo, {TRank, TRankList}) ->
		#guild_rank{guild_id = GuildId} = RankInfo,
		{TRank + 1, lists:keystore(GuildId, #guild_rank.guild_id, TRankList, RankInfo#guild_rank{live_rank = TRank})}
	     end,
	{_, GuildRankList2} = lists:foldl(F1, {1, []}, GuildRankList1),
	lists:reverse(GuildRankList2).

sort_member_rank(MemberRankList) ->
	F = fun(RankA, RankB) ->
		#member_rank{power = PowerA} = RankA,
		#member_rank{power = PowerB} = RankB,
		if
			PowerA >= PowerB ->
				true;
			true ->
				false
		end end,
	MemberRankList1 = lists:sort(F, MemberRankList),
	F1 = fun(RankInfo, {TRank, TRankList}) ->
			#member_rank{role_id = RoleId} = RankInfo,
			{TRank + 1, lists:keystore(RoleId, #member_rank.role_id, TRankList, RankInfo#member_rank{rank = TRank})}
	     end,
	{_, MemberRankList2} = lists:foldl(F1, {1, []}, MemberRankList1),
	lists:reverse(MemberRankList2).

move_live_rank(GuildRankList) ->
	F = fun(TGuildRank, TList) ->
		#guild_rank{guild_id = TGuildId, sum_live = NowSumLive, live_rank = NowLiveRank} = TGuildRank,
		lists:keystore(TGuildId, #guild_rank.guild_id, TList,
			TGuildRank#guild_rank{old_live = NowSumLive, old_live_rank = NowLiveRank}
		)
	end,
	lists:foldl(F, [], GuildRankList).

%% 更新公会人数上限
update_guild_limit_num(GuildRankList) ->
	OpenDay = util:get_open_day(),
	F = fun(GuildRank, TList) ->
		#guild_rank{rank = Rank, guild_id = GuildId, guild_lv = GuildLv} = GuildRank,
		Capacity = data_guild_m:get_guild_member_capacity(GuildLv),
		case lists:member(Rank, ?GUILD_TOP_RANK) of
			true ->
				case data_guild_race_rank:get_guild_race_rank_expand_cfg(OpenDay) of
					AddNum when is_integer(AddNum) -> skip;
					_ -> AddNum = data_guild_race_rank:get_guild_race_rank_expand_cfg(?DEFAULT_END_DAY)
				end;
			_ ->
				AddNum = 0
		end,
		lists:keystore(GuildId, #guild_rank.guild_id, TList, GuildRank#guild_rank{limit_num = Capacity + AddNum})
	end,
	lists:foldl(F, GuildRankList, GuildRankList).

update_one_guild_limit_num(GuildRank) ->
	OpenDay = util:get_open_day(),
	#guild_rank{rank = Rank, guild_lv = GuildLv} = GuildRank,
	Capacity = data_guild_m:get_guild_member_capacity(GuildLv),
	case lists:member(Rank, ?GUILD_TOP_RANK) of
		true ->
			case data_guild_race_rank:get_guild_race_rank_expand_cfg(OpenDay) of
				AddNum when is_integer(AddNum) -> skip;
				_ -> AddNum = data_guild_race_rank:get_guild_race_rank_expand_cfg(?DEFAULT_END_DAY)
			end;
		_ ->
			AddNum = 0
	end,
	GuildRank#guild_rank{limit_num = Capacity + AddNum}.

cancel_dsg_after_leave_guild(State, RoleId, GuildId) ->
	#guild_race_rank_state{guild_rank_list = GuildRankList, member_rank_map = MemberRankMap} = State,
	case lists:keyfind(GuildId, #guild_rank.guild_id, GuildRankList) of
		#guild_rank{old_rank = GuildRank} ->
			case maps:get(GuildId, MemberRankMap, []) of
				[_|_] = MemberRankList ->
					case lists:keyfind(RoleId, #member_rank.role_id, MemberRankList) of
						#member_rank{old_rank = MemberRank} ->
							case data_guild_race_rank:get_guild_race_rank_reward_cfg(GuildRank, MemberRank) of
								#guild_race_rank_reward_cfg{reward = [{_, DsgId, _}]} ->
									lib_designation_api:cancel_dsgt(RoleId, DsgId);
								_ -> skip
							end;
						_ -> skip
					end;
				_ -> skip
			end;
		_ -> skip
	end.

%% @doc 检测(StartStatus)是否已经开始结算
%% StartStatus记录节点：
%% 1. 正常情况: 1）开服时间在当天23点以下, 在当天23点记录
%%				2）开服时间再当天23点以上，在开服第二天记录
%% 2. 异常情况：1）在记录StartStatus节点时重启，即错过了以上两种情况的任意
%%              一种，需要做特殊处理，重启后第一个整点记录StartStatus
%%              2）StartStatus记录异常
%% @end
check_has_start_reward(StartStatus) ->
	OpenDay = util:get_open_day(),
	{NowDate, {NowH,NowM,_}} = calendar:local_time(),
	Now = NowH * 60 + NowM,
	{StartH, StartM} = data_guild_race_rank:get_guild_race_rank_kv_cfg(reward_time),
	StartMin = StartH * 60 + StartM,
	case lib_custom_act_util:get_act_cfg_info(?CUSTOM_ACT_TYPE_GUILD_RACE_RANK, 1) of
		#custom_act_cfg{opday_lim = [{StartDay, _}]} -> ok;
		_ -> StartDay = 1
	end,
	case StartStatus of
		%% 未开始结算
		?START_REWARD_NO ->
			%% 开服天数
			case OpenDay == StartDay of
				true ->
					case Now >= StartMin of
						true ->
							%% 如果大于配置的结算时间开服,到下一天的这个时候(23点)开始结算
							NowTime = utime:unixtime(),
							Start = utime:get_next_logic_midnight(NowTime, StartH),
							NewRef = erlang:send_after(max(0, (Start-NowTime))*1000, self(), {start_reward});
						_ ->
							Start = utime:unixtime({NowDate, {StartH, StartM, 0}}),
							NowTime = utime:unixtime(),
							NewRef = erlang:send_after(max(0, (Start-NowTime))*1000, self(), {start_reward})
					end;
				_ ->
					%% 特殊情况处理--如果还在活动时间内(因为某些原因重启并错过了StartStatus的记录)
					case lib_guild_race_rank:is_open_act() of
						true ->
							NowHourT = utime:unixtime({NowDate, {NowH,0,0}}),
							Start = NowHourT + ?ONE_HOUR_SECOND,
							NowTime = utime:unixtime(),
							NewRef = erlang:send_after(max(0, (Start-NowTime))*1000, self(), {start_reward});
						_ ->
							NewRef = []
					end
			end;
		%% 已经开始了结算
		_ ->
			%% 活动结束判断
			case lib_guild_race_rank:get_end_day() of
				EndDay when OpenDay > EndDay ->
					NewRef = [];
				_ ->
					%% 设置下一个小时开始的定时器
					NowHourT = utime:unixtime({NowDate, {NowH,0,0}}),
					Start = NowHourT + ?ONE_HOUR_SECOND,
					NowTime = utime:unixtime(),
					NewRef = erlang:send_after(max(0, (Start-NowTime))*1000, self(), {timer_check})
					% NewRef = erlang:send_after(0, self(), {timer_check})
			end
	end,
	NewRef.

%% @doc 检查(SendStatus)是否已经统一邮件发送奖励
%% date: 2021-04-25
%% 1> 正常情况：
%%      第九天前，都定时到第九天0点邮件结算
%% 2> 异常情况：
%%      如果是特殊原因错过第九天0点，不结算
%%      因考虑到这是补充功能，已经开服很久的服触发相关逻辑会很突兀
%%      一般也很少会出现0点重启服务器的情况
%% @end
check_has_send_mail_reward(SendStatus) ->
	case SendStatus of
		%% 未开始邮件结算
		?START_REWARD_NO ->
			OpenDay = util:get_open_day(),
			case OpenDay > ?REWARD_RECEIVE_DAY of
				true -> NewRef = [];
				_ ->
					ThatDay = max(1, ?REWARD_RECEIVE_DAY + 1 - OpenDay),
					ThatDate = utime:that_day_after(ThatDay),
					NowTime  = utime:unixtime(),
					NewRef = erlang:send_after(max(0, ThatDate - NowTime)*1000, self(), {send_not_received_reward})
			end;
		_ ->
			NewRef = []
	end,
	NewRef.