%% --------------------------------
%% desc		:爬塔副本
%% module	:lib_dungeon_tower.erl
%% author	:hh
%% time		:20/02/18
%% --------------------------------

-module(lib_dungeon_tower).

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

-export([
	check_extra/2
	,get_role_rec/6
	,handle_dungeon_result/1	%% 副本结算特殊处理
    ,get_send_reward/2
	,calc_score_rewards/3
	,handle_score_reward_rec/3
	,push_settlement/2
	,role_score_rewarded_update/2
    ,role_be_hurted/4
	]).

-export([
	get_tower_daily_reward/1
    ,get_pass_max_floor/1      %% 获取通关最大层数
    ,get_pass_max_floor/2      %% 获取通关最大层数
    ,calc_sweep_reward/1
    ,update_tower_star_data/3
    ,handle_event/2
    ,get_dun_result_succ_args/2
	]).

-export([
	role_update_dun_data/4
    ,role_update_dun_rec/4
    ,is_diffcult_dun/1
    ,refresh_dun_count_state/1
    ,role_recover_time_out/1
    ,get_sweep_dun/3
    ,sweep_tower_dun/2
	]).

-export([gm_reset_tower_dun/1]).
-export([gm_update_star_score/0]).
-export([reset_tower_count/1]).
-export([calc_recovery_count/4]).

login(RoleId) ->
    case db:get_all(<<"SELECT `index`, `state` FROM role_tower_star_reward WHERE `role_id` = ~p">>, [RoleId]) of
        DataList = [_|_] ->
            make_star_reward_info(DataList, #{});
        _ -> #{}
    end.

make_star_reward_info([], Map) -> Map;
make_star_reward_info([[Index, State]|Rest], AccMap) ->
    NewAccMap = maps:put(Index, State, AccMap),
    make_star_reward_info(Rest, NewAccMap).

handle_event(Player, #event_callback{type_id = ?EVENT_LOGIN_CAST}) ->
    #player_status{id = RoleId, figure = #figure{name = RoleName}} = Player,
    TowerStarData = login(RoleId),
    refresh_dun_count_state(RoleId),
    case TowerStarData of
        #{} ->
            Star = tower_dun_rank:load_tower_star(RoleId),
            Star =/= 0 andalso tower_dun_rank:refresh_tower_rank(RoleId, RoleName, Star),
            AccessIndexs = tower_dun_rank:get_access_index_by_star(Star),
            NewTowerStarData =
            lists:foldl(fun(Index1, AccTowerStarData) ->
                case maps:get(Index1, AccTowerStarData, 0) of
                    OldState when OldState =/= 0 -> AccTowerStarData;
                    _ ->
                        update_tower_star_data(RoleId, Index1, 1),
                        maps:put(Index1, 1, AccTowerStarData)
                end
            end, TowerStarData, AccessIndexs);
        _ ->
            NewTowerStarData = TowerStarData
    end,
    {ok, Player#player_status{tower_dun_star = NewTowerStarData}}.

check_extra(Player, Dun) ->
    #player_status{id = RoleId, dungeon_record = DungeonRecs} = Player,
	#dun_cfg{id = DunId, type = DunType} = Dun,
    DungeonRec = maps:get(DunId, DungeonRecs, []),
    case lists:keyfind(?DUNGEON_REC_DATA, 1, DungeonRec) of
        {_, RewardSign} -> ok;
        _   -> RewardSign = 0
    end,
	MinDunId = data_tower_dun:get_min_dun(),
    SuccessCount = lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_SUCCESS, DunId),

    IsDiffcultDun = is_diffcult_dun(DunId),
    SweepSign     = lib_daily:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, ?DUNGEON_TYPE_TOWER),  
	if
        (not IsDiffcultDun andalso SuccessCount =/= 0) orelse (IsDiffcultDun andalso RewardSign =/= 0) ->
            {false, ?ERRCODE(err610_just_once_finish)};
        SweepSign == 0 andalso MinDunId =/= DunId ->
            {false, ?ERRCODE(err610_have_not_sweeped)};
		true ->
            case is_diffcult_dun(DunId) of
                true ->
                    % refresh_dun_count_state(RoleId),
                    AftCount  = lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, DunType),
                    CfgCount  = data_dungeon_special:get(DunType, guard_harddun_num),
                    MinDiffcultDun = data_tower_dun:get_min_diffcult_dun(),
                    DiffcultDuns = data_tower_dun:get_diffcult_dun(DunId),
                    [{_, NeedUnlock}|_] = DiffcultDuns,
                    if 
                        AftCount >= CfgCount ->
                            {false, ?ERRCODE(err610_challen_count_limit)};
                        DunId == MinDiffcultDun ->
                            %% 第一个难度副本只需指定普通难度副本通关即可
                            FirstPass = lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_SUCCESS, NeedUnlock),
                            if FirstPass == 0 ->
                                    {false, ?ERRCODE(err610_need_finish_dun_id)};
                                true ->
                                    true
                            end;
                        true ->
                            %% 第二个难度副本需普通难度副本通关+上一个困难难度副本通关
                            PrevDunId = data_tower_dun:get_last_diffcult_dun(DunId),
                            if PrevDunId == 0 ->
                                    {false, ?ERRCODE(data_error)};
                                true ->
                                    [{_, FirstPass},{_, FirstPass1}|_] = lib_counter:get_count(RoleId, [{?MOD_DUNGEON, ?MOD_DUNGEON_SUCCESS, NeedUnlock},
                                        {?MOD_DUNGEON, ?MOD_DUNGEON_SUCCESS, PrevDunId}]),
                                    if FirstPass == 0 orelse FirstPass1 == 0 ->
                                            {false, ?ERRCODE(err610_need_finish_dun_id)};
                                        true -> true
                                    end
                            end
                    end;
                _ ->
                    if  
                        DunId == MinDunId ->
                            true;
                        true ->
                			case data_tower_dun:get_tower_prev_dun(DunId) of
                				[PrevDunId|_] ->
                					Count = lib_counter:get_count(Player#player_status.id, ?MOD_DUNGEON, ?MOD_DUNGEON_SUCCESS, PrevDunId),
                					if Count =/= 0 ->
                							true;
                						true ->
                							{false, ?ERRCODE(err610_need_finish_dun_id)}
                					end;
                				_ ->
                					{false, ?ERRCODE(data_error)}
                			end
                    end
            end
	end.

get_role_rec(DunId, _DunType, RoleId, DungeonRec, DunData, TypicalData) ->
    case lists:keyfind(?DUNGEON_REC_MAX_STARS, 1, DungeonRec) of
        {_, MaxDunScoreList} -> MaxDunScore = length(MaxDunScoreList);
        _ -> MaxDunScore = 0
    end,
    case lists:keyfind(?DUNGEON_REC_DATA, 1, DungeonRec) of
    	{_, RewardSign} -> ok;
    	_ -> RewardSign = 0
    end,
    case DunData of
    	[MaxPass, MaxDiffcultPass|_] -> ok;
    	_ 			-> MaxPass = 0, MaxDiffcultPass = 0
    end,
    %% 副本最大星级
    NewTypicalData = maps:put(?DUNGEON_REC_MAX_SCORE, MaxDunScore, TypicalData),
    %% 是否首通 0未首通|1已首通
    IsFirstPass = lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_SUCCESS, DunId),
    NewTypicalData1 = maps:put(?DUN_ROLE_TOW_DUN_FIRST_PASS, IsFirstPass, NewTypicalData),
 	%% 副本三星级奖励是否已领
    NewTypicalData2 = maps:put(?DUNGEON_REC_DATA, RewardSign, NewTypicalData1),
    %% 最佳副本数据
    NewTypicalData3 = maps:put(?DUN_ROLE_TOW_DUN_MAX_DUN, {MaxPass, MaxDiffcultPass}, NewTypicalData2),
    NewTypicalData3.

%% 副本结算特殊处理
handle_dungeon_result(State) ->
	#dungeon_state{dun_id = DunId, dun_type = DunType, role_list = RoleList, result_type = ResultType} = State,
	[DunRole|_] = RoleList,
	NewState =
	if ResultType == ?DUN_RESULT_TYPE_SUCCESS ->
			#dungeon_role{id = RoleId, figure = #figure{lv = RoleLv}, typical_data = TypicalData, grade_progress = GradeProgress} = DunRole,
			NewTypicalData = update_tower_dun_data(RoleId, RoleLv, DunType, DunId, GradeProgress, TypicalData),
			NewRoleList = lists:keystore(RoleId, #dungeon_role.id, RoleList, DunRole#dungeon_role{typical_data = NewTypicalData}),
            check_broad_cast_info(DunId, NewRoleList),
			State#dungeon_state{role_list = NewRoleList};
		true ->
			State
	end,
    NewState.
	% push_settlement(NewState, NewDungeonRole, dungeon_result).

check_broad_cast_info(DunId, [Role|_]) ->
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{name = Name} -> Name;
        _ -> Name = ""
    end,
    CheckResult =
    case is_diffcult_dun(DunId) of
        true ->
            case data_tower_dun:get_broad_info(DunId) of
                [1|_] -> {true, diffcult};
                _     -> {false, normal}
            end;
        _ ->
            Result = DunId =/= 0 andalso DunId rem 10 == 0,
            {Result, normal}
    end,
    case CheckResult of
        {true, normal} ->
            #dungeon_role{id = RoleId} = Role,
            RoleName = lib_role:get_role_name(RoleId),
            lib_chat:send_TV({all}, ?MOD_DUNGEON, 9, [RoleName, RoleId, Name]);
        {true, diffcult} ->
            #dungeon_role{id = RoleId} = Role,
            RoleName = lib_role:get_role_name(RoleId),
            lib_chat:send_TV({all}, ?MOD_DUNGEON, 10, [RoleName, RoleId, Name]);
        _ ->
            skip
    end.


get_send_reward(State, DungeonRole) ->
    #dungeon_state{dun_id = DunId, result_type = ResultType} = State,
    #dungeon_role{id = RoleId, is_first  = IsFirst, help_type = HelpType} = DungeonRole,
    case is_diffcult_dun(DunId) of
        true ->
            dungeon_default:get_send_reward_normal(State, DungeonRole);
        _ -> %% 普通难度:首通奖励+每日奖励
            if ResultType =:= ?DUN_RESULT_TYPE_SUCCESS
             andalso HelpType =:= ?HELP_TYPE_NO ->
                    if IsFirst == false ->
                            case data_dungeon:get_dun(DunId) of
                                #dun_cfg{first_reward = FirstRewards} -> ok;
                                _ -> FirstRewards = []
                            end,
                            DailyRewards = data_tower_dun:get_tower_dun_daily_reward(DunId),
                            FirstRewards1 = dungeon_default:calc_reward_with_rand(FirstRewards),
                            DailyRewards1 = dungeon_default:calc_reward_with_rand(DailyRewards),
                            lib_daily:set_count_offline(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunId, 1),
                            % ?ERR("~p ~p", [FirstRewards1, DailyRewards1]),
                            RewardList = lists:append([FirstRewards1, DailyRewards1]),
	                        DungeonRole#dungeon_role{calc_reward_list = RewardList};
                        true   ->
	                        DungeonRole
                    end;
                true ->
	                DungeonRole
            end
    end.


%% 计算评分奖励
calc_score_rewards(_DunType, DunId, DungeonRole) ->
    #dungeon_role{figure = #figure{lv = Lv}, typical_data = TypicalData, grade_progress = GradeProgress} = DungeonRole,
    RewardSign = maps:get(?DUNGEON_REC_DATA, TypicalData, 0),
    case GradeProgress of
        #dungeon_role_grade{map = Maps} ->
            case data_dungeon_grade:get_dungeon_grade(DunId, Lv) of
                #dungeon_grade{condition = Condition, reward = Rewards} ->
                            StarNum = dungeon_default:calc_dun_score(Maps),
                            ConfigMaxStar = length(Condition),
                            if StarNum >= ConfigMaxStar andalso RewardSign == 0 ->
                                    {[true], Rewards};
                                true ->
                                    {[false], []}
                            end;
                _ ->
                    {[false], []}
            end;
        _ ->
            ?ERR("grade progress data error", []),
            {[false], []}
    end.

%%
handle_score_reward_rec(DungeonRole, DunId, ExtraArgs) ->
    #dungeon_role{id = RoleId, node = Node} = DungeonRole,
    [IsScoreRewarded|_] = ExtraArgs,
    if IsScoreRewarded ->
            lib_player:apply_cast(Node, RoleId, ?APPLY_CAST_SAVE, ?NOT_HAND_OFFLINE, ?MODULE, role_score_rewarded_update, [DunId]);
        true ->
            ok
    end.

%% 三星评分奖励只能领取一次,不能重复领取
role_score_rewarded_update(Player, DunId) ->
    #player_status{id = RoleId, dungeon_record = DungeonRec} = Player,
    DunRec = maps:get(DunId, DungeonRec, []),
    {IsUpdate, NewDunRec} = case lists:keyfind(?DUNGEON_REC_DATA, 1, DunRec) of
        {_, 1} ->
            {false, DunRec};
        _ ->
            {true, lists:keystore(?DUNGEON_REC_DATA, 1, DunRec, {?DUNGEON_REC_DATA, 1})}
    end,
    if IsUpdate ->
            lib_dungeon:save_dungeon_record(RoleId, DunId, NewDunRec),
            NewDungeonRec = maps:put(DunId, NewDunRec, DungeonRec),
            NewPlayer = Player#player_status{dungeon_record = NewDungeonRec},
            {ok, NewPlayer};
        true ->
            {ok, Player}
    end.

role_be_hurted(State, RoleId, Hp, HpLim) ->
    case lists:keyfind(RoleId, #dungeon_role.id, State#dungeon_state.role_list) of
        DungeonRole = #dungeon_role{} ->
            NewDungeonRole = DungeonRole#dungeon_role{hp = Hp, hp_lim = HpLim},
            NewRoleList = lists:keystore(RoleId, #dungeon_role.id, State#dungeon_state.role_list, NewDungeonRole),
            State#dungeon_state{role_list = NewRoleList};
        _ ->
            State
    end.

push_settlement(State, DungeonRole) ->
	push_settlement(State, DungeonRole, dungeon_result).

push_settlement(State, DungeonRole, SettleType) ->
	PushOrNot =
	if
	 	SettleType == dungeon_result ->
	 		true;
	 	true ->
	 		false
	end,
	% ?ERR("~p ~p", [SettleType, PushOrNot]),
	do_push_settlement(State, DungeonRole, PushOrNot).

do_push_settlement(_, _, false) -> skip;
do_push_settlement(State, DungeonRole, true) ->
	#dungeon_state{dun_id = DunId, scene_id = SceneId, result_type = ResultType, start_time = StartTime, result_time = ResultTime, result_subtype = SubType} = State,
	#dungeon_role{sid = Sid, typical_data = TypicalData, grade_progress = GradeProgress, figure = #figure{lv = Lv}, calc_reward_list = DunRewards} = DungeonRole,
	case GradeProgress of
		#dungeon_role_grade{map = Maps} ->
			StarNum = dungeon_default:calc_dun_score(Maps);
		_ ->
			StarNum = 0
	end,
	RewardSign = maps:get(?DUNGEON_REC_DATA, TypicalData, 0),
	ScoreRewards =
	if RewardSign == 0 ->
	        case data_dungeon_grade:get_dungeon_grade(DunId, Lv) of
	          	#dungeon_grade{reward = Rewards, condition = Condition} ->
	        		ConfigMaxStar = length(Condition),
	        		if StarNum >= ConfigMaxStar -> Rewards;
	        			true -> []
	        		end;
	        	_ -> []
	        end;
   		true -> []
    end,
    PassTime = max(0, ResultTime - StartTime),
    %?ERR("61026 ~p", [DunRewards]),
    lib_server_send:send_to_sid(Sid, pt_610, 61026, [ResultType, SubType, DunId, SceneId, PassTime, DunRewards, ScoreRewards, StarNum]),
    ok.

update_tower_dun_data(RoleId, RoleLv, DunType, DunId, GradeProgress, TypicalData) ->
	%% 最大星级
	MaxScore = maps:get(?DUNGEON_REC_MAX_SCORE, TypicalData, 0),
	NewScore = case GradeProgress of
		#dungeon_role_grade{map = Maps} ->
			dungeon_default:calc_dun_score(Maps);
		_ ->
			0
	end,
	if NewScore > MaxScore ->
			NewTypicalData = maps:put(?DUNGEON_REC_MAX_SCORE, NewScore, TypicalData),
            cast_update_role_dun_rec(RoleId, DunId, [{?DUNGEON_REC_DATA_1, RoleLv}], NewScore);
        true ->
			NewTypicalData = TypicalData
	end,
	%% 通关最大副本id
	{MaxDunId, MaxDiffcultDunId} = maps:get(?DUN_ROLE_TOW_DUN_MAX_DUN, NewTypicalData, 0),
    IsDiffcultDun = is_diffcult_dun(DunId),
    MaxId = if IsDiffcultDun -> MaxDiffcultDunId;
        true -> MaxDunId
    end,
	if IsDiffcultDun ->
			NewScore >= 3 andalso
			lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, lib_elite_assess_api, diff_tower_dun_event, [DunId]);
		true ->
			lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, lib_elite_assess_api, tower_dun_event, [DunId])
	end,
    if DunId > MaxId ->
            if IsDiffcultDun ->
                    NewTypicalData1 = maps:put(?DUN_ROLE_TOW_DUN_MAX_DUN, {MaxDunId, DunId}, NewTypicalData);
                true ->
                    NewTypicalData1 = maps:put(?DUN_ROLE_TOW_DUN_MAX_DUN, {DunId, MaxDiffcultDunId}, NewTypicalData)
            end,
            DunAchieve = calc_new_achieve_pass(IsDiffcultDun, DunId, MaxDunId, MaxDiffcultDunId),
	        lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, lib_achievement_api, dungeon_tower_event, [DunAchieve]),
            cast_role_update_dun_data(RoleId, DunType, IsDiffcultDun, DunId);
        true ->
            NewTypicalData1 = NewTypicalData
    end,
	NewTypicalData1.

cast_update_role_dun_rec(RoleId, DunId, DunRecData, NewScore) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?MODULE, role_update_dun_rec, [DunId, DunRecData, NewScore]).

role_update_dun_rec(Player, DunId, DunRecData, NewScore) ->
    #player_status{id = RoleId, figure = #figure{name = RoleName}, dungeon_record = DungeonRecs, tower_dun_star = TowerStarData} = Player,
    DunRec = maps:get(DunId, DungeonRecs, []),
    NewDunRec = lists:foldl(fun({Key, Data}, AccDunRec) ->
        lists:keystore(Key, 1, AccDunRec, {Key, Data})
    end, DunRec, DunRecData),
    Star = tower_dun_rank:calc_tower_star(DunId, NewScore, DungeonRecs),
    Star =/= 0 andalso tower_dun_rank:refresh_tower_rank(RoleId, RoleName, Star),
    AccessIndexs = tower_dun_rank:get_access_index_by_star(Star),
    NewTowerStarData =
    lists:foldl(fun(Index1, AccTowerStarData) ->
        case maps:get(Index1, AccTowerStarData, 0) of
            OldState when OldState =/= 0 -> AccTowerStarData;
            _ ->
                update_tower_star_data(RoleId, Index1, 1),
                maps:put(Index1, 1, AccTowerStarData)
        end
    end, TowerStarData, AccessIndexs),
    lib_dungeon:save_dungeon_record(RoleId, DunId, NewDunRec),
    NewDungeonRecs = maps:put(DunId, NewDunRec, DungeonRecs),
    {ok, Player#player_status{dungeon_record = NewDungeonRecs, tower_dun_star = NewTowerStarData}}.

update_tower_star_data(RoleId, Index, State) ->
    db:execute(<<"REPLACE INTO role_tower_star_reward SET `role_id` = ~p, `index` = ~p, `state` = ~p">>, [RoleId, Index, State]),
    ok.

calc_new_achieve_pass(IsDiffcultDun, DunId, MaxDunId, MaxDiffcultDunId) ->
    if IsDiffcultDun ->
            NewMaxDiffcultDunId = DunId,
            NewMaxDunId = MaxDunId;
        true ->
            NewMaxDiffcultDunId = MaxDiffcultDunId,
            NewMaxDunId = DunId
    end,
    ConfigMinDun = data_tower_dun:get_min_dun(),
    ConfigMinDiffcultDUn = data_tower_dun:get_min_diffcult_dun(),
    Pass1 = NewMaxDunId - ConfigMinDun + 1,
    if NewMaxDiffcultDunId < ConfigMinDiffcultDUn ->
            Pass2 = 0;
        true ->
            Pass2 = NewMaxDiffcultDunId - ConfigMinDiffcultDUn + 1
    end,
    round(Pass1 + Pass2).


cast_role_update_dun_data(RoleId, DunType, IsDiffcultDun, DunId) ->
	lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?NOT_HAND_OFFLINE, ?MODULE, role_update_dun_data, [DunType, IsDiffcultDun, DunId]).

role_update_dun_data(Player, DunType, IsDiffcultDun, DunId) ->
	#player_status{id = RoleId, dungeon = StatusDungeon} = Player,
	#status_dungeon{dun_data = DunData} = StatusDungeon,
	case lists:keyfind(DunType, 1, DunData) of
		{_, [OldMaxDunId, OldDiffcultMaxDun|_]} ->
            MaxId = if IsDiffcultDun -> OldDiffcultMaxDun;
                true -> OldMaxDunId
            end,
			if DunId > MaxId ->
                    MaxDunData = if IsDiffcultDun -> [OldMaxDunId, DunId];
                        true -> [DunId, OldDiffcultMaxDun]
                    end,
                    MinDunId = get_config_min_dun(),
                    if DunId == MinDunId ->
                            lib_daily:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, ?DUNGEON_TYPE_TOWER, 1);
                        true -> skip
                    end,
					lib_dungeon:db_replace_dun_data(RoleId, DunType, MaxDunData),
					NewDunData = lists:keystore(DunType, 1, DunData, {DunType, MaxDunData}),
					NewStatusDungeon = StatusDungeon#status_dungeon{dun_data = NewDunData},
                    {ok, Player#player_status{dungeon = NewStatusDungeon}};
				true ->
					{ok, Player}
			end;
		_ ->
			if DunId =/= 0 ->
                    MaxDunData = if IsDiffcultDun -> [0, DunId];
                        true -> [DunId, 0]
                    end,
                    MinDunId = get_config_min_dun(),
                    if DunId == MinDunId ->
                            lib_daily:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, ?DUNGEON_TYPE_TOWER, 1);
                        true -> skip
                    end,
            		lib_dungeon:db_replace_dun_data(RoleId, DunType, MaxDunData),
					NewDunData = lists:keystore(DunType, 1, DunData, {DunType, MaxDunData}),
					NewStatusDungeon = StatusDungeon#status_dungeon{dun_data = NewDunData},
					{ok, Player#player_status{dungeon = NewStatusDungeon}};
				true ->
					{ok, Player}
			end
	end.

get_tower_daily_reward(Player) ->
	case tower_daily_reward_check(Player) of
		{true, DunIds, Rewards} -> %?ERR("~p ~p", [DunIds, Rewards]),
			if DunIds =/= [] orelse Rewards =/= [] ->
					NewPlayer = lib_goods_api:send_reward(Player, Rewards, tower_dun_daily_reward),
					CountList = [{{?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunIdX}, 1}|| DunIdX <- DunIds],
					lib_daily:set_count(Player#player_status.id, CountList),
					lib_server_send:send_to_sid(Player#player_status.sid, pt_610, 61025, [?SUCCESS, DunIds]),
					lib_activitycalen_api:role_success_end_activity(Player#player_status.id,?MOD_DUNGEON,?DUNGEON_TYPE_TOWER,1),
                    {ok, NewPlayer};
				true ->
					lib_server_send:send_to_sid(Player#player_status.sid, pt_610, 61025, [?ERRCODE(err610_tower_dun_rewarded), []]),
					ok
			end;
		{false, Errcode} ->
			lib_server_send:send_to_sid(Player#player_status.sid, pt_610, 61025, [Errcode, []]),
			ok
	end.

tower_daily_reward_check(Player) ->
	#player_status{id = RoleId, dungeon = #status_dungeon{dun_data = DunData}} = Player,
	MaxDun = get_tower_max_pass_dun(DunData),
	% ?ERR("~p", [MaxDun]),
	if MaxDun == 0 -> {false, ?ERRCODE(err610_not_first_pass)};
		true ->
			MinDunId = get_config_min_dun(),
            MaxDunId = get_config_max_dun(),
			if MaxDun < MinDunId orelse MaxDun > MaxDunId -> {false, ?ERRCODE(data_error)};
				true ->
					FirstPass = calc_first_pass_dun(MinDunId, MaxDun, []),
					SweepDun = check_daily_sweep_dun(RoleId, FirstPass),
					SweepRewards = calc_sweep_reward(SweepDun),
					{true, SweepDun, SweepRewards}
			end
	end.

calc_first_pass_dun(MaxDunId, MaxDunId, FirstPassDuns) ->
	[MaxDunId|FirstPassDuns];
calc_first_pass_dun(MinDunId, MaxDunId, FirstPassDuns) ->
	NextDunId = data_tower_dun:get_tower_next_dun(MinDunId),
	case lists:member(MinDunId, FirstPassDuns) of
		true ->
			calc_first_pass_dun(NextDunId, MaxDunId, FirstPassDuns);
		_ 	 ->
			calc_first_pass_dun(NextDunId, MaxDunId, [MinDunId|FirstPassDuns])
	end.

check_daily_sweep_dun(RoleId, FirstPass) ->
	case FirstPass of
		[_|_] ->
			CountList = [{?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunId} || DunId <- FirstPass],
			DailyList = lib_daily:get_count_offline(RoleId, CountList),
			lists:foldl(fun({{_, _, DunIdX}, Count}, Acc) ->
				if Count == 0 -> [DunIdX|Acc];
					true -> Acc
				end
			 end, [], DailyList);
		_ ->
			[]
	end.

calc_sweep_reward(SweepDuns) ->
	lists:foldl(fun(DunId, Acc) ->
	 	case data_tower_dun:get_tower_dun_daily_reward(DunId) of
	 		Rewards = [_|_] ->
	 			lists:append(Rewards, Acc);
	 		_ ->
	 			Acc
	 	end
	 end, [], SweepDuns).

get_config_min_dun() ->
    data_tower_dun:get_min_dun().
	% ConfigIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_TOWER),
	% [MinId|_] = lists:sort(ConfigIds),
	% MinId.

get_config_max_dun() ->
    data_tower_dun:get_max_dun().

get_tower_max_pass_dun(DunData) ->
    get_tower_max_pass_dun(0, DunData).

-spec get_tower_max_pass_dun(DiffcultType, DunData) -> DunId when
    DiffcultType :: 0|1,    %% 普通|困难
    DunData :: list(),      %% 副本历史记录
    DunId :: integer().     %% 最大通关副本id
get_tower_max_pass_dun(DiffcultType, DunData) ->
	case lists:keyfind(?DUNGEON_TYPE_TOWER, 1, DunData) of
		{_, [MaxDun, DiffcultDun|_]} ->
            if DiffcultType == 0 ->
                    MaxDun;
                true ->
                    DiffcultDun
            end;
		_ ->
            0
	end.

%% 获取通关最大层数
get_pass_max_floor(Val) ->
    get_pass_max_floor(0, Val).

-spec get_pass_max_floor(DiffcultType, Val) -> Floor when
    DiffcultType :: 0|1,    %% 普通难度|困难
    Val :: list() | integer() | #player_status{}, %% 副本记录|副本id|人物记录
    Floor :: integer().     %% 层数
get_pass_max_floor(DiffcultType, DunData) when is_list(DunData) ->
    DunId = get_tower_max_pass_dun(DiffcultType, DunData),
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{condition = Condition} ->
            case lists:keyfind(floor, 1, Condition) of
                {_, _, Floor} -> Floor;
                _ -> 0
            end;
        _ ->
            0
    end;
get_pass_max_floor(_DiffcultType, DunId) when is_integer(DunId) ->
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{condition = Condition} ->
            case lists:keyfind(floor, 1, Condition) of
                {_, _, Floor} -> Floor;
                _ -> 0
            end;
        _ ->
            0
    end;
get_pass_max_floor(DiffcultType, Player) when is_record(Player, player_status) ->
    #player_status{dungeon = #status_dungeon{dun_data = DunData}} = Player,
    get_pass_max_floor(DiffcultType, DunData).


%% 是否是困难难度副本
is_diffcult_dun(DunId) ->
    DiffcultDuns = data_tower_dun:get_all_diffcult_dun(),
    lists:member(DunId, DiffcultDuns).

gm_reset_tower_dun(Player) ->
    #player_status{id = RoleId} = Player,
    db:execute(<<"DELETE FROM dungeon_data WHERE role_id = ~p AND dun_type = ~p">>, [RoleId, ?DUNGEON_TYPE_TOWER]),
    DunIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_TOWER),
    lists:map(fun(D) ->
        db:execute(<<"DELETE FROM dungeon_best_record WHERE player_id = ~p AND dun_id = ~p">>, [RoleId, D])
     end, DunIds),
    CountList = [{{?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, DunIdX}, 0}|| DunIdX <- DunIds],
    lib_daily:set_count(RoleId, CountList),
    CountList1 = [{{?MOD_DUNGEON, ?MOD_DUNGEON_SUCCESS, DunIdX1}, 0} || DunIdX1 <- DunIds],
    lib_counter:set_count(RoleId, CountList1),
    case pp_dungeon:handle(61020, Player, [?DUNGEON_TYPE_TOWER]) of
        {ok, NewPlayer} ->
            NewPlayer;
        _ ->
            Player
    end.

gm_update_star_score() ->
    case db:get_all(<<"SELECT player_id, dun_id, data FROM dungeon_best_record">>, []) of
        L = [_|_] ->
            update_star_score_do(L, 1);
        _ ->
            skip
    end.

update_star_score_do([], _) -> ?ERR("end", []), skip;
update_star_score_do([[RoleId, DunId, DataBin]|Rest], Acc) ->
    if Acc rem 10 == 0 ->
            timer:sleep(1000);
        true ->
            skip
    end,
    DunType = data_dungeon:get_dun_type(DunId),
    if DunType == ?DUNGEON_TYPE_MATERIAL orelse
        DunType == ?DUNGEON_TYPE_TOWER orelse
         DunType == ?DUNGEON_TYPE_VIP orelse DunType == ?DUNGEON_TYPE_PER_BOSS
         orelse DunType == ?DUNGEON_TYPE_NORMAL ->
            Data = util:bitstring_to_term(DataBin),
            case lists:keyfind(3, 1, Data) of
                {_, L} when is_list(L) ->
                    NewData  = lists:keydelete(3, 1, Data),
                    NewData1 = lists:keystore(8, 1, NewData, {8, L}),
                    lib_dungeon:save_dungeon_record(RoleId, DunId, NewData1),
                    update_star_score_do(Rest, Acc+1);
                _ ->
                    update_star_score_do(Rest, Acc+1)
            end;
        true ->
            update_star_score_do(Rest, Acc)
    end.

get_dun_result_succ_args(DunRole, [ResultSubType|_]) ->
    #dungeon_role{grade_progress = GradeProgress} = DunRole,
    if is_record(GradeProgress, dungeon_role_grade) ->
            DunStar = dungeon_default:calc_dun_score(GradeProgress#dungeon_role_grade.map);
        true ->
            DunStar = 0
    end,
    [ResultSubType, DunStar].

refresh_dun_count_state(RoleId) ->
    DungeonType = ?DUNGEON_TYPE_TOWER,
    Now = utime:unixtime(),
    AftCount  = lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, DungeonType),
    ResetTime = lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_RESET_TIME, DungeonType),
    CfgCount  = data_dungeon_special:get(DungeonType, guard_harddun_num),
    RecoverTime = data_dungeon_special:get(DungeonType, guard_harddun_reftime),
    if AftCount =/= 0 ->
            if ResetTime == 0 ->
                    EndTime = Now+RecoverTime,
                    lib_counter:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_RESET_TIME, DungeonType, EndTime),
                    tower_dun_rank:add_role_recover(RoleId, EndTime);
                true ->
                    if ResetTime > Now -> %% 恢复中
                            tower_dun_rank:add_role_recover(RoleId, ResetTime);
                        true -> %% ResetTime =< Now 已有可恢复的次数
                            {_HaveRecovered, NewResetTime, NewAftCount} =
                             calc_recovery_count(AftCount, ResetTime, {RecoverTime, CfgCount}, Now),
                            if NewAftCount =/= AftCount orelse ResetTime =/= NewResetTime -> 
                                    lib_counter:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, DungeonType, NewAftCount),
                                    lib_counter:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_RESET_TIME, DungeonType, NewResetTime);
                                true -> skip
                            end,
                            tower_dun_rank:add_role_recover(RoleId, NewResetTime)
                    end
            end;
        true -> %% 已使用次数变为0表示恢复次数已满
            if ResetTime == 0 -> skip;
                true -> lib_counter:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_RESET_TIME, DungeonType, 0)
            end,
            tower_dun_rank:add_role_recover(RoleId, 0)
    end.

%% 计算本次恢复的次数,下次恢复的时间,恢复后的总次数
calc_recovery_count(Count, ResetTime, {RecoverTime, _CfgCount}, Now) ->
    LastTime = Now - ResetTime,
    RecoveryCount = LastTime div RecoverTime,
    if RecoveryCount == 0 -> %% 上次恢复时间至今未满足再恢复额外次数
            NewCount = Count - 1,
            if NewCount =< 0 -> %% 已恢复满
                    {1, 0, 0};
                true ->
                    {1, ResetTime + RecoverTime, Count-1}
            end;
        true ->
            Count1 = Count - (RecoveryCount+1),
            if Count1 =< 0 -> %% 已恢复满
                    AddCount = Count,
                    NewCount = 0,
                    NextRecoverTime = 0;
                true ->
                    AddCount = RecoveryCount+1,
                    NewCount = Count1,
                    RestRecoverTime = LastTime - (RecoverTime * AddCount), %% LastTime = (Now - ResetTime)
                    NextRecoverTime = Now + RestRecoverTime
            end,
            {AddCount, NextRecoverTime, NewCount}
    end.

role_recover_time_out(Player) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    refresh_dun_count_state(RoleId),
    DungeonType = ?DUNGEON_TYPE_TOWER,
    CfgCount  = data_dungeon_special:get(DungeonType, guard_harddun_num),
    AftCount  = lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, DungeonType),
    ResetTime = lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_RESET_TIME, DungeonType),
    % ?ERR("role time out ~p ~p", [AftCount, ResetTime]),
    lib_server_send:send_to_sid(Sid, pt_611, 61121, [max(0, CfgCount - AftCount), ResetTime]),
    ok.

reset_tower_count(RoleId) ->
    DungeonType = ?DUNGEON_TYPE_TOWER,
    CfgCount  = data_dungeon_special:get(DungeonType, guard_harddun_num),
    lib_counter:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, DungeonType, 0),
    lib_counter:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_RESET_TIME, DungeonType, 0),
    lib_server_send:send_to_uid(RoleId, pt_611, 61121, [CfgCount, 0]),
    tower_dun_rank:add_role_recover(RoleId, 0),
    ok.

get_sweep_dun(RoleId, DunType, MaxDunIds) ->
    case MaxDunIds of
        [MaxDunId|_] -> 
            DunIds = data_dungeon:get_ids_by_type(DunType),
            case lists:filter(fun(DunId) -> DunId =< MaxDunId end, DunIds) of
                AccessDunIds = [_|_] ->
                    DunCountList = [{?MOD_DUNGEON, ?MOD_DUNGEON_SUCCESS, DunIdX} || DunIdX <- AccessDunIds],
                    DunCountList1 = lib_counter:get_count(RoleId, DunCountList),
                    case lists:foldl(fun({{_, _, DunIdX1}, DunCount}, Acc) -> 
                        if DunCount > 0 -> [DunIdX1|Acc]; %% 已经通关
                            true -> Acc
                        end
                    end, [], DunCountList1) of
                        AccessDunIds1 = [_|_] ->
                            AccessDunIds1;
                        _ -> []
                    end;
                _ -> []
            end;
        _ ->
            []
    end.

sweep_tower_dun(Player, SweepDunIds) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    RewardList = get_sweep_rewards(SweepDunIds, []),
    Player1 = lib_goods_api:send_reward(Player, RewardList, tower_dun_sweep_rewards),
    lib_daily:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUN_SWEEP_SIGN, ?DUNGEON_TYPE_TOWER, 1),
    lib_server_send:send_to_sid(Sid, pt_611, 61129, [?SUCCESS]),
    {ok, Player1}.

get_sweep_rewards([], AccRewards) -> AccRewards;
get_sweep_rewards([DunId|Rest], AccRewards) ->
    case data_tower_dun:get_tower_dun_daily_reward(DunId) of
        BaseRewards = [_|_] ->
            AccRewards1 = lists:append(BaseRewards, AccRewards),
            get_sweep_rewards(Rest, AccRewards1);
        _ ->
            get_sweep_rewards(Rest, AccRewards)
    end.

% reset_all_tower_dun() ->
%     db:execute(<<"DELETE FROM dungeon_data WHERE dun_type = ~p">>, [?DUNGEON_TYPE_TOWER]),
%     DunIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_TOWER),
%     [begin
%         db:execute(<<"DELETE FROM dungeon_best_record WHERE dun_id = ~p">>, [DunId]),
%         db:execute(<<"DELETE FROM counter_daily_four WHERE module = 610 and type = ~p">>, [DunId]),
%         db:execute(<<"DELETE FROM counter_daily_twelve WHERE module = 610 and type = ~p">>, [DunId]),
%         db:execute(<<"DELETE FROM counter WHERE module = 610 and type = ~p">>, [DunId])
%      end || DunId <- DunIds],
%     DunIds1 = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_MATERIAL),
%     [begin
%         db:execute(<<"DELETE FROM dungeon_best_record WHERE dun_id = ~p">>, [DunIdX]),
%         db:execute(<<"DELETE FROM counter_daily_four WHERE module = 610 and type = ~p">>, [DunIdX]),
%         db:execute(<<"DELETE FROM counter_daily_twelve WHERE module = 610 and type = ~p">>, [DunIdX]),
%         db:execute(<<"DELETE FROM counter WHERE module = 610 and type = ~p">>, [DunIdX])
%      end || DunIdX <- DunIds1],
%     db:execute(<<"DELETE FROM dungeon_data WHERE dun_type = ~p">>, [9]),
%     ok.