% %%-----------------------------------------------------------------------------
% %% module  :       mod_void_fam_local
% %% @author  :       Czc
% %% email   :       389853407@qq.com
% %% Created :       2017-11-14
% %% description:    无尽领域(本服)
% %%-----------------------------------------------------------------------------
-module(mod_void_fam_local).

-include("void_fam.hrl").
-include("errcode.hrl").
-include("common.hrl").
-include("goods.hrl").
-include("def_module.hrl").
-include("predefine.hrl").
-include("czone.hrl").

-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-compile([export_all]).

%% 定时器启动
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

is_act_open() ->
    case catch gen_server:call(?MODULE, {'is_act_open'}) of
        IsOpen when is_boolean(IsOpen) ->
            IsOpen;
        _Err ->
            ?ERR("get_act_status err:~p", [_Err]),
            false
    end.

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

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

send_rank_info(RoleID)->
    gen_server:cast(?MODULE, {'send_rank_info',RoleID}).

act_start(ActId) ->
    gen_server:cast(?MODULE, {'act_start', ActId}).

kf_act_start(Stime, Etime) ->
    gen_server:cast(?MODULE, {'kf_act_start', Stime, Etime}).

kf_act_end() ->
    gen_server:cast(?MODULE, {'kf_act_end'}).

enter_scene(RoleId,Name,Lv,HpLim,Vip,Sex,Figure) ->
    gen_server:cast(?MODULE, {'enter_scene', RoleId, Name,Lv,HpLim,Vip,Sex,Figure}).

exit_scene(RoleId,Lv,HpLim) ->
    gen_server:cast(?MODULE, {'exit_scene', RoleId, Lv,HpLim}).

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

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

update_role_scene(RoleID,Floor,Scene)->
    gen_server:cast(?MODULE, {'update_role_scene', RoleID,Floor,Scene}).

% update_role_hp(RoleId, Scene, Hp) ->
%     gen_server:cast(?MODULE, {'update_role_hp', RoleId, Scene, Hp}).

kill_player(AttackerId, AttackerName, DefenderId, DefenderName, BattleSceneId) ->
    gen_server:cast(?MODULE, {'kill_player', AttackerId, AttackerName, DefenderId, DefenderName, BattleSceneId}).

kill_mon(AttackerId, AttackerName, MonId, BattleSceneId) ->
    gen_server:cast(?MODULE, {'kill_mon', AttackerId, AttackerName, MonId, BattleSceneId}).

gm_start(Duration) ->
    gen_server:cast(?MODULE, {'gm_start', Duration}).

brocast_drop()->
    gen_server:cast(?MODULE, {'brocast_drop'}).

init([]) ->
    State = lib_void_fam:init_act(),
    Ranks = lib_void_fam:load_data(),
    lib_void_fam:save_role_rank(Ranks),
    {ok, State}.

handle_cast(Msg, State) ->
    case catch do_handle_cast(Msg, State) of
        {ok, NewState} when is_record(NewState, status_void_fam)->
            {noreply, NewState};
        Err ->
            ?ERR("handle_cast Msg:~p error:~p~n", [Msg, Err]),
            {noreply, State}
    end.

%% 活动信息
do_handle_cast({'send_act_info', RoleId}, State) ->
    #status_void_fam{status = Status, cls_type = ClsType, etime = Etime} = State,
    NowTime = utime:unixtime(),
    case Status == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            case ClsType of
                ?ACT_TYPE_BF ->
                    IsPass = 0,
                    {ok, BinData} = pt_600:write(60001, [Status, Etime, IsPass]),
                    lib_server_send:send_to_uid(RoleId, BinData);
                ?ACT_TYPE_KF ->
                    Node = clusters_node:get_node(),
                    clusters_node:apply_cast(mod_void_fam, send_act_info, [Node, RoleId]);
                _ -> skip
            end;
        false ->
            {ok, BinData} = pt_600:write(60001, [Status, Etime, 0]),
            lib_server_send:send_to_uid(RoleId, BinData)
    end,
    {ok, State};

%%战报
do_handle_cast({'send_rank_info',RoleID},State)->
    #status_void_fam{cls_type = ClsType} = State,
    case ClsType of
        ?ACT_TYPE_BF ->
            Bin = lib_void_fam:send_rank_info(RoleID),
            lib_server_send:send_to_uid(RoleID,Bin);
        ?ACT_TYPE_KF ->
            Node = clusters_node:get_node(),
            SerId = config:get_server_id(),
            clusters_node:apply_cast(mod_void_fam, send_rank_info, [Node,SerId,RoleID]);
        _ ->
            skip
    end,
    {ok, State};

