%% --------------------------------
%% desc		:星辰副本
%% module	:lib_dungeon_galaxy.erl
%% author	:ljy
%% time		:20/08/28
%% --------------------------------
-module(lib_dungeon_galaxy).

-include("dungeon.hrl").
-include("server.hrl").
-include("def_module.hrl").
-include("errcode.hrl").
-include("figure.hrl").
-include("common.hrl").
-include("predefine.hrl").
-include("dungeon_galaxy.hrl").
-include("scene.hrl").
-include("battle.hrl").
-include("attr.hrl").
-include("custom_act.hrl").

%% API
-export([
    check_extra/2,
    handle_dungeon_result/1,
    get_role_rec/6,
    push_settlement/2,
    role_update_dun_rec/5,
    get_send_reward/2,
    boss_status_change/2,
    hp_change_handle/3,
    get_start_dun_args/4,
    get_dun_args/2,
    gm_reset_galaxy_dun/1
]).

-export([
    login/1,
    get_chapter_info/1,             %% 章节信息
    galaxy_chapter_reward/3,        %% 领取章节奖励
    enter_next_chapter/1            %% 进入下一章节
]).

login(RoleId) ->
    Sql1 = io_lib:format(?SQL_SELECT_GALAXY_CHAPTER, [RoleId]),
    case db:get_row(Sql1) of
        [ChapterId] ->
            ChapterId;
        _ ->
            ChapterId = data_galaxy_dun:get_min_dun_chapter(),
            save_db_dun_chapter(RoleId, ChapterId)
    end,
    Sql2 = io_lib:format(?SQL_SELECT_GALAXY_DUN_REWARD, [RoleId, ChapterId]),
    RewardStatus =
        case db:get_row(Sql2) of
            [DbData] ->
                util:bitstring_to_term(DbData);
            _ ->
                []
        end,
    #role_chapter_info{chapter_id = ChapterId, reward_info = RewardStatus}.

%% 检查是否可进入
check_extra(Player, Dun) ->
    #player_status{dungeon_record = DungeonRecs, figure = #figure{lv = Lv}, role_dun_galaxy_chapter = ChapterInfo} = Player,
    #role_chapter_info{chapter_id = ChapterId} = ChapterInfo,
    case data_galaxy_dun:get_chapter_cfg(ChapterId) of
        #dun_galaxy_chapter_cfg{dun_ids = DunIds} -> ok;
        _ -> DunIds = []
    end,
    #dun_cfg{id = DunId} = Dun,
    MinDunId = data_galaxy_dun:get_min_dun(),
    MaxDunId = data_galaxy_dun:get_max_dun(),
    PrevDunId = data_galaxy_dun:get_galaxy_prev_dun(DunId),
    DungeonRec = maps:get(DunId, DungeonRecs, []),
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DungeonRec) of
        {_, MaxScore} -> ok;
        _ -> MaxScore = 0
    end,
    case data_dungeon_grade:get_dungeon_grade(DunId, Lv) of
        #dungeon_grade{condition = Condition} ->
            ConfigMaxScore = length(Condition);
        _ ->
            ConfigMaxScore = 0
    end,
    case lists:member(DunId, DunIds) of
        true when DunId == MinDunId andalso ConfigMaxScore > MaxScore ->
            true;
        true when DunId > PrevDunId andalso ConfigMaxScore > MaxScore ->
            PrevDungeonRec = maps:get(PrevDunId, DungeonRecs, []),
            case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, PrevDungeonRec) of
                {_, PrevMaxScore} -> ok;
                _ -> PrevMaxScore = 0
            end,
            case data_dungeon_grade:get_dungeon_grade(PrevDunId, Lv) of
                #dungeon_grade{condition = PrevCondition} ->
                    PrevConfigMaxScore = length(PrevCondition);
                _ ->
                    PrevConfigMaxScore = 0
            end,
            case ConfigMaxScore == 0 orelse PrevConfigMaxScore == 0 of
                true -> {false, ?ERRCODE(missing_config)};
                false when PrevMaxScore < PrevConfigMaxScore ->
                    {false, ?ERRCODE(err610_dun_score_limit)};
                false ->
                    true
            end;
        true when DunId == MaxDunId ->  %% 最后一个关卡可以重复挑战
            true;
        false when DunIds == [] ->
            {false, ?ERRCODE(missing_config)};
        false ->
            {false, ?ERRCODE(err610_not_in_chapter)};
        _ ->
            {false, ?ERRCODE(err610_just_once_finish)}
    end.

%% 将玩家信息转为副本信息
get_role_rec(_DunId, _DunType, _RoleId, DungeonRec, _DunData, TypicalData) ->
    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DungeonRec) of
        {_, MaxScore} -> ok;
        _ -> MaxScore = 0
    end,
    %% 副本最大星级
    NewTypicalData1 = maps:put(?DUNGEON_REC_MAX_SCORE, MaxScore, TypicalData),
    NewTypicalData2 = maps:put(?DUN_STATE_SPECIAL_KEY_MAX_SCORE, MaxScore, NewTypicalData1),
    NewTypicalData2.

%% 结算展示
push_settlement(State, DungeonRole) ->
    #dungeon_state{
        result_type = ResultType,
        result_subtype = ResultSubType,
        dun_id = DunId,
        scene_id = SceneId,
        wave_num = WaveNum,
        role_list = RoleList
    } = State,
    [DungeonRole | _] = RoleList,
    #dungeon_role{custom_reward_list = CustomRewards, typical_data = RoleTypicalData, grade_progress = GradeProgress} = DungeonRole,
    #dungeon_role{node = Node, sid = Sid, calc_reward_list = RewardList} = DungeonRole,
    HistoryScore = maps:get(?DUN_STATE_SPECIAL_KEY_MAX_SCORE, RoleTypicalData, 0),
    NowScore = case GradeProgress of
                   #dungeon_role_grade{map = Maps} ->
                       dungeon_default:calc_dun_score(Maps);
                   _ ->
                       0
               end,
    {ok, BinData} = pt_610:write(61003, [ResultType, ResultSubType, DunId, SceneId, WaveNum, RewardList, CustomRewards, [{?DUNGEON_REC_MAX_SCORE, HistoryScore}, {?DUNGEON_REC_DATA, NowScore}]]),
    lib_server_send:send_to_sid(Node, Sid, BinData).

%% 副本结算发放奖励后的处理
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, typical_data = TypicalData, grade_progress = GradeProgress} = DunRole,
            NewTypicalData = update_dun_data(RoleId, DunType, DunId, GradeProgress, TypicalData),
            NewRoleList = lists:keystore(RoleId, #dungeon_role.id, RoleList, DunRole#dungeon_role{typical_data = NewTypicalData}),
            State#dungeon_state{role_list = NewRoleList};
            true ->
                State
        end,
    NewState.

%% 更新玩家此副本类型的最大副本id
update_dun_data(RoleId, 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),
        lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?MODULE, role_update_dun_rec, [DunId, DunType, ?DUNGEON_REC_MAX_SCORE, NewScore]);
        true ->
            NewTypicalData = TypicalData
    end,
    NewTypicalData.

%% 将副本信息更新回玩家进程
role_update_dun_rec(Player, DunId, DunType, Type, Score) ->
    #player_status{id = RoleId, dungeon_record = DunRecs, dungeon = Dungeon} = Player,
    #status_dungeon{dun_data = DunData} = Dungeon,
    DunRec = maps:get(DunId, DunRecs, []),
    NewDunRec = lists:keystore(Type, 1, DunRec, {Type, Score}),
    NewDunRecs = maps:put(DunId, NewDunRec, DunRecs),
    lib_dungeon:save_dungeon_record(RoleId, DunId, NewDunRec),
    MaxDunData = [DunId],
    lib_dungeon:db_replace_dun_data(RoleId, DunType, MaxDunData),
    NewDunData = lists:keystore(DunType, 1, DunData, {DunType, MaxDunData}),
    NewDungeon = Dungeon#status_dungeon{dun_data = NewDunData},
    {ok, Player#player_status{dungeon_record = NewDunRecs, dungeon = NewDungeon}}.

%% 副本结束计算奖励
get_send_reward(State, DungeonRole) ->
    #dungeon_state{dun_id = DunId, result_type = ResultType} = State,
    #dungeon_role{typical_data = TypicalData, grade_progress = GradeProgress, figure = Figure} = DungeonRole,
    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
        ResultType =:= ?DUN_RESULT_TYPE_SUCCESS ->
            #dun_galaxy_next_cfg{stars_reward = StarsReward, normal_reward = NormalReward} = data_galaxy_dun:get_galaxy_next_cfg(DunId),
            %% 每个星级的首通奖励只可领一次
            Rewards1 = calc_rewards(StarsReward, NewScore, MaxScore + 1),
            %% 每个星级的普通奖励无次数限制
            Rewards2 = calc_rewards(NormalReward, NewScore, MaxScore),
            %% 定制活动多倍奖励
            MultiTimes = lib_dungeon_api:get_custom_act_reward_times(?DUNGEON_TYPE_GALAXY, DunId, Figure#figure.lv),
            case MultiTimes > 1 of
                true ->
                    CustomReward = [{TGoodsType1, TGoodsId1, TGoodsNum1 * max(1, MultiTimes - 1)} || {TGoodsType1, TGoodsId1, TGoodsNum1} <- Rewards2];
                _ ->
                    CustomReward = []
            end,
            DungeonRole#dungeon_role{calc_reward_list = Rewards1 ++ Rewards2, custom_reward_list = CustomReward};
        true ->
            DungeonRole
    end.

%% 计算奖励
calc_rewards(StarsReward, NewScore, OldScore) when NewScore >= OldScore ->
    case lists:keyfind(NewScore, 1, StarsReward) of
        {_, Rewards} -> Rewards;
        _ -> []
    end;
calc_rewards(_StarsReward, _NewScore, _OldScore) ->
    [].

%% 副本启动参数
get_start_dun_args(_Player, _DungeonRole, _Dun, _OptionType) ->
    [{typical_data, [{?DUN_STATE_SPECIAL_KEY_HANDLE_MFA, {?MODULE, get_dun_args, []}}]}].

get_dun_args(State, _Args) ->
    #dungeon_state{dun_id = DunId, typical_data = TypicalData} = State,
    #base_dun_wave_cfg{mon_list = MonList} = data_dungeon_wave:get_dun_wave(DunId, 1),
    NewBossHp =
        lists:foldl(fun({MonId, _X, _Y, _Args1, _Args2}, TmpBossHp) ->
            #mon{hp_lim = InitHp} = data_mon:get(MonId),
            NewTmpBossHp = lists:keystore(MonId, 1, TmpBossHp, {MonId, InitHp}),
            NewTmpBossHp
                    end, [], MonList),
    NewTypicalData = maps:put(?DUN_STATE_SPECIAL_KEY_BOSS_HP, NewBossHp, TypicalData),
    State#dungeon_state{typical_data = NewTypicalData}.

%% 副本进程，统计触发ai的boss
boss_status_change(State = #dungeon_state{is_end = ?DUN_IS_END_NO}, [Object]) ->
    #dungeon_state{dun_id = DunId, scene_id = Scene, scene_pool_id = PoolId} = State,
    #scene_object{id = ObjectId, aid = MonPid, config_id = MonId, battle_attr = #battle_attr{hp_lim_origin = HpLim}} = Object,
    #base_dun_wave_cfg{mon_list = MonList} = data_dungeon_wave:get_dun_wave(DunId, 1),
    [Critical] = data_mon_ai_hp:get(MonId),
    %% 将触发ai怪物的血量回调到触发点血量
    % old: MonPid ! {'change_attr', [{hp, trunc(HpLim * Critical)}]},
    mon_mgr:send(MonPid, ObjectId, {'change_attr', [{hp, trunc(HpLim * Critical) - 1}]}),
    case get(object_ids) of
        undefined ->
            ObjectIds = [];
        ObjectIds ->
            ok
    end,
    InvincibleNum = length(ObjectIds),
    MonNum = length(MonList),
    %% 记录触发ai的怪物，当副本所有怪物触发Ai，则取消所有怪物的ai技能，
    if
        InvincibleNum == MonNum - 1 ->
            AiList = data_mon_ai_hp:get(MonId, Critical),
            case lists:keyfind(ac_skill_can_cancel, 1, AiList) of
                {ac_skill_can_cancel, {_, SkillId, _}, _} ->
                    [mod_scene_agent:apply_cast_with_scene(Scene, PoolId, lib_skill_buff, clean_mon_buff, [Id, SkillId]) || Id <- [ObjectId | ObjectIds]],
                    Args = data_galaxy_dun:get_dun_galaxy_kv_cfg(invincible_remove),
                    lib_scene_object_ai:mon_center_msg(Object, Args),
                    put(object_ids, [ObjectId | ObjectIds]);
                _ -> ?ERR("dungeon galaxy config error ~n")
            end;
        true ->
            if
                InvincibleNum == 0 -> Args = data_galaxy_dun:get_dun_galaxy_kv_cfg(invincible_first);
                true -> Args = data_galaxy_dun:get_dun_galaxy_kv_cfg(invincible_second)
            end,
            lib_scene_object_ai:mon_center_msg(Object, Args),
            put(object_ids, [ObjectId | ObjectIds])
    end,
    {noreply, State};
boss_status_change(State, _) ->
    {noreply, State}.

%% 怪物进程回调-血量变化通知
hp_change_handle(_State, _MonAtter, [0]) ->
    stop;
hp_change_handle(State, _MonAtter, [OldHp]) ->
    #ob_act{object = Object} = State,
    #battle_attr{hp = NowHp, hp_lim = HpLim} = Object#scene_object.battle_attr,
    #scene_object{config_id = MonTypeId, copy_id=CopyId} = Object,
    case data_mon_ai_hp:get(MonTypeId) of
        [] ->
            stop;
        [V|_] ->
            OldHpR = OldHp/HpLim,
            NowHpR = NowHp/HpLim,
            if
                OldHpR > V andalso NowHpR =< V ->
                    AiList =  data_mon_ai_hp:get(MonTypeId, V),
                    case lists:keyfind(ac_skill_can_cancel, 1, AiList) of
                        {_, {act_now,SkillId,SkillLv},_} ->
                            NewState = lib_scene_object_ai:object_ac_skill(State, SkillId, SkillLv),
                            %% 统计触发数量
                            gen_server:cast(CopyId, {apply, lib_dungeon_galaxy, boss_status_change, [Object]}),
                            {ok, NewState, {lib_dungeon_galaxy, hp_change_handle, [0]}};
                        _ ->
                            stop
                    end;
                true ->
                    {ok, State, {lib_dungeon_galaxy, hp_change_handle, [NowHp]}}
            end
    end.

%% 获取所处章节信息
get_chapter_info(Player) ->
    #player_status{dungeon = Dungeon, dungeon_record = Rec, sid = Sid, role_dun_galaxy_chapter = ChapterInfo} = Player,
    #role_chapter_info{chapter_id = ChapterId, reward_info = RewardInfo} = ChapterInfo,
    #status_dungeon{dun_data = Data} = Dungeon,
    #dun_galaxy_chapter_cfg{dun_ids = DunIds, reward = Rewards} = data_galaxy_dun:get_chapter_cfg(ChapterId),
    StarList = [Star || {Star, _} <- Rewards],
    TotalStar = get_total_stars(DunIds, Rec),
    F = fun(StarNum, Acc) ->
        case lists:member(StarNum, RewardInfo) of
            true -> [{StarNum, 2} | Acc];
            false when TotalStar >= StarNum -> [{StarNum, 1} | Acc];
            _ -> [{StarNum, 0} | Acc]
        end
        end,
    RewardStatus = lists:foldl(F, [], StarList),
    case lists:keyfind(?DUNGEON_TYPE_GALAXY, 1, Data) of
        {_, [DungeonId]} ->
            MaxStar = get_total_stars([DungeonId], Rec),
            #dun_galaxy_next_cfg{stars_reward = StarsReward} = data_galaxy_dun:get_galaxy_next_cfg(DungeonId),
            NextDun = data_galaxy_dun:get_galaxy_next_dun(DungeonId),
            if
                MaxStar == length(StarsReward) andalso NextDun =/= 0 ->
                    DunId = NextDun;
                true ->
                    DunId = DungeonId
            end;
        _ ->
            DunId = data_galaxy_dun:get_min_dun()
    end,
    {ok, BinData} = pt_610:write(61032, [ChapterId, DunId, RewardStatus]),
    lib_server_send:send_to_sid(Sid, BinData),
    ok.

%% 玩家所在章节的总星数
get_total_stars(DunIds, Rec) ->
    F = fun(DunId, Sum) ->
        Data = maps:get(DunId, Rec, []),
        case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, Data) of
            {_, MaxScore} -> Sum + MaxScore;
            _ -> Sum
        end
        end,
    lists:foldl(F, 0, DunIds).

%% 领取章节奖励
galaxy_chapter_reward(Player, ChapterId, StarNum) ->
    #player_status{id = RoleId, dungeon_record = Rec, sid = Sid, role_dun_galaxy_chapter = ChapterInfo} = Player,
    #role_chapter_info{chapter_id = ChapId, reward_info = RewardInfo} = ChapterInfo,
    case ChapterId == ChapId of
        true ->
            #dun_galaxy_chapter_cfg{dun_ids = DunIds, reward = Rewards} = data_galaxy_dun:get_chapter_cfg(ChapterId),
            StarList = [Star || {Star, _} <- Rewards],
            TotalStar = get_total_stars(DunIds, Rec),
            IsGot = lists:member(StarNum, RewardInfo),
            case lists:member(StarNum, StarList) == true andalso TotalStar >= StarNum of
                true when IsGot ->
                    lib_server_send:send_to_sid(Sid, pt_610, 61033, [?ERRCODE(reward_is_got)]);
                true ->
                    [AllReward] = [Reward || {Star, Reward} <- Rewards, Star == StarNum],
                    NewRewardInfo = [StarNum | RewardInfo],
                    NewStarList = util:term_to_string(NewRewardInfo),
                    save_db_dun_reward(RoleId, ChapId, NewStarList),
                    lib_goods_api:send_reward(Player, AllReward, galaxy_dun_chapter_reward),
                    lib_server_send:send_to_sid(Sid, pt_610, 61033, [?SUCCESS]),
                    NewChapterInfo = ChapterInfo#role_chapter_info{reward_info = NewRewardInfo},
                    NewPlayer = Player#player_status{role_dun_galaxy_chapter = NewChapterInfo},
                    {ok, NewPlayer};
                _ ->
                    lib_server_send:send_to_sid(Sid, pt_610, 61033, [?ERRCODE(err610_star_not_enough)])
            end;
        false -> lib_server_send:send_to_sid(Sid, pt_610, 61033, [?ERRCODE(err610_not_in_chapter)])
    end.

%% 进入下一章节
enter_next_chapter(Player) ->
    #player_status{id = RoleId, dungeon_record = Rec, sid = Sid, role_dun_galaxy_chapter = ChapterInfo} = Player,
    #role_chapter_info{chapter_id = ChapterId, reward_info = RewardInfo} = ChapterInfo,
    #dun_galaxy_chapter_cfg{dun_ids = DunIds, reward = Rewards, next_id = NextChapterId} = data_galaxy_dun:get_chapter_cfg(ChapterId),
    TotalStar = get_total_stars(DunIds, Rec),
    StarList = [Star || {Star, _} <- Rewards],
    StarSum = lists:max(StarList),
    if
        TotalStar == StarSum andalso NextChapterId =/= 0 ->
            save_db_dun_chapter(RoleId, NextChapterId),
            NewStarList = util:term_to_string([]),
            save_db_dun_reward(RoleId, NextChapterId, NewStarList),
            F = fun(StarNum, AllReward) ->
                case lists:member(StarNum, RewardInfo) of
                    false ->
                        {StarNum, Reward} = lists:keyfind(StarNum, 1, Rewards),
                        Reward ++ AllReward;
                    _ -> AllReward
                end
                end,
            FinalReward = lists:foldl(F, [], StarList),
            if
                FinalReward =/= [] -> %%补发未领取奖励
                    lib_goods_api:send_reward(Player, FinalReward, galaxy_dun_chapter_reward);
                true -> skip
            end,
            NewChapterInfo = ChapterInfo#role_chapter_info{chapter_id = NextChapterId, reward_info = []},
            NewPlayer = Player#player_status{role_dun_galaxy_chapter = NewChapterInfo},
            lib_server_send:send_to_sid(Sid, pt_610, 61034, [?SUCCESS]),
            get_chapter_info(NewPlayer),
            %% 完成章节成就
            lib_achievement_api:async_event(RoleId, lib_achievement_api, dungeon_galaxy_event, ChapterId),
            {ok, NewPlayer};
        TotalStar == StarSum ->
            lib_server_send:send_to_sid(Sid, pt_610, 61034, [?ERRCODE(err610_already_max_chapter)]),
            ok;
        true ->
            lib_server_send:send_to_sid(Sid, pt_610, 61034, [?ERRCODE(err610_not_through_chapter)]),
            ok
    end.

%% ----------------- 数据库操作 -------------------------------a
save_db_dun_chapter(RoleId, ChapterId) ->
    Sql = io_lib:format(?SQL_INSERT_DUN_GALAXY_CHAPTER, [RoleId, ChapterId]),
    db:execute(Sql).

save_db_dun_reward(RoleId, ChapterId, StarList) ->
    Sql = io_lib:format(?SQL_INSERT_GALAXY_DUN_REWARD, [RoleId, ChapterId, StarList]),
    db:execute(Sql).

%% ----------------- gm指令操作 -------------------------------
gm_reset_galaxy_dun(Player) ->
    #player_status{id = RoleId, dungeon = Dungeon, dungeon_record = Rec} = Player,
    db:execute(<<"DELETE FROM dungeon_data WHERE role_id = ~p AND dun_type = ~p">>, [RoleId, ?DUNGEON_TYPE_GALAXY]),
    db:execute(<<"DELETE FROM galaxy_dun_chapter WHERE role_id = ~p">>, [RoleId]),
    db:execute(<<"DELETE FROM galaxy_dun_chapter_reward WHERE role_id = ~p">>, [RoleId]),
    DunIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_GALAXY),
    lists:map(fun(D) ->
        db:execute(<<"DELETE FROM dungeon_best_record WHERE player_id = ~p AND dun_id = ~p">>, [RoleId, D])
              end, DunIds),
    F = fun(DunId, Map) ->
        maps:remove(DunId, Map)
        end,
    NewRec = lists:foldl(F, Rec, DunIds),
    #status_dungeon{dun_data = Data} = Dungeon,
    NewData = lists:keystore(?DUNGEON_TYPE_GALAXY, 1, Data, {?DUNGEON_TYPE_GALAXY, 15001}),
    NewDungeon = Dungeon#status_dungeon{dun_data = NewData},

    ChapterId = data_galaxy_dun:get_min_dun_chapter(),
    ChapterInfo = #role_chapter_info{chapter_id = ChapterId, reward_info = []},
    NewPlayer = Player#player_status{dungeon = NewDungeon, dungeon_record = NewRec, role_dun_galaxy_chapter = ChapterInfo},
    get_chapter_info(NewPlayer),
    NewPlayer.
