%%%-------------------------------------------------------------------
%%% @doc    宠物-声援管理进程
%%% Module  : mod_pet_support_mgr.erl
%%% Created : 2019-12-09
%%% @Author : tyl
%%% modify  : 2021-05-12 修改为宠物的总战力榜，模块名字保留不改了
%%%-------------------------------------------------------------------
-module(mod_pet_support_mgr).
-behaviour(gen_server).

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


-export([
	start_link/0
	,send_pet_power_rank/1             %% 查询全服宠物投票信息
	,send_server_yest_power_rank/1     %% 查询全服昨日战力前八名宠物
	,send_reward_receive_info/2        %% 查询昨日榜奖励领取状态
	,receive_rank_reward/2             %% 领取昨日战力排行奖励
	,pet_power_change/4                %% 宠物战力变更
	,vote_pet_info/3                   %% 宠物投票信息
	,server_vote_for_pet/2             %% 宠物投票
	,player_power_refresh/2            %% 玩家战力刷新,刷新全服战力排行榜

	,midnight_upload_pet_info/0        %% 4点定时更新宠物排行信息到跨服
	,daily_refresh/0                   %% 零点刷新昨日投票榜

	,set_center_rank_len/1             %% 更新跨服榜单长度

	,offline_login_refresh/1           %% 离线登陆,刷新支援加成
]).

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

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

%% API
%% 查询全服宠物战力信息
send_pet_power_rank(RoleId) ->
	cast({send_pet_power_rank, RoleId}).

%% 查询全服昨日投票前八名宠物
send_server_yest_power_rank(RoleId) ->
	cast({send_server_yest_power_rank, RoleId}).

%% 查询昨日榜奖励领取状态
send_reward_receive_info(RoleId, ReceiveList) ->
	cast({send_reward_receive_info, RoleId, ReceiveList}).

%% 领取昨日投票排行奖励
receive_rank_reward(RoleId, PetId) ->
	cast({receive_rank_reward, RoleId, PetId}).

%% 宠物战力变更
pet_power_change(RoleMsg, PetId, NewPower, OldPower) ->
	cast({pet_power_change, RoleMsg, PetId, NewPower, OldPower}).

%% 投票信息
vote_pet_info(RoleId, VoteList, SupportTimes) ->
	cast({vote_pet_info, RoleId, VoteList, SupportTimes}).

%% 宠物投票
server_vote_for_pet(RoleId, PetId) ->
	cast({server_vote_for_pet, RoleId, PetId}).

%% 玩家战力刷新,刷新全服投票排行榜
player_power_refresh(RoleInfo, PetList) ->
	cast({player_power_refresh, RoleInfo, PetList}).

%% 4点上传一次
midnight_upload_pet_info() ->
	cast({midnight_upload_pet_info}).

%% 零点计算宠物投票榜
daily_refresh() ->
	cast({daily_refresh}).

%% 离线登陆,刷新支援加成
offline_login_refresh(RoleId) ->
	gen_server:call(?MODULE, {offline_login_refresh, RoleId}, 2000).

%% ---------------------------------------------------------------
%% @doc
%% 游戏服缓存跨服榜单长度，避免频繁到跨服请求
%% @end
%% ---------------------------------------------------------------
set_center_rank_len(LenMap) ->
	cast({set_center_rank_len, LenMap}).

%% ---------------------------------------------------------------------------
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),
	ets:new(?ETS_PET, [named_table, public, set, {keypos, #ets_role_pets.rid}, {read_concurrency, true}]),  % 玩家宠物数据
	{ok, NewState} = init_pet_support_mgr(#pet_power_rank_state{}),
	{ok, NewState}.

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({offline_login_refresh, _RoleId}, _From, State) ->
	AddList = [{PetId, 0} || #pet_rank_info{pet_id = PetId} <- State#pet_power_rank_state.yest_rank_info_list],
	{reply, AddList, State};

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

%% ---------------------------------------------------------------------------
do_handle_cast({daily_refresh}, State) ->
	NewState = lib_pet_support_mod:daily_refresh(State),
	{noreply, NewState};

do_handle_cast({set_center_rank_len, LenMap}, State) ->
	case lib_pet_support_mod:check_enter_center() of
		{_, ZoneId} when is_integer(ZoneId) ->
			RankLen = maps:get(ZoneId, LenMap, 0),
			NewState = State#pet_power_rank_state{center_rank_length = RankLen};
		_ ->
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast({send_pet_power_rank, RoleId}, State) ->
	lib_pet_support_mod:send_pet_power_rank(State, RoleId),
	{noreply, State};

%% 查询昨日全服战力前八位
do_handle_cast({send_server_yest_power_rank, RoleId}, State) ->
	lib_pet_support_mod:send_server_yest_power_rank(State, RoleId),
	{noreply, State};

%% 查询昨日榜奖励领取状态
do_handle_cast({send_reward_receive_info, RoleId, ReceiveList}, State) ->
	lib_pet_support_mod:send_reward_receive_info(State, RoleId, ReceiveList),
	{noreply, State};

%% 领取昨日投票排行奖励
do_handle_cast({receive_rank_reward, RoleId, PetId}, State) ->
	lib_pet_support_mod:receive_rank_reward(State, RoleId, PetId),
	{noreply, State};

%% 宠物战力变更
do_handle_cast({pet_power_change, RoleMsg, PetId, NewPower, OldPower}, State) ->
	NewState = lib_pet_support_mod:pet_power_change(State, RoleMsg, PetId, NewPower, OldPower),
	{noreply, NewState};

%% 玩家战力改变,修改排行榜
do_handle_cast({player_power_refresh, RoleInfo, PetList}, State) ->
	NewState = lib_pet_support_mod:refresh_player_power_on_rank(State, RoleInfo, PetList),
	{noreply, NewState};

%% 点赞信息
do_handle_cast({vote_pet_info, RoleId, VoteList, SupportTimes}, State) ->
	lib_pet_support_mod:vote_pet_info(State, RoleId, VoteList, SupportTimes),
	{noreply, State};

%% 点赞(只有在前八的宠物才可以点赞)
do_handle_cast({server_vote_for_pet, RoleId, PetId}, State) ->
	lib_pet_support_mod:server_vote_for_pet(State, RoleId, PetId),
	{noreply, State};

%% 4点上传一次全服数据到跨服
do_handle_cast({midnight_upload_pet_info}, State) ->
	#pet_power_rank_state{rank_info_list = VoteInfoList} = State,
	upload_pet_info_core(VoteInfoList),
	{noreply, State};

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

%% ---------------------------------------------------------------------------
%% 暂时不使用
do_handle_info({async_power_rank_to_center}, State) ->
	#pet_power_rank_state{rank_info_list = VoteInfoList} = State,
	upload_pet_info_core(VoteInfoList),
	{noreply, State};

do_handle_info(_Info, State) ->
	{noreply, State}.

%% -------------------------- builtin function -------------------------------------
%% 初始化宠物支援榜单
init_pet_support_mgr(State) ->
	%% 读库
	Sql = io_lib:format(?SQL_SELECT_PET_POWER_RANK, []),
	case db:get_all(Sql) of
		[_|_] = SupportRankInfos -> ok;
		_ -> SupportRankInfos = []
	end,
	%% 今天榜单
	SupportRankList = trans_to_support_rank_record(SupportRankInfos, []),
	%% 昨日榜单
	Sql1 = io_lib:format(?SQL_SELECT_PET_POWER_OLD_RANK, []),
	case db:get_all(Sql1) of
		[_|_] = SupportOldRankInfos -> ok;
		_ -> SupportOldRankInfos = []
	end,
	OldSupportRankList = make_support_rank_list(SupportOldRankInfos, [], init),
	%% 开服第一天没有昨日榜
	OpenDay = util:get_open_day(),
	case OpenDay == 1 of
		true -> OldSupportRankList1 = [];
		_ -> OldSupportRankList1 = OldSupportRankList
	end,
	NewState = State#pet_power_rank_state{
		rank_info_list  = SupportRankList
		,yest_rank_info_list = OldSupportRankList1
	},
	{ok, NewState}.

trans_to_support_rank_record([], RecordList) -> RecordList;
trans_to_support_rank_record([SupportRankInfo|L], RecordList) ->
	[PetId, RoleId, PetPower, SumPower] = SupportRankInfo,
	case lib_pet_data:db_get_role_power(RoleId) of
		[Power] -> ok;
		_ -> Power = 0
	end,
	PetRankInfo = #pet_power_rank_info{
		pet_id        = PetId,
		player_id     = RoleId,
		pet_power     = PetPower,
		player_power  = Power,
		sum_power     = SumPower
	},
	NewRecordList = [PetRankInfo|RecordList],
	trans_to_support_rank_record(L, NewRecordList).

make_support_rank_list([], List, _Type) ->
	%% 排序(如果战力相同,选择PetId更小的)
	RankList = lib_pet_support_mod:sort_rank_list(List),
	RankList;
make_support_rank_list([[PetId, OldRoleId, OldSumPower]|L], TList, Type) ->
	case lib_pet_data:db_get_role_power(OldRoleId) of
		[OldPower] -> ok;
		_ -> OldPower = 0
	end,
	SupportRank = #pet_power_rank_info{
		pet_id = PetId, player_id = OldRoleId, player_power  = OldPower
		,sum_power = OldSumPower
	},
	make_support_rank_list(L, [SupportRank|TList], Type).

upload_pet_info_core(VoteInfoList) ->
	case czone_api:zone_open_check() of
		{true, ZoneId} when is_integer(ZoneId) andalso ZoneId > 0 ->
			F = fun(PetRankInfo, TList) ->
				#pet_power_rank_info{
					pet_id        = PetId
					,player_id    = PlayerId
					,sum_power    = SumPower
				} = PetRankInfo,
				case lib_role:get_role_show(PlayerId) of
					#ets_role_show{figure = Figure} -> ok;
					_ -> Figure = #figure{}
				end,
				DressId = case lists:keyfind(2, 1, Figure#figure.dress_list) of
					          {_, TDressId} -> TDressId;
					          _ -> 0
				          end,
				UpInfo = #up_pet_info{
					pet_id             = PetId
					,player_id         = PlayerId
					,sex               = Figure#figure.sex
					,player_name       = Figure#figure.name
					,player_power      = Figure#figure.power
					,vip               = Figure#figure.vip
					,sup_vip           = Figure#figure.sup_vip
					,dress_id          = DressId
					,photo_id          = Figure#figure.profile_photo_id
					,server_num        = config:get_server_num()
					,sum_power         = SumPower
					,server_id         = config:get_server_id()
				},
				[UpInfo|TList]
			    end,
			UpInfoList = lists:foldl(F, [], VoteInfoList),
			clusters_node:apply_cast(mod_pet_support_center, upload_pet_info_to_center, [ZoneId, UpInfoList]);
		_ ->
			ignore
	end.