%% 跨服中心同步数据到本服
do_handle_cast({'sync_data', Data}, State) ->
    ActClsType = lib_void_fam:get_act_cls_type(),
    case ActClsType of
        ?ACT_TYPE_KF ->
            NewState = lib_void_fam:sync_data(Data, State),
            {ok, NewState};
        _ ->
            {ok, State}
    end;

%% 开启活动
do_handle_cast({'act_start', ActId}, State) ->
    #status_void_fam{ref = Ref, add_ref =ARef,drop_ref=[_,DRef,_], status = Status, etime = Etime} = State,
    NowTime = utime:unixtime(),
    case Status =/= ?ACT_STATUS_OPEN orelse NowTime > Etime of
        true ->
            ActClsType = lib_void_fam:get_act_cls_type(),
            case ActClsType of
                ?ACT_TYPE_KF -> %% 跨服的活动通知跨服中心开启
                    % clusters_node:apply_cast(mod_void_fam, act_start, [ActId]),
                    {ok, State};
                _ ->
                    util:cancel_timer(Ref),
                    util:cancel_timer(ARef),
                    util:cancel_timer(DRef),
                    db:execute(<<"TRUNCATE TABLE void_fam_rank">>),
                    NewState = lib_void_fam:act_start(ActId),
                    {ok, NewState}
            end;
        _ -> {ok, State}
    end;

%% 进入场景
do_handle_cast({'enter_scene', RoleId,Name,Lv,HpLim,Vip,Sex,Figure}, State) ->
    #status_void_fam{cls_type = ClsType, status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    SerNum = config:get_server_num(),
    case czone_api:env_get_zone() of
        #czone_svr_local{stage=Stage}->
            skip;
        _->
            Stage = 0
    end,
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            case ClsType of
                ?ACT_TYPE_BF ->
                    RoleMap = lib_void_fam:get_role_map(),
                    DefRoleInfo = #role_info{
                        role_id = RoleId,
                        stage=Stage,
                        name=Name,
                        ser_num=SerNum,
                        level=Lv,
                        floor = ?MIN_FLOOR,
                        hp = HpLim,
                        vip = Vip,
                        sex = Sex,
                        figure = Figure
                    },
                    #role_info{
                        floor = Floor,
                        score = Score,
                        kills = Kills,
                        combo = Combo,
                        awarded = HasF,
                        hp = _Hp,
                        time = Time
                    } = RoleInfo = maps:get(RoleId, RoleMap, DefRoleInfo),
                    case data_void_fam:get_floor_cfg(Floor) of
                        #void_fam_floor_cfg{scene = Scene,reward = FloorReward} ->
                            NewRoleInfo = RoleInfo#role_info{scene = Scene,time=NowTime},
                            NewRoleMap = maps:put(RoleId, NewRoleInfo, RoleMap),
                            lib_void_fam:save_role_map(NewRoleMap),
                            %% 增加活跃度
                            lib_activitycalen_api:role_success_end_activity(RoleId, ?MOD_VOID_FAM, 0),
                            %% 嗨点,首次进入才计算
                            maps:get(RoleId, RoleMap, []) == [] andalso begin
                                mod_hi_point:success_end(RoleId, ?MOD_VOID_FAM, 1),
                                lib_custom_act_slices:task_event(RoleId, ?MOD_VOID_FAM, 1),
                                custom_act_adventure:do_trigger_adventure_task(RoleId, ?MOD_VOID_FAM, 1)
                            end,
                            %lib_void_fam:trigger_sp_task(RoleId, ?MOD_VOID_FAM, 1),
                            % 日志
                            lib_void_fam:add_log(RoleId, Name, 1, Floor, Score),
                            %% 切换场景
                            KeyValueList = [
                                {group, 0},
                                {hp, HpLim},
                                {pk, {?PK_ALL, true}},
                                {action_lock, ?ERRCODE(err600_cant_change_scene_in_void_fam)}
                                ],
                            {X, Y} = lib_void_fam:get_scene_born_xy(Scene),
                            Radius = 50,
                            {RandX, RandY} = umath:rand_point_in_circle(X, Y, Radius, 1),

                            lib_void_fam:get_floor_award(RoleId,Time,FloorReward),

                            Grade = lists:max([0|HasF]),
                            {ok, BinData} = pt_600:write(60005, [Score,Kills,Combo,Grade]),
                            lib_server_send:send_to_uid(RoleId, BinData),

                            lib_scene:change_player_scene(RoleId, Scene, 0, 0, RandX, RandY, true, KeyValueList),
                            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, lib_activity_reserve, enter_act, [?MOD_VOID_FAM]);
                        _ ->
                            skip
                    end;
                ?ACT_TYPE_KF ->
                    Node = clusters_node:get_node(),
                    SerId = config:get_server_id(),
                    clusters_node:apply_cast(mod_void_fam, enter_scene, [Node, SerId,SerNum,Stage,RoleId,Name,Lv,HpLim,Vip,Sex,Figure]);
                _ -> skip
            end;
        false ->
            {ok, BinData} = pt_600:write(60000, [?ERRCODE(err600_act_close)]),
            lib_server_send:send_to_uid(RoleId, BinData)
    end,
    {ok, State};

