%% -----------------------------------------------------
%% @author  :ljy
%% moudle   :mod_goods_convert.erl
%% desc     :物品转换管理，当物品达到转换时间时，将物品转换为另一种物品
%% time     :2021.01.11
%% ------------------------------------------------------
-module(mod_convert_mgr).

-include("common.hrl").
-include("goods_convert.hrl").
-include("def_goods.hrl").
-include("predefine.hrl").
-include("goods.hrl").
-include("server.hrl").

%% API
-export([
    start_link/0
    , remove_convert_good/2
    , daily_refresh/0
    , get_time_for_goods_id/2
]).

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


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

%% 每日0点检测是否触发定时器
daily_refresh() ->
    gen_server:cast(?MODULE, {daily_refresh}).

%% 设置定时器：
%% 有物品的转换时间在24小时又10秒内则设置定时器
%% 没有则每天0点会检查是否需要触发定时器
set_timer(State) ->
    #convert_state{ref = OldRef} = State,
    NextTime = get_next_time(),
    if
        NextTime > 0 ->
            NowTime = utime:unixtime(),
            DiffTime = (NextTime - NowTime) * 1000,
            if
                DiffTime >= ?INTERVAL_TIME ->
                    State;
                true ->
                    Ref = util:send_after(OldRef, DiffTime, self(), goods_convert),
                    #convert_state{next_time = NextTime, ref = Ref}
            end;
        true ->
            State
    end.

%% 从配置中筛选出转换时间大于当前时间且离当前时间最近的转换时间，找不到则返回0
get_next_time() ->
    NowTime = utime:unixtime(),
    IdList = data_goods_convert:get_all_goods_type_id(),
    TimeList =
        [begin
             #goods_convert{convert_time = ConvertTime} = data_goods_convert:get_goods_convert_cfg(GoodsTypeId),
             ConvertTime
         end || GoodsTypeId <- IdList],

    EffectList = lists:filter(fun(Time) -> Time > NowTime end, TimeList),
    if
        EffectList =/= [] -> lists:min(EffectList);
        true -> 0
    end.

%% 物品转换，先从数据库表中查询出拥有此物品的玩家id和拥有数量
%% 再将此物品从表中删除，若玩家在线则清除玩家缓存中的此物品信息
convert_goods(GoodsId) ->
    SQL1 = io_lib:format(?SELECT_GOODS_INFO_BY_ID, [GoodsId]),
    case db:get_all(SQL1) of
        [] -> skip;
        List ->
            SQL2 = io_lib:format(?DELETE_ALL_GOODS_BY_ID, [GoodsId]),
            db:execute(SQL2),
            case data_goods_convert:get_goods_convert_cfg(GoodsId) of
                #goods_convert{min_num = OldNum, goods_convert = NewGoodsId, get_num = NewNum} ->
                    F = fun([Key, Value], Result) ->
                        case lists:keyfind(Key, 1, Result) of
                            false -> [{Key, Value} | Result];
                            {_, Value1} ->
                                lists:keystore(Key, 1, Result, {Key, Value + Value1})
                        end
                        end,
                    MergeList = lists:foldl(F, [], List),
                    Time = utime:unixtime(),
                    [begin
                         Pid = misc:get_player_process(RoleId),
                         case misc:is_process_alive(Pid) of
                             true ->
                                 lib_player:apply_cast(Pid, ?APPLY_CAST_STATUS, ?MODULE, remove_convert_good, [GoodsId]);
                             false ->
                                 skip
                         end,
                         GoodsNum = umath:floor(OwnNum * NewNum / OldNum),
                         if
                             GoodsNum > 0 ->
                                 RewardList = [{?TYPE_GOODS, NewGoodsId, GoodsNum}],
                                 lib_mail_api:send_sys_mail([RoleId], utext:get(33100101), utext:get(33100102, [GoodsId, OwnNum, NewGoodsId, GoodsNum]), RewardList),
                                 %% 日志
                                 lib_log_api:log_goods_convert(RoleId, GoodsId, RewardList, Time);
                             true ->
                                 %% 日志
                                 lib_log_api:log_goods_convert(RoleId, GoodsId, [], Time)
                         end
                     end || {RoleId, OwnNum} <- MergeList],
                    ok;
                _ -> skip
            end,
            ok
    end,
    ok.

%% 移除玩家背包物品，只清缓存，数据库的数据已经清理了
remove_convert_good(Player, GoodsTypeId) ->
    GoodsStatus = lib_goods_do:get_goods_status(),
    ok = lib_goods_dict:start_dict(),
    case data_goods_type:get(GoodsTypeId) of
        #ets_goods_type{bag_location = Location} ->
            GoodsMaps = GoodsStatus#goods_status.dict,
            Dict = lib_goods_util:get_location_goods(GoodsMaps, Location),
            Dict1 = dict:filter(fun(_Key, Value) -> Value#goods.goods_id == GoodsTypeId end, Dict),
            Dict2 = dict:filter(fun(_Key, Value) -> Value#goods.goods_id =/= GoodsTypeId end, Dict),
            GoodsList = lib_goods_dict:fetch_values(Dict1),
            NewGoodsMaps = lib_goods_util:put_location_goods(GoodsMaps, Location, Dict2),
            NewGoodsStatus = GoodsStatus#goods_status{dict = NewGoodsMaps},
            lib_goods_do:set_goods_status(NewGoodsStatus),
            F = fun(#goods{id = Id}, SendInfo) ->
                [{Id, 0, GoodsTypeId} | SendInfo]
                end,
            SendList = lists:foldl(F, [], GoodsList),
            {ok, BinData} = pt_150:write(15018, [Location, SendList]),
            lib_server_send:send_to_sid(Player#player_status.sid, BinData),
            ok;
        _ ->
            ?ERR("delete_type_list_goods err: goods_type_id = ~p err_config", [GoodsTypeId]),
            not_enough
    end.

get_time_for_goods_id(GoodsTypeId, ConvertList) ->
    case lists:member(GoodsTypeId, ConvertList) of
        true ->
            case data_goods_convert:get_goods_convert_cfg(GoodsTypeId) of
                #goods_convert{convert_time = CTime} -> CTime;
                _ -> 0
            end;
        false -> 0
    end.

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) ->
    State = set_timer(#convert_state{}),
    {ok, State}.

handle_call(Request, From, State) ->
    try
        do_handle_call(Request, From, State)
    catch ?EXCEPTION(_ErrClass, _ErrReason, _Stacktrace) ->
        ?ERR("handle_call Request:~w, Error:~w", [Request, {_ErrClass, _ErrReason, ?GET_STACK(_Stacktrace)}]),
        {reply, error, State}
    end.

handle_cast(Msg, State) ->
    try
        do_handle_cast(Msg, State)
    catch ?EXCEPTION(_ErrClass, _ErrReason, _Stacktrace) ->
        ?ERR("handle_cast Msg:~w, Error:~w", [Msg, {_ErrClass, _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", [Info, {_ErrClass, _ErrReason, ?GET_STACK(_Stacktrace)}]),
        {noreply, State}
    end.

terminate(_Reason, _State) ->
    ok.

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

do_handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

do_handle_cast({daily_refresh}, State) ->
    #convert_state{ref = Ref} = State,
    if
        is_reference(Ref) -> NewState = State;
        true -> NewState = set_timer(State)
    end,
    {noreply, NewState};

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

%% 物品转换
do_handle_info(goods_convert, State) ->
    #convert_state{next_time = ConvertTime} = State,
    IdList = data_goods_convert:get_all_goods_type_id(),
    F = fun(Id) ->
        case data_goods_convert:get_goods_convert_cfg(Id) of
            #goods_convert{convert_time = ConvertTime} -> true;
            _ -> false
        end
        end,
    GoodsList = lists:filter(F, IdList),
    [convert_goods(GoodsId) || GoodsId <- GoodsList],
    NewState = set_timer(State),
    {noreply, NewState};

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