%%% -------------------------------------------------------
%%% @author huangyongxing@yeah.net
%%% @doc
%%% 弹珠活动共享数据管理
%%% 主要指抽奖记录
%%% @end
%%% -------------------------------------------------------
-module(custom_act_pellet_mgr).

-behaviour(gen_server).

%% API
-export([
        start_link/0
        ,record_awards/5
        ,query_award_record/3
    ]).

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

-define(SERVER, ?MODULE).

%% 抽奖记录最大条目数
-define(ITEM_COUNT_MAX, 100).           % 标准最大值（每次处理截断时的最大值）
-define(ITEM_COUNT_MAX_REAL, 120).      % 实际最大值（弹性的最大值）

%% 进程状态数据
-record(proc_state, {
        award_records = []
        ,count = 0
    }).

%%%===================================================================
%%% API
%%%===================================================================

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

%% 记录“抽奖产出”/“领取累计次数奖励”
record_awards(RoleId, RoleName, Awards, GetType, Time) ->
    GoodsList = [{GoodsTypeId, GoodsNum} || {_, GoodsTypeId, GoodsNum} <- Awards],
    gen_server:cast(?SERVER, {record_awards, RoleId, RoleName, GoodsList, GetType, Time}).

%% 查询“弹珠活动产出记录”
query_award_record(RoleSid, PageSize, PageNum) ->
    gen_server:cast(?SERVER, {query_award_record, RoleSid, PageSize, PageNum}).

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

init([]) ->
    process_flag(trap_exit, true),
    {MinId, AwardRecords} = load_data(),
    Count = length(AwardRecords),
    erlang:send_after(20000, self(), {clean, MinId}),
    {ok, #proc_state{
            award_records = AwardRecords
            ,count = Count
        }}.

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

handle_cast(Msg, State) ->
    try
        do_handle_cast(Msg, State)
    catch
        ?EXCEPTION(_ErrClass, _ErrReason, _Stacktrace) ->
            ?ERR("handle_cast Msg:~w, Error:~w, ~w", [Msg, _ErrReason, ?GET_STACK(_Stacktrace)]),
            {noreply, State}
    end.

handle_info(Info, State) ->
    try
        do_handle_info(Info, State)
    catch
        ?EXCEPTION(_ErrClass, _ErrReason, _Stacktrace) ->
            ?ERR("handle_info info:~w, Error:~w, ~w", [Info, _ErrReason, ?GET_STACK(_Stacktrace)]),
            {noreply, State}
    end.

terminate(_Reason, _State = #proc_state{award_records = AwardRecords}) ->
    % ?INFO("terminate:~w ", [AwardRecords]),
    db_save_award_records(AwardRecords),
    ok.

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

%% 记录产出奖励信息
do_handle_cast({record_awards, RoleId, RoleName, GoodsList, GetType, Time}, State) ->
    AddAwardRecords = [
        to_award_record([RoleId, RoleName, GoodsTypeId, GoodsNum, GetType, Time], false) ||
        {GoodsTypeId, GoodsNum} <- GoodsList
    ],
    #proc_state{
        award_records = AwardRecords
        ,count = Count
    } = State,
    TmpAwardRecords = AddAwardRecords ++ AwardRecords,
    {NewCount, NewAwardRecords} =
    case Count >= ?ITEM_COUNT_MAX_REAL of
        true  ->
            {?ITEM_COUNT_MAX, lists:sublist(TmpAwardRecords, ?ITEM_COUNT_MAX)};
        false ->
            {Count + length(AddAwardRecords), TmpAwardRecords}
    end,
    NewState = State#proc_state{
        award_records = NewAwardRecords
        ,count = NewCount
    },
    {noreply, NewState};

%% 查询抽奖/累计次数奖励记录
do_handle_cast({query_award_record, RoleSid, PageSize, PageNum}, State = #proc_state{
        award_records = AwardRecords
        ,count = Count
    }) ->
    VisibleLen = erlang:min(Count, ?ITEM_COUNT_MAX),
    FixPageSize = erlang:max(PageSize, 1),
    {Begin, Len, NewPageNum, TotalPage} = util:count_page(PageNum, FixPageSize, VisibleLen),
    DataList = lists:sublist(AwardRecords, Begin, Len),
    send_award_records(RoleSid, FixPageSize, NewPageNum, TotalPage, DataList),
    {noreply, State};

do_handle_cast(_, State) ->
    {noreply, State}.

%% 数据清理
do_handle_info({clean, MinId}, State) ->
    case MinId > 1 of
        true  ->
            Sql = util:fmt_bin(?SQL_DEL_PELLET_AWARD_RECORD_ID, [MinId]),
            db:execute(Sql);
        false ->
            ignore
    end,
    {noreply, State};

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

%%%===================================================================
%%% Internal functions
%%%===================================================================

load_data() ->
    Sql = util:fmt_bin(?SQL_SELECT_PELLET_AWARD_RECORD_RECENTLY, [?ITEM_COUNT_MAX]),
    DbDataList = db:get_all(Sql),
    {MinId, AwardRecords} =
    lists:foldl(
        fun([Id | Data = [_,_,_,_,_,_] ], {MinId, Records}) ->
                AwardRecord = to_award_record(Data, true),
                NewMinId =
                case MinId =:= 0 orelse Id < MinId of
                    true  -> Id;
                    false -> MinId
                end,
                {NewMinId, [AwardRecord | Records]}
        end, {0, []}, DbDataList),
    {MinId, lists:reverse(AwardRecords)}.

to_award_record([RoleId, RoleName, GoodsTypeId, GoodsNum, GetType, Time], IsSaved) ->
    #pellet_award_record{
        role_id = RoleId
        ,name = RoleName
        ,goods_type_id = GoodsTypeId
        ,goods_num = GoodsNum
        ,get_type = GetType
        ,time = Time
        ,is_saved = IsSaved
    }.

to_cli_data(#pellet_award_record{
        role_id = RoleId
        ,name = RoleName
        ,goods_type_id = GoodsTypeId
        ,goods_num = GoodsNum
        ,get_type = GetType
    }) ->
    {RoleId, RoleName, GoodsTypeId, GoodsNum, GetType}.

send_award_records(RoleSid, PageSize, PageNum, PageMax, AwardRecords) ->
    DataList = [
        to_cli_data(AwardRecord) || AwardRecord <- AwardRecords
    ],
    {ok, BinData} = pt_331:write(33183, [PageSize, PageNum, PageMax, DataList]),
    lib_server_send:send_to_sid(RoleSid, BinData).

db_save_award_records(AwardRecords) ->
    %% 只保存尚未保存的数据，且需要反转顺序进行存储，以保证记录的正确顺序
    NewAwardRecords = lists:foldl(
        fun (AwardRecord = #pellet_award_record{is_saved = false}, Acc) ->
                [AwardRecord | Acc];
            (_, Acc) -> Acc
        end, [], AwardRecords),
    DataFmtFun = fun fmt_award_record/1,
    SqlPrefix = ?SQL_REPLACE_PELLET_AWARD_RECORD_HEAD,
    udb:save_data_list(NewAwardRecords, SqlPrefix, DataFmtFun).

fmt_award_record(#pellet_award_record{
        role_id = RoleId
        ,name = RoleName
        ,goods_type_id = GoodsTypeId
        ,goods_num = GoodsNum
        ,get_type = GetType
        ,time = Time
        ,is_saved = false
    }) ->
    util:fmt_bin("(~w,'~ts',~w,~w,~w,~w)", [
            RoleId, RoleName, GoodsTypeId, GoodsNum, GetType, Time
        ]);
fmt_award_record(_) ->
    <<>>.

