%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     世界BOSS管理进程
%%% @end
%%%-------------------------------------------------------------------
-module(world_boss_mgr).
-author("liushl").

-behaviour(gen_server).

-include("local_boss.hrl").
-include("scene.hrl").
-include("battle.hrl").
-include("attr.hrl").
-include("server.hrl").
-include("common.hrl").
-include("predefine.hrl").
-include("def_module.hrl").
-include("figure.hrl").
-include("goods.hrl").
-include("def_goods.hrl").
-include("red_envelopes.hrl").
-include("guild.hrl").
-include("def_event.hrl").
-include("def_act_label.hrl").
-include("language.hrl").
-include("custom_act.hrl").

%% API
-export([start_link/0]).

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

-export([
    get_world_boss_act_state/1,         %% 世界BOSS-活动状态
    get_world_boss_info/1,              %% 世界BOSS-列表信息
    get_world_boss_hp/2,                %% 世界BOSS-血量
    get_world_boss_week_rank/3,         %% 世界BOSS-周榜
    enter_scene/2,                      %% 世界BOSS-进入BOSS场景
    get_shield_award/3,                 %% 世界BOSS-领取护盾奖励
    increase_world_boss_score/3,        %% 世界BOSS-增加世界BOSS积分
    week_clear_zero/0,                  %% 世界BOSS-周一0点结算周榜
    refresh_power/2,                    %% 战力变更
    boss_week_settlement/2,

    gm_reborn/0,
    gm_monday/0
]).

%% 怪物逻辑
-export([
    when_boss_born/3,                   %% 出生监听器
    when_boss_be_hurt/3,                %% BOSS血量变化监听器
    check_shield_buff/3,                %% 护盾监听器

    when_boss_die/5,                    %% 死亡监听器了
    enter_boss_scene/2,                 %% 进入BOSS场景
    send_world_boss_hp/2,               %% 给单个玩家发送BOSS血量
    role_get_boss_last_hit/2,           %% 玩家进程结算世界BOSS最后一击
    role_get_boss_settlement/5          %% 世界BOSS在线结算：玩家进程
]).
-define(SERVER, ?MODULE).


%%%===================================================================
%%% API
%%%===================================================================
%% 世界BOSS-活动状态
get_world_boss_act_state(RoleSid) ->
    gen_server:cast(?MODULE, {get_world_boss_act_state, RoleSid}).

%% 世界BOSS-列表信息
get_world_boss_info(RoleSid) ->
    gen_server:cast(?MODULE, {get_world_boss_info, RoleSid}).

%% 世界BOSS-血量
get_world_boss_hp(RoleSid, BossId) ->
    gen_server:cast(?MODULE, {get_world_boss_hp, RoleSid, BossId}).

%% 世界BOSS-周榜
get_world_boss_week_rank(RoleSid, BossId, MyScore) ->
    gen_server:cast(?MODULE, {get_world_boss_week_rank, RoleSid, BossId, MyScore}).

%% 世界BOSS-进入BOSS场景
enter_scene(RoleId, BossId) ->
    gen_server:cast(?MODULE, {enter_scene, RoleId, BossId}).

%% 世界BOSS-领取护盾奖励
get_shield_award(RoleId, BossId, No) ->
    gen_server:cast(?MODULE, {get_shield_award, BossId, No, RoleId}).

%% 世界BOSS-增加世界BOSS积分
increase_world_boss_score(RoleId, RankType, Score) ->
    gen_server:cast(?MODULE, {increase_world_boss_score, RoleId, RankType, Score}).

%% 世界BOSS-周一0点结算周榜
week_clear_zero() ->
    gen_server:cast(?MODULE, week_clear_zero).

%% 战力变更
refresh_power(RoleId, Power) ->
    gen_server:cast(?MODULE, {refresh_power, RoleId, Power}).
%%%===================================================================
%% @doc 怪物逻辑
%% 出生监听器:管理进程记录BOSS进程PID
%% {?MODULE, when_boss_born, []}
when_boss_born(init, #scene_object{id = MonId, battle_attr = #battle_attr{hp_lim = HpLim}, config_id = BossId, scene = SceneId, scene_pool_id = PoolId}, _Args) ->
    gen_server:cast(?MODULE, {when_boss_born, BossId, self(), MonId, HpLim}),
    {ok, Bin} = pt_460:write(46007, [BossId, HpLim, []]),
    lib_server_send:send_to_scene(SceneId, PoolId, Bin),
    %% 出生时，需要清除场景内玩家的第一标识
    ok.

%% BOSS血量变化监听器 需要更新头顶输出第一的标识
%% {?MODULE, when_boss_be_hurt, 0}
when_boss_be_hurt(#ob_act{object = Boss, klist = [Atter|_]}, #mon_atter{hurt = OneHurt}, OldTop) when OneHurt > 0 ->
    #scene_object{config_id = BossId, scene = SceneId, scene_pool_id = PoolId, battle_attr = #battle_attr{hp = Hp}} = Boss,
    #mon_atter{hurt = Hurt, id = RoleId, name = Name} = Atter,
    NewTop = case OldTop of
        {OldGuy, _} when OldGuy =:= RoleId ->
            NewGuy = OldGuy,
            {RoleId, Hurt};
        {OldGuy, OldHurt} when Hurt > OldHurt ->
            NewGuy = RoleId,
            {RoleId, Hurt};
        {OldGuy, OldHurt} when Hurt < OldHurt ->
            NewGuy = OldGuy,
            OldTop;
        _ ->
            OldGuy = 0,
            NewGuy = RoleId,
            {RoleId, Hurt}
    end,
    case NewGuy =/= OldGuy of
        true  ->
            OldGuy > 0 andalso
                local_boss_api:update_act_label(OldGuy, SceneId, PoolId, ?ACT_LABEL_MAX_HURT, 0),
            local_boss_api:update_act_label(NewGuy, SceneId, PoolId, ?ACT_LABEL_MAX_HURT, 1);
        false ->
            ok
    end,
    {ok, Bin} = pt_460:write(46008, [BossId, RoleId, 0, Name, Hurt]),
    lib_server_send:send_to_scene(SceneId, PoolId, Bin),
    gen_server:cast(?MODULE, {when_boss_be_hurt, BossId, Hp}),
    Hp =< 0 andalso local_boss_api:update_act_label(NewGuy, SceneId, PoolId, ?ACT_LABEL_MAX_HURT, 0),
    {ok, NewTop};
when_boss_be_hurt(_MonState, _MonAtter, OldTop) ->
    {ok, OldTop}.

%% 判断是否释放了护盾
check_shield_buff(AerOldBA, AerNewBA, DerBA) ->
    #battle_status{battle_attr=#battle_attr{other_buff_list = OldBuff}, boss = Boss, config_id = BossId} = AerOldBA,
    #battle_status{battle_attr=#battle_attr{other_buff_list = NewBuff}} = AerNewBA,
    case Boss =:= ?MON_WORLD_BOSS of
        true  ->
            case lib_skill_buff:buff_find_id(?SPBUFF_SHIELD_COUNT, NewBuff) of
                [#battle_buff{param_int = Int} | _] when Int > 0 ->
                    case lib_skill_buff:buff_find_id(?SPBUFF_SHIELD_COUNT, OldBuff) of
                        [_|_] ->
                            ok;
                        [] ->
                            gen_server:cast(?MODULE, {boss_add_shield, BossId, Int})
                    end;
                _ ->
                    ok
            end;
        false ->
            ok
    end,
    when_shield_count_change(DerBA).
when_shield_count_change(#battle_status{
        config_id = BossId, boss = ?MON_WORLD_BOSS,
        battle_attr = #battle_attr{skill_effect = #skill_effect{shield_count = 1}}
    }) ->
    gen_server:cast(?MODULE, {when_shield_break, BossId});
when_shield_count_change(_) -> ok.

%% 死亡监听器了:结算
%% {?MODULE, when_boss_die, []}
when_boss_die(#ob_act{object = #scene_object{config_id = BossId, scene = SceneId, scene_pool_id = PoolId, copy_id = CopyId}},
    Klist, #battle_return_atter{real_name = _Name} = Killer, _, Clock) ->
    mod_scene_agent:apply_cast(SceneId, PoolId, phantom_task_api, kill_world_boss_trigger, [CopyId, Klist]),
    %% 广播状态
    {ok, Bin} = pt_460:write(46009, [BossId, 0]),
    lib_server_send:send_to_all(Bin),
    %% 发送击杀奖励/其他功能触发，在玩家进程 local_boss_api:handle_event
    boss_die_settlement(BossId, Klist, Killer, Clock),
    gen_server:cast(?MODULE, {when_boss_die, BossId, Clock}),
    ok.

%%%===================================================================
%% @doc GM
%% 手动生成所有世界BOSS
gm_reborn() ->
    {_, BornClock} = get_last_act_clock(),
    ?MODULE ! {act_begin, BornClock}.
%% 世界BOSS周榜结算
gm_monday() ->
    week_clear_zero().
%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @end
%%--------------------------------------------------------------------
-spec(start_link() ->
    {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
-spec(init(Args :: term()) ->
    {ok, State :: #local_world_boss{}} | {ok, State :: #local_world_boss{}, timeout() | hibernate} |
    {stop, Reason :: term()} | ignore).
init([]) ->
    %% TODO 据说世界BOSS不开启了，由其他功能再触发开启
    %% erlang:send_after(?LOCAL_BOSS_DELAY_TIME, self(), init),
    {ok, #local_world_boss{}}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
-spec(handle_info(Info :: timeout() | term(), State :: #local_world_boss{}) ->
    {noreply, NewState :: #local_world_boss{}} |
    {noreply, NewState :: #local_world_boss{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #local_world_boss{}}).
handle_info(Msg, State) ->
    try
        do_handle_info(Msg, State)
    catch _:_Error ->
        io:format("do handle_info Msg:~w, Error:~w, ~p", [Msg, _Error, erlang:get_stacktrace()]),
        {noreply, State}
    end.
%% 初始化
do_handle_info(init, _State) ->
    BossStatus = [
        list_to_tuple(One) || One <- db:get_all(?SQL_GET_WORLD_BOSS_STATUS, [?MON_WORLD_BOSS])
    ],
    %% 循环检测活动开启
    RebornLocalBoss = calc_next_act_time(#local_world_boss{}),
    {When, LastClock} = get_last_act_clock(),
    BossInfo = [
        begin
            case lists:keyfind(BossId, 1, BossStatus) of
                {BossId, BornTime, DeadTime} ->
                    case DeadTime =:= 0 of
                        true  ->
                            %% 停服前BOSS没死，需要重生
                            create_boss(BossId, BornTime);
                        false ->
                            %% 停服前BOSS死了
                            case When of
                                today ->
                                    case LastClock > BornTime of
                                        true  ->
                                            %% 但是开服时已经是新的一轮活动了
                                            create_boss(BossId, BornTime);
                                        false ->
                                            ok
                                    end;
                                yesterday ->
                                    ok
                            end,
                            ok
                    end;
                _ ->
                    %% 数据库里没数据，可能是第一次启动，需要生成BOSS
                    DeadTime = 0,
                    create_boss(BossId, LastClock)
            end,
            #world_boss_info{
                boss_id = BossId
                , dead_time = DeadTime
            }
        end
        || BossId <- data_local_boss:get_all_world_boss()
    ],
    WeekRanks = maps:from_list(
        [ {RankType, {false, init_week_score_rank(RankType)}}|| RankType <- data_local_boss:get_all_world_boss_rank()]
    ),
    BossState = RebornLocalBoss#local_world_boss{boss_info = BossInfo, week_rank = WeekRanks},
    {noreply, BossState};

%% 活动时间到
do_handle_info({check_act, Id}, State0) ->
    State = calc_next_act_time(State0),
    case data_local_boss:get_world_boss_time_by_id(Id) of
        [{OpenWeek, [{BeginH, BeginM, BeginS}, {EndH, EndM, EndS}]}] ->
            Today = utime:day_of_week(),
            Now = utime:unixtime(),
            {Date, _} = utime:unixtime_to_localtime(Now),
            BeginTime = utime:unixtime({Date, {BeginH, BeginM, BeginS}}),
            EndTime = utime:unixtime({Date, {EndH, EndM, EndS}}),
            case Now >= BeginTime andalso Now =< EndTime andalso lists:member(Today, OpenWeek) of
                true  ->
                    do_handle_info({act_begin, BeginH}, State);
                false ->
                    {noreply, State}
            end;
        _ ->
            {noreply, State}
    end;

%% 开始活动
do_handle_info({act_begin, BornClock}, State) ->
    #local_world_boss{boss_info = BossInfo, end_time = EndTime} = State,
    %% 保存BOSS状态
    BossData = [
        io_lib:format(?SQL_FORMAT_WORLD_BOSS_STATUS, [BossId, ?MON_WORLD_BOSS, BornClock, 0])
        || #world_boss_info{boss_id = BossId} <- BossInfo
    ],
    JoinData = string:join(BossData, ","),
    BossData =/= [] andalso
        db:execute(io_lib:format(?SQL_SAVE_WORLD_BOSS_STATUS, [JoinData])),
    %% 广播时间
    {ok, Bin} = pt_460:write(46001, [EndTime]),
    lib_server_send:send_to_all(Bin),
    %% 发送传闻
    local_boss_api:send_tv(world_boss_reborn, []),
    %% 重生BOSS
    NewBossInfo = [
        begin
            %% 杀死所有的BOSS
            misc:is_process_alive(Pid) andalso
                % mod_mon_active:stop(Pid, 1),
                mon_mgr:stop_mon(Pid, Id, 1),
            %% 重生BOSS
            create_boss(BossId, BornClock),
            Boss#world_boss_info{shield_reward = [], dead_time = 0, boss_pid = undefined}
        end
        || #world_boss_info{boss_id = BossId, boss_pid = Pid, mon_id = Id} = Boss <- BossInfo
    ],
    {noreply, State#local_world_boss{boss_info = NewBossInfo}};

%% 护盾奖励有效期过了
do_handle_info({remove_shield_reward, BossId, No}, State) ->
    #local_world_boss{boss_info = BossInfo} = State,
    case lists:keyfind(BossId, #world_boss_info.boss_id, BossInfo) of
        #world_boss_info{shield_reward = ShieldReward} = Boss ->
            NewShieldReward = lists:keydelete(No, 1, ShieldReward),
            NewBossInfo = lists:keyreplace(BossId, #world_boss_info.boss_id, BossInfo,
                Boss#world_boss_info{shield_reward = NewShieldReward}),
            {noreply, State#local_world_boss{boss_info = NewBossInfo}};
        _ ->
            {noreply, State}
    end;

do_handle_info(_Info, State) ->
    io:format("module:~p: handle_info unexpect message:~p", [?MODULE, _Info]),
    {noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
    State :: #local_world_boss{}) ->
    {reply, Reply :: term(), NewState :: #local_world_boss{}} |
    {reply, Reply :: term(), NewState :: #local_world_boss{}, timeout() | hibernate} |
    {noreply, NewState :: #local_world_boss{}} |
    {noreply, NewState :: #local_world_boss{}, timeout() | hibernate} |
    {stop, Reason :: term(), Reply :: term(), NewState :: #local_world_boss{}} |
    {stop, Reason :: term(), NewState :: #local_world_boss{}}).
handle_call(Msg, From, State) ->
    try
        do_handle_call(Msg, From, State)
    catch _:_Error ->
        io:format("do_handle_call Msg:~w, Error:~w, ~p", [Msg, _Error, erlang:get_stacktrace()]),
        {reply, error, State}
    end.
do_handle_call(_Request, _From, State) ->
    io:format("module:~p: handle_call unexpect message:~p", [?MODULE, _Request]),
    {reply, ok, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_cast(Request :: term(), State :: #local_world_boss{}) ->
    {noreply, NewState :: #local_world_boss{}} |
    {noreply, NewState :: #local_world_boss{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #local_world_boss{}}).

handle_cast(Msg, State) ->
    try
        do_handle_cast(Msg, State)
    catch _:_Error ->
        io:format("do handle_cast Msg:~w, Error:~w, ~p", [Msg, _Error, erlang:get_stacktrace()]),
        {noreply, State}
    end.

%% 进入场景 到怪物进程去发送伤害列表
do_handle_cast({enter_scene, RoleId, BossId}, State) ->
    #local_world_boss{boss_info = BossInfo} = State,
    case lists:keyfind(BossId, #world_boss_info.boss_id, BossInfo) of
        #world_boss_info{boss_pid = BossPid, mon_id = MonId, shield_value = Int, shield_reward_no = No} ->
            {ok, Bin}  = pt_460:write(46020, [BossId, No, Int]),
            lib_server_send:send_to_uid(RoleId, Bin),
            case misc:is_process_alive(BossPid) of
                true  ->
                    % old: BossPid ! {mod, ?MODULE, enter_boss_scene, [RoleId]};
                    mon_mgr:send(BossPid, MonId, {mod, ?MODULE, enter_boss_scene, [RoleId]});
                false ->
                    {ok, Bin1} = pt_460:write(46007, [BossId, 0, []]),
                    lib_server_send:send_to_uid(RoleId, Bin1)
            end;
        _ ->
            ok
    end,
    {noreply, State};

%% boss出生：记录怪物进程
do_handle_cast({when_boss_born, BossId, BossPid, MonId, HpLim}, State) ->
    #local_world_boss{boss_info = BossInfo} = State,
    case lists:keyfind(BossId, #world_boss_info.boss_id, BossInfo) of
        #world_boss_info{} = Boss ->
            NewBossInfo = lists:keyreplace(BossId, #world_boss_info.boss_id, BossInfo,
                Boss#world_boss_info{
                    boss_pid = BossPid,
                    mon_id = MonId,
                    shield_reward_no = 0,
                    hp = HpLim,
                    hp_lim = HpLim}
            ),
            {noreply, State#local_world_boss{boss_info = NewBossInfo}};
        _ ->
            {noreply, State}
    end;

%% 受击血量变化
do_handle_cast({when_boss_be_hurt, BossId, Hp}, State) ->
    #local_world_boss{boss_info = BossInfo} = State,
    case lists:keyfind(BossId, #world_boss_info.boss_id, BossInfo) of
        #world_boss_info{} = Boss ->
            NewBossInfo = lists:keyreplace(BossId, #world_boss_info.boss_id, BossInfo,
                Boss#world_boss_info{
                    hp = Hp}
            ),
            {noreply, State#local_world_boss{boss_info = NewBossInfo}};
        _ ->
            {noreply, State}
    end;

%% 添加护盾
do_handle_cast({boss_add_shield, BossId, Int}, State) ->
    #local_world_boss{boss_info = BossInfo} = State,
    case lists:keyfind(BossId, #world_boss_info.boss_id, BossInfo) of
        #world_boss_info{shield_reward_no = No} = Boss ->
            {ok, Bin}  = pt_460:write(46020, [BossId, No, Int]),
            case data_local_boss:get_world_boss_info(BossId) of
                #base_world_boss_info{boss_scene = Scene} ->
                    lib_server_send:send_to_scene(Scene, BossId, Bin);
                _ ->
                    ok
            end,
            NewBossInfo = lists:keyreplace(BossId, #world_boss_info.boss_id, BossInfo, Boss#world_boss_info{shield_value = Int}),
            {noreply, State#local_world_boss{boss_info = NewBossInfo}};
        _ ->
            {noreply, State}
    end;

%% 护盾打破时：允许领奖
do_handle_cast({when_shield_break, BossId}, State) ->
    #local_world_boss{boss_info = BossInfo} = State,
    case lists:keyfind(BossId, #world_boss_info.boss_id, BossInfo) of
        #world_boss_info{shield_reward_no = No, shield_reward = ShieldReward} = Boss ->
            NewNo = No + 1,
            Ref = erlang:send_after(?WORLD_BOSS_SHIELD_AWARD_TIME*1000, self(), {remove_shield_reward, BossId, NewNo}),
            NewBoss = Boss#world_boss_info{
                shield_reward_no = NewNo,
                shield_reward = [{NewNo, [], Ref}|ShieldReward],
                shield_value = 0
            },
            NewBossInfo = lists:keyreplace(BossId, #world_boss_info.boss_id, BossInfo, NewBoss),
            %% 场景广播
            {ok, Bin} = pt_460:write(46005, [BossId, NewNo]),
            case data_local_boss:get_world_boss_info(BossId) of
                #base_world_boss_info{boss_scene = SceneId} ->
                    lib_server_send:send_to_scene(SceneId, BossId, Bin);
                _ ->
                    ok
            end,
            {noreply, State#local_world_boss{boss_info = NewBossInfo}};
        _ ->
            {noreply, State}
    end;

%% 领取BOSS护盾奖励
do_handle_cast({get_shield_award, BossId, No, RoleId}, State) ->
    #local_world_boss{boss_info = BossInfo} = State,
    case lists:keyfind(BossId, #world_boss_info.boss_id, BossInfo) of
        #world_boss_info{shield_reward = ShieldReward} = Boss ->
            case lists:keyfind(No, 1, ShieldReward) of
                {No, RoleList, Ref} ->
                    case lists:member(RoleId, RoleList) of
                        true  ->
                            {noreply, State};
                        false ->
                            NewShieldReward = lists:keyreplace(No, 1, ShieldReward, {No, [RoleId|RoleList], Ref}),
                            NewBossInfo = lists:keyreplace(BossId, #world_boss_info.boss_id, BossInfo,
                                Boss#world_boss_info{shield_reward = NewShieldReward}),
                            send_shield_reward(BossId, No, RoleId),
                            {ok, Bin} = pt_460:write(46006, [1]),
                            lib_server_send:send_to_uid(RoleId, Bin),
                            {noreply, State#local_world_boss{boss_info = NewBossInfo}}
                    end;
                _ ->
                    {noreply, State}
            end;
        _ ->
            {noreply, State}
    end;

%% boss死亡
do_handle_cast({when_boss_die, BossId, Clock}, State) ->
    #local_world_boss{boss_info = BossInfo} = State,
    case lists:keyfind(BossId, #world_boss_info.boss_id, BossInfo) of
        #world_boss_info{} = Boss ->
            %% 重置BOSS
            Now = utime:unixtime(),
            NewBoss = Boss#world_boss_info{
                boss_pid = undefined,
                shield_reward_no = 0,
                dead_time = Now,
                hp = 0,
                hp_lim = 0
            },
            NewBossInfo = lists:keyreplace(BossId, #world_boss_info.boss_id, BossInfo, NewBoss),
            %% 保存状态
            db:execute(?SQL_SAVE_ONE_WORLD_BOSS, [BossId, ?MON_WORLD_BOSS, Clock, Now]),
            local_boss_api:one_local_boss_die(?MON_WORLD_BOSS, BossId),
            {noreply, State#local_world_boss{boss_info = NewBossInfo}};
        _ ->
            {noreply, State}
    end;

%% 活动状态
do_handle_cast({get_world_boss_act_state, Sid}, State) ->
    #local_world_boss{end_time = EndTime} = State,
    {ok, Bin} = pt_460:write(46001, [EndTime]),
    lib_server_send:send_to_sid(Sid, Bin),
    {noreply, State};

%% 查看BOSS信息
do_handle_cast({get_world_boss_info, Sid}, State) ->
    #local_world_boss{boss_info = BossInfo, next_time = NextTime} = State,
    List =
        [
            {BossId, DeadTime, Hp, HpLim}
            || #world_boss_info{boss_id = BossId, dead_time = DeadTime, hp = Hp, hp_lim = HpLim} <- BossInfo
        ],
    {ok, Bin} = pt_460:write(46002, [List, NextTime]),
    lib_server_send:send_to_sid(Sid, Bin),
    {noreply, State};

%% 查看BOSS血量
do_handle_cast({get_world_boss_hp, Sid, BossId}, State) ->
    #local_world_boss{boss_info = BossInfo} = State,
    case lists:keyfind(BossId, #world_boss_info.boss_id, BossInfo) of
        #world_boss_info{boss_pid = BossPid, mon_id = MonId} ->
            case misc:is_process_alive(BossPid) of
                true  ->
                    % old: BossPid ! {mod, ?MODULE, send_world_boss_hp, [Sid]};
                    mon_mgr:send(BossPid, MonId, {mod, ?MODULE, send_world_boss_hp, [Sid]});
                false ->
                    {ok, Bin} = pt_460:write(46003, [BossId, 0, 0]),
                    lib_server_send:send_to_sid(Sid, Bin)
            end;
        _ ->
            ok
    end,
    {noreply, State};

%% 查看BOSS周榜
do_handle_cast({get_world_boss_week_rank, Sid, RankType, MyScore}, State) ->
    #local_world_boss{week_rank = WeekRanks} = State,
    {Flag, TypeRank} = maps:get(RankType, WeekRanks, {true, []}),
    case Flag of
        true  ->
            send_boss_week_rank(Sid, RankType, TypeRank, MyScore),
            {noreply, State};
        false ->
            SortRank = sort_week_score(TypeRank),
            Length = 50,
            NewWeekRank = lists:sublist(SortRank, Length),
            send_boss_week_rank(Sid, RankType, NewWeekRank, MyScore),
            NewWeekRanks = maps:put(RankType, {true, NewWeekRank}, WeekRanks),
            {noreply, State#local_world_boss{week_rank = NewWeekRanks}}
    end;

%% 增加周榜积分
do_handle_cast({increase_world_boss_score, RoleId, RankType, Score}, State) ->
    #local_world_boss{week_rank = WeekRanks} = State,
    {_Flag, TypeRank} = maps:get(RankType, WeekRanks, {true, []}),
    Now = utime:unixtime(),
    NewTypeRank =
        case lists:keyfind(RoleId, #boss_score_rank.role_id, TypeRank) of
            #boss_score_rank{score = OldScore} = One ->
                NewOne = One#boss_score_rank{score = OldScore + Score, time = Now},
                lists:keystore(RoleId, #boss_score_rank.role_id, TypeRank, NewOne);
            _ ->
                Power = lib_role:get_role_combat_power(RoleId),
                [#boss_score_rank{score = Score, role_id = RoleId, time = Now, power = Power}|TypeRank]
        end,
    NewBWeekRanks = maps:put(RankType, {false, NewTypeRank}, WeekRanks),
    {noreply, State#local_world_boss{week_rank = NewBWeekRanks}};

%% 玩家战力变更
do_handle_cast({refresh_power, RoleId, Power}, State) ->
    #local_world_boss{week_rank = WeekRanks} = State,
    NewWeekRanks = maps:map(
        fun(_RankType, {Flag, Ranks}) ->
            case lists:keyfind(RoleId, #boss_score_rank.role_id, Ranks) of
                #boss_score_rank{} = One ->
                    NewOne = One#boss_score_rank{power = Power},
                    NewWeekRank = lists:keystore(RoleId, #boss_score_rank.role_id, Ranks, NewOne),
                    {false, NewWeekRank};
                _ ->
                    {Flag, Ranks}
            end
        end, WeekRanks
    ),
    {noreply, State#local_world_boss{week_rank = NewWeekRanks}};

%% 周一0点结算
do_handle_cast(week_clear_zero, State) ->
    #local_world_boss{week_rank = WeekRanks} = State,
    maps:fold(
        fun(RankType, {_Flag, Ranks}, Temp) ->
            spawn(fun() ->
                util:multiserver_delay(Temp, ?MODULE, boss_week_settlement, [RankType, Ranks])
                  end),
            Temp+2
        end, 61, WeekRanks
    ),
    {noreply, State#local_world_boss{week_rank = #{}}};

do_handle_cast(_Request, State) ->
    io:format("module:~p: handle_cast unexpect message:~p", [?MODULE, _Request]),
    {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
    State :: #local_world_boss{}) ->
    term()).
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
-spec(code_change(OldVsn :: term() | {down, term()}, State :: #local_world_boss{},
    Extra :: term()) ->
    {ok, NewState :: #local_world_boss{}} | {error, Reason :: term()}).
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%================ ==================================================
%% @doc 初始化周榜
init_week_score_rank(RankType) ->
    %% 获得本周一0点的时间
    MondayTime = utime:unixtime(week),
    Data = db:get_all(?SQL_GET_WEEK_RANK, [?MOD_LOCAL_BOSS, ?WORLD_BOSS_WEEK_SCORE, RankType, MondayTime]),
    [
        #boss_score_rank{role_id = RoleId, score = Count, time = Time, power = Power}
        || [RoleId, Count, Time, Power] <- Data
    ].

%% @doc 下一场活动管理：只看时间，定时到下一时刻，到时候再判断星期是否开启
calc_next_act_time(LocalBoss) ->
    AllCfg = data_local_boss:get_all_world_boss_time(),
    Today = utime:day_of_week(),
    Now = utime:unixtime(),
    Date = erlang:date(),
    calc_next_act_time(AllCfg, AllCfg, Today, Date, Now, LocalBoss).
calc_next_act_time(AllCfg, [{Id, OpenWeek, [{BeginH, BeginM, BeginS}, {EndH, EndM, EndS}]}|T], Today, Date, Now, LocalBoss) ->
    BeginTime = utime:unixtime({Date, {BeginH, BeginM, BeginS}}),
    EndTime = utime:unixtime({Date, {EndH, EndM, EndS}}),
    case Now < BeginTime of
        true  ->
            Ref = erlang:send_after(1000*(BeginTime-Now), self(), {check_act, Id}),
            LocalBoss#local_world_boss{ref = Ref, next_time = BeginTime};
        false ->
            case Now < EndTime of
                true  ->
                    case lists:member(Today, OpenWeek) of
                        true  ->
                            %% 活动期间
                            calc_next_act_time(AllCfg, T, Today, Date, Now, LocalBoss#local_world_boss{end_time = EndTime});
                        false ->
                            %% 这个时间段今天不开，那就看下一个时间段
                            calc_next_act_time(AllCfg, T, Today, Date, Now, LocalBoss)
                    end;
                false ->
                    %% 时间过了
                    calc_next_act_time(AllCfg, T, Today, Date, Now, LocalBoss)
            end
    end;
%% 今天的所有的时间都过了，那就看明天的
calc_next_act_time([{Id, _OpenWeek, [{BeginH, BeginM, BeginS}, _]}|_], [], _Today, Date, Now, LocalBoss) ->
    TomorrowDate = utime:get_before_day(Date, 1, 1),
    BeginTime = utime:unixtime({TomorrowDate, {BeginH, BeginM, BeginS}}),
    Ref = erlang:send_after(1000*(BeginTime-Now), self(), {check_act, Id}),
    LocalBoss#local_world_boss{ref = Ref, next_time = BeginTime};
calc_next_act_time(_, _, _, _, _, LocalBoss) -> LocalBoss.

%% 获得上一个活动时刻
get_last_act_clock() ->
    AllCfg = lists:reverse(data_local_boss:get_all_world_boss_time()),
    Now = utime:unixtime(),
    Date = erlang:date(),
    Today = utime:day_of_week(),
    get_last_act_clock(AllCfg, AllCfg, Today, Date, Now).
get_last_act_clock(AllCfg, [{_, OpenWeek, [{BeginH, BeginM, BeginS}, _]}|T], Today, Date, Now) ->
    BeginTime = utime:unixtime({Date, {BeginH, BeginM, BeginS}}),
    case Now > BeginTime of
        true  ->
            case lists:member(Today, OpenWeek) of
                true  ->
                    {today, BeginH};
                false ->
                    get_last_act_clock(AllCfg, T, Today, Date, Now)
            end;
        false ->
            get_last_act_clock(AllCfg, T, Today, Date, Now)
    end;
get_last_act_clock([{_, _, [{BeginH, _BeginM, _BeginS}, _]}|_], [], _Today, _ZeroClock, _Now) ->
    %% 说明是在早上开服，不管其他，认为是最后一个时刻
    {yesterday, BeginH};
get_last_act_clock(_, _, _, _, _) -> {yesterday, 0}.

%% @doc BOSS生成
create_boss(BossId, BornClock) ->
    case data_local_boss:get_world_boss_info(BossId) of
        #base_world_boss_info{boss_scene = Scene, boss_position = {X, Y}} ->
            AddKV = [
                {born_handler, {?MODULE, when_boss_born, []}},
                {hp_change_handler, {?MODULE, when_boss_be_hurt, []}},
                {shield_count_handler, {?MODULE, when_shield_count_change, 0}},
                {die_handler, {?MODULE, when_boss_die, BornClock}}
            ],
            lib_mon:async_create_mon(BossId, Scene, BossId, X, Y, 1, 0, 1, AddKV);
        _ ->
            ok
    end.

%% @doc 发送周榜信息
send_boss_week_rank(Sid, RankType, WeekRank, MyScore) ->
    List = [
        begin
            #figure{name = Name, vip = Vip, sex = Sex} = lib_role:get_role_figure(RoleId),
            {RoleId, Name, Vip, Sex, Score}
        end
        ||#boss_score_rank{role_id = RoleId, score = Score} <- WeekRank
    ],
    {ok, Bin} = pt_460:write(46004, [RankType, MyScore, List]),
    lib_server_send:send_to_sid(Sid, Bin),
    ok.

%% @doc 发送护盾奖励
send_shield_reward(BossId, No, RoleId) ->
    case data_local_boss:get_world_boss_info(BossId) of
        #base_world_boss_info{award_30_p = AwardList} when No =:= 1 ->
            AwardList;
        #base_world_boss_info{award_60_p = AwardList} when No =:= 2  ->
            AwardList;
        _ ->
            AwardList = []
    end,
    case urand:rand_with_weight(AwardList) of
        {_, _, _} = Goods ->
            Produce = #produce{
                reward = [Goods],
                type = world_boss_shield,
                subtype = BossId,
                remark = lists:concat(["no=", No])
            },
            lib_goods_api:send_reward_by_id(Produce, RoleId);
        _ ->
            ?ERR("world_boss_shield_award_err:~p", [{BossId, No}]),
            ok
    end.

%% @doc 周榜结算
boss_week_settlement(RankType, WeekRank) ->
    SortRank = sort_week_score(WeekRank),
    RankName = data_local_boss:get_world_boss_week_rank_name(RankType),
    send_week_settlement(SortRank, RankType, 1, RankName).
send_week_settlement([#boss_score_rank{role_id = RoleId}|T], RankType, Rank, RankName) ->
    case data_local_boss:get_world_boss_week_rank(RankType, Rank) of
        [] -> ok;
        Award ->
            Title = utext:get(?LAN_TITLE_WORLD_BOSS_WEEK),
            Content = utext:get(?LAN_CONTENT_WORLD_BOSS_WEEK, [RankName, Rank]),
            lib_mail_api:send_sys_mail([RoleId], Title, Content, Award),
            send_week_settlement(T, RankType, Rank+1, RankName)
    end;
send_week_settlement([], _, _, _) -> ok.

%%%===================================================================
%% @doc 进入BOSS场景 :怪物进程
%% 1 给单个玩家发送整个伤害列表/2 刷新输出第一玩家
enter_boss_scene(MonState, RoleId) ->
    #ob_act{klist = Klist, hp_change_handler = HpChangeHandler,
        object = #scene_object{battle_attr = #battle_attr{hp_lim = HpLim}, config_id = BossId, scene = Scene, scene_pool_id = PoolId}} = MonState,
    HurtList = [
        {Id, 0, Name, Hurt}||#mon_atter{id = Id, name = Name, hurt = Hurt} <- Klist
    ],
    {ok, Bin} = pt_460:write(46007, [BossId, HpLim, HurtList]),
    lib_server_send:send_to_uid(RoleId, Bin),
    case HpChangeHandler of
        {_, _, {RoleId, _}} ->
            local_boss_api:update_act_label(RoleId, Scene, PoolId, ?ACT_LABEL_MAX_HURT, 1);
        _ ->
            ok
    end.

%% @doc 给单个玩家发送BOSS血量:怪物进程
send_world_boss_hp(MonState, RoleSid) ->
    #ob_act{object = #scene_object{battle_attr = #battle_attr{hp_lim = HpLim, hp = Hp}, config_id = BossId}} = MonState,
    {ok, Bin} = pt_460:write(46003, [BossId, Hp, HpLim]),
    lib_server_send:send_to_sid(RoleSid, Bin).

%% @doc BOSS击杀结算:怪物进程
boss_die_settlement(BossId, Klist, Killer, Clock) ->
    SortList = sort_hurt_list(Klist),
    %% 击杀奖励单独发送
    case data_local_boss:get_world_boss_info(BossId) of
        #base_world_boss_info{score_limit_day = NeedDay} = BossCfg ->
            case SortList of
                [Role|_] ->
                    %% 世界BOSS输出第一玩家处理
                    deal_with_max_hurt_role(Role, BossCfg);
                _ ->
                    ok
            end,
            %% 世界BOSS最后一击玩家处理
            deal_with_last_hit_role(Killer, BossCfg),
            %% 发送积分倍率
            case util:get_open_day() >= NeedDay of
                true  ->
                    ScoreRatio = 1;
                false ->
                    ScoreRatio = 0
            end,
            Opens = lib_custom_act_util:get_open_subtype_list(?CUSTOM_ACT_THEME_LIMIT_DROP),
            send_boss_settlement(SortList, BossId, 1, BossCfg, ScoreRatio, Clock,Opens);
        _ ->
            ok
    end.

%% 世界BOSS输出第一玩家处理 判断有没有公会
deal_with_max_hurt_role(Role, BossCfg) ->
    #mon_atter{id = RoleId, name = Name, guild_id = GuildId} = Role,
    #base_world_boss_info{boss_name = BossName, boss_id = BossId} = BossCfg,
    case GuildId > 0 of
        true  ->
            Player = #player_status{id = RoleId, guild = #status_guild{id = GuildId}},
            lib_red_envelopes:trigger_red_envelopes(Player, ?RED_ENVELOPES_TYPE_WORLD_BOSS, ?HAS_SEND, [{boss_id, BossId}]),
            local_boss_api:send_tv(world_boss_max_hurt, [Name, RoleId, BossName]);
        false ->
            ok
    end,
    ok.

%% 世界BOSS最后一击玩家处理
deal_with_last_hit_role(Killer, BossCfg) ->
    #battle_return_atter{id = KillerId, real_name = Name} = Killer,
    #base_world_boss_info{award_kill = AwardKill, boss_name = BossName, boss_id = BossId} = BossCfg,
    case urand:rand_with_weight(AwardKill) of
        {_, GoodsId, _} = Goods ->
            local_boss_api:send_tv(world_boss_last_hit, [Name, KillerId, BossName, GoodsId]),
            Produce = #produce{
                reward = [Goods],
                type = world_boss_last_hit,
                subtype = BossId
            },
            lib_player:apply_cast(KillerId, ?APPLY_CAST_STATUS, ?MODULE, role_get_boss_last_hit, [Produce]);
        _ ->
            ?ERR("world_boss_kill_award_err:~p", [BossCfg])
    end.

%% 玩家进程结算世界BOSS最后一击
role_get_boss_last_hit(Player, Produce) ->
    Player1 = lib_goods_api:send_reward(Player, Produce),
    {ok, Player2} = lib_player_event:dispatch(Player1, ?EVENT_WORLD_BOSS_LAST_HIT, Produce#produce.subtype),
    Player2.

%% 世界BOSS伤害输出所有玩家处理
send_boss_settlement([One|T], BossId, Rank, BossCfg, ScoreRatio, Clock,Opens) ->
    case data_local_boss:get_world_boss_kill_rank(BossId, Rank) of
        [RankAward, Score] ->
            AddScore = Score*ScoreRatio;
        _ ->
            RankAward = [],
            AddScore = 0
    end,
    #base_world_boss_info{award_assists = AwardAssists} = BossCfg,
    #mon_atter{id = RoleId,att_lv=Rlv} = One,
    AssistsAward =
        case urand:rand_with_weight(AwardAssists) of
            {_, _, _} = Goods ->
                [Goods];
            _ ->
                ?ERR("world_boss_assists_award_err:~p", [BossId]),
                []
        end,
    LimitDrops = drop_logic:calc_all_limit_drop(Opens,?MON_WORLD_BOSS,Rlv,[]),
    Award = RankAward ++ AssistsAward ++ LimitDrops,
    case lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, role_get_boss_settlement, [BossId, Award, AddScore, Clock]) of
        skip ->
            role_get_boss_settlement_offline(RoleId, Award, AddScore, Clock, BossCfg);
        _ ->
            ok
    end,
    send_boss_settlement(T, BossId, Rank+1, BossCfg, ScoreRatio, Clock,Opens);
send_boss_settlement([], _, _, _, _, _,_) -> ok.

%% 世界BOSS在线结算：玩家进程
role_get_boss_settlement(Player, BossId, Award, AddScore, Clock) ->
    #player_status{id = RoleId, scene = SceneId, scene_pool_id = PoolId} = Player,
    #base_world_boss_info{boss_scene = BossScene, boss_name = BossName, rank_type = RankType} = BossCfg
        = data_local_boss:get_world_boss_info(BossId),
    AddScore > 0 andalso check_can_get_score(Player, BossCfg) andalso
            add_world_boss_kill_score(RoleId, RankType, AddScore),
    Player1 = case BossScene =:= SceneId andalso PoolId =:= BossId of
        true  ->
            Produce = #produce{
                reward = Award,
                type = world_boss_kill,
                subtype = BossId,
                remark = lists:concat(["score=", AddScore])
            },
            lib_goods_api:send_reward(Player, Produce);
        false ->
            %% 不在BOSS场景，那就发邮件
            boss_settlement_mail(RoleId, Award, Clock, BossName),
            Player
    end,
    {ok, Player2} = lib_player_event:dispatch(Player1, ?EVENT_KILL_LOCAL_BOSS, [BossId, 0]),
    Player2.
%% 世界BOSS离线结算: 仍然在怪物进程
role_get_boss_settlement_offline(RoleId, Award, AddScore, Clock, BossCfg) ->
    #base_world_boss_info{boss_name = BossName, rank_type = RankType} = BossCfg,
    %% 发邮件
    boss_settlement_mail(RoleId, Award, Clock, BossName),
    AddScore > 0 andalso check_can_get_score(RoleId, BossCfg) andalso
        add_world_boss_kill_score(RoleId, RankType, AddScore),
    ok.

%% 结算邮件
boss_settlement_mail(RoleId, Award, Clock, BossName) ->
    Title = utext:get(?LAN_TITLE_WORLD_BOSS_AWARD),
    Content = utext:get(?LAN_CONTENT_WORLD_BOSS_AWARD, [Clock, BossName]),
    lib_mail_api:send_sys_mail([RoleId], Title, Content, Award).

%% 增加积分
add_world_boss_kill_score(RoleId, RankType, Score) ->
    lib_week:plus_count_offline(RoleId, ?MOD_LOCAL_BOSS, ?WORLD_BOSS_WEEK_SCORE, RankType, Score),
    increase_world_boss_score(RoleId, RankType, Score).

%% 伤害列表排序
sort_hurt_list(Klist) ->
    F = fun(#mon_atter{hurt = HurtA}, #mon_atter{hurt = HurtB}) ->
        HurtA > HurtB
        end,
    lists:sort(F, Klist).

%% 周积分排序
sort_week_score(WeekRank) ->
    F = fun
            (
                #boss_score_rank{score = ScoreA, time = TimeA, power = PowerA},
                #boss_score_rank{score = ScoreB, time = TimeB, power = PowerB}
            ) ->
                if
                    ScoreA > ScoreB -> true;
                    ScoreA < ScoreB -> false;
                    TimeA < TimeB -> true;
                    TimeA > TimeB -> true;
                    true -> PowerA > PowerB
                end
        end,
    lists:sort(F, WeekRank).

%% 能否增加积分
check_can_get_score(RoleId, BossCfg) when is_integer(RoleId) ->
    %#figure{nucleon_lv = NucleonLv} =  lib_role:get_role_figure(RoleId),
    check_nucleon(0, BossCfg);

check_can_get_score(Player, BossCfg) ->
    NucleonLv = lib_role_nucleon:get_nucleon_lv(Player),
    check_nucleon(NucleonLv, BossCfg).

check_nucleon(NucleonLv, #base_world_boss_info{score_limit = ScoreLimit}) ->
    case ScoreLimit of
        [Min, Max] ->
            NucleonLv >= Min andalso NucleonLv =< Max;
        _ ->
            true
    end.

