%%%-------------------------------------------------------------------
%%% author : ljy
%%% desc   : 活动-源能转盘
%%% Time   : 2021.03.08
%%%-------------------------------------------------------------------
-module(custom_act_turntable).

-include("common.hrl").
-include("turntable.hrl").
-include("server.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("custom_act.hrl").
-include("def_module.hrl").
-include("goods.hrl").
-include("def_fun.hrl").
-include("predefine.hrl").

%% API
-export([
    get_pool_info/2,
    score_show_info/2,
    score_swap/4,
    lottery/4,
    send_lottery_record/2,
    act_end/1,
    online_send_reward/4
]).

%% 获取奖池信息
get_pool_info(Player, SubType) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    CheckList = [{open, SubType}, {free_time}],
    case check_list(CheckList) of
        true ->
            Ids = data_turntable:get_pool_ids(SubType),
            PoolInfo = get_cfg_info(SubType, Ids, []),
            {_, ActData} = get_act_data(RoleId, SubType),
            #role_turntable_info{score = Score, lucky = Lucky, free_times = FreeTimes} = ActData,
            CfgTimes = data_turntable:get_config(free_time),
            LeftTimes = CfgTimes - FreeTimes,
            Times = ?IF(LeftTimes > 0, LeftTimes, 0),
            lib_server_send:send_to_sid(Sid, pt_335, 33501, [?SUCCESS, SubType, Score, Lucky, Times, PoolInfo]);
        {false, ErrCode} ->
            lib_server_send:send_to_sid(Sid, pt_335, 33501, [ErrCode, SubType, 0, 0, 0, []])
    end.

get_cfg_info(_, [], List) ->
    List;
get_cfg_info(SubType, [Id | Ids], List) ->
    case data_turntable:turntable_reward_cfg(SubType, Id) of
        #base_turntable_reward{reward = Reward, tag = Tag} ->
            get_cfg_info(SubType, Ids, [{Id, util:term_to_string(Reward), Tag} | List]);
        _ ->
            get_cfg_info(SubType, Ids, List)
    end.

%% 积分兑换信息
score_show_info(Player, SubType) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    CheckList = [{open, SubType}],
    case check_list(CheckList) of
        true ->
            {_, ActData} = get_act_data(RoleId, SubType),
            #role_turntable_info{score = Score, swap_list = SwapList} = ActData,
            Ids = data_turntable:get_point_ids(SubType),
            PackInfo =
                [begin
                     #base_turntable_point_reward{reward = Reward, limit = Limit, points = Points} =
                         data_turntable:turntable_point_reward(SubType, Id),
                     case lists:keyfind(Id, 1, SwapList) of
                         {_, SwapTimes} ->
                             LeftTimes = max(0, Limit - SwapTimes);
                         _ ->
                             LeftTimes = Limit
                     end,
                     {Id, util:term_to_string(Reward), LeftTimes, Points}
                 end || Id <- Ids],
            lib_server_send:send_to_sid(Sid, pt_335, 33502, [?SUCCESS, SubType, Score, PackInfo]);
        {false, ErrCode} ->
            lib_server_send:send_to_sid(Sid, pt_335, 33502, [ErrCode, SubType, 0, []])
    end.

%% 积分兑换
score_swap(Player, SubType, Id, Num) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    CheckList = [{open, SubType}, {check_cfg, SubType, Id}],
    Result =
        case check_list(CheckList) of
            true ->
                {CustomData, ActData} = get_act_data(RoleId, SubType),
                #role_turntable_info{score = Score, swap_list = SwapList} = ActData,
                #base_turntable_point_reward{reward = Reward, limit = Limit, points = Points} = data_turntable:turntable_point_reward(SubType, Id),
                case lists:keyfind(Id, 1, SwapList) of
                    {_, SwapTimes} ->
                        skip;
                    _ ->
                        SwapTimes = 0
                end,
                LeftTimes = max(0, Limit - SwapTimes),
                if
                    LeftTimes < Num -> {false, ?ERRCODE(err331_exceed_max_times)};
                    Score < Num * Points -> {false, ?ERRCODE(err331_lack_score)};
                    true ->
                        NewSwapList = lists:keystore(Id, 1, SwapList, {Id, SwapTimes + Num}),
                        NewScore = Score - Num * Points,
                        NewActData = ActData#role_turntable_info{score = NewScore, swap_list = NewSwapList},
                        NewCustomData = CustomData#custom_act_data{act_data = NewActData},
                        lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
                        RewardList = [{Type, GoodsId, GoodsNum * Num} || {Type, GoodsId, GoodsNum} <- Reward],
                        Produce = #produce{reward = RewardList, type = turntable_swap},
                        lib_goods_api:send_reward_with_mail(RoleId, Produce),
                        lib_server_send:send_to_sid(Sid, pt_335, 33503, [?SUCCESS, SubType, Id, LeftTimes - Num, NewScore]),
                        ok
                end;
            {false, ErrCode} ->
                {false, ErrCode}
        end,
    case Result of
        {false, Error} ->
            lib_server_send:send_to_sid(Sid, pt_335, 33503, [Error, SubType, Id, 0, 0]);
        _ ->
            skip
    end.

%% 获取活动数据
get_act_data(RoleId, SubType) ->
    #custom_act_data{
        act_data = ActData
    } = CustomData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_TURNTABLE, SubType),
    NewData =
        if
            is_record(ActData, role_turntable_info) ->
                ActData;
            true ->
                #role_turntable_info{}
        end,
    if
        NewData =/= ActData ->
            NewCustomData = CustomData#custom_act_data{act_data = NewData},
            lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
            {NewCustomData, NewData};
        true ->
            {CustomData, NewData}
    end.

%% 抽奖
lottery(Player, SubType, LotteryType, IsSkip) ->
    #player_status{id = RoleId} = Player,
    CheckList = [{open, SubType}, {check_lottery_cfg}],
    case check_list(CheckList) of
        true ->
            CfgTimes = data_turntable:get_config(free_time),
            {CustomData, ActData} = get_act_data(RoleId, SubType),
            #role_turntable_info{free_times = FreeTimes} = ActData,
            if
                LotteryType == 1 ->
                    LotteryTimes = 1,
                    Cost =
                        if
                            FreeTimes >= CfgTimes ->
                                NFreeTimes = FreeTimes,
                                data_turntable:get_config(turn_cost_1);
                            true ->
                                NFreeTimes = FreeTimes + 1,
                                []
                        end;
                true ->
                    NFreeTimes = FreeTimes,
                    LotteryTimes = 10,
                    Cost = data_turntable:get_config(turn_cost_10)
            end,
            case lib_goods_api:cost_objects_with_auto_buy(Player, Cost, turntable_lottery, "") of
                {true, NewPlayer, _} ->
                    do_lottery(NewPlayer, SubType, LotteryTimes, IsSkip, CustomData,
                        ActData#role_turntable_info{free_times = NFreeTimes}),
                    {ok, NewPlayer};
                {false, ErrCode, _} ->
                    {false, ErrCode}
            end;
        {false, ErrCode} ->
            {false, ErrCode}
    end.

do_lottery(Player, SubType, LotteryTimes, IsSkip, CustomData, ActData) ->
    #player_status{id = RoleId, sid = Sid, figure = Figure} = Player,
    #figure{name = RoleName} = Figure,
    LuckyLimit = data_turntable:get_config(luck_total),
    OneWaitTime = data_turntable:get_config(one_time),
    TenWaitTime = data_turntable:get_config(ten_time),
    AddScore = data_turntable:get_config(turn_score),
    OneLucky = data_turntable:get_config(turn_luck),
    CfgTimes = data_turntable:get_config(free_time),
    RandList = data_turntable:get_pool_weight(SubType),
    #role_turntable_info{free_times = FreeTimes, score = Score, lucky = Lucky} = ActData,
    {NewLucky, Ids} = rand_ids(LotteryTimes, RandList, Lucky, OneLucky, LuckyLimit, SubType, []),
    {RewardList, TvList, RcList} = collect_reward(Ids, SubType, [], [], []),
    NewScore = Score + AddScore * LotteryTimes,
    NewActData = ActData#role_turntable_info{score = NewScore, lucky = NewLucky},
    NewCustomData = CustomData#custom_act_data{act_data = NewActData},
    lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
    F = fun({GoodsId, Num}, {List1, List2}) ->
        SqlValue = io_lib:format(?SQL_TURNTABLE_RECORD_VALUES, [SubType, RoleName, GoodsId, Num]),
        {[SqlValue | List1], [{RoleName, GoodsId, Num} | List2]}
        end,
    {SaveValue, RcList2} = lists:foldl(F, {[], []}, RcList),
    SaveValue =/= [] andalso db:execute(io_lib:format(?SQL_INSERT_TURNTABLE_RECORD, [ulists:list_to_string(SaveValue, ",")])),
    Pid = erlang:whereis(mod_custom_act_log),
    if
        TvList == [] andalso RcList2 == [] ->
            skip;
        IsSkip == 1 ->
            mod_custom_act_log:info({record_lottery_info, RoleId, RoleName, SubType, RcList2, TvList});
        true ->
            WaitTime = ?IF(LotteryTimes == 1, OneWaitTime, TenWaitTime),
            erlang:send_after(WaitTime * 1000, Pid, {record_lottery_info, RoleId, RoleName, SubType, RcList2, TvList})
    end,
    Produce = #produce{reward = RewardList, type = turntable_lottery},
    lib_goods_api:send_reward_with_mail(Player, Produce),
    LeftTimes = CfgTimes - FreeTimes,
    Times = ?IF(LeftTimes > 0, LeftTimes, 0),
    lib_server_send:send_to_sid(Sid, pt_335, 33504, [?SUCCESS, SubType, NewScore, NewLucky, Times, lists:reverse(Ids)]),
    ok.

%% 发送全服记录
send_lottery_record(Player, SubType) ->
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_TURNTABLE, SubType) of
        #act_info{} ->
            mod_custom_act_log:cast({send_lottery_record, Player#player_status.sid, SubType});
        _ ->
            skip
    end.

%% 活动结束清理数据 & 邮件发放未领取奖励
act_end(#act_info{key = {ActType, SubType}}) ->
    SuitId =
        case lib_custom_act_util:get_act_cfg_info(ActType, SubType) of
            #custom_act_cfg{condition = Condition} ->
                case lists:keyfind(suit, 1, Condition) of
                    {suit, Suit} ->
                        Suit;
                    _ -> data_turntable:get_min_suit()
                end;
            _ ->
                data_turntable:get_min_suit()
        end,
    if
        is_integer(SuitId) ->
            OpenLv = lib_custom_act_util:get_act_open_lv(ActType, SubType),
            SQL = io_lib:format(?SELECT_ACT_TURNTABLE_ROLES, [OpenLv]),
            Roles = lists:flatten(db:get_all(SQL)),
            [begin
                 Pid = misc:get_player_process(RoleId),
                 case misc:is_process_alive(Pid) of
                     true ->
                         lib_player:apply_cast(Pid, ?APPLY_CAST_STATUS, ?MODULE, online_send_reward, [ActType, SubType, SuitId]);
                     false ->
                         offline_send_reward(RoleId, ActType, SubType, SuitId)
                 end
             end || RoleId <- Roles];
        true ->
            ?ERR("act turntable config error suit = ~p", [SuitId])
    end,
    % 清理缓存数据 & db数据
    mod_custom_act_log:cast({clear_turntable_data, SubType}),
    SQL2 = io_lib:format(?SQL_DELETE_TURNTABLE_RECORD, [SubType]),
    db:execute(SQL2),
    ok;
act_end(_) ->
    ok.

%% 给在线玩家补发未领取奖励
online_send_reward(Player, ActType, SubType, Suit) ->
    #custom_act_data{
        act_data = ActData
    } = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_TURNTABLE, SubType),
    send_act_end_reward(Player#player_status.id, ActType, SubType, Suit, ActData),
    {ok, Player}.

%% 补发离线玩家未领取奖励
offline_send_reward(RoleId, ActType, SubType, Suit) ->
    ActData = get_role_data(RoleId, ActType, SubType),
    send_act_end_reward(RoleId, ActType, SubType, Suit, ActData).

%% 发送邮件
send_act_end_reward(RoleId, ActType, SubType, Suit, #role_turntable_info{score = Score}) ->
    RewardList = data_turntable:get_offset_reward(Suit, Score),
    if
        RewardList =/= [] ->
            lib_mail_api:send_sys_mail([RoleId], ActType, SubType, utext:get(3311101), utext:get(3311102), RewardList);
        true -> skip
    end;
send_act_end_reward(_, _, _, _, _) ->
    skip.

%% 获取离线玩家活动数据
get_role_data(RoleId, Type, SubType) ->
    SQL = io_lib:format(?SELECT_ACT_TURNTABLE_DATA, [RoleId, Type, SubType]),
    case db:get_row(SQL) of
        [] -> skip;
        [BinData] ->
            ActData = util:bitstring_to_term(BinData),
            case ActData of
                #role_turntable_info{} ->
                    ActData;
                _ -> skip
            end
    end.

%% 获取随机id列表
rand_ids(0, _, Lucky, _, _, _, Ids) ->
    {Lucky, Ids};
rand_ids(LotteryTimes, RandList, Lucky, OneLucky, LuckyLimit, SubType, Ids) ->
    if
        Lucky + OneLucky =< LuckyLimit ->
            Id = urand:rand_with_weight(RandList),
            rand_ids(LotteryTimes - 1, RandList, Lucky + OneLucky, OneLucky, LuckyLimit, SubType, [Id | Ids]);
        true ->
            % 源能值满了后必得大奖，清空源能值
            List = data_turntable:get_pool_whit_tag(SubType),
            WeightList = [{Weight, Id} || {Tag, Weight, Id} <- List, Tag == 1 orelse Tag == 6],
            if
                is_list(WeightList) ->
                    Id = urand:rand_with_weight(WeightList);
                true ->
                    Id = urand:rand_with_weight(RandList)
            end,
            NewLucky = Lucky + OneLucky - LuckyLimit - 1,
            rand_ids(LotteryTimes - 1, RandList, NewLucky, OneLucky, LuckyLimit, SubType, [Id | Ids])
    end.

%% 由id列表收集奖励
collect_reward([], _SubType, RewardList, TvList, RcList) ->
    {RewardList, TvList, RcList};
collect_reward([Id | Ids], SubType, RewardList, TvList, RcList) ->
    case data_turntable:turntable_reward_cfg(SubType, Id) of
        #base_turntable_reward{reward = [{_, GoodsId, Num}|_] = Rewards, is_tv = IsTv, tag = Tag} ->
            NTvList = ?IF(IsTv == 1, [{GoodsId, Num, Tag} | TvList], TvList),
            NRcList = ?IF(Tag =/= 0, [{GoodsId, Num} | RcList], RcList),
            collect_reward(Ids, SubType, Rewards ++ RewardList, NTvList, NRcList);
        _ ->
            collect_reward(Ids, SubType, RewardList, TvList, RcList)
    end.


check_list([]) -> true;
check_list([KeyVal | Rest]) ->
    case check(KeyVal) of
        true ->
            check_list(Rest);
        Reason ->
            Reason
    end.

check({open, SubType}) ->
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_TURNTABLE, SubType) of
        #act_info{key = {_, SubType}} ->
            true;
        _ ->
            {false, ?ERRCODE(err331_act_closed)}
    end;
check({free_time}) ->
    case data_turntable:get_config(free_time) of
        CfgTimes when is_integer(CfgTimes) ->
            true;
        _ ->
            {false, ?ERRCODE(missing_config)}
    end;
check({check_cfg, SubType, Id}) ->
    case data_turntable:turntable_point_reward(SubType, Id) of
        #base_turntable_point_reward{} ->
            true;
        _ ->
            {false, ?ERRCODE(missing_config)}
    end;
check({check_lottery_cfg}) ->
    CfgTimes = data_turntable:get_config(free_time),
    LuckyLimit = data_turntable:get_config(luck_total),
    OneWaitTime = data_turntable:get_config(one_time),
    TenWaitTime = data_turntable:get_config(ten_time),
    AddScore = data_turntable:get_config(turn_score),
    OneLucky = data_turntable:get_config(turn_luck),
    CfgTrue = is_integer(CfgTimes) andalso is_integer(LuckyLimit) andalso is_integer(OneWaitTime)
        andalso is_integer(TenWaitTime) andalso is_integer(AddScore) andalso is_integer(OneLucky),
    if
        CfgTrue -> true;
        true -> {false, ?ERRCODE(missing_config)}
    end.

