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

-include("void_fam.hrl").
-include("errcode.hrl").
-include("common.hrl").
-include("goods.hrl").
-include("def_module.hrl").
-include("predefine.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, [], []).

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

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

%% 本服通知跨服中心开启活动
%% 跨服中心检测是否符合开启条件
act_start(ActId) ->
    gen_server:cast(?MODULE, {'act_start', ActId}).

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

send_rank_info(Node,SerID,RoleId)->
    gen_server:cast(?MODULE,{'send_rank_info',Node,SerID,RoleId}).

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

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

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

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

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

% 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}).

init([]) ->
    State = lib_void_fam:init_act(),
    Ranks = lib_void_fam:load_kf_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', Node, RoleId}, State) ->
    #status_void_fam{status = Status, etime = Etime} = State,
    NowTime = utime:unixtime(),
    case Status == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            RoleMap = lib_void_fam:get_role_map(),
            DefRoleInfo = #role_info{role_id = RoleId, floor = ?MIN_FLOOR},
            RoleInfo = maps:get(RoleId, RoleMap, DefRoleInfo),
            IsPass = case lib_void_fam:is_pass(RoleInfo) of
                false -> 0;
                true -> 1
            end,
            {ok, BinData} = pt_600:write(60001, [Status, Etime, IsPass]),
            lib_void_fam:send_to_uid(Node, RoleId, BinData);
        false ->
            {ok, BinData} = pt_600:write(60001, [Status, Etime, 0]),
            lib_void_fam:send_to_uid(Node, RoleId, BinData)
    end,
    {ok, State};

%%榜单信息
do_handle_cast({'send_rank_info',Node,SerID,RoleId},State)->
    Bin = lib_void_fam:send_rank_info(RoleId,SerID),
    lib_void_fam:send_to_uid(Node,RoleId,Bin),
    {ok,State};

do_handle_cast({'sync_act_status', Node}, State) ->
    NewState = lib_void_fam:request_sync_data([{?SYNC_TYPE_ACT_STATUS, Node}], State),
    {ok, NewState};

%% 请求同步数据到本服
do_handle_cast({'request_sync_data', Args}, State) ->
    NewState = lib_void_fam:request_sync_data(Args, State),
    {ok, NewState};

%% 开始活动
do_handle_cast({'act_start', ActId}, State) ->
    #status_void_fam{ref = Ref,add_ref =ARef, status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    db:execute(<<"TRUNCATE TABLE void_fam_kf_rank">>),
    case ActStatus =/= ?ACT_STATUS_OPEN orelse NowTime > Etime of
        true ->
            util:cancel_timer(Ref),
            util:cancel_timer(ARef),
            NewState = lib_void_fam:act_start(ActId);
        false -> %% 已经开启不用重复开启
            NewState = State
    end,
    {ok, NewState};

%% 进入场景
do_handle_cast({'enter_scene', Node, SerId,SerNum, Stage, RoleId,Name, Lv,HpLim,Vip,Sex,Figure}, State) ->
    #status_void_fam{status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    PoolID = cross_zone:get_server_zone(czone,SerId),
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            RoleMap = lib_void_fam:get_role_map(),
            DefRoleInfo = #role_info{
                node = Node, ser_id = SerId,
                ser_num=SerNum,level=Lv,stage=Stage,
                zone=PoolID,role_id = RoleId,
                name=Name, floor = ?MIN_FLOOR,
                hp = HpLim,vip= Vip,sex = Sex,figure=Figure
            },
            #role_info{
                node = Node,
                name = RName,
                floor = Floor,
                score = Score,
                kills = Kills,
                combo = Combo,
                awarded = HasF,
                time = Time,
                hp = _Hp
            } = RoleInfo = maps:get(RoleId, RoleMap, DefRoleInfo),
            case data_void_fam:get_floor_cfg(Floor) of
                #void_fam_floor_cfg{kf_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),
                    %% 日志
                    clusters_center:apply_cast(Node, lib_void_fam, add_log, [RoleId, RName, 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),
                    Args = [RoleId, Scene, 0, 0, X, Y,true,KeyValueList],

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

                    %% 增加活跃度
                    clusters_center:apply_cast(Node, lib_activitycalen_api, role_success_end_activity, [RoleId, ?MOD_VOID_FAM, 0]),
                    maps:get(RoleId, RoleMap, []) == [] andalso begin
                        clusters_center:apply_cast(Node, mod_hi_point, success_end, [RoleId, ?MOD_VOID_FAM, 1]),
                        lib_player:apply_cast(Node, RoleId, ?APPLY_CAST, 0,custom_act_adventure, do_trigger_adventure_task, [RoleId, ?MOD_VOID_FAM, 1, 1]),
                        clusters_center:apply_cast(Node,lib_custom_act_slices,task_event,[RoleId, ?MOD_VOID_FAM, 1]),
                        lib_player:apply_cast(Node, RoleId, ?APPLY_CAST_STATUS, 0, lib_activity_reserve, enter_act, [?MOD_VOID_FAM])
                    end,
                    clusters_center:apply_cast(Node, lib_void_fam, get_floor_award, [RoleId,Time,FloorReward]),
                    clusters_center:apply_cast(Node, lib_scene, change_player_scene, Args);
                _ ->
                    skip
            end;
        _ ->
            {ok, BinData} = pt_600:write(60000, [?ERRCODE(err600_act_close)]),
            lib_void_fam:send_to_uid(Node, RoleId, BinData)
    end,
    {ok, State};

do_handle_cast({'update_role_scene', RoleID,Floor,Scene},State)->
    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),
    {ok, State};

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

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


%% 掉落信息
do_handle_cast({'send_drop_info',Node,RoleId}, State) ->
    #status_void_fam{
        status = ActStatus,
        etime = Etime,
        drop_ref=[_Inter,_,DropT]
        %click=Click
    } = State,
    NowTime = utime:unixtime(),
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            {ok,Bin} = pt_600:write(60011,[0,max(DropT-NowTime,0)]),
            lib_void_fam:send_to_uid(Node,RoleId,Bin);
        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{status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            %% 如果攻击者不在场景不做处理
            lib_void_fam:kill_player(AttackerId, AttackerName, DefenderId, DefenderName, BattleSceneId);
        false -> skip
    end,
    {ok, State};

%% 击杀怪物
do_handle_cast({'kill_mon', AttackerId, AttackerName, MonId, BattleSceneId}, State) ->
    #status_void_fam{status = ActStatus, etime = Etime} = State,
    NowTime = utime:unixtime(),
    case ActStatus == ?ACT_STATUS_OPEN andalso NowTime < Etime of
        true ->
            lib_void_fam:kill_mon(AttackerId, AttackerName, MonId, BattleSceneId);
        false -> skip
    end,
    {ok, State};

%% 秘籍开启活动
do_handle_cast({'gm_start',Duration},State) when Duration=<0->
    #status_void_fam{ref = Ref,add_ref=ARef,drop_ref=[_,DRef,_]} = State,
    db:execute(<<"TRUNCATE TABLE void_fam_kf_rank">>),
    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};
do_handle_cast({'gm_start', Duration}, State) ->
    #status_void_fam{ref = Ref,add_ref=ARef, status = Status ,drop_ref=[_,DRef,_], etime = Etime} = State,
    NowTime = utime:unixtime(),
    db:execute(<<"TRUNCATE TABLE void_fam_kf_rank">>),
    erase(?P_ROLE_RANK),

    VoidFamPid = get(check_hosting),
    misc:is_process_alive(VoidFamPid) andalso erlang:exit(VoidFamPid, kill),

    case Status =/= ?ACT_STATUS_OPEN orelse NowTime > Etime of
        true ->
            util:cancel_timer(Ref),
            util:cancel_timer(ARef),
            util:cancel_timer(DRef),
            NewEtime = NowTime + Duration,
            Args = [{?SYNC_TYPE_ACT_STATUS, ?ACT_STATUS_OPEN, NewEtime}],
            clusters_center:apply_to_all_node(mod_void_fam_local, sync_data, [Args]),
            NewRef = erlang:send_after(Duration * 1000, self(), {'act_end'}),
            IntAdd = data_void_fam:get_cfg(interval_add),
            NewARef = erlang:send_after(IntAdd * 1000, self(), {'interval_add'}),
            [{1,DropAdd}|_] = data_void_fam:get_cfg(interval_drop),
            NewDRef = erlang:send_after( DropAdd * 1000, self(), {'interval_drop'}),
            Zones = cross_zone:get_all_zone_ids(czone),
            lib_void_fam:clean_mon(1,Zones),
            NewState = #status_void_fam{
                cls_type = ?ACT_TYPE_KF,
                status = ?ACT_STATUS_OPEN,
                etime = NewEtime,
                ref = NewRef,
                add_ref = NewARef,
                drop_ref = [1,NewDRef,NowTime+DropAdd]
            },
            {ok, NewState};
        _ -> {ok, State}
    end;

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{
            node = Node,
            scene = Scene,
            name = RName,
            ref = Ref,
            floor = Floor,
            score = Score
        } = RoleInfo when Scene > 0 ->
            util:cancel_timer(Ref),

            %% 日志
            clusters_center:apply_cast(Node, 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),
            Args = [RoleId, 0, 0, 0, 0, 0, [{change_scene_hp_lim, 1}, {action_free, ?ERRCODE(err600_cant_change_scene_in_void_fam)}]],
            clusters_center:apply_cast(Node, lib_scene, change_player_scene_queue, Args);
        _ -> skip
    end,
    {ok, State};

%% 活动结束
do_handle_info({'act_end'}, State) ->
    #status_void_fam{ref = ORef} = State,
    util:cancel_timer(ORef),

    RoleMap = lib_void_fam:get_role_map(),
    RoleList = maps:values(RoleMap),
    F = fun(RoleInfo) ->
        case RoleInfo of
            #role_info{
                node = Node,
                role_id = RoleId,
                name = RName,
                scene = Scene,
                ref = Ref,
                zone = Zone,
                floor = Floor,
                mfloor = MFloor,
                awarded = HasF,
                sort = Sort,
                score = Score
            } when Scene > 0 ->
                util:cancel_timer(Ref),

                %% 日志
                QuitType = 4,
                clusters_center:apply_cast(Node, lib_void_fam, add_log, [RoleId, RName, QuitType, Floor, Score]),
                Rank = lib_void_fam:get_rank(RoleId,Zone),
                Grade = lists:max([0|HasF]),
                {ok,Bin} = pt_600:write(60009,[Rank,MFloor,Score,Grade,Sort]),
                lib_void_fam:send_to_uid(Node,RoleId,Bin),
                Args = [RoleId, 0, 0, 0, 0, 0, [{change_scene_hp_lim, 1}, {action_free, ?ERRCODE(err600_cant_change_scene_in_void_fam)}]],
                clusters_center:apply_cast(Node, lib_scene, change_player_scene_queue, Args);
            _ -> skip
        end
    end,
    lists:foreach(F, RoleList),

    Args = [{?SYNC_TYPE_ACT_STATUS, ?ACT_STATUS_CLOSE, 0}],
    clusters_center:apply_to_all_node(mod_void_fam_local, sync_data, [Args]),
    AllRoleRanks = maps:values(lib_void_fam:get_role_rank()),
    [lib_void_fam:account_kf_void_fam(RoleRanks)||RoleRanks<-AllRoleRanks],

    %% 删除进程字典数据
    erase(?P_ACHIEVE_FLOOR_RECORD_MAP),
    erase(?P_ROLE_MAP),
    NewState = #status_void_fam{
        status = ?ACT_STATUS_CLOSE
        },
    lib_void_fam:batch_save_rank(?ACT_TYPE_KF),
    lib_void_fam:batch_save_rank_log(?ACT_TYPE_KF),
    clusters_center:apply_to_all_node(offline_hosting_void_fam, notice_logout, []),
    {ok, NewState};

% do_handle_info({'act_real_close'},_State)->
%     RoleMap = lib_void_fam:get_role_map(),
%     RoleList = maps:values(RoleMap),
%     F = fun(RoleInfo) ->
%         case RoleInfo of
%             #role_info{
%                 node = Node,
%                 role_id = RoleId,
%                 scene = Scene,
%                 ref = Ref,
%                 zone = Zone,
%                 floor = Floor,
%                 mfloor = MFloor,
%                 awarded = HasF,
%                 sort = Sort,
%                 score = Score
%             } when Scene > 0 ->
%                 util:cancel_timer(Ref),

%                 %% 日志
%                 QuitType = 4,
%                 clusters_center:apply_cast(Node, lib_void_fam, add_log, [RoleId, QuitType, Floor, Score]),
%                 Rank = lib_void_fam:get_rank(RoleId,Zone),
%                 Grade = lists:max([0|HasF]),
%                 {ok,Bin} = pt_600:write(60009,[Rank,MFloor,Score,Grade,Sort]),
%                 lib_void_fam:send_to_uid(Node,RoleId,Bin),
%                 Args = [RoleId, 0, 0, 0, 0, 0, [{change_scene_hp_lim, 1}, {action_free, ?ERRCODE(err600_cant_change_scene_in_void_fam)}]],
%                 clusters_center:apply_cast(Node, lib_scene, change_player_scene_queue, Args);
%             _ -> skip
%         end
%     end,
%     lists:foreach(F, RoleList),

%     Args = [{?SYNC_TYPE_ACT_STATUS, ?ACT_STATUS_CLOSE, 0}],
%     clusters_center:apply_to_all_node(mod_void_fam_local, sync_data, [Args]),
%     AllRoleRanks = maps:values(lib_void_fam:get_role_rank()),
%     [lib_void_fam:account_kf_void_fam(RoleRanks)||RoleRanks<-AllRoleRanks],

%     %% 删除进程字典数据
%     erase(?P_ACHIEVE_FLOOR_RECORD_MAP),
%     erase(?P_ROLE_MAP),
%     {ok,#status_void_fam{status = ?ACT_STATUS_CLOSE}};

%%挂机积分
%%挂机积分
do_handle_info({'interval_add'},State)->
    #status_void_fam{status = Status} = State,
    case Status =:= ?ACT_STATUS_OPEN  of
        true ->
            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{status = Status,drop_ref=[Inter,_,_]} = State,
    case Status =:= ?ACT_STATUS_OPEN  of
        true ->
            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{kf_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),
                    clusters_center:apply_to_all_node(mod_void_fam_local,brocast_drop,[]),
                    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(_Request, _State) -> {ok, ok}.

terminate(_Reason, _State) ->
    ok.

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