%%% ===================================================================
%%% @desc    唤神副本本地进程管理
%%% @module  god_dun_rank_local
%%%
%%% ==================================================================

-module(god_dun_rank_local).

-behaviour(gen_server).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
%% API
-export([
    start_link/0
    ,update_rank_data/1
    ,get_role_attrs_by_other_rank/3
    ,get_score_rank/2
    ,get_role_power_rank/1

    ,check_god_mon_info_update/2
    ,god_mon_info_update/3
    ,god_mon_info_update_in_source/2
    ,god_mon_info_back_center/4
    ,god_mon_info_back_req_node/3
    ,get_other_node_role/1
    
    ,is_local_svr/1
    ]).

-define(SERVER, ?MODULE).

-define (CD_TIME, 86400).

-ifdef(DEV_SERVER).
-define(DELAY_POWER_GET, 60000).
-else.
-define(DELAY_POWER_GET, 300000).
-endif.

-define (ZONE_POWER_RANK_KEY, zone_power_rank_key).

-include("god_dun.hrl").
-include("figure.hrl").
-include("common.hrl").

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

update_rank_data(Data) ->
    gen_server:cast(?MODULE, {update_rank_data, Data}).

get_role_attrs_by_other_rank(GodMonCfg, From, Mid) ->
    gen_server:cast(?MODULE, {get_role_attrs_by_other_rank, GodMonCfg, From, Mid}).

get_score_rank(RoleId, Score) ->
    gen_server:cast(?MODULE, {get_score_rank, RoleId, Score}).

get_role_power_rank(RoleId) ->
    gen_server:cast(?MODULE, {get_role_power_rank, RoleId}).

check_god_mon_info_update(DunPid, MonList) ->
    gen_server:cast(?MODULE, {check_god_mon_info_update, DunPid, MonList}).

%%%-------------------------------------------------------------------
%%% Callback Functions
%%%-------------------------------------------------------------------
init(_Args) ->
    ets:new(?ETS_GOD_DUN_CLS_ROLE, 
        [{keypos, #god_dun_cls_role.role_id}, named_table, public, set]
    ),
    erlang:send_after(?DELAY_POWER_GET, self(), {get_zone_power_rank}),
    {ok, #god_dun_rank_local_state{}}.

handle_call(_Request, _From, State) ->
    {reply, ok, State}.

%% 周期性更新本地数据
handle_cast({update_rank_data, Data}, State) ->
    RankDataBin = zlib:unzip(Data),
    RankData = util:bitstring_to_term(RankDataBin),
    NewState = 
    case lists:keyfind(score_rank, 1, RankData) of
        {_, ScoreRank} ->
            State#god_dun_rank_local_state{score_rank = ScoreRank};
        _ ->
            State
    end,
    NewState1 = 
    case lists:keyfind(today_roles, 1, RankData) of
        {_, RoleNum} ->
            NewState#god_dun_rank_local_state{today_roles = RoleNum};
        _ ->
            NewState
    end,
    {noreply, NewState1};

%% 通知下载最新战力排行
handle_cast({download_power_rank_data}, State) ->
    Pid = self(),
    ZoneId = czone_api:env_get_zone_id(),
    ServerId = config:get_server_id(),
    CallBackMsg = update_power_rank_data,
    spawn(fun() -> 
        timer:sleep(urand:rand(1000, 5000)), 
        clusters_node:apply_cast(god_dun_rank_mgr, get_power_rank, [ServerId, ZoneId, Pid, CallBackMsg])
    end),
    {noreply, State};


handle_cast({update_power_rank_data, RankL}, State) ->
    case RankL of
        [_|_] ->
            erlang:put(?ZONE_POWER_RANK_KEY, 1),
            check_god_mon_info_reset(RankL),
            NewState = State#god_dun_rank_local_state{power_rank = RankL};
        _ ->
            NewState = State
    end,
    {noreply, NewState};

handle_cast({get_role_attrs_by_other_rank, GodMonCfg, MonMgrPid, Mid}, State) ->
    #god_dun_rank_local_state{power_rank = PowerRank} = State,
    #base_god_dun_mon_cfg{rank = Rank, attrs = AttrCfg, info_from_rank = InfoRank} = GodMonCfg,
    case lists:keyfind(Rank, #god_dun_power_role.rank, PowerRank) of
        _Role = #god_dun_power_role{attrs = RoleAttrs} ->
            MonAttrs = change_attrs_by_cfg(RoleAttrs, AttrCfg),
            mon_mgr:send(MonMgrPid, Mid, {'change_attr', [{attr_list, MonAttrs}]}),
            ok;
        _ ->
            skip
    end,
    case lists:keyfind(InfoRank, #god_dun_power_role.rank, PowerRank) of
        #god_dun_power_role{lv = RoleLv, server_num = ServNum, role_name = RoleName} when InfoRank =/= 0 ->
            ServL = integer_to_list(ServNum),
            RoleNameL = unicode:characters_to_list(RoleName),
            MonName = unicode:characters_to_binary("S" ++ ServL ++ ". " ++ RoleNameL),
            MonAttrs1 = [{lv, RoleLv}, {mon_name, MonName}],
            mon_mgr:send(MonMgrPid, Mid, {'change_attr', MonAttrs1});
        _A ->
            skip
    end,
    {noreply, State};

handle_cast({get_score_rank, RoleId, RoleScore}, State) ->
    #god_dun_rank_local_state{score_rank = ScoreRank, today_roles = TodayRoleNum} = State,
    PackData = god_dun_rank_api:pack_score_rank(ScoreRank),
    case lists:keyfind(RoleId, #god_dun_score_role.role_id, ScoreRank) of
        #god_dun_score_role{score = Score, rank = Rank} -> ok;
        _   -> Score = RoleScore, Rank = 0
    end,
    [{_, RankNumL}|_] = god_dun_rank_api:calc_zone_rank([{1, TodayRoleNum}]),
    PercentRankMax = data_god_dun:get_max_rank_by_type(1),
    if Rank > PercentRankMax ->
            RankType = 2,
            {NewRankMin, NewRankMax} = calc_role_percent_rank(RankNumL, Rank);
        true ->
            RankType = 1, NewRankMin = Rank, NewRankMax = 0
    end,
    lib_server_send:send_to_uid(RoleId, pt_610, 61088, [0, PackData, RankType, NewRankMin, NewRankMax, Score]),
    {noreply, State};

% handle_cast({get_god_dun_wave_score_rank, Sid, Wave}, State) ->
%     #god_dun_rank_local_state{dun_wave_rank = DunWaveRank} = State,
%     case lists:keyfind(Wave, 1, DunWaveRank) of
%         {_, WaveRankL} ->
%             PackData = pack_wave_score_rank(WaveRankL);
%         _ ->
%             PackData = []
%     end,
%     lib_server_send:send_to_sid(Sid, pt_610, 61093, [PackData]),
%     {noreply, State};

handle_cast({get_role_info_by_rank, [MonMgrPid, Mid, InfoRank]}, State) ->
    #god_dun_rank_local_state{power_rank = PowerRank} = State,
    case lists:keyfind(InfoRank, #god_dun_power_role.rank, PowerRank) of
        #god_dun_power_role{lv = RoleLv, server_num = ServNum, role_name = RoleName} when InfoRank =/= 0 ->
            ServL = integer_to_list(ServNum),
            RoleNameL = unicode:characters_to_list(RoleName),
            MonName = unicode:characters_to_binary("S" ++ ServL ++ ". " ++ RoleNameL),
            MonAttrs = [{lv, RoleLv}, {mon_name, MonName}],
            mon_mgr:send(MonMgrPid, Mid, {'change_attr', MonAttrs});
        _ ->
            ok
    end,
    {noreply, State};

handle_cast({get_role_power_rank, RoleId}, State) ->
    #god_dun_rank_local_state{power_rank = Rank} = State,
    case lists:keyfind(RoleId, #god_dun_power_role.role_id, Rank) of
        #god_dun_power_role{rank = Rank} -> ok;
        _ ->
            Rank = 0
    end,
    {noreply, State};

handle_cast({check_god_mon_info_update, DunPid, MonList}, State) ->
    #god_dun_rank_local_state{power_rank = PowerRank} = State,
    MonArgs =
    lists:map(fun({MonId, _, _}) -> 
        case data_god_dun:get_mon(MonId) of
            #base_god_dun_mon_cfg{info_from_rank = InfoRank, figure = FigureType} ->
                case lists:keyfind(InfoRank, #god_dun_power_role.rank, PowerRank) of
                    #god_dun_power_role{role_id = RoleId, server_id = SvrId} ->
                        if 
                            FigureType == 1 -> %% 需抽取玩家形象
                                IsLocalSvr = is_local_svr(RoleId),
                                if 
                                    not IsLocalSvr ->
                                        check_god_mon_info_update_do(RoleId, SvrId);
                                    true -> skip
                                end,
                                {MonId, RoleId, IsLocalSvr, FigureType};
                            true -> []
                        end;
                    _A -> []
                end;
            _ -> []
        end
    end, MonList),
    MonArgs1 = lists:flatten(MonArgs),
    MonArgs1 =/= [] andalso mod_dungeon:apply(DunPid, lib_dungeon_god, god_dun_mon_info_ready, [MonArgs]),
    {noreply, State};

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


%% 新连接的游戏服或重启的游戏服获取战力排行
handle_info({get_zone_power_rank}, State) ->
    case erlang:get(?ZONE_POWER_RANK_KEY) of
        undefined ->
            handle_cast({download_power_rank_data}, State),
            erlang:send_after(?DELAY_POWER_GET, self(), {get_zone_power_rank});
        A when is_integer(A) ->
            skip;
        _ -> skip
    end,
    {noreply, State};

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

terminate(_Reason, _State) ->
    ok.

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

%%%-------------------------------------------------------------------
%%% Internal Functions
%%%-------------------------------------------------------------------

change_attrs_by_cfg(Attrs, []) -> Attrs;
change_attrs_by_cfg(Attrs, [{AttrId, Val}|Rest]) ->
    NewAttrs =
    case lists:keyfind(AttrId, 1, Attrs) of
        {_, BaseVal} ->
            lists:keystore(AttrId, 1, Attrs, {AttrId, round(BaseVal*Val/100)});
        _ ->
            Attrs
    end,
    change_attrs_by_cfg(NewAttrs, Rest).

% pack_wave_score_rank(RankL) ->
%     lists:foldl(fun(Role, Acc) -> 
%         #god_dun_wave_score_role{rank = Rank, role_id = RoleId, score = Score, show = #wave_key_figure{name = RoleName, profile_photo_id = PhotoId}} = Role,
%         [{RoleId, Rank, Score, RoleName, PhotoId}|Acc]
%     end, [], RankL).


calc_role_percent_rank([], _Rank) -> {0, 0};
calc_role_percent_rank([{{RankMinPer, RankMaxPer}, RankMin, RankMax}|Rest], Rank) ->
    if
        Rank >= RankMin andalso Rank =< RankMax ->
            {RankMinPer, RankMaxPer};
        true ->
            calc_role_percent_rank(Rest, Rank)
    end.

is_local_svr(RoleId) ->
    ServerId = mod_player_create:get_serid_by_id(RoleId),
    case config:get_merge_server_ids() of
        [] ->
            false;
        MergeSerIds ->
            lists:member(ServerId, MergeSerIds)
    end.

check_god_mon_info_update_do(RoleId, SvrId) ->
    Now = utime:unixtime(),
    case ets:lookup(?ETS_GOD_DUN_CLS_ROLE, RoleId) of
        #god_dun_cls_role{time = UpdateTime} ->
            if Now >= UpdateTime ->
                    clusters_node:apply_cast(?MODULE, god_mon_info_update, [SvrId, RoleId, node()]);
                true ->
                    ok
            end;
        _ ->
            clusters_node:apply_cast(?MODULE, god_mon_info_update, [SvrId, RoleId, node()])
    end,
    ok.

check_god_mon_info_reset(NewPowerRank) ->
    spawn(fun() -> 
        OldClsRole = ets:tab2list(?ETS_GOD_DUN_CLS_ROLE),
        lists:map(fun(#god_dun_cls_role{role_id = RoleId}) -> 
            case lists:keymember(RoleId, #god_dun_power_role.role_id, NewPowerRank) of
                false ->
                    ets:delete(?ETS_GOD_DUN_CLS_ROLE, RoleId);
                _ -> skip
            end
        end, OldClsRole)
    end),
    ok.

god_mon_info_update(SvrId, RoleId, FromNode) ->
    clusters_center:apply_cast(SvrId, ?MODULE, god_mon_info_update_in_source, [RoleId, FromNode]).

god_mon_info_update_in_source(RoleId, FromNode) ->
    Figure = lib_role:get_role_figure(RoleId),
    SvrNum = config:get_server_num(),
    clusters_node:apply_cast(?MODULE, god_mon_info_back_center, [FromNode, Figure, node(), SvrNum]).

god_mon_info_back_center(FromNode, RoleFigure, RoleNode, SvrNum) ->
    clusters_center:apply_cast(FromNode, ?MODULE, god_mon_info_back_req_node, [RoleFigure, RoleNode, SvrNum]).

god_mon_info_back_req_node(RoleFigure, RoleNode, SvrNum) ->
    #figure{id = RoleId} = RoleFigure,
    GodDunClsRole = #god_dun_cls_role{
        role_id = RoleId,
        node = RoleNode,
        svr_num = SvrNum,
        figure = RoleFigure,
        time = utime:unixtime() + ?CD_TIME
    },
    ets:insert(?ETS_GOD_DUN_CLS_ROLE, GodDunClsRole),
    ok.

get_other_node_role(RoleId) ->
    case ets:lookup(?ETS_GOD_DUN_CLS_ROLE, RoleId) of
        [#god_dun_cls_role{figure = RoleFigure, svr_num = SvrNum}] ->
            {RoleFigure, SvrNum};
        _ ->
            {#figure{id = RoleId}, 0}
    end.