do_handle_cast({'update_role_scene', RoleID,Floor,Scene},State)->
    #status_void_fam{cls_type = ClsType, status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            case ClsType of
                ?ACT_TYPE_BF ->
                    RoleMap = lib_void_fam:get_role_map(),
                    DefRoleInfo = #role_info{role_id = RoleID, floor = ?MIN_FLOOR},
                    RoleInfo = maps:get(RoleID, RoleMap, DefRoleInfo),
                    NewRoleInfo = RoleInfo#role_info{scene = Scene,kills=0,floor=Floor},
                    NewRoleMap = maps:put(RoleID, NewRoleInfo, RoleMap),
                    lib_void_fam:save_role_map(NewRoleMap);
                ?ACT_TYPE_KF ->
                    clusters_node:apply_cast(mod_void_fam, update_role_scene, [RoleID,Floor,Scene]);
                _ ->
                    ignore
            end;
        false ->
            ignore
    end,
    {ok, State};

%% 退出场景
do_handle_cast({'exit_scene', RoleId, Lv, HpLim}, State) ->
    #status_void_fam{cls_type = ClsType} = State,
    case ClsType of
        ?ACT_TYPE_BF ->
            RoleMap = lib_void_fam:get_role_map(),
            NewRoleMap = case maps:get(RoleId, RoleMap, false) of
                #role_info{
                    scene = Scene,
                    name = RName,
                    ref = Ref,
                    floor = Floor,
                    mfloor = MaxFloor,
                    die_num = DieNum,
                    score = Score,
                    time = Time
                } = RoleInfo when Scene > 0 ->
                    util:cancel_timer(Ref),
                    %% 日志
                    QuitType = 2,
                    lib_void_fam:add_log(RoleId, RName, QuitType, Floor, Score),
                    StayTime = utime:unixtime() - Time,
                    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE, lib_void_fam, ta_void_fam, [MaxFloor, Score, DieNum, StayTime]),
                    maps:put(RoleId, RoleInfo#role_info{scene = 0,combo=0,floor=1,level=Lv,kills=0, ref = []}, RoleMap);
                _ -> RoleMap
            end,
            lib_void_fam:save_role_map(NewRoleMap),
            lib_scene:change_player_scene(RoleId, 0, 0, 0, [{change_scene_hp_lim, 1}, {action_free, ?ERRCODE(err600_cant_change_scene_in_void_fam)}]);
        ?ACT_TYPE_KF ->
            clusters_node:apply_cast(mod_void_fam, exit_scene, [node(),RoleId,Lv,HpLim]);
        _ ->
            skip
    end,
    {ok, State};

%% 楼层信息
do_handle_cast({'send_floor_info', RoleId}, State) ->
    #status_void_fam{cls_type = ClsType, status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            case ClsType of
                ?ACT_TYPE_BF ->
                    RoleMap = lib_void_fam:get_role_map(),
                    case maps:get(RoleId, RoleMap, false) of
                        #role_info{floor = Floor,mfloor=MFloor, kills=Kills,awarded=Awarded,score = Score} ->
                            Grade = lists:max([0|Awarded]),
                            {ok, BinData} = pt_600:write(60004, [Etime, Floor,MFloor, Score, Kills, Grade]),
                            lib_server_send:send_to_uid(RoleId, BinData);
                        _ -> skip
                    end;
                ?ACT_TYPE_KF ->
                    clusters_node:apply_cast(mod_void_fam, send_floor_info, [RoleId]);
                _ -> skip
            end;
        false -> skip
    end,
    {ok, State};


%% 掉落信息
do_handle_cast({'send_drop_info', RoleId}, State) ->
    #status_void_fam{
        cls_type = ClsType,
        status = ActStatus,
        etime = Etime,
        drop_ref=[_Inter,_,DropT]
        %click = Click
    } = State,
    NowTime = utime:unixtime(),
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            case ClsType of
                ?ACT_TYPE_BF ->
                    {ok,Bin} = pt_600:write(60011,[0,max(DropT-NowTime,0)]),
                    lib_server_send:send_to_uid(RoleId,Bin);
                ?ACT_TYPE_KF ->
                    clusters_node:apply_cast(mod_void_fam, send_drop_info, [node(),RoleId]);
                _ -> skip
            end;
        false -> skip
    end,
    {ok, State};

% %% 更新玩家血量
% do_handle_cast({'update_role_hp', RoleId, Scene, Hp}, State) ->
%     lib_void_fam:update_role_hp(RoleId, Scene, Hp),
%     {ok, State};

%% 击杀玩家
do_handle_cast({'kill_player', AttackerId, AttackerName, DefenderId, DefenderName, BattleSceneId}, State) ->
    #status_void_fam{cls_type = ClsType, status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            case ClsType of
                ?ACT_TYPE_BF ->
                    %% 如果攻击者不在场景不做处理
                    lib_void_fam:kill_player(AttackerId, AttackerName, DefenderId, DefenderName, BattleSceneId);
                _ ->
                    clusters_node:apply_cast(mod_void_fam, kill_player, [AttackerId, AttackerName, DefenderId, DefenderName, BattleSceneId])
            end;
        false -> skip
    end,
    {ok, State};

%% 击杀怪物
do_handle_cast({'kill_mon', AttackerId, AttackerName, MonId, BattleSceneId}, State) ->
    #status_void_fam{cls_type = ClsType, status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            case ClsType of
                ?ACT_TYPE_BF ->
                    %% 如果攻击者不在场景不做处理
                    lib_void_fam:kill_mon(AttackerId, AttackerName, MonId, BattleSceneId);
                _ -> skip
            end;
        false -> skip
    end,
    {ok, State};

%% 秘籍开启活动
do_handle_cast({'gm_start',Duration},State) when Duration=<0->
    #status_void_fam{ref = Ref,add_ref=ARef,status = Status,drop_ref=[_,DRef,_]} = State,
    case Status == ?ACT_STATUS_OPEN of
        true->
            case lib_void_fam:get_act_cls_type() of
                ?ACT_TYPE_KF -> %% 跨服的活动通知跨服中心开启
                    clusters_node:apply_cast(mod_void_fam, gm_start, [Duration]),
                    {ok, State};
                _->
                    util:cancel_timer(Ref),
                    util:cancel_timer(ARef),
                    util:cancel_timer(DRef),
                    NewRef = erlang:send_after(1000, self(), {'act_end'}),
                    NewState = State#status_void_fam{
                        ref = NewRef
                    },
                    VoidFamPid = get(check_hosting),
                    misc:is_process_alive(VoidFamPid) andalso erlang:exit(VoidFamPid, kill),
                    {ok, NewState}
            end;
        _->
            {ok,State}
    end;
do_handle_cast({'gm_start', Duration}, State) ->
    #status_void_fam{ref = Ref, status = Status, etime = Etime,drop_ref=[_,DRef,_]} = State,
    NowTime = utime:unixtime(),
    VoidFamPid = get(check_hosting),
    misc:is_process_alive(VoidFamPid) andalso erlang:exit(VoidFamPid, kill),
    case Status =/= ?ACT_STATUS_OPEN orelse NowTime > Etime of
        true ->
            ActClsType = lib_void_fam:get_act_cls_type(),
            case ActClsType of
                ?ACT_TYPE_KF -> %% 跨服的活动通知跨服中心开启
                    clusters_node:apply_cast(mod_void_fam, gm_start, [Duration]),
                    {ok, State};
                _ ->
                    util:cancel_timer(Ref),
                    util:cancel_timer(DRef),
                    NewState = lib_void_fam:act_start_by_time(NowTime + Duration),
                    {ok, NewState}
            end;
        _ -> {ok, State}
    end;

do_handle_cast({'brocast_drop'},State)->
    #status_void_fam{status = Status} = State,
    ActClsType = lib_void_fam:get_act_cls_type(),
    case Status =:= ?ACT_STATUS_OPEN of
        true when ActClsType == ?ACT_TYPE_KF ->
            lib_chat:send_TV({all}, ?MOD_VOID_FAM, 13, []);
        _->
            skip
    end,
    {ok,State};

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

handle_info(Info, State) ->
    case catch do_handle_info(Info, State) of
        {ok, NewState} when is_record(NewState, status_void_fam)->
            {noreply, NewState};
        Err ->
            ?ERR("handle_info Info:~p error:~p~n", [Info, Err]),
            {noreply, State}
    end.

%% 通关退出场景
do_handle_info({'pass_exit_scene', RoleId}, State) ->
    RoleMap = lib_void_fam:get_role_map(),
    case maps:get(RoleId, RoleMap, false) of
        #role_info{
            scene = Scene,
            name = RName,
            ref = Ref,
            floor = Floor,
            score = Score
        } = RoleInfo when Scene > 0 ->
            util:cancel_timer(Ref),

            %% 日志
            lib_void_fam:add_log(RoleId, RName, 3, Floor, Score),

            NewRoleMap = maps:put(RoleId, RoleInfo#role_info{scene = 0, ref = []}, RoleMap),
            lib_void_fam:save_role_map(NewRoleMap),
            lib_scene:change_player_scene(RoleId, 0, 0, 0, [{change_scene_hp_lim, 1}, {action_free, ?ERRCODE(err600_cant_change_scene_in_void_fam)}]);
        _ -> skip
    end,
    {ok, State};

%% 活动结束
do_handle_info({'act_end'}, State) ->
    #status_void_fam{cls_type = ClsType} = State,
    case ClsType of
        ?ACT_TYPE_BF ->
            lib_void_fam:batch_save_rank(?ACT_TYPE_BF),
            NewState = lib_void_fam:act_close(State);
        _ ->
            NewState = State
    end,
    {ok, NewState};

% do_handle_info({'act_real_close'}, State) ->
%     #status_void_fam{cls_type = ClsType} = State,
%     case ClsType of
%         ?ACT_TYPE_BF ->
%             NewState = lib_void_fam:act_real_close(State);
%         _ ->
%             NewState = State
%     end,
%     {ok, NewState};

%%挂机积分
do_handle_info({'interval_add'},State)->
    #status_void_fam{cls_type = ClsType,status = Status} = State,
    case ClsType of
        ?ACT_TYPE_BF when Status =:= ?ACT_STATUS_OPEN->
            IntAdd = data_void_fam:get_cfg(interval_add),
            ARef = erlang:send_after(IntAdd * 1000, self(), {'interval_add'}),
            lib_void_fam:add_interval_score(),
            NewState = State#status_void_fam{add_ref=ARef};
        _ -> NewState = State
    end,
    {ok, NewState};

%%第九层生成掉落
do_handle_info({'interval_drop'},State)->
    #status_void_fam{cls_type = ClsType,status = Status,drop_ref=[Inter,_,_]} = State,
    case ClsType of
        ?ACT_TYPE_BF when Status =:= ?ACT_STATUS_OPEN->
            Intervals = data_void_fam:get_cfg(interval_drop),
            NewInter  = Inter+1,
            case lists:keyfind(NewInter,1,Intervals) of
                {_,InterDrop}->
                    ignore;
                _->
                    InterDrop = 0
            end,
            #void_fam_floor_cfg{scene = Scene}=data_void_fam:get_floor_cfg(9),
            case InterDrop>0 of
                true->
                    Click = lib_void_fam:calc_click(),
                    {ok,Bin} = pt_600:write(60011,[2,InterDrop]),
                    lib_void_fam:create_drops(Scene,Bin),
                    lib_chat:send_TV({all}, ?MOD_VOID_FAM, 13, []),
                    DRef = erlang:send_after(InterDrop * 1000, self(), {'interval_drop'});
                _->
                    case NewInter>=length(Intervals) of
                        true->
                            lib_void_fam:create_drops(Scene,false),
                            Click = lib_void_fam:calc_click();
                        _->
                            Click = 0
                    end,
                    DRef = 0
            end,
            NewState = State#status_void_fam{drop_ref=[NewInter,DRef,utime:unixtime()+InterDrop],click=Click};
        _ ->
            NewState = State
    end,
    {ok, NewState};

do_handle_info({handle_event_collected,CollectorId,Mid,_SceneID,Pool},State)->
    NState = lib_void_fam:drops_reduce_event(Pool,State),
    lib_void_fam:brocast_success_collect(CollectorId,Mid),
    {ok,NState};

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

handle_call(Request, _From, State) ->
    case catch do_handle_call(Request, State) of
        {ok, Reply} ->
            {reply, Reply, State};
        Err ->
            ?ERR("handle_call Request:~p error:~p~n", [Request, Err]),
            {noreply, State}
    end.

do_handle_call({'is_act_open'}, State) ->
    #status_void_fam{status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    OpenStatus = ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime,
    {ok, OpenStatus};

do_handle_call(_Request, _State) -> {ok, ok}.

terminate(_Reason, _State) ->
    ok.

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