%% ------------------------------
%% module:lib_dungeon_exp.erl
%% author:hh
%% time  :20/01/02
%% desc  :经验副本
%% ------------------------------

-module(lib_dungeon_exp).

-export([
    dispatch_dun_event/2

    ,get_role_dun_born_xy/4
    ,get_dun_result_succ_args/2
    ,handle_wave_state/2
    ,send_dun_rewards/1
    ,push_settlement/2
    ,get_start_dun_args/4
    ,get_role_rec/6
    ,get_send_reward/2
    ,get_sweep_wave/5
    ,get_dun_sweep_sign/2

    ,check_quit_state/2
    ,handle_dungeon_result/1
    ,check_extra/2
    ,sweep_exp_dun/1
    ]).

-export([
    do_wave_update_role_scene/5
    ,role_dun_pass_lv_update/4
    ,role_exp_dun_log/4
    ,calc_exp_dun_wave_rewards/3
    ]).

-export([
    login/1
    ,db_update_exp_dun_achieve/2
    ,get_achieve_info/1
    ,calc_new_achive_wave/2
    ,update_exp_dun_achieve/5
    ]).

-include("dungeon.hrl").
-include("common.hrl").
-include("def_module.hrl").
-include("predefine.hrl").
-include("errcode.hrl").
-include("server.hrl").
-include("exp_dun_drop.hrl").
-include("figure.hrl").
-include("def_goods.hrl").
-include("def_fun.hrl").

-define(TASK_SP_WAVE,  5).

dispatch_dun_event(Msg, State) ->
    dungeon_default:dispatch_dun_event(Msg, State).


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) ->
    if WaveState =/= ?WAVE_NEXT ->
            #dungeon_state{dun_id = DunId, wave_num = WaveNum, role_list = RoleList, wave_info = WaveInfos} = State,
            case data_dungeon_wave:get_next_wave(DunId, WaveNum) of
                [0] -> %% 已到最大波 历史最佳波数及日常层数触发
                    NewRoleList = update_role_dun_pass_lv(DunId, WaveNum, RoleList),
                    NewState = State#dungeon_state{role_list = NewRoleList};
                _ ->
                    NewState = State
            end,
            NewRoleList1 = settle_wave_reward(WaveInfos, DunId, WaveNum, WaveState, NewState#dungeon_state.role_list),
            dungeon_default:handle_wave_state_normal(NewState#dungeon_state{role_list = NewRoleList1}, Args);
        true -> %% 下一波
            #dungeon_state{dun_id = DunId, wave_num = WaveNum, role_list = RoleList, wave_info = WaveInfos} = State,
            RoleList0 = settle_wave_reward(WaveInfos, DunId, WaveNum, WaveState, RoleList),
            %% 历史最佳波数及日常层数
            [#dungeon_role{typical_data = TypicalData}|_] = NewRoleList = update_role_dun_pass_lv(DunId, WaveNum, RoleList0),
            DuringMainTask  = maps:get(?DUN_ROLE_EXP_DUN_DOING_TASK, TypicalData, 0),
            if DuringMainTask == 1 andalso WaveNum == ?TASK_SP_WAVE ->
                    StateSign = ?DUN_PASS,
                    dungeon_default:handle_wave_state_normal(State#dungeon_state{role_list = NewRoleList}, [StateSign]);
                true ->
                    do_handle_next_wave_state(State#dungeon_state{role_list = NewRoleList})
            end
    end.

push_settlement(State, DungeonRole) ->
    #dungeon_state{
        dun_id = DunId,
        scene_id = SceneId,
        result_subtype = ResultSubtype
    } = State,
    #dungeon_role{node = Node, id = RoleId, typical_data = TypicalData, custom_reward_list = CustomRewards} = DungeonRole,
    %%奖励
    RewardList = lib_dungeon:calc_base_reward_list(DungeonRole),
    KvData = lib_dungeon_distribute:pack_account_kv_data(State, RoleId),
    if RewardList =/= [] -> ResultType = ?DUN_RESULT_TYPE_SUCCESS;
        true -> ResultType = ?DUN_RESULT_TYPE_FAIL
    end,
    NewWaveNum = maps:get(?DUNGEON_REC_MAX_SCORE, TypicalData, 1),
    % ?ERR("~p ~p ~p", [ResultType, ResultSubtype, RewardList]),
    {ok, BinData} = pt_610:write(61003, [ResultType, ResultSubtype, DunId, SceneId, NewWaveNum, RewardList, CustomRewards, KvData]),
    dungeon_logic:send_to_uid(Node, RoleId, BinData).

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.

get_dun_result_succ_args(DunRole, [ResultSubType, WaveNum, LastWave|_]) ->
    #dungeon_role{typical_data = TypicalData} = DunRole,
    OldMaxDunWave = maps:get(?DUN_ROLE_EXP_DUN_INIT_MAX_WAVE, TypicalData, 0),
    [ResultSubType, OldMaxDunWave, LastWave, ?IF(LastWave == WaveNum, 1, 0)].

%% 自己处理新波数状态
do_handle_next_wave_state(State) ->
    #dungeon_state{scene_id = OldScene, dun_id = DunId, dun_type = DunType, wave_num = OldWave,
        role_list = [#dungeon_role{id = RoleId}|_] = RoleList, wave_ref = [WaveStartRef, WaveEndRef|_]} = State,
    NewWave = OldWave+1,
    Chapter = data_dun_exp:get_wave_chapter(NewWave),
    ChapterLv = data_dun_exp:get_chapter_lv(Chapter),
    #figure{lv = Lv, power = Power} = lib_role:get_role_figure(RoleId),
    DunWaveCfg = data_dungeon_wave:get_dun_wave(DunId, NewWave),
    #dun_cfg{born_type = BornType} = data_dungeon:get_dun(DunId),
    IsWaveLimit = wave_limit_check(NewWave, {Lv, Power}),
    if
        not is_integer(Chapter) orelse ChapterLv > Lv -> %% 等级不足,无法挑战下一关
            dungeon_default:handle_wave_state_normal(State, [?DUN_PASS]);
        IsWaveLimit == false ->
            dungeon_default:handle_wave_state_normal(State, [?DUN_PASS]);
        is_record(DunWaveCfg, base_dun_wave_cfg) ->
            #base_dun_wave_cfg{
                create_time = CreateTime, limit_time = LimitTime, scene = SceneId,
                mon_list = MonList, next_wave = NextWave, born_xy = BornXy
            } = DunWaveCfg,
            % ?ERR("~p ~p", [NewWave, MonList]),
            %% 切换场景检查
            NeedUpdate = OldScene =/= SceneId,
            NewState0 = if NeedUpdate == true ->
                    {SceneId1, NewScenePoolId} = dungeon_logic:get_dun_scene_pool_id(DunType, BornType, SceneId, RoleList),
                    State#dungeon_state{scene_id = SceneId1, scene_pool_id = NewScenePoolId};
                true ->
                    State
            end,
            %% 创建新波数
            Now = utime:unixtime(),
            WaveStartTime = Now + CreateTime,
            NewState = create_exp_dun_wave(NewWave, NextWave, length(MonList), WaveStartTime, NewState0),
            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,
            wave_update_scene(NeedUpdate, BornXy, NewState1),
            NewState1#dungeon_state{wave_ref = [NewWaveStartRef, NewWaveEndRef]};
        true ->
            State
    end.

wave_limit_check(NewWave, {Lv, Power}) ->
    case data_dun_exp:get_wave_limit(NewWave) of
        [] -> true;
        [{LvLimit, PowerLimit}] ->
            Lv >= LvLimit andalso Power >= PowerLimit
    end.

settle_wave_reward([#dun_wave_info{status = WaveStatus}|_], DunId, Wave, WaveState, RoleList) ->
    case data_dungeon_wave:get_dun_wave(DunId, Wave) of
        #base_dun_wave_cfg{first_reward = FirstRewards} ->
            lists:foldl(fun(Role = #dungeon_role{id = RoleId, is_reward = IsReward}, Acc) ->
                if WaveStatus == ?DUN_WAVE_UNFIN ->
                        skip;
                    true ->
                        lib_goods_api:send_reward_by_id(FirstRewards, exp_wave, RoleId)
                end,
                NewIsReward =
                case WaveState of
                    dungeon_pass -> ?DUN_IS_REWARD_YES;
                    _ -> IsReward
                end,
                [Role#dungeon_role{is_reward = NewIsReward}|Acc]
            end, [], RoleList);
        _ -> %% 配置问题
            RoleList
    end.

%% 更新日常通关层数及历史最高通关层数
update_role_dun_pass_lv(DunId, Wave, RoleList) ->
    lists:foldl(fun(Role = #dungeon_role{id = RoleId, typical_data = TypicalData}, TmpRoles) ->
        {UpdateCheck, NewTypicalData} = do_update_role_dun_pass_lv(RoleId, DunId, Wave, TypicalData),
        case UpdateCheck of
            true ->
                lists:keystore(RoleId, #dungeon_role.id, TmpRoles, Role#dungeon_role{typical_data = NewTypicalData});
            _ ->
                lists:keystore(RoleId, #dungeon_role.id, TmpRoles, Role)
        end
     end, [], RoleList).

do_update_role_dun_pass_lv(RoleId, DunId, Wave, TypicalData) ->
    {Res1, NewTypicalData}  = update_dun_pass_lv(RoleId, DunId, ?DUNGEON_REC_MAX_SCORE, Wave, TypicalData),
    {Res2, NewTypicalData1} = update_dun_pass_lv(RoleId, DunId, ?MOD_DUNGEON_PASS_LV, Wave, NewTypicalData),
    {_Bool = (Res1 orelse Res2), NewTypicalData1}.

update_dun_pass_lv(RoleId, DunId, Type, Wave, TypicalData) ->
    TypeData = maps:get(Type, TypicalData, 0),
    % ?ERR("~p ~p ~p", [Type, Wave, TypeData]),
    if Wave > TypeData ->
            NewTypicalData = maps:put(Type, Wave, TypicalData),
            cast_role_dun_pass_lv_update(RoleId, DunId, Type, Wave),
            {true, NewTypicalData};
        true ->
            {false, TypicalData}
    end.

wave_update_scene(false, _, State) -> State;
wave_update_scene(true, BornXy, State) ->
    #dungeon_state{
        scene_id = SceneId, scene_pool_id = ScenePoolId, role_list = RoleList
    } = State,
    [do_wave_update_scene(SceneId, ScenePoolId, self(), RoleId, Node, BornXy) || #dungeon_role{id = RoleId, node = Node} <- RoleList],
    ok.

%% 更新玩家通关层数
cast_role_dun_pass_lv_update(RoleId, DunId, Type, Wave) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?MODULE, role_dun_pass_lv_update, [DunId, Type, Wave]).

role_dun_pass_lv_update(Player, DunId, Type, Wave) ->
    if Type == ?DUNGEON_REC_MAX_SCORE ->
            #player_status{
                id = RoleId, exp_dun_drop = RoleFastDrop = #role_exp_dun_drop{fast_drop_time = FastDropTime},
                dungeon_record = DungeonRecs,
                exp_dun_achieve = ExpDunAchieve} = Player,
            DunRec = maps:get(DunId, DungeonRecs, []),
            case lists:keyfind(Type, 1, DunRec) of
                {_, OldWave} -> ok;
                _ -> OldWave = 0
            end,
            if Wave > OldWave ->
                    NewDunRec = lists:keystore(Type, 1, DunRec, {Type, Wave}),
                    NewDungeonRecs = maps:put(DunId, NewDunRec, DungeonRecs),
                    lib_dungeon:save_dungeon_record(RoleId, DunId, NewDunRec),
                    {NeedUpdate, NewAddWaves} = calc_new_achive_wave(OldWave, Wave),
                    Now = utime:unixtime(),
                    if Wave == 1 andalso FastDropTime == 0 ->
                            NewFastDropTime = Now;
                        true ->
                            NewFastDropTime = FastDropTime
                    end,
                    if Wave == 1 ->
                            lib_daily:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunId, 1),
                            lib_counter:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_FIRTS_ENTER, DunId, Now);
                        true ->
                            skip
                    end,
                    %% 放置经验历史最佳波数更新
                    exp_dun_drop_api:notice_exp_dun_achieve(RoleId, Wave),
                    %% 触发成就
                    lib_achievement_api:dungeon_exp_event(Player, Wave),
                    lib_elite_assess_api:fast_dun_event(Player, Wave),
                    exp_dun_wave_rank:refresh_exp_wave_rank(RoleId, Wave, utime:unixtime()),
                    NewExpDunAchieve = update_exp_dun_achieve(NeedUpdate, RoleId, DunId, NewAddWaves, ExpDunAchieve);
                true ->
                    NewDungeonRecs = DungeonRecs,
                    NewExpDunAchieve = ExpDunAchieve,
                    NewFastDropTime = FastDropTime
            end,
            NewPlayer = Player#player_status{
                dungeon_record = NewDungeonRecs,
                exp_dun_achieve = NewExpDunAchieve,
                exp_dun_drop = RoleFastDrop#role_exp_dun_drop{fast_drop_time = NewFastDropTime}},
            {ok, NewPlayer};
        Type == ?MOD_DUNGEON_PASS_LV ->
            #player_status{id = RoleId} = Player,
            DunType = data_dungeon:get_dun_type(DunId),
            lib_daily:set_count(RoleId, ?MOD_DUNGEON, Type, DunType, Wave),
            {ok, Player};
        true ->
            {ok, Player}
    end.

%% 计算新的里程碑波数
calc_new_achive_wave(OldWave, Wave) ->
    SeqWaves = lists:seq(OldWave+1, Wave),
    NewAchieveWaves = lists:foldl(fun(WaveId, Acc) ->
        case data_dun_exp:get_achieve(WaveId) of
            [_|_] ->
                case lists:member(WaveId, Acc) of
                    true -> Acc;
                    _    -> [WaveId|Acc]
                end;
            _ ->
                Acc
        end
     end, [], SeqWaves),
    case NewAchieveWaves of
        [_|_] ->
            {true, NewAchieveWaves};
        _ ->
            {false, NewAchieveWaves}
    end.

update_exp_dun_achieve(true, RoleId, DunId, NewRewards, ExpDunAchieve) ->
    {NewAddAchieve, NewExpDunAchieve} = lists:foldl(fun(WaveId, {NewAchieve, TmpAchieve}) ->
        case lists:keyfind({DunId, WaveId}, 1, TmpAchieve) of
            {_, _} ->
                {NewAchieve, TmpAchieve};
            _ ->
                NewAchieveWave = {{DunId, WaveId}, 0},
                NewAchieve1 = lists:keystore({DunId, WaveId}, 1, NewAchieve, NewAchieveWave),
                NewTmpAchieve = lists:keystore({DunId, WaveId}, 1, TmpAchieve, NewAchieveWave),
                {NewAchieve1, NewTmpAchieve}
        end
     end, {[], ExpDunAchieve}, NewRewards),
    case NewAddAchieve of
        [_|_] ->
            db_update_exp_dun_achieve(RoleId, NewAddAchieve),
            %% 通知更新
            NewAddAchieve1 = [{WaveX, StatusX} || {{_DunIdX, WaveX}, StatusX} <- NewAddAchieve],
            lib_server_send:send_to_uid(RoleId, pt_610, 61046, [DunId, NewAddAchieve1]),
            ok;
        _ ->
            ok
    end,
    NewExpDunAchieve;
update_exp_dun_achieve(_, _, _, _, ExpDunAchieve) -> ExpDunAchieve.

db_update_exp_dun_achieve(RoleId, ExpDunAchieve) ->
    DbDataList = [io_lib:format("(~p,~p,~p,~p)", [RoleId, DunIdX, WaveX, StatusX]) || {{DunIdX, WaveX}, StatusX} <- ExpDunAchieve],
    ValueStr = ulists:list_to_string(DbDataList, ","),
    SQL = io_lib:format(?SQL_EXP_DUN_REPLACE, [ValueStr]),
    db:execute(SQL).

get_achieve_info(Player) ->
    #player_status{sid = Sid, exp_dun_achieve = ExpDunAchieve, dungeon_record = DungeonRecs} = Player,
    [DunId|_] = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_EXP),
    DunRec = maps:get(DunId, DungeonRecs, []),
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
        {_, MaxScore} -> ok;
        _ -> MaxScore = 0
    end,
    AchieveInfo = [{Wave, Status} || {{_DunIdX, Wave}, Status} <- ExpDunAchieve, Wave =< MaxScore],
    lib_server_send:send_to_sid(Sid, pt_610, 61049, [DunId, AchieveInfo]),
    ok.

%% 经验副本有一百多波,数量太多了.
%% 因此只用最新一波代替,不保存所有波数
create_exp_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}.


%%
get_start_dun_args(_Player, DunRole, _DunCfg, OptionType) ->
    #dungeon_role{typical_data = TypicalData} = DunRole,
    % Wave = maps:get(?MOD_DUNGEON_PASS_LV, TypicalData, 1),
    Wave = maps:get(?DUNGEON_REC_MAX_SCORE, TypicalData, 1),
    [{wave, Wave+1}, {typical_data, [{?DUN_STATE_EXP_DUN_AUTO, OptionType}]}].


%% 玩家进程下获取经验副本记录
%% DungeonRec 副本id记录
%% DunData    副本类型数据
get_role_rec(_DunId, DunType, RoleId, DungeonRec, _DunData, TypicalData) ->
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DungeonRec) of
        {_, MaxDunWave} -> ok;
        _ -> MaxDunWave = 0
    end,
    IsNew = case lib_role_task:is_during_task(10720) of
        true->
            1;
        _->
            0
    end,
    DailyWave = lib_daily:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_PASS_LV, DunType),
    %% 最高通关记录
    NewTypicalData = maps:put(?DUNGEON_REC_MAX_SCORE, MaxDunWave, TypicalData),
    %% 今日通关波数
    NewTypicalData1 = maps:put(?MOD_DUNGEON_PASS_LV, DailyWave, NewTypicalData),
    %% 进入副本时初始波数
    NewTypicalData2 = maps:put(?DUN_ROLE_EXP_DUN_INIT_WAVE, MaxDunWave+1, NewTypicalData1),
    %% 进入副本时初始最大波数
    NewTypicalData3 = maps:put(?DUN_ROLE_EXP_DUN_INIT_MAX_WAVE, MaxDunWave, NewTypicalData2),
    %%是否新手任务在身
    NewTypicalData4 = maps:put(?DUN_ROLE_EXP_DUN_DOING_TASK,IsNew,NewTypicalData3),
    NewTypicalData4.

get_sweep_wave(Player, DunId, _DunType, _SweepNum, _CostType) ->
    #player_status{dungeon_record = DungeonRecs} = Player,
    DunRec = maps:get(DunId, DungeonRecs, []),
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
        {_, MaxWave} -> MaxWave;
        _ -> 0
    end.

%% 计算奖励
get_send_reward(State = #dungeon_state{wave_info = [_|_]}, DungeonRole) ->
    #dungeon_state{dun_id = DunId, wave_info = [WaveInfo|_]} = State,
    #dungeon_role{typical_data = TypicalData} = DungeonRole,
    %% 只要通关了波数就有奖励
    OldInitWave = maps:get(?DUN_ROLE_EXP_DUN_INIT_WAVE, TypicalData, 1),
    #dun_wave_info{key = {NowWave, _}, status = Status} = WaveInfo,
    % ?ERR("~p ~p ~p", [NowWave, OldInitWave, WaveInfo]),
    if NowWave > OldInitWave orelse (NowWave == OldInitWave andalso Status == 1) ->
            WaveRewards  = calc_exp_dun_wave_rewards(DunId, OldInitWave, WaveInfo),
            FinalRewards = WaveRewards,
            %% 都是首通奖励,不需要判断定制活动多倍
            DungeonRole#dungeon_role{calc_reward_list = FinalRewards, custom_reward_list = []};
        true ->
            DungeonRole
    end;
get_send_reward(_, DungeonRole) ->
    DungeonRole.

check_quit_state(State, RoleId) ->
    #dungeon_state{is_end = IsEnd, role_list = RoleList, 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 ->
            [WaveInfo|_] = WaveInfos,
            case lists:keyfind(RoleId, #dungeon_role.id, RoleList) of
                #dungeon_role{typical_data = TypicalData, hp = Hp} ->
                    if Hp =< 0 -> ResultSubtype1 = ?DUN_RESULT_SUBTYPE_ROLE_DIE;
                        true   -> ResultSubtype1 = ?DUN_RESULT_SUBTYPE_ACTIVE_QUIT
                    end,
                    InitWave = maps:get(?DUN_ROLE_EXP_DUN_INIT_WAVE, TypicalData, 1),
                    case check_new_pass_exp_wave(InitWave, WaveInfo) of
                        [_|_] ->
                            {?DUN_RESULT_TYPE_SUCCESS, ResultSubtype1};
                        _ ->
                            {?DUN_RESULT_TYPE_FAIL, ResultSubtype1}
                    end;
                _ ->
                    {?DUN_RESULT_TYPE_FAIL, ResultSubtype}
            end;
        true ->
            {ResultType, ResultSubtype}
    end.

%% 1 1未 [1,0]
%% 1 1通 [1,1]
%% 1 2未 [1,1]
%% 1 2通 [1,2]
calc_exp_dun_wave_rewards(DunId, InitWave, WaveInfo) ->
    case check_new_pass_exp_wave(InitWave, WaveInfo) of
        Waves1 = [_|_] ->
            do_calc_exp_dun_wave_rewards(DunId, Waves1);
        _ ->
            []
    end.

check_new_pass_exp_wave(InitWave, WaveInfo) ->
    #dun_wave_info{key = {Wave, _}, status = Status} = WaveInfo,
    ResultPassWave =
        if Status == 0 -> max(0, Wave-1);
            true       -> Wave
        end,
    lists:seq(InitWave, ResultPassWave).

%% 默认都是首通,因为只能打一次
do_calc_exp_dun_wave_rewards(DunId, Waves) ->
    lists:foldl(fun(WaveId, TmpRewards) ->
        case data_dungeon_wave:get_dun_wave(DunId, WaveId) of
            #base_dun_wave_cfg{first_reward = FirstRewards} ->
                lists:append([FirstRewards, TmpRewards]);
            _ ->
                TmpRewards
        end
     end, [], Waves).

do_wave_update_scene(SceneId, ScenePoolId, CopyId, RoleId, Node, BornXy) ->
    unode:apply(Node, ?MODULE, do_wave_update_role_scene, [SceneId, ScenePoolId, CopyId, RoleId, BornXy]).

do_wave_update_role_scene(SceneId, ScenePoolId, CopyId, RoleId, BornXy) ->
    case lib_player:is_online_global(RoleId) of
        true ->
            case BornXy of
                [{X, Y}|_] -> ok;
                _   -> [X, Y|_] = lib_scene:get_born_xy(SceneId)
            end,
            AttrList = [{group, ?DUN_DEF_GROUP}],
            lib_scene:change_player_scene(RoleId, SceneId, ScenePoolId, CopyId, X, Y, AttrList);
        _ ->
            ok
    end.

handle_dungeon_result(State) ->
    #dungeon_state{result_type = ResultType, dun_type = DunType, wave_info = WaveInfos, start_time = StartTime, role_list = RoleList} = State,
    Now = utime:unixtime(),
    [#dun_wave_info{key = {WaveNum, _}, status = WaveState}|_] = WaveInfos,
    LogWave = if WaveState == ?DUN_WAVE_FIN -> WaveNum;
        true -> max(WaveNum-1, 0)
    end,
    [begin
        case lists:keyfind(?TYPE_EXP, 1, RewardList) of
            {_, _, ExpAdd} -> ok;
            {_, ExpAdd} -> ok;
            _ -> ExpAdd = 0
        end,
        exp_dun_log(RoleId, LogWave, ResultType, ExpAdd)
    end || #dungeon_role{id = RoleId, calc_reward_list = RewardList} <- RoleList],
    %% 经验副本内跨天特殊处理
    case utime:is_logic_same_day(StartTime, Now) of
        false ->
            [lib_daily:set_count_offline(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_PASS_LV, DunType, 0) || #dungeon_role{id = RoleId} <- RoleList];
        _ ->
            State
    end.

exp_dun_log(RoleId, LogWave, ResultType, ExpAdd) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?MODULE, role_exp_dun_log, [LogWave, ResultType, ExpAdd]).

role_exp_dun_log(Player, LogWave, ResultType, ExpAdd) ->
    #player_status{id = RoleId, figure = #figure{power = Power}} = Player,
    Chapter = data_dun_exp:get_wave_chapter(LogWave),
    Ratio = lib_player:get_exp_drop_ratio(Player),
    ExpRatio = round(Ratio/100),
    Now = utime:unixtime(),
    lib_log_api:log_exp_dun([RoleId, Power, Chapter, LogWave, ExpRatio, ResultType, ExpAdd, Now]),
    {ok, Player}.

%% 判断是否已达最大波数了
check_extra(Player, Dun) ->
    #player_status{dungeon_record = DungeonRecs, figure = #figure{power = Power, lv = RoleLv}} = Player,
    #dun_cfg{id = DunId} = Dun,
    DungeonRec = maps:get(DunId, DungeonRecs, []),
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DungeonRec) of
        {_, MaxWave} ->
            case data_dungeon_wave:get_next_wave(DunId, MaxWave) of
                [NextWave|_] ->
                    if NextWave == 0 ->
                            {false, ?ERRCODE(err610_just_once_finish)};
                        true ->
                            IsWaveLimit = wave_limit_check(NextWave, {RoleLv, Power}),
                            if IsWaveLimit == false ->
                                    {false, ?ERRCODE(err610_conditon_not_enough)};
                                true ->
                                    % SweepSign = lib_daily:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunId),
                                    if
                                        % SweepSign == 0 andalso NextWave =/= 1 -> %% 不是第一波
                                        %     {false, ?ERRCODE(err610_have_not_sweeped)};
                                        true ->
                                            case data_dun_exp:get_wave_chapter(NextWave) of
                                                Chapter when is_integer(Chapter) ->
                                                    ChapterLv = data_dun_exp:get_chapter_lv(Chapter),
                                                    if RoleLv >= ChapterLv ->
                                                            true;
                                                        true ->
                                                            {false, ?ERRCODE(err610_not_enough_lv_to_enter_dungeon)}
                                                    end;
                                                _ ->
                                                    {false, ?ERRCODE(err610_not_enough_lv_to_enter_dungeon)}
                                            end
                                    end
                            end
                    end;
                _ ->
                    {false, ?ERRCODE(err610_dungeon_count_cfg_error)}
            end;
        _ ->
            true
    end.

login(RoleId) ->
    case db:get_all(?SQL_SELECT_EXP_DUN, [RoleId]) of
        DbData = [_|_] ->
            [{{DunId, Wave}, Status}  || [DunId, Wave, Status] <- DbData];
        _ ->
            []
    end.

get_dun_sweep_sign(RoleId, DunData) ->
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunData) of
        {_, MaxWave} -> ok;
        _ -> MaxWave = 0
    end,
    case lists:seq(1, MaxWave) of
        [_|_] ->
            [DunId|_] = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_EXP),
            lib_daily:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunId);
        _ ->
            false
    end.

sweep_exp_dun(Player) ->
    #player_status{id = RoleId, sid = Sid, dungeon_record = DungeonRecs} = Player,
    DunType = ?DUNGEON_TYPE_EXP,
    [DunId|_] = data_dungeon:get_ids_by_type(DunType),
    SweepSign = lib_daily:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunId),
    if SweepSign == 0 -> %% 今日未扫荡
            CanSweepWave = get_pass_wave(DunId, DungeonRecs),
            if CanSweepWave == [] ->
                    lib_server_send:send_to_sid(Sid, pt_611, 61130, [?ERRCODE(err610_have_not_pass)]);
                true ->
                    lib_activitycalen_api:role_success_end_activity(RoleId, ?MOD_DUNGEON, 5001),
                    RewardList = get_exp_dun_sweep_reward(DunId, CanSweepWave),
                    Player1 = lib_goods_api:send_reward(Player, RewardList, exp_dun_sweep),
                    lib_daily:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunId, 1),
                    lib_server_send:send_to_sid(Sid, pt_611, 61130, [?SUCCESS]),
                    pp_dungeon:handle(61020, Player1, [DunType]),
                    lib_role_legion:trigger_task(Player1, ?MOD_DUNGEON, 2),
                    {ok, Player1}
            end;
        true ->
            lib_server_send:send_to_sid(Sid, pt_611, 61130, [?ERRCODE(err610_have_sweeped)]),
            ok
    end.

get_pass_wave(DunId, DungeonRecs) ->
    DunRec = maps:get(DunId, DungeonRecs, []),
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
        {_, MaxScore} -> ok;
        _             -> MaxScore = 0
    end,
    lists:seq(1, MaxScore).


%% 获取扫荡奖励
get_exp_dun_sweep_reward(DunId, CanSweepWave) ->
    RewardList0 = lists:foldl(fun(WaveId, TmpRewards) ->
        case data_dungeon_wave:get_dun_wave(DunId, WaveId) of
            #base_dun_wave_cfg{normal_reward = NormalReward} ->
                lists:append([NormalReward, TmpRewards]);
            _ ->
                TmpRewards
        end
     end, [], CanSweepWave),
    % ?ERR("~p", [RewardList]),
    RewardList = lib_goods_util:merge_same_goods(RewardList0),
    RewardList.