%%%----------------------------------------------------------------------
%%% @Module  : mod_daily
%%% @Author  : xyao
%%% @Email   : jiexiaowen@gmail.com
%%% @Created : 2010.05.08
%%% @Description: 每天记录器

%%% Modified: 18/10/2016 by hekai <1472524632@qq.com>
%%% Note: 重构记录器，记录器id由功能模块id跟type类型id唯一确定
%%%       功能模块跟type类型在后台配置注册使用
%%% Usage:
%%% mod_daily:get_count(RoleId, ?PET, ?FREE_GROW_LV).
%%% mod_daily:increment(RoleId, ?PET, ?FREE_GROW_LV).
%%% ?PET::integer()，在后台及def_module.hrl中定义
%%% ?FREE_GROW_LV::integer()，在后台及def_daily.hrl中定义
%%%----------------------------------------------------------------------
-module(mod_daily).
-include("daily.hrl").
-include("server.hrl").
-include("common.hrl").
-compile(export_all).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([]).
-define(get_module_pid(RoleId), misc:get_daily_process(RoleId)).

%% 计数器数据带有时间戳，会根据时间戳来判断数据是否有效，理论上可以不做数据库清理。
%% 为了使数据库中不累积流失玩家的数据，依然每天定时清理数据库；为了减少需要删除的
%% 数据记录数量，减轻io压力，清理计数器记录时:
%% 只删除前一天的计算器数据(即refresh_time 小于 当天?TWELVE/?FOUR - 86400 的数据)

%% 凌晨0点的计数器每天凌晨2点清理数据。
-define(TWELVE_CLEAR_HOUR, 2).
%% 凌晨4点的计数器每天凌晨3点清理数据。
-define(FOUR_CLEAR_HOUR, 3).


%% 下线操作
stop(Pid) ->
    gen_server:call(Pid, stop).

start_link(RoleId) ->
    gen_server:start_link(?MODULE, [RoleId], []).

init([RoleId]) ->
    lib_daily:online(RoleId),
    misc:register(global, misc:daily_process_name(RoleId), self()),
    {ok, ?MODULE}.

%%停止任务进程
handle_cast(stop, Status) ->
    {stop, normal, Status};

%% cast数据调用
handle_cast({Fun, Arg}, Status) ->
    apply(lib_daily, Fun, Arg),
    {noreply, Status};

handle_cast(_R , Status) ->
    {noreply, Status}.

%% call数据调用
handle_call(stop, _FROM, Status) ->
    gen_server:reply(_FROM, ok),
    {stop, normal, Status};

handle_call({Fun, Arg} , _FROM, Status) ->
    {reply, apply(lib_daily, Fun, Arg), Status};

handle_call(_R , _FROM, Status) ->
    {reply, ok, Status}.

handle_info(_Reason, Status) ->
    {noreply, Status}.

terminate(_Reason, Status) ->
    {ok, Status}.

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


%%--------------以下有效函数-------------------

%% 每天数据清除
%% 只需要清理内存中的数据
daily_clear(?TWELVE) ->
    mod_daily:daily_clear_ref(?TWELVE);
daily_clear(?FOUR) ->
    mod_daily:daily_clear_ref(?FOUR).
%% 另外的时间清理数据库
db_clear(Hour) ->
    OpenDay = util:get_open_day(),
    case OpenDay > 2 of
        false -> %% 开服第一天第二天不需要清理数据，理由参见daily_clear/1注释
            ok;
        true ->
            case Hour of
                ?TWELVE_CLEAR_HOUR ->
                    RandTime = urand:rand(5, 600) * 1000,
                    spawn(
                        fun() -> timer:sleep(RandTime), daily_db_clear(?TWELVE) end
                    );
                ?FOUR_CLEAR_HOUR ->
                    RandTime = urand:rand(5, 600) * 1000,
                    spawn(
                        fun() -> timer:sleep(RandTime), daily_db_clear(?FOUR) end
                    );
                _ ->
                    ok
            end
    end.


%% 计数器数据带有时间戳，会根据时间戳来判断数据是否有效，理论上可以不做数据库清理。
%% 为了使数据库中不累积流失玩家的数据，依然每天定时清理数据库；为了减少需要删除的
%% 数据记录数量，减轻io压力，清理计数器记录时:
%% 只删除前一天的计算器数据(即refresh_time 小于 当天?TWELVE/?FOUR - 86400 的数据)
daily_db_clear(?TWELVE) ->
    Time = utime:unixdate() - 86400,
    db:execute(io_lib:format(?sql_daily_clear_by_time, [Time]));
daily_db_clear(?FOUR) ->
    Time = utime:unixdate() + (?FOUR * 3600) - 86400,
    db:execute(io_lib:format(?sql_daily_four_clear_by_time, [Time])).

%% 清除每个游戏线内所有玩家的Daily数据
daily_clear_ref(?TWELVE) ->
    ?INFO("0_clock start", []),
    Pids = lib_player:get_online_pids(),
    [gen_server:cast(Pid, {'refresh_and_clear_daily', ?TWELVE}) || Pid <- Pids],
    ok;
daily_clear_ref(?FOUR) ->
    ?INFO("4_clock start", []),
    Pids = lib_player:get_online_pids(),
    [gen_server:cast(Pid, {'refresh_and_clear_daily', ?FOUR}) || Pid <- Pids],
    ok.

%% 清除某玩家的所有日常(只能用于在线玩家)
daily_clear_role_all(RoleId, ?TWELVE) ->
    catch db:execute(io_lib:format(?sql_daily_role_clear, [RoleId])),
    erase(?DAILY_KEY(?TWELVE));
daily_clear_role_all(RoleId, ?FOUR) ->
    catch db:execute(io_lib:format(?sql_daily_four_role_clear, [RoleId])),
    erase(?DAILY_KEY(?FOUR));
daily_clear_role_all(RoleId, Clock) ->
    ?ERR("error clock args:~p~n", [{RoleId, Clock}]),
    error.