%%%-------------------------------------------------------------------
%%% @author ljy
%%% @doc
%%%       活动-冒险征途
%%% @end
%%% Created : 09. 11月 2020 16:16
%%%-------------------------------------------------------------------
-module(custom_act_adventure).

-include("server.hrl").
-include("act_adventure.hrl").
-include("common.hrl").
-include("custom_act.hrl").
-include("errcode.hrl").
-include("hi_point.hrl").
-include("def_module.hrl").
-include("predefine.hrl").
-include("goods.hrl").

%% API
-export([
    adventure_task_info/2                     %% 任务完成情况
    , check_receive_reward_conditions/3       %% 检查奖励是否可领取
    , lottery/3                               %% 抽奖
    , trigger_adventure_task/4                %% 完成任务触发
    , do_trigger_adventure_task/3
    , do_trigger_adventure_task/4
    , send_task_info/2                        %% 任务信息
    , get_suit_num/1                          %% 获取本期活动对应的活动套数
    , get_act_data/2                          %% 获取活动数据
    , refresh_recharge/3                      %% 触发充值任务
    , refresh_consume/3                       %% 触发消费彩钻任务
    , use_goods_add/2                         %% 使用物品增加热点值
    , act_end/1                               %% 活动结束
    , send_act_end_reward/3                   %% 给在线玩家补发未领取奖励
    , gm_set_score_and_progress/4
    , gm_reset/1
]).

%% 任务信息
adventure_task_info(RoleId, SubType) ->
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_ADVENTURE_ROLE, SubType) of
        #act_info{key = {_, SubType}} ->
            {_NewCustomData, ActData} = get_act_data(RoleId, SubType),
            send_task_finish_info(RoleId, SubType, ActData);
        _ ->
            {ok, BinData} = pt_332:write(33263, [?ERRCODE(err331_act_closed), SubType, 0, 0, []]),
            lib_server_send:send_to_uid(RoleId, BinData)
    end.

send_task_finish_info(RoleId, SubType, ActData) ->
    #adventure_road{score = Score, progress = Progress, recharge = Recharge, adventure_task = AdventureTask} = ActData,
    TaskInfo = [{ModId, SubId, Times} || {{ModId, SubId}, Times} <- AdventureTask],
    {ok, BinData} = pt_332:write(33263, [?SUCCESS, SubType, Score, Progress, Recharge, TaskInfo]),
    lib_server_send:send_to_uid(RoleId, BinData).

%% 完成任务触发
trigger_adventure_task(RoleId, ModId, SubId, Count) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST, ?MODULE, do_trigger_adventure_task, [RoleId, ModId, SubId, Count]).

do_trigger_adventure_task(RoleId, ModId, SubId) ->
    do_trigger_adventure_task(RoleId, ModId, SubId, 1).

do_trigger_adventure_task(RoleId, ModId, SubId, Count) ->
    [execut_trigger_adventure_task(RoleId, SubType, ModId, SubId, Count)
        || SubType <- lib_custom_act_api:get_open_subtype_ids(?CUSTOM_ACT_TYPE_ADVENTURE_ROLE)].

execut_trigger_adventure_task(RoleId, SubType, ModId, SubId, Count) ->
    SuitNum = get_suit_num(SubType),
    case lists:member({ModId, SubId}, data_act_adventure:get_keys(SuitNum)) of
        true ->
            {CustomData, ActData} = get_act_data(RoleId, SubType),
            #adventure_road{score = Score, adventure_task = AdventureTask} = ActData,
            case lists:keyfind({ModId, SubId}, 1, AdventureTask) of
                {_, Times} -> skip;
                _ -> Times = 0
            end,
            #base_hi_point{one_points = Points, max_count = MaxCount} = data_act_adventure:get_task_cfg(SuitNum, ModId, SubId),
            RealTimes =
                if
                    Times >= MaxCount -> 0;
                    Times + Count >= MaxCount ->
                        MaxCount - Times;
                    true ->
                        Count
                end,
            if
                RealTimes > 0 ->
                    NewAdventureTask = lists:keystore({ModId, SubId}, 1, AdventureTask, {{ModId, SubId}, Times + RealTimes}),
                    NewScore = Score + Points * RealTimes,
                    NewActData = ActData#adventure_road{score = NewScore, adventure_task = NewAdventureTask},
                    NewCustomData = CustomData#custom_act_data{act_data = NewActData},
                    lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
                    send_task_finish_info(RoleId, SubType, NewActData);
                true ->
                    skip
            end,
            ok;
        _ -> skip
    end,
    ok.

%% 发送任务信息
send_task_info(Player, SubType) ->
    #player_status{sid = Sid} = Player,
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_ADVENTURE_ROLE, SubType) of
        #act_info{key = {_, SubType}} ->
            SuitNum = get_suit_num(SubType),
            F = fun({ModId, SubId}, Acc) ->
                case data_act_adventure:get_task_cfg(SuitNum, ModId, SubId) of
                    #base_hi_point{name = Name, one_points = OnePots, max_count = MaxCount, order_id = OrderId, jump_id = JumpId} ->
                        [{ModId, SubId, Name, OnePots, MaxCount, OrderId, util:term_to_string(JumpId)} | Acc];
                    _ ->
                        ?ERR("adventure config error:[~p, ~p]~n", [ModId, SubId]), Acc
                end end,
            SendList = lists:foldl(F, [], data_act_adventure:get_keys(SuitNum)),
            RewardList = [{GoodId, Num} || [{_, GoodId, Num}] <- data_act_adventure:get_reward_list(SubType)],
            {ok, BinData} = pt_332:write(33265, [?SUCCESS, SubType, SendList, RewardList]),
            lib_server_send:send_to_sid(Sid, BinData);
        _ ->
            {ok, BinData} = pt_332:write(33265, [?ERRCODE(err331_act_closed), SubType, [], []]),
            lib_server_send:send_to_sid(Sid, BinData)
    end.

%% 检查奖励是否可领取
check_receive_reward_conditions(Player, #act_info{key = {_, SubType}}, RwCfg) ->
    #player_status{id = RoleId} = Player,
    {_, ActData} = get_act_data(RoleId, SubType),
    #adventure_road{progress = Progress} = ActData,
    #custom_act_reward_cfg{condition = Condition} = RwCfg,
    case lists:keyfind(hi_point, 1, Condition) of
        {hi_point, NeedProgress} when Progress >= NeedProgress -> true;
        {hi_point, _} -> {false, ?ERRCODE(err331_progress_lack)};
        _ -> {false, ?ERRCODE(config_err)}
    end.

%% 觅宝 Type 1 单抽|2 十连抽
lottery(Player, SubType, LotteryType) when LotteryType =:= 1 orelse LotteryType =:= 2 ->
    ActType = ?CUSTOM_ACT_TYPE_ADVENTURE_ROLE,
    case lib_custom_act_util:get_custom_act_open_info(ActType, SubType) of
        #act_info{key = {_, SubType}} ->
            case lib_custom_act_util:get_act_cfg_info(ActType, SubType) of
                #custom_act_cfg{condition = Condition} -> skip;
                _ -> Condition = []
            end,
            RandGrade =
                case data_act_adventure:get_rand_list(SubType) of
                    RandList when RandList =/= [] -> RandList;
                    _ -> []
                end,
            case lists:keyfind(roll, 1, Condition) of
                {roll, ProgressList} when RandGrade =/= [] ->
                    do_lottery(Player, SubType, LotteryType, ProgressList, RandGrade);
                _ -> send_lottery_info(Player#player_status.sid, SubType, ?ERRCODE(config_err))
            end;
        _ ->
            send_lottery_info(Player#player_status.sid, SubType, ?ERRCODE(err331_act_closed))
    end;
lottery(Player, SubType, _Type) ->
    send_lottery_info(Player#player_status.sid, SubType, ?FAIL).

do_lottery(Player, SubType, LotteryType, ProgressList, RandGrade) ->
    #player_status{id = RoleId, sid = Sid, gold = Gold, bgold = Bgold} = Player,
    {CustomData, ActData} = get_act_data(RoleId, SubType),
    #adventure_road{score = Score, progress = Progress} = ActData,
    {Times, NeedScore} =
        if
            LotteryType =:= 1 -> {1, ?ONE_LOTTERY};
            true -> {10, ?TEN_LOTTERY}
        end,
    if
        Score >= NeedScore ->
            {ObProgress, RewardList, GradeList} = get_progress_and_reward(RandGrade, SubType, ProgressList, Times),
            LeftScore = Score - NeedScore,
            ProgressSum = Progress + ObProgress,
            NewActData = ActData#adventure_road{progress = ProgressSum, score = LeftScore},
            NewCustomData = CustomData#custom_act_data{act_data = NewActData},
            % 先保存数据再发奖励
            lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
            NewPlayer = lib_goods_api:send_reward(Player, RewardList, adventure_lottery),
            lib_log_api:log_adventure_lottery([RoleId, ?CUSTOM_ACT_TYPE_ADVENTURE_ROLE, SubType, LotteryType, util:term_to_bitstring(GradeList),
                Score, Progress, utime:unixtime(), Gold, NewPlayer#player_status.gold, Bgold, NewPlayer#player_status.bgold]),
            RewardInfo = [{GoodsTypeId, GoodsNum} || {_Type, GoodsTypeId, GoodsNum} <- RewardList],
            {ok, BinData} = pt_332:write(33264, [?SUCCESS, SubType, ObProgress, LeftScore, ProgressSum, RewardInfo]),
            lib_server_send:send_to_sid(Sid, BinData),
            {ok, NewPlayer};
        true -> send_lottery_info(Sid, SubType, ?ERRCODE(err331_score_lack))
    end.

%% 发送抽奖信息
send_lottery_info(Sid, SubType, Result) ->
    {ok, BinData} = pt_332:write(33264, [Result, SubType, 0, 0, 0, []]),
    lib_server_send:send_to_sid(Sid, BinData).

%% 获取进度值以及随机奖励
get_progress_and_reward(RandGrade, SubType, ProgressList, Times) ->
    TimesList = lists:seq(1, Times),
    F = fun(_, {Progress, RewardList, GradeList}) ->
        Grade = urand:rand_with_weight(RandGrade),
        AddProgress = urand:rand_with_weight(ProgressList),
        #rand_reward{reward_list = Rewards} = data_act_adventure:get_rand_reward(SubType, Grade),
        {Progress + AddProgress, Rewards ++ RewardList, [Grade | GradeList]}
        end,
    lists:foldl(F, {0, [], []}, TimesList).

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

%% 触发充值任务
refresh_recharge(RoleId, SubType, PayNum) ->
    SuitNum = custom_act_adventure:get_suit_num(SubType),
    KeyList = data_act_adventure:get_keys(SuitNum),
    Keys = [{ModId, SubId} || {ModId, SubId} <- KeyList, ModId =:= ?MOD_RECHARGE],
    F = fun({ModId, SubId}) ->
        Times = PayNum div SubId,
        {CustomData, ActData} = custom_act_adventure:get_act_data(RoleId, SubType),
        #adventure_road{score = Score, adventure_task = AdventureTask} = ActData,
        Result = lists:keyfind({ModId, SubId}, 1, AdventureTask),
        case data_act_adventure:get_task_cfg(SuitNum, ModId, SubId) of
            #base_hi_point{max_count = MaxCount, one_points = OnePoints} when Result =/= false ->
                {_, V} = Result,
                if
                    V >= Times orelse V >= MaxCount -> % 达到最大次数或未达到下一次数触发条件所需金额
                        NewActData = ActData#adventure_road{recharge = PayNum};
                    true ->
                        Max = min(Times, MaxCount),
                        NewTimes = Max - V,
                        NewScore = Score + NewTimes * OnePoints,
                        NewTaskInfo = lists:keystore({ModId, SubId}, 1, AdventureTask, {{ModId, SubId}, Max}),
                        NewActData = ActData#adventure_road{score = NewScore, recharge = PayNum, adventure_task = NewTaskInfo}
                end,
                NewCustomData = CustomData#custom_act_data{act_data = NewActData},
                lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
                send_task_finish_info(RoleId, SubType, NewActData);
            _ when Result =:= false -> skip;
            _ ->
                ?ERR("data_act_adventure config error SuitNum = ~p, ModId = ~p, SubId = ~p", [SuitNum, ModId, SubId])
        end
        end,
    lists:map(F, Keys),
    ok.

%% 触发消费彩钻任务
refresh_consume(Player, ActInfos, MoneyMap) ->
    #player_status{id = RoleId, gold = NewGold} = Player,
    OldGold = maps:get(old_gold, MoneyMap, NewGold),
    if is_integer(OldGold) andalso OldGold > NewGold -> %% 消费了
        Cost = round(OldGold - NewGold),
        lists:map(fun(#act_info{key = {_, SubType}}) ->
            SuitNum = custom_act_adventure:get_suit_num(SubType),
            KeyList = data_act_adventure:get_keys(SuitNum),
            case lists:keyfind(?MOD_CONSUME, 1, KeyList) of
                {ModId, SubId} ->
                    {CustomData, ActData} = custom_act_adventure:get_act_data(RoleId, SubType),
                    #adventure_road{score = Score, consume = Consume, adventure_task = AdventureTask} = ActData,
                    NewConsume = Cost + Consume,
                    Times = NewConsume div SubId,
                    Result = lists:keyfind({ModId, SubId}, 1, AdventureTask),
                    case data_act_adventure:get_task_cfg(SuitNum, ModId, SubId) of
                        #base_hi_point{max_count = MaxCount, one_points = OnePoints} when Result =/= false ->
                            {_, V} = Result,
                            if
                                V >= Times orelse V >= MaxCount ->
                                    NewActData = ActData#adventure_road{consume = NewConsume};
                                true ->
                                    Max = min(Times, MaxCount),
                                    NewTimes = Max - V,
                                    NewScore = Score + NewTimes * OnePoints,
                                    NewTaskInfo = lists:keystore({ModId, SubId}, 1, AdventureTask, {{ModId, SubId}, Max}),
                                    NewActData = ActData#adventure_road{score = NewScore, consume = NewConsume, adventure_task = NewTaskInfo}
                            end,
                            NewCustomData = CustomData#custom_act_data{act_data = NewActData},
                            lib_custom_act_data:save_custom_data(RoleId, NewCustomData),
                            send_task_finish_info(RoleId, SubType, NewActData);
                        _ when Result =:= false -> skip;
                        _ ->
                            ?ERR("data_act_adventure config error SuitNum = ~p, ModId = ~p, SubId = ~p", [SuitNum, ModId, SubId])
                    end;
                _ -> skip
            end
                  end, ActInfos);
        true -> %% 没有消费行为
            skip
    end,
    ok.

%% 使用物品增加嗨点值
use_goods_add(RoleId, Value) ->
    case lib_custom_act_util:get_open_subtype_list(?CUSTOM_ACT_TYPE_ADVENTURE_ROLE) of
        [#act_info{key = {_, SubType}} | _] ->
            {CustomData, ActData} = get_act_data(RoleId, SubType),
            #adventure_road{score = Score} = ActData,
            NewScore = Score + Value,
            NewActData = ActData#adventure_road{score = NewScore},
            NewCustomData = CustomData#custom_act_data{act_data = NewActData},
            lib_custom_act_data:save_custom_data(RoleId, NewCustomData);
        _ ->
            skip
    end.

act_end(#act_info{key = {ActType, SubType}}) ->
    OpenLv = lib_custom_act_util:get_act_open_lv(ActType, SubType),
    SQL = io_lib:format(?SELECT_OPEN_ACT_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, send_act_end_reward, [ActType, SubType]);
             false ->
                 send_offline_reward(RoleId, ActType, SubType)
         end
     end || RoleId <- Roles],
    ok.

%% 给在线玩家补发未领取奖励
send_act_end_reward(Player, ActType, SubType) ->
    {_CustomData, ActData} = get_act_data(Player#player_status.id, SubType),
    #adventure_road{progress = Progress} = ActData,
    RewardMap = Player#player_status.status_custom_act#status_custom_act.reward_map,
    send_act_end_reward(Player#player_status.id, ActType, SubType, Progress, RewardMap).

%% 补发离线玩家未领取奖励
send_offline_reward(RoleId, ActType, SubType) ->
    Progress = get_progress(RoleId, ActType, SubType),
    if
        Progress =< 0 -> skip;
        true ->
            List = db:get_all(io_lib:format(?SELECT_REWARD_RECEIVE_TIMES, [RoleId, ActType, SubType])),
            F = fun([Grade, ReceiveTimes], AccMap) ->
                RewardStatus = #reward_status{receive_times = ReceiveTimes},
                maps:put({ActType, SubType, Grade}, RewardStatus, AccMap)
                end,
            RewardMap = lists:foldl(F, #{}, List),
            send_act_end_reward(RoleId, ActType, SubType, Progress, RewardMap)
    end.

send_act_end_reward(RoleId, ActType, SubType, Progress, RewardMap) ->
    GradeIds = lib_custom_act_util:get_act_reward_grade_list(ActType, SubType),
    F = fun(GradeId, Acc) ->
        Times =
            case maps:get({ActType, SubType, GradeId}, RewardMap, []) of
                #reward_status{receive_times = ReceiveTimes} ->
                    ReceiveTimes;
                _ -> 0
            end,
        if
            Times =:= 0 -> % 只有未领过并且进度值满足条件才补发
                case lib_custom_act_util:get_act_reward_cfg_info(ActType, SubType, GradeId) of
                    #custom_act_reward_cfg{reward = Reward, condition = Condition} ->
                        case lists:keyfind(hi_point, 1, Condition) of
                            {hi_point, NeedProgress} when Progress >= NeedProgress -> Reward ++ Acc;
                            _ -> Acc
                        end;
                    _ -> Acc
                end;
            true -> Acc
        end
        end,
    RewardList = lists:foldl(F, [], GradeIds),
    if
        RewardList =/= [] ->
            lib_mail_api:send_sys_mail([RoleId], ActType, SubType, utext:get(33111301), utext:get(33111302), RewardList);
        true -> skip
    end.

%% 获取离线玩家进度值
get_progress(RoleId, Type, SubType) ->
    SQL = io_lib:format(?SELECT_CUSTOM_ACT_DATA, [RoleId, Type, SubType]),
    case db:get_row(SQL) of
        [] -> 0;
        [BinData] ->
            ActData = util:bitstring_to_term(BinData),
            case ActData of
                #adventure_road{progress = Progress} ->
                    Progress;
                _ -> 0
            end
    end.

%% 初始化活动数据
init_act_adventure(SubType) ->
    SuitNum = get_suit_num(SubType),
    AllTask = data_act_adventure:get_keys(SuitNum),
    AdventureTask = [{{Modid, Subid}, 0} || {Modid, Subid} <- AllTask],
    #adventure_road{adventure_task = AdventureTask}.

%% 获取本期活动对应的活动信息
get_suit_num(SubType) ->
    case lib_custom_act_util:get_act_cfg_info(?CUSTOM_ACT_TYPE_ADVENTURE_ROLE, SubType) of
        #custom_act_cfg{condition = Condition} ->
            case lists:keyfind(suit, 1, Condition) of
                {_, Suit} -> ok;
                _ -> Suit = 1
            end;
        _ ->
            Suit = 1
    end,
    Suit.

%% 更改热点值和进度值
gm_set_score_and_progress(Player, SubType, Score, Progress) ->
    #player_status{id = RoleId} = Player,
    case lib_custom_act_util:get_custom_act_open_info(?CUSTOM_ACT_TYPE_ADVENTURE_ROLE, SubType) of
        #act_info{} ->
            {CustomData, ActData} = get_act_data(RoleId, SubType),
            lib_custom_act_data:save_custom_data(RoleId,
                CustomData#custom_act_data{act_data = ActData#adventure_road{score = Score, progress = Progress}}),
            adventure_task_info(RoleId, SubType);
        _ ->
            skip
    end.

%% 重置活动数据
gm_reset(Player) ->
    #player_status{id = RoleId} = Player,
    case lib_custom_act_util:get_open_subtype_list(?CUSTOM_ACT_TYPE_ADVENTURE_ROLE) of
        ActInfos = [_ | _] ->
            lists:map(fun(#act_info{key = {_, SubType}}) ->
                CustomData = lib_custom_act_data:custom_act_data(?CUSTOM_ACT_TYPE_ADVENTURE_ROLE, SubType),
                #custom_act_data{act_data = OldActData} = CustomData,
                case OldActData of
                    #adventure_road{} ->
                        lib_custom_act_data:save_custom_data(RoleId, CustomData#custom_act_data{act_data = []}),
                        SQL = io_lib:format("DELETE FROM custom_act_receive_reward WHERE role_id = ~p and type = ~p and subtype = ~p", [RoleId, ?CUSTOM_ACT_TYPE_ADVENTURE_ROLE, SubType]),
                        db:execute(SQL),
                        adventure_task_info(RoleId, SubType);
                    _ ->
                        skip
                end
                      end, ActInfos);
        _ ->
            skip
    end.


