%% --------------------------------------
%% desc     :幻光副本逻辑
%% module   :lib_dungeon_visus.erl
%% author   :hh
%% time     :20/06/02
%% --------------------------------------


-module(lib_dungeon_visus).

-export([
    get_role_rec/6,
    get_start_dun_args/4,
    get_dun_result_succ_args/2,
    check_extra/2,
    get_send_reward/2,
    send_dun_rewards/1,
    check_quit_state/2,
    handle_wave_state/2,
    get_role_dun_born_xy/4,
    calc_visus_dun_sweep/1,
    get_last_highest_wave/1,
    get_daily_count_list/1,
    check_dun_all_wave_pass/2,
    send_dun_achieve_rewards/2
    ]).

-export([
    visus_dun_sweep/3,
    handle_event/2,
    role_update_visus_dun_wave_rec/4,
    role_update_visus_data/3,
    logic_daily_reset/1
    ]).

-export([
    gm_reset_dun_wave/3,
    calc_sql_args/0,
    login_init/2
    ]).

-include("errcode.hrl").
-include("server.hrl").
-include("dungeon.hrl").
-include("predefine.hrl").
-include("def_module.hrl").
-include("common.hrl").
-include("figure.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("def_fun.hrl").

handle_event(Player, #event_callback{type_id = ?EVENT_LOGIN_CAST}) ->
    #player_status{id = RoleId} = Player,
    Now = utime:unixtime(),
    RoleDunVisus = login_init(RoleId, Now),
    NewPlayer = Player#player_status{role_dun_visus = RoleDunVisus},
    {ok, NewPlayer}.


login_init(RoleId, Now) ->
    DunArgs = calc_sql_args(),
    SQL = io_lib:format(?SQL_VISUS_DUN_REC_SELECT, [RoleId, DunArgs]),
    case db:get_all(SQL) of
        VisusDuns = [_|_] -> ok;
        _   -> VisusDuns = []
    end,
    DungeonRecs =
    lists:foldl(fun([DunIdX, DunDataX], TempMap) -> 
        maps:put(DunIdX, util:bitstring_to_term(DunDataX), TempMap)
    end, #{}, VisusDuns),
    % ?ERR("~p ~p", [VisusDuns, DungeonRecs]),
    case db:get_all(?SQL_VISUS_DUN_SELECT, [RoleId]) of
        DbData = [_|_] ->
            init_role_dun_visus_data(RoleId, Now, DbData, DungeonRecs);
        _ ->
            []
    end.

calc_sql_args() ->
    DunIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_VISUS),
    DunArgs1 =
    lists:foldl(fun(DunIdX, Acc) -> 
        DunData = lists:concat(["dun_id = ", DunIdX]),
        [DunData|Acc]
    end, [], DunIds),
    DunArgs2 = ulists:implode(" or ", DunArgs1),
    DunArgs3 = "(" ++ lists:flatten(DunArgs2) ++ ")",
    DunArgs3.

init_one_visus_data(RoleId, Now, DunId, DunData) ->
    case db:get_row(?SQL_ONE_VISUS_DUN_SELECT, [RoleId, DunId]) of
        [PassWavesBin, Time, LastHighestWaveBin|_] ->
            PassWaves = util:bitstring_to_term(PassWavesBin),
            LastHighestWave = util:bitstring_to_term(LastHighestWaveBin),
            case utime:is_logic_same_day(Time, Now, ?DAY_LOGIC_OFFSET_HOUR) of
                true ->
                    [{DunId, PassWaves, Time, LastHighestWave}];
                _ ->
                    if PassWaves =/= [] ->
                            NewLastHighestWave = lists:max(PassWaves),
                            NewHighestWaveData = [{NewLastHighestWave, Time}],
                            [{DunId, [], Time, NewHighestWaveData}];
                        true ->
                            case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunData) of
                                {_, DunMaxWave} -> ok;
                                _   -> DunMaxWave = 0
                            end,
                            NewHighestWaveData = [{DunMaxWave, Time}],
                            [{DunId, [], Time, NewHighestWaveData}]
                    end
            end;
        _ ->
            []
    end.

init_role_dun_visus_data(RoleId, Now, Data, DungeonRecs) ->
    {DunVisusData, ChangeData} =
    lists:foldl(fun([DunId, WaveBin, Time, LastHighestWaveBin], {Acc1, Acc2}) ->
        Wave = util:bitstring_to_term(WaveBin),
        LastHighestWave = util:bitstring_to_term(LastHighestWaveBin),
        case utime:is_logic_same_day(Time, Now, ?DAY_LOGIC_OFFSET_HOUR) of
            true ->
                {[{DunId, Wave, Time, LastHighestWave}|Acc1], Acc2};
            _ ->
                if Wave =/= [] ->
                        NewLastHighestWave  = lists:max(Wave),
                        LastHighestWaveData = [{NewLastHighestWave, Time}],
                        LastWaveDataBin     = util:term_to_bitstring(LastHighestWaveData),
                        NewWaveBin = util:term_to_bitstring([]);
                    true ->
                        DunRec = maps:get(DunId, DungeonRecs, []),
                        case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
                            {_, DunMaxWave} -> ok;
                            _   -> DunMaxWave = 0
                        end,
                        LastHighestWaveData = [{DunMaxWave, Time}],
                        LastWaveDataBin     = util:term_to_bitstring(LastHighestWaveData),
                        NewWaveBin = util:term_to_bitstring([])
                end,
                {[{DunId, [], Now, LastHighestWaveData}|Acc1], [{DunId, NewWaveBin, Now, LastWaveDataBin}|Acc2]}
        end
    end, {[], []}, Data),
    SqlList = [io_lib:format(?SQL_VISUS_DUN_BATCH_VAL, [RoleId, DunIdX, WavesX, TimeX, LastHighestWaveX]) 
    || {DunIdX, WavesX, TimeX, LastHighestWaveX} <- ChangeData],
    Sql = io_lib:format(?SQL_VISUS_DUN_BATCH_REPLACE, [string:join(SqlList, ",")]),
    SqlList =/= [] andalso db:execute(Sql),
    DunVisusData.

%% 获取人物副本相关记录
get_role_rec(_DunId, _DunType, _RoleId, DungeonRec, _DunData, TypicalData) ->
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DungeonRec) of
        {_, MaxDunWave} -> ok;
        _ -> MaxDunWave = 0
    end,
    NewTypicalData  = maps:put(?DUN_ROLE_VISUS_INIT_WAVE, MaxDunWave+1, TypicalData),
    NewTypicalData1 = maps:put(?DUN_ROLE_VISUS_INIT_MAX_WAVE, MaxDunWave, NewTypicalData),
    NewTypicalData2 = maps:put(?DUNGEON_REC_MAX_SCORE, MaxDunWave, NewTypicalData1),
    NewTypicalData2.

%% 获取初始化参数
get_start_dun_args(_Player, DunRole, _DunCfg, _OptionType) ->
    #dungeon_role{typical_data = TypicalData} = DunRole,
    Wave = maps:get(?DUN_ROLE_VISUS_INIT_WAVE, TypicalData, 1),
    [{wave, Wave}]. %, {typical_data, [{?DUN_STATE_EXP_DUN_AUTO, OptionType}]}].

% dun_type,old_dun_wave,new_dun_wave,result_type,result_subtype,use_time
get_dun_result_succ_args(DunRole, [ResultSubType, WaveNum, LastWave|_]) ->
    #dungeon_role{typical_data = TypicalData} = DunRole,
    OldMaxDunWave = maps:get(?DUN_ROLE_VISUS_INIT_MAX_WAVE, TypicalData, 0),
    [ResultSubType, OldMaxDunWave, LastWave, ?IF(LastWave == WaveNum, 1, 0)].

get_role_dun_born_xy(State, _, X, Y) ->
    #dungeon_state{dun_id = DunId, wave_num = Wave} = State,
    case data_dungeon_wave:get_dun_wave(DunId, Wave) of
        #base_dun_wave_cfg{born_xy = [{CfgX, CfgY}|_]} ->
            {CfgX, CfgY};
        _ ->
            {X, Y}
    end.

check_extra(Player, Dun) ->
    #player_status{dungeon_record = DunRecs} = Player,
    #dun_cfg{id = DunId, condition = Condition} = Dun,
    DunRec = maps:get(DunId, DunRecs, []),
    Res =
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
        {_, MaxWave} ->
            DunIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_VISUS),
            DunWaves = data_dungeon_wave:get_dun_waves(DunId),
            MaxDunId = lists:max(DunIds),
            MaxConfigWave = lists:max(DunWaves),
            if DunId >= MaxDunId andalso MaxWave >= MaxConfigWave ->
                    {false, ?ERRCODE(err610_just_once_finish)};
                true ->
                    true
            end;
        _ ->
            true
    end,
    case Res of
        {false, Errcode} ->
            {false, Errcode};
        _ ->
            case lists:keyfind(finish_dun_id, 1, Condition) of
                {_, FinishDunId} ->
                    FinishDunRec = maps:get(FinishDunId, DunRecs, []),
                    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, FinishDunRec) of
                        {_, FinishMaxWave} ->
                            DunWaves1 = data_dungeon_wave:get_dun_waves(FinishDunId),
                            MaxConfigWave1 = lists:max(DunWaves1),
                            if FinishMaxWave >= MaxConfigWave1 ->
                                    true;
                                true ->
                                    {false, ?ERRCODE(err610_dun_pet_pre_must)}
                            end;
                        _ ->
                            {false, ?ERRCODE(err610_dun_pet_pre_must)}
                    end;
                _ ->
                    true
            end
    end.

logic_daily_reset(Player) ->
    % #player_status{role_dun_visus = RoleDunVisus,} = Player,
    #player_status{role_dun_visus = RoleDunVisus, dungeon_record = DungeonRecs} = NewPlayer = lib_dungeon:load_dungeon_record(Player),
    Now = utime:unixtime(),
    NewRoleDunVisus =
    lists:map(fun({DunId, Waves, _Time, _LastHighestWave}) ->
        if Waves =/= [] ->
                NewLastHighestWave  = lists:max(Waves),
                LastHighestWaveData = [{NewLastHighestWave, _Time}],
                {DunId, [], Now, LastHighestWaveData};
            true ->
                DunRec = maps:get(DunId, DungeonRecs, []),
                case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
                    {_, DunMaxWave} -> ok;
                    _   -> DunMaxWave = 0
                end,
                LastHighestWaveData = [{DunMaxWave, _Time}],
                {DunId, [], Now, LastHighestWaveData}
        end
    end, RoleDunVisus),
    NewPlayer1 = NewPlayer#player_status{role_dun_visus = NewRoleDunVisus},
    pp_dungeon:handle(61071, NewPlayer, []),
    NewPlayer1.

%% 获取副本结算奖励
get_send_reward(DungeonState, DungeonRole) ->
    #dungeon_state{dun_type = DunType, dun_id = DunId, result_type = ResultType, wave_num = WaveNum, wave_info = WaveInfos} = DungeonState,
    if ResultType =:= ?DUN_RESULT_TYPE_SUCCESS ->
            #dungeon_role{typical_data = TypicalData, id = RoleId} = DungeonRole,
            case maps:get(?DUN_ROLE_VISUS_INIT_WAVE, TypicalData, false) of
                InitWave when is_integer(InitWave) andalso WaveNum >= InitWave ->
                    {RewardWaves, Rewards} = calc_visus_dun_wave_reward(DunType, DunId, InitWave, WaveNum, WaveInfos),
                    % ?ERR("~p", [Rewards]),
                    Pid = misc:get_player_process(RoleId),
                    case is_pid(Pid) andalso misc:is_process_alive(Pid) of
                        true ->
                            lib_player:apply_cast(DungeonRole#dungeon_role.id, ?APPLY_CAST_SAVE, ?NOT_HAND_OFFLINE, ?MODULE, role_update_visus_data, [DunId, RewardWaves]);
                        _ ->
                            db_update_role_dun_visus(RoleId, DunId, RewardWaves)
                    end,
                    DungeonRole#dungeon_role{calc_reward_list = Rewards};
                _ ->
                    DungeonRole
            end;
        true ->
            DungeonRole
    end.

role_update_visus_data(Player, DunId, RewardWaves) ->
    #player_status{id = RoleId, role_dun_visus = RoleDunVisus} = Player,
    Now = utime:unixtime(),
    case lists:keyfind(DunId, 1, RoleDunVisus) of
        {_, Waves, Time, LastHighestWave} ->
            case utime:is_today_four(Time) of
                true ->
                    NewWaves = do_update_visus_waves(Waves, RewardWaves);
                _ ->
                    NewWaves = RewardWaves
            end,
            WaveBin = util:term_to_bitstring(NewWaves),
            LastHighestWaveBin = util:term_to_bitstring(LastHighestWave),
            db:execute(?SQL_VISUS_DUN_REPLACE, [RoleId, DunId, WaveBin, Now, LastHighestWaveBin]),
            NewRoleDunVisus = lists:keystore(DunId, 1, RoleDunVisus, {DunId, NewWaves, Now, LastHighestWave}),
            {ok, Player#player_status{role_dun_visus = NewRoleDunVisus}};
        _ ->
            WaveBin = util:term_to_bitstring(RewardWaves),
            db:execute(?SQL_VISUS_DUN_REPLACE, [RoleId, DunId, WaveBin, Now, util:term_to_bitstring([])]),
            NewRoleDunVisus = lists:keystore(DunId, 1, RoleDunVisus, {DunId, RewardWaves, Now, []}),
            {ok, Player#player_status{role_dun_visus = NewRoleDunVisus}}
    end.

db_update_role_dun_visus(RoleId, DunId, RewardWaves) ->
    Now = utime:unixtime(),
    case db:get_row(?SQL_SP_EXP_DUN_REC_SELECT, [RoleId, DunId]) of
        [DunDataBin|_] ->
            DunData = util:bitstring_to_term(DunDataBin);
        _ ->
            DunData = []
    end,
    OneDunData = init_one_visus_data(RoleId, Now, DunId, DunData),
    case lists:keyfind(DunId, 1, OneDunData) of
        {_, Waves, _Time, LastHighestWave} ->
            NewWaves = do_update_visus_waves(Waves, RewardWaves),
            WaveBin = util:term_to_bitstring(NewWaves),
            LastHighestWaveBin = util:term_to_bitstring(LastHighestWave),
            db:execute(?SQL_VISUS_DUN_REPLACE, [RoleId, DunId, WaveBin, Now, LastHighestWaveBin]);
        _ ->
            WaveBin = util:term_to_bitstring(RewardWaves),
            db:execute(?SQL_VISUS_DUN_REPLACE, [RoleId, DunId, WaveBin, Now, util:term_to_bitstring([])])
            % lists:keystore(DunId, 1, DunVisusData, {DunId, RewardWaves, Now, []})
    end.

do_update_visus_waves(Waves, NewPassWaves) ->
    lists:foldl(fun(NewWave, Acc) ->
        case lists:member(NewWave, Acc) of
            true -> Acc;
            _    -> [NewWave|Acc]
        end
    end, Waves, NewPassWaves).

calc_visus_dun_wave_reward(DunType, DunId, InitWave, WaveNum, WaveInfos) ->
    PassWaves = check_new_pass_wave(InitWave, WaveNum, WaveInfos),
    calc_wave_rewards_do(PassWaves, DunType, DunId, [], []).

%% 首通+当日扫荡
calc_wave_rewards_do([], _DunType, _, RewardWaves, Rewards) ->
    {RewardWaves, Rewards};
calc_wave_rewards_do([Wave|Rest], DunType, DunId, RewardWaves, Rewards) ->
    case data_dungeon_wave:get_dun_wave(DunId, Wave) of
        #base_dun_wave_cfg{first_reward = FirstRewards, normal_reward = NormalReward} ->
            calc_wave_rewards_do(Rest, DunType, DunId, [Wave|RewardWaves], lists:append([FirstRewards, NormalReward, Rewards]));
        _ ->
            calc_wave_rewards_do(Rest, DunType, DunId, RewardWaves, Rewards)
    end.

send_dun_rewards(State) ->
    #dungeon_state{role_list = RoleList, is_end = IsEnd} = State,
    NewRoleList = lists:foldl(fun(DungeonRole, Acc) ->
        case DungeonRole of
            #dungeon_role{is_reward = ?DUN_IS_REWARD_NO, online = OL} = DungeonRole
             when OL =:= ?DUN_ONLINE_YES orelse OL =:= ?DUN_ONLINE_DELAY ->
                case IsEnd of
                    ?DUN_IS_END_YES ->
                        NewDungeonRole = DungeonRole#dungeon_role{is_end_out = ?DUN_IS_END_OUT_YES};
                    _ ->
                        NewDungeonRole = DungeonRole
                end,
                [NewDungeonRole|Acc];
            _ ->
                [DungeonRole|Acc]
        end
    end, [], RoleList),
    State#dungeon_state{role_list = NewRoleList}.

handle_wave_state(State, [WaveState|_] = Args) ->
    #dungeon_state{dun_id = DunId, wave_num = WaveNum, role_list = RoleList, wave_info = WaveInfos} = State,
    NewRoleList1 = if WaveState == ?WAVE_NEXT orelse WaveState == ?DUN_PASS ->
            NewRoleList = update_visus_dun_wave_rec(DunId, WaveNum, RoleList),
            settle_wave_reward(WaveInfos, DunId, WaveNum, WaveState, NewRoleList);
        true ->
            RoleList
    end,
    State0 = State#dungeon_state{role_list = NewRoleList1},
    if WaveState == ?DUN_PASS ->
            dungeon_default:handle_wave_state_normal(State0, Args);
        WaveState == ?WAVE_NEXT ->
            do_handle_next_wave_state(State0);
        true ->
            State0
    end.

do_handle_next_wave_state(State) ->
    #dungeon_state{dun_id = DunId, wave_num = OldWave, wave_ref = [WaveStartRef, WaveEndRef|_]} = State,
    NewWave = OldWave+1,
    DunWaveCfg = data_dungeon_wave:get_dun_wave(DunId, NewWave),
    if is_record(DunWaveCfg, base_dun_wave_cfg) ->
            #base_dun_wave_cfg{create_time = CreateTime, limit_time = LimitTime, mon_list = MonList, next_wave = NextWave} = DunWaveCfg,
            Now = utime:unixtime(),
            WaveStartTime = Now + CreateTime,
            NewState = create_next_dun_wave(NewWave, NextWave, length(MonList), WaveStartTime, State),
            if CreateTime == 0 ->
                    NewWaveStartRef = WaveStartRef,
                    NewState1 = dungeon_default:create_dun_wave_mon(MonList, NewState#dungeon_state.wave_num, NewState);
                true ->
                    NewWaveStartRef = util:send_after(WaveStartRef, CreateTime*1000, self(), {'create_dun_wave_mon', NewState#dungeon_state.wave_num, MonList}),
                    NewState1 = NewState
            end,
            %% 波数截止
            if LimitTime == 0 ->
                    NewWaveEndRef = WaveEndRef;
                true ->
                    NewWaveEndRef = util:send_after(WaveEndRef, LimitTime*1000, self(), {'wave_timeout', DunId, NewWave, NewState1#dungeon_state.index_of_wave})
            end,
            NewState1#dungeon_state{wave_ref = [NewWaveStartRef, NewWaveEndRef]};
        true ->
            State
    end.

settle_wave_reward([#dun_wave_info{status = WaveStatus}|_], DunId, Wave, WaveState, RoleList) ->
    %% 副本多倍活动
    [#dungeon_role{figure = Figure}|_ ] = RoleList,
    MultiTimes = lib_dungeon_api:get_custom_act_reward_times(?DUNGEON_TYPE_VISUS, 0, Figure#figure.lv),
    case data_dungeon_wave:get_dun_wave(DunId, Wave) of
        #base_dun_wave_cfg{normal_reward = NormalReward, first_reward = FirstRewards} ->
            lists:foldl(fun(Role = #dungeon_role{id = RoleId, typical_data = TypicalData, is_reward = IsReward}, Acc) ->
                if WaveStatus == ?DUN_WAVE_UNFIN ->
                        CustomReward = [];
                    true ->
                        case MultiTimes > 1 of
                            true -> CustomReward = [{TGoodsType, TGoodsId, TGoodsNum*max(1, MultiTimes-1)}||{TGoodsType, TGoodsId, TGoodsNum}<-NormalReward];
                            _    -> CustomReward = []
                        end,
                        lib_goods_api:send_reward_by_id(NormalReward++FirstRewards++CustomReward, visus_wave, RoleId)
                end,
                Waves = maps:get(?DUN_ROLE_VISUS_REWARD_WAVE, TypicalData, []),
                NewWaves =
                case lists:member(Wave, Waves) of
                    true -> Waves;
                    _    -> [Wave|Waves]
                end,
                NewIsReward =
                case WaveState of
                    dungeon_pass -> ?DUN_IS_REWARD_YES;
                    _ -> IsReward
                end,
                NewTypicalData = maps:put(?DUN_ROLE_VISUS_REWARD_WAVE, NewWaves, TypicalData),
                OldCustomReward = Role#dungeon_role.custom_reward_list,
                NewCustomReward = OldCustomReward ++ CustomReward,
                [Role#dungeon_role{typical_data = NewTypicalData, is_reward = NewIsReward, custom_reward_list = NewCustomReward}|Acc]
            end, [], RoleList);
        _ -> %% 配置问题
            RoleList
    end.

create_next_dun_wave(Wave, NextWave, MonNum, WaveStartTime, State) ->
    #dungeon_state{index_of_wave = WaveIndex} = State,
    % ?ERR("~p", [State#dungeon_state.wave_info]),
    WaveKey = {Wave, WaveIndex},
    WaveInfo = dungeon_default:make_record(dun_wave_info, [WaveKey, 0, MonNum, ?DUN_WAVE_UNFIN, #{start_time => WaveStartTime}]),
    NewWaveInfos = [WaveInfo],
    if Wave =/= NextWave ->
            NewWaveIndex = WaveIndex;
        true ->
            NewWaveIndex = WaveIndex+1
    end,
    State#dungeon_state{wave_num = Wave, index_of_wave = NewWaveIndex, wave_info = NewWaveInfos}.


update_visus_dun_wave_rec(DunId, WaveNum, RoleList) ->
    lists:foldl(fun(DunRole, TmpRoleList) ->
        NewDunRole = do_update_visus_dun_wave_rec(DunId, WaveNum, DunRole),
        [NewDunRole|TmpRoleList]
     end, [], RoleList).

do_update_visus_dun_wave_rec(DunId, WaveNum, DunRole) ->
    #dungeon_role{id = RoleId, typical_data = TypicalData} = DunRole,
    OldMaxWave = maps:get(?DUNGEON_REC_MAX_SCORE, TypicalData, 0),
    if WaveNum > OldMaxWave ->
            NewTypicalData = maps:put(?DUNGEON_REC_MAX_SCORE, WaveNum, TypicalData),
            lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?MODULE, role_update_visus_dun_wave_rec, [DunId, ?DUNGEON_REC_MAX_SCORE, WaveNum]),
            DunRole#dungeon_role{typical_data = NewTypicalData};
        true ->
            DunRole
    end.

role_update_visus_dun_wave_rec(Player, DunId, Type, WaveNum) ->
    #player_status{id = RoleId, dungeon_record = DunRecs} = Player,
    DunRec = maps:get(DunId, DunRecs, []),
    NewDunRec = lists:keystore(Type, 1, DunRec, {Type, WaveNum}),
    NewDunRecs = maps:put(DunId, NewDunRec, DunRecs),
    NewPlayer = Player#player_status{dungeon_record = NewDunRecs},
    lib_dungeon:save_dungeon_record(RoleId, DunId, NewDunRec),
    %% 成就
    Value = max(0, DunId rem 11000),
    lib_achievement_api:dungeon_visus_event(Player, {Value, WaveNum}),
    lib_elite_assess_api:visus_dun_event(Player, {DunId, WaveNum}),
    visus_dun_rank:refresh_visus_rank(NewPlayer, DunId, WaveNum),
    {ok, NewPlayer}.

%% 幻光副本扫荡
visus_dun_sweep(Player, DunWaveRecs, DailyCountList) ->
    #player_status{id = RoleId, sid = Sid, role_dun_visus = RoleDunVisus, figure = Figure} = Player,
    SweepWaves = calc_dun_sweep_waves(DunWaveRecs, DailyCountList, RoleDunVisus),
    %% 副本多倍活动
    Times = lib_dungeon_api:get_custom_act_reward_times(?DUNGEON_TYPE_VISUS, 0, Figure#figure.lv),
    case calc_visus_dun_sweep(SweepWaves) of
        Rewards = [_|_] ->
            case Times > 1 of
                true ->
                    Rewards1 = [{TGoodsType, TGoodsId, TGoodsNum*Times}||{TGoodsType, TGoodsId, TGoodsNum}<-Rewards],
                    CustomRewards = [{TGoodsType1, TGoodsId1, TGoodsNum1*max(1, Times-1)}||{TGoodsType1, TGoodsId1, TGoodsNum1}<-Rewards];
                _ ->
                    Rewards1 = Rewards,
                    CustomRewards = []
            end,
            update_visus_dun_sweep_count(RoleId, DunWaveRecs),
            NewPlayer = lib_goods_api:send_reward(Player, Rewards1, visus_dun_sweep),
            NewPlayer1 = visus_dun_sweep_trigger(DunWaveRecs, NewPlayer),
            lib_server_send:send_to_sid(Sid, pt_610, 61072, [?SUCCESS, Rewards, CustomRewards]),
            NewPlayer1;
        _ ->
            case lists:any(fun({_DunId, MaxWave}) ->
                MaxWave > 0
            end, DunWaveRecs) of
                true ->
                    lib_server_send:send_to_sid(Sid, pt_610, 61072, [?ERRCODE(err610_dun_have_all_sweeped), []]);
                _ ->
                    lib_server_send:send_to_sid(Sid, pt_610, 61072, [?ERRCODE(err610_nothing_sweep), []])
            end,
            Player
    end.

%% 计算可扫荡的副本波数
calc_dun_sweep_waves(DunWaveRecs, DailyCountList, RoleDunVisus) ->
    lists:foldl(fun({DunId, MaxWave}, Acc) ->
        case lists:keyfind(DunId, 1, DailyCountList) of
            {_, DailyCount} ->
                if DailyCount < MaxWave ->
                        SweepWaves = lists:seq(DailyCount, MaxWave),
                        HaveRewardedWaves = get_dun_visus_data(DunId, RoleDunVisus),
                        SweepWaves1 = SweepWaves -- HaveRewardedWaves,
                        case SweepWaves1 of
                            [_|_] ->
                                [{DunId, SweepWaves1}|Acc];
                            _ ->
                                Acc
                        end;
                    true ->
                        Acc
                end;
            _ ->
                Acc
        end
    end, [], DunWaveRecs).

get_dun_visus_data(DunId, RoleDunVisus) ->
    case lists:keyfind(DunId, 1, RoleDunVisus) of
        {_, PassWaves, Time, _} ->
            case utime:is_today_four(Time) of
                true ->
                    PassWaves;
                _ ->
                    []
            end;
        _ ->
            []
    end.

%% 计算可扫荡的副本波数奖励
calc_visus_dun_sweep(SweepWaves) ->
    lists:foldl(fun({DunId, Waves}, Rewards) ->
        WaveRewards = lists:foldl(fun(WaveId, Acc) ->
            case data_dungeon_wave:get_dun_wave(DunId, WaveId) of
                #base_dun_wave_cfg{normal_reward = NormalReward} ->
                    lists:append(NormalReward, Acc);
                _ ->
                    Acc
            end
        end, [], Waves),
        lists:append(WaveRewards, Rewards)
    end, [], SweepWaves).

%% 更新幻光副本日常次数
update_visus_dun_sweep_count(RoleId, DunWaveRecs) ->
    [lib_daily:set_count_offline(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_PASS_LV, DunId, Wave) || {DunId, Wave} <- DunWaveRecs],
    % lib_daily:set_count_offline(RoleId, DailyCountList),
    ok.

%% 幻光副本扫荡触发
visus_dun_sweep_trigger(DunWaveRecs, Player) ->
    TriggerDuns = lists:foldl(fun({DunId, MaxWave}, Acc) ->
        DunWaves = data_dungeon_wave:get_dun_waves(DunId),
        if DunWaves == [] ->
                Acc;
            true ->
                ConfigMaxWave = lists:max(DunWaves),
                if MaxWave >= ConfigMaxWave ->
                        [DunId|Acc];
                    true ->
                        Acc
                end
        end
    end, [], DunWaveRecs),
    lib_dungeon_sweep:sweep_dungeon_trigger(Player, ?DUNGEON_TYPE_VISUS, TriggerDuns).

check_quit_state(State, RoleId) ->
    #dungeon_state{is_end = IsEnd, role_list = RoleList, wave_num = WaveNum, wave_info = WaveInfos, result_type = ResultType, result_subtype = ResultSubtype} = State,
    if
        WaveInfos == [] ->
            {?DUN_RESULT_TYPE_FAIL, ?DUN_RESULT_SUBTYPE_ACTIVE_QUIT};
        IsEnd =/= ?DUN_IS_END_YES ->
            case lists:keyfind(RoleId, #dungeon_role.id, RoleList) of
                #dungeon_role{typical_data = TypicalData, hp = Hp} ->
                    InitWave = maps:get(?DUN_ROLE_VISUS_INIT_WAVE, TypicalData, 1),
                    ResultType1 =
                    case check_new_pass_wave(InitWave, WaveNum, WaveInfos) of
                        [_|_] ->
                            ?DUN_RESULT_TYPE_SUCCESS;
                        _ ->
                            ?DUN_RESULT_TYPE_FAIL
                    end,
                    ResultSubtype1 =
                    if Hp > 0 ->
                            ?DUN_RESULT_SUBTYPE_ACTIVE_QUIT;
                        true ->
                            ?DUN_RESULT_SUBTYPE_ROLE_DIE
                    end,
                    {ResultType1, ResultSubtype1};
                _ ->
                    {?DUN_RESULT_TYPE_FAIL, ?DUN_RESULT_SUBTYPE_ACTIVE_QUIT}
            end;
        true ->
            {ResultType, ResultSubtype}
    end.

check_new_pass_wave(InitWave, _WaveNum, WaveInfos) ->
    SortedWaveInfos = lists:sort(
        fun(#dun_wave_info{key = {W1, _}}, #dun_wave_info{key = {W2, _}}) -> W1 >= W2 end,
    WaveInfos),
    case SortedWaveInfos of
        [#dun_wave_info{key = {Wave, _Index}, status = Status}|_] ->
            ResultPassWave =
                if Status == ?DUN_WAVE_UNFIN -> max(0, Wave-1);
                    true -> Wave
                end,
            lists:seq(InitWave, ResultPassWave);
        _ ->
            []
    end.

%% 获取昨日最高通关波数
get_last_highest_wave(RoleDunVisus) ->
    case lists:sort(fun({DunId1, _, _Time1, _}, {DunId2, _, _Time2, _}) -> 
        DunId1 >= DunId2
    end, RoleDunVisus) of
        [{DunId, _SweepWaves, _Time, [{MaxWave, _}|_]}|_] ->
            {DunId, MaxWave};
        _ ->
            {0, 0}
    end.

get_daily_count_list(DunIds) ->
    get_daily_count_list(DunIds, [], []).

get_daily_count_list([], DailyPassCountList, DailySweepCountList) ->
    {DailyPassCountList, DailySweepCountList};
get_daily_count_list([DunId|Rest], DailyPassCountList, DailySweepCountList) ->
    DailyPassCountList1  = [{?MOD_DUNGEON, ?MOD_DUNGEON_PASS_LV, DunId}|DailyPassCountList],
    DailySweepCountList1 = [{?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunId}|DailySweepCountList],
    get_daily_count_list(Rest, DailyPassCountList1, DailySweepCountList1).

check_dun_all_wave_pass(DunId, DunRec) ->
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
        {_, MaxWave} -> ok;
        _ -> MaxWave = 0
    end,
    DunWaves = data_dungeon_wave:get_dun_waves(DunId),
    MaxConfigWave = lists:max(DunWaves),
    if MaxWave >= MaxConfigWave ->
            true;
        true ->
            false
    end.

send_dun_achieve_rewards(Player, DunId) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    #dun_cfg{first_reward = FirstRewards} = data_dungeon:get_dun(DunId),
    case FirstRewards of
        [_|_] ->
            Player1 = lib_goods_api:send_reward(Player, FirstRewards, visus_dun_sweep),
            lib_counter:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunId, 1),
            lib_server_send:send_to_sid(Sid, pt_610, 61087, [?SUCCESS, DunId, 2]),
            {ok, Player1};
        _ ->
            lib_server_send:send_to_sid(Sid, pt_610, 61087, [?FAIL, DunId, 0])
    end.

gm_reset_dun_wave(MaxWave, Level, Status) ->
    DunId = gm_dun_visus_map(Level),
    if DunId == 0 ->
            Status;
        true ->
            #player_status{id = RoleId, dungeon_record = DunRecs} = Status,
            DunIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_VISUS),
            NewDunRecs = lists:foldl(fun(DunIdX, Acc) ->
                db:execute(<<"DELETE FROM dungeon_best_record WHERE player_id = ~p AND dun_id = ~p">>, [RoleId, DunIdX]),
                maps:remove(DunIdX, Acc)
            end, DunRecs, DunIds),
            db:execute(<<"DELETE FROM role_dun_visus WHERE role_id = ~p">>, [RoleId]),
            CountL = [{{?MOD_DUNGEON, ?MOD_DUNGEON_PASS_LV, DunIdX2}, 0} || DunIdX2 <- DunIds],
            lib_daily:set_count(RoleId, CountL),
            case lists:filter(fun(DunIdX1) -> DunIdX1 =< DunId end, DunIds) of
                AvaliableL = [_|_] ->
                    NewDunRecs1 = lists:foldl(fun(DunIdX3, TmpAcc) ->
                        if DunIdX3 < DunId ->
                                DunMaxWave = lists:max(data_dungeon_wave:get_dun_waves(DunIdX3)),
                                DunRec = [{?DUNGEON_REC_MAX_SCORE, DunMaxWave}],
                                lib_dungeon:save_dungeon_record(RoleId, DunIdX3, DunRec),
                                maps:put(DunIdX3, DunRec, TmpAcc);
                            true ->
                                DunRec = [{?DUNGEON_REC_MAX_SCORE, MaxWave}],
                                lib_dungeon:save_dungeon_record(RoleId, DunIdX3, DunRec),
                                maps:put(DunIdX3, DunRec, TmpAcc)
                        end
                     end, NewDunRecs, AvaliableL),
                    NewStatus = Status#player_status{dungeon_record = NewDunRecs1, role_dun_visus = []},
                    pp_dungeon:handle(61020, NewStatus, [?DUNGEON_TYPE_VISUS]),
                    NewStatus;
                _ ->
                    Status#player_status{role_dun_visus = []}
            end
    end.

gm_dun_visus_map(1) -> 11001;
gm_dun_visus_map(2) -> 11002;
gm_dun_visus_map(3) -> 11003;
gm_dun_visus_map(_) -> 0.