using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Data.Custom;
using EggLink.DanhengServer.Data.Excel;
using EggLink.DanhengServer.Enums.Rogue;
using EggLink.DanhengServer.Enums.Scene;
using EggLink.DanhengServer.Enums.TournRogue;
using EggLink.DanhengServer.GameServer.Game.Battle;
using EggLink.DanhengServer.GameServer.Game.Player;
using EggLink.DanhengServer.GameServer.Game.Rogue;
using EggLink.DanhengServer.GameServer.Game.Rogue.Buff;
using EggLink.DanhengServer.GameServer.Game.Rogue.Event;
using EggLink.DanhengServer.GameServer.Game.RogueTourn.Formula;
using EggLink.DanhengServer.GameServer.Game.RogueTourn.Scene;
using EggLink.DanhengServer.GameServer.Game.RogueTourn.Titan;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.EraFlipper;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lineup;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.RogueCommon;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.RogueTourn;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Util;
using FormulaTypeValue = EggLink.DanhengServer.Proto.FormulaTypeValue;

namespace EggLink.DanhengServer.GameServer.Game.RogueTourn;

public class RogueTournInstance : BaseRogueInstance
{
    #region Initializer

    public RogueTournInstance(PlayerInstance player, int areaId) : base(player, RogueSubModeEnum.TournRogue, 0)
    {
        // generate levels
        AreaExcel = GameData.RogueTournAreaData.GetValueOrDefault(areaId) ??
                    throw new Exception("Invalid area id"); // wont be null because of validation in RogueTournManager

        var index = 0;
        foreach (var id in AreaExcel.LayerIDList)
        {
            var levelInstance = new RogueTournLevelInstance(++index, id);
            Levels.Add(levelInstance.LayerId, levelInstance);
        }

        foreach (var difficulty in AreaExcel.DifficultyIDList)
            if (GameData.RogueTournDifficultyData.TryGetValue(difficulty, out var diff))
                DifficultyExcels.Add(diff);

        CurLayerId = AreaExcel.LayerIDList.FirstOrDefault();
        EventManager = new RogueEventManager(player, this);

        BaseRerollCount = 0;
        var t1 = RollTitanBless(1, true);
        t1.AsTask().Wait();

        var t = RollFormula(1, [RogueFormulaCategoryEnum.Epic]);
        t.AsTask().Wait();
    }

    #endregion

    #region Properties

    public List<RogueTournFormulaExcel> RogueFormulas { get; set; } = [];
    public List<int> ExpandedFormulaIdList { get; set; } = [];
    public Dictionary<int, RogueTournLevelInstance> Levels { get; set; } = [];
    public List<RogueTournDifficultyExcel> DifficultyExcels { get; set; } = [];
    public int CurLayerId { get; set; }
    public int TitanProgress { get; set; } = -1;
    public RogueTournAreaExcel AreaExcel { get; set; }
    public RogueTournLevelStatus LevelStatus { get; set; } = RogueTournLevelStatus.Processing;

    public Dictionary<RogueTournRoomTypeEnum, int> RoomTypeWeight { get; set; } = new()
    {
        { RogueTournRoomTypeEnum.Battle, 15 },
        { RogueTournRoomTypeEnum.Event, 7 },
        { RogueTournRoomTypeEnum.Reward, 5 },
        { RogueTournRoomTypeEnum.Encounter, 5 },
    };

    public RogueTournLevelInstance? CurLevel => Levels.GetValueOrDefault(CurLayerId);

    #endregion

    #region Scene

    public async ValueTask EnterNextLayer(int roomIndex, RogueTournRoomTypeEnum type)
    {
        var curInd = Levels.Keys.ToList().IndexOf(CurLayerId);
        var nextLayerId = Levels.Keys.ToList()[curInd + 1];
        var nextLayer = Levels[nextLayerId];
        CurLevel!.LevelStatus = RogueTournLayerStatus.Finish;

        CurLayerId = nextLayer.LayerId;
        await EnterRoom(1, type);
    }

    public async ValueTask EnterRoom(int roomIndex, RogueTournRoomTypeEnum type)
    {
        if (CurLevel == null) return;

        //if (CurLevel.CurRoomIndex == roomIndex)
        //    // same room
        //    return;

        //if (CurLevel.CurRoomIndex + 1 != roomIndex) // only allow to enter next room
        //    // invalid room
        //    return;
        if (CurLevel.CurRoom != null)
            CurLevel.CurRoom.Status = RogueTournRoomStatus.Finish;

        // enter room
        CurLevel.CurRoomIndex = roomIndex;
        CurLevel.CurRoom?.Init(type);

        // next room
        var next = CurLevel.Rooms.Find(x => x.RoomIndex == roomIndex + 1);
        if (next != null)
            next.Status = RogueTournRoomStatus.Inited;

        // scene
        var entrance = CurLevel.CurRoom?.Config?.EntranceId ?? 0;
        var group = CurLevel.CurRoom?.Config?.AnchorGroup ?? 0;
        var anchor = CurLevel.CurRoom?.Config?.AnchorId ?? 1;

        // call event
        EventManager?.OnNextRoom();
        foreach (var miracle in RogueMiracles.Values) miracle.OnEnterNextRoom();

        await Player.EnterSceneByEntranceId(entrance, group, anchor, false);

        // check if era flipper
        if (Player.SceneInstance!.FloorInfo!.LevelFeatureModules.Contains(LevelFeatureTypeEnum.EraFlipper))
            await Player.SendPacket(new PacketEraFlipperDataChangeScNotify(Player.SceneInstance!.FloorId));

        // sync
        await Player.SendPacket(new PacketRogueTournLevelInfoUpdateScNotify(this, [CurLevel]));

        TitanProgress++;
        await Player.SendPacket(new PacketRogueTournTitanUpdateTitanBlessProgressScNotify(this));

        if (TitanProgress >= 4)
            await RollTitanBless(1,
                RogueTitanBlessInstance.EnhanceBlessList.Count / 3 >= RogueTitanBlessInstance.BlessTypeExcel.Count);
    }


    public async ValueTask QuitRogue()
    {
        Player.LineupManager?.SetExtraLineup(ExtraLineupType.LineupNone, []);

        var currentLineup = Player.LineupManager!.GetCurLineup()!;

        await Player.SendPacket(new PacketSyncLineupNotify(currentLineup));

        await Player.EnterSceneByEntranceId(1034102, 0, 0, false);

        Player.RogueTournManager!.RogueTournInstance = null;
    }

    #endregion

    #region Titan

    public RogueTitanTypeEnum TitanType { get; set; } = RogueTitanTypeEnum.None;
    public RogueTitanBlessInstance RogueTitanBlessInstance { get; set; } = new();

    public async ValueTask RollTitanBless(int amount, bool selectType)
    {
        for (var i = 0; i < amount; i++)
        {
            var menu = new RogueTitanBlessSelectMenu(this);
            menu.RollTitanBless(typeSelect: selectType);
            var action = menu.GetActionInstance();
            RogueActions.Add(action.QueuePosition, action);
        }

        await UpdateMenu();

        TitanProgress = 0;
        await Player.SendPacket(new PacketRogueTournTitanUpdateTitanBlessProgressScNotify(this));
    }

    public async ValueTask HandleTitanBlessSelect(int blessId, int location)
    {
        if (RogueActions.Count == 0) return;
        var action = RogueActions.First().Value;
        if (action.RogueTitanBlessSelectMenu != null)
        {
            var bless = action.RogueTitanBlessSelectMenu.Blesses.Find(x => x.TitanBlessID == blessId);
            if (bless != null) // check if bless is in the list
                if (!RogueTitanBlessInstance.BlessTypeExcel.Exists(x =>
                        x.TitanBlessID == blessId)) // check if bless already exists
                {
                    if (action.RogueTitanBlessSelectMenu.TypeSelect)
                    {
                        RogueTitanBlessInstance.BlessTypeExcel.Add(bless);
                        TitanType =
                            GameData.RogueTournTitanTypeData.GetValueOrDefault(bless.TitanType)?.RogueTitanType ??
                            RogueTitanTypeEnum.None;
                    }
                    else
                    {
                        RogueTitanBlessInstance.EnhanceBlessList.Add(bless);
                    }

                    await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueSubMode,
                        bless.ToResultProto(RogueCommonActionResultSourceType.None), RogueCommonActionResultDisplayType.Multi));
                }

            RogueActions.Remove(action.QueuePosition);
            if (action.RogueTitanBlessSelectMenu.TypeSelect)
                await Player.SendPacket(
                    new PacketHandleRogueCommonPendingActionScRsp(action.QueuePosition, location,
                        selectTitanBlessType: true));
            else
                await Player.SendPacket(
                    new PacketHandleRogueCommonPendingActionScRsp(action.QueuePosition, location,
                        selectTitanBlessEnhance: true));
        }

        await UpdateMenu();
    }

    public async ValueTask HandleRerollTitanBless(int location)
    {
        if (RogueActions.Count == 0) return;
        var action = RogueActions.First().Value;
        if (action.RogueTitanBlessSelectMenu != null)
        {
            action.RogueTitanBlessSelectMenu.Reroll(); // reroll
            await Player.SendPacket(
                new PacketHandleRogueCommonPendingActionScRsp(action.QueuePosition, location,
                    titanMenu: action.RogueTitanBlessSelectMenu));
        }
    }

    #endregion

    #region Buff & Formula

    public override async ValueTask RollBuff(int amount)
    {
        await RollBuff(amount, 2000101);
    }

    public async ValueTask RollFormula(int amount, List<RogueFormulaCategoryEnum> categories)
    {
        var formulaList = GameData.RogueTournFormulaData.Values
            .Where(x => !RogueFormulas.Contains(x) && categories.Contains(x.FormulaCategory)).ToList();

        for (var i = 0; i < amount; i++)
        {
            var menu = new RogueFormulaSelectMenu(this);
            menu.RollFormula(formulaList);
            var action = menu.GetActionInstance();
            RogueActions.Add(action.QueuePosition, action);
        }

        await UpdateMenu();
    }

    public override async ValueTask HandleBuffSelect(int buffId, int location)
    {
        if (RogueActions.Count == 0) return;

        var action = RogueActions.First().Value;
        if (action.RogueBuffSelectMenu != null)
        {
            var buff = action.RogueBuffSelectMenu.Buffs.Find(x => x.MazeBuffID == buffId);
            if (buff != null) // check if buff is in the list
            {
                if (RogueBuffs.Exists(x => x.BuffExcel.MazeBuffID == buffId)) // check if buff already exists
                {
                    // enhance
                    await EnhanceBuff(buffId, RogueCommonActionResultSourceType.Select);
                }
                else
                {
                    var instance = new RogueBuffInstance(buff.MazeBuffID, buff.MazeBuffLevel);
                    RogueBuffs.Add(instance);
                    await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueSubMode,
                        instance.ToResultProto(RogueCommonActionResultSourceType.Select)));
                }
            }

            RogueActions.Remove(action.QueuePosition);
            if (action.RogueBuffSelectMenu.IsAeonBuff) AeonBuffPending = false; // aeon buff added
        }

        await ExpandFormula();
        await UpdateMenu();

        await Player.SendPacket(new PacketHandleRogueCommonPendingActionScRsp(action.QueuePosition, location, true));
    }

    public override async ValueTask<RogueCommonActionResult?> AddBuff(int buffId, int level = 1,
        RogueCommonActionResultSourceType source = RogueCommonActionResultSourceType.Dialogue,
        RogueCommonActionResultDisplayType displayType = RogueCommonActionResultDisplayType.Single,
        bool updateMenu = true, bool notify = true)
    {
        var res = await base.AddBuff(buffId, level, source, displayType, updateMenu, notify);

        await ExpandFormula();

        return res;
    }

    public override async ValueTask<RogueCommonActionResult?> RemoveBuff(int buffId,
        RogueCommonActionResultSourceType source = RogueCommonActionResultSourceType.Dialogue,
        RogueCommonActionResultDisplayType displayType = RogueCommonActionResultDisplayType.Single,
        bool updateMenu = true, bool notify = true)
    {
        var res = await base.RemoveBuff(buffId, source, displayType, updateMenu, notify);

        await ExpandFormula();

        return res;
    }

    public override async ValueTask AddBuffList(List<BaseRogueBuffExcel> excel)
    {
        await base.AddBuffList(excel);

        await ExpandFormula();
    }

    public async ValueTask ExpandFormula()
    {
        // expand formula
        foreach (var formula in RogueFormulas)
            if (formula.IsExpanded(RogueBuffs.Select(x => x.BuffId).ToList()) &&
                !ExpandedFormulaIdList.Contains(formula.FormulaID))
            {
                ExpandedFormulaIdList.Add(formula.FormulaID);
                await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueSubMode,
                    formula.ToExpandResultProto(RogueCommonActionResultSourceType.Buff,
                        RogueBuffs.Select(x => x.BuffId).ToList()), RogueCommonActionResultDisplayType.Single));
            }

            else if (!formula.IsExpanded(RogueBuffs.Select(x => x.BuffId).ToList()) &&
                     ExpandedFormulaIdList.Contains(formula.FormulaID)) // remove expanded formula
            {
                ExpandedFormulaIdList.Remove(formula.FormulaID);
                await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueSubMode,
                    formula.ToContractResultProto(RogueCommonActionResultSourceType.Buff,
                        RogueBuffs.Select(x => x.BuffId).ToList()), RogueCommonActionResultDisplayType.Single));
            }

        // buff type
        Dictionary<uint, int> buffTypeDict = [];
        foreach (var type in RogueBuffs.Select(buff => buff.BuffExcel.RogueBuffType)
                     .Where(type => !buffTypeDict.TryAdd((uint)type, 1)))
            buffTypeDict[(uint)type]++;

        await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueSubMode, new RogueCommonActionResult
        {
            RogueAction = new RogueCommonActionResultData
            {
                PathBuffList = new RogueCommonPathBuff
                {
                    Value = new FormulaTypeValue
                    {
                        FormulaTypeMap = { buffTypeDict }
                    }
                }
            },
            Source = RogueCommonActionResultSourceType.Buff
        }, RogueCommonActionResultDisplayType.Single));
    }

    public async ValueTask HandleFormulaSelect(int formulaId, int location)
    {
        if (RogueActions.Count == 0) return;

        var action = RogueActions.First().Value;
        if (action.RogueFormulaSelectMenu != null)
        {
            var formula = action.RogueFormulaSelectMenu.Formulas.Find(x => x.FormulaID == formulaId);
            if (formula != null) // check if buff is in the list
                if (!RogueFormulas.Exists(x => x.FormulaID == formulaId)) // check if buff already exists
                {
                    RogueFormulas.Add(formula);
                    await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueSubMode,
                        formula.ToResultProto(RogueCommonActionResultSourceType.Select,
                            RogueBuffs.Select(x => x.BuffId).ToList()), RogueCommonActionResultDisplayType.Multi));
                }

            RogueActions.Remove(action.QueuePosition);
        }

        await ExpandFormula();
        await UpdateMenu();

        await Player.SendPacket(
            new PacketHandleRogueCommonPendingActionScRsp(action.QueuePosition, location, selectFormula: true));
    }

    public virtual async ValueTask<RogueCommonActionResult?> RemoveFormula(int formulaId,
        RogueCommonActionResultSourceType source = RogueCommonActionResultSourceType.Dialogue,
        RogueCommonActionResultDisplayType displayType = RogueCommonActionResultDisplayType.Single,
        bool updateMenu = true, bool notify = true)
    {
        var formula = RogueFormulas.Find(x => x.FormulaID == formulaId);
        if (formula == null) return null; // buff not found
        RogueFormulas.Remove(formula);
        var result = formula.ToRemoveResultProto(source,
            RogueBuffs.Select(x => x.BuffId).ToList());

        if (ExpandedFormulaIdList.Contains(formulaId))
            ExpandedFormulaIdList.Remove(formulaId);

        if (notify)
            await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueSubMode, result, displayType));

        if (updateMenu) await UpdateMenu();

        return result;
    }

    #endregion

    #region Handlers

    public override void OnBattleStart(BattleInstance battle)
    {
        base.OnBattleStart(battle);

        battle.MagicInfo = new BattleRogueMagicInfo
        {
            ModifierContent = new BattleRogueMagicModifierInfo
            {
                RogueMagicBattleConst = 3
            },
            DetailInfo = new BattleRogueMagicDetailInfo
            {
                ENNPJGLCBEM = new FKOCBOOCDNL
                {
                    POIIAIAKILF = 1
                }
            }
        };

        Dictionary<int, int> divisionBuffs =
        new() {
            { 1, 651031 },
            { 2, 651032 },
            { 3, 651033 },
            { 4, 651034 },
            { 5, 651035 },
            { 6, 651036 },
        };

        var divisionLevel = AreaExcel.DivisionLevel;
        foreach (var buff in divisionBuffs.Where(x => divisionLevel >= x.Key))
        {
            battle.Buffs.Add(new MazeBuff(buff.Value, 1, -1)
            {
                WaveFlag = -1,
                DynamicValues =
                {
                    { "_RogueLayer", CurLevel?.LevelIndex ?? 1 }
                }
            });
        }

        battle.IsTournRogue = true;

        if (DifficultyExcels.Count > 0)
        {
            var diff = DifficultyExcels[Math.Min(DifficultyExcels.Count, CurLevel!.LevelIndex) - 1];
            if (diff.LevelList.Count > 0)
                battle.CustomLevel = diff.LevelList.First();
        }

        foreach (var formula in RogueFormulas.Where(formula =>
                     formula.IsExpanded(RogueBuffs.Select(x => x.BuffId).ToList()) &&
                     formula.FormulaCategory != RogueFormulaCategoryEnum.PathEcho))
            // apply formula effect
            battle.Buffs.Add(new MazeBuff(formula.MazeBuffID, 1, -1)
            {
                WaveFlag = -1
            });

        battle.Buffs.Add(new MazeBuff(634000, 1, -1)  // day and night
        {
            WaveFlag = -1
        });

        RogueTitanBlessInstance.OnBattleStart(battle);
    }

    public override async ValueTask OnBattleEnd(BattleInstance battle, PVEBattleResultCsReq req)
    {
        foreach (var miracle in RogueMiracles.Values) miracle.OnEndBattle(battle);

        if (req.EndStatus != BattleEndStatus.BattleEndWin)
            // quit
            //await QuitRogue();
            return;

        if (CurLevel?.Rooms.Last().RoomIndex == CurLevel?.CurRoom?.RoomIndex)
        {
            // layer last room
            if (Levels.Keys.Last() == CurLayerId)
            {
                // last layer
            }
            else
            {
                // trigger formula
                await RollBuff(battle.Stages.Count, 2000103);
                await RollFormula(battle.Stages.Count, [RogueFormulaCategoryEnum.Legendary]);
                await GainMoney(Random.Shared.Next(100, 150) * battle.Stages.Count);
            }
        }
        else
        {
            await RollBuff(battle.Stages.Count);
            await GainMoney(Random.Shared.Next(20, 60) * battle.Stages.Count);
        }
    }

    #endregion

    #region Workbench

    public async ValueTask<Retcode> HandleFunc(RogueWorkbenchFunc func, RogueWorkbenchContentInfo content)
    {
        switch (func.Excel.FuncType)
        {
            case RogueTournWorkbenchFuncTypeEnum.BuffEnhance:
                return await HandleBuffEnhance(func, content);
            case RogueTournWorkbenchFuncTypeEnum.BuffReforge:
                return await HandleBuffReforge(func, content);
        }

        return Retcode.RetSucc;
    }

    public async ValueTask<Retcode> HandleBuffEnhance(RogueWorkbenchFunc func, RogueWorkbenchContentInfo content)
    {
        var buffId = content.EnhanceBuffFunc.TargetBuffId;
        var buff = RogueBuffs.Find(x => x.BuffId == buffId);
        if (buff == null) return Retcode.RetRogueSelectBuffNotExist;

        if (buff.BuffLevel == 2) return Retcode.RetRogueSelectBuffCertainMismatch; // already enhanced

        var cost = (int)buff.BuffExcel.RogueBuffCategory;
        if (func.CurNum < cost) return Retcode.RetRogueCoinNotEnough;

        func.CurNum -= cost;

        await EnhanceBuff(buff.BuffId, RogueCommonActionResultSourceType.None);
        await ExpandFormula();
        return Retcode.RetSucc;
    }

    public async ValueTask<Retcode> HandleBuffReforge(RogueWorkbenchFunc func, RogueWorkbenchContentInfo content)
    {
        var buffId = content.ReforgeBuffFunc.TargetReforgeBuffId;
        var buff = RogueBuffs.Find(x => x.BuffId == buffId);
        if (buff == null) return Retcode.RetRogueSelectBuffNotExist;

        var cost = func.CurCost;
        if (CurMoney < cost) return Retcode.RetRogueCoinNotEnough;

        if (func.CurFreeNum > 0) func.CurFreeNum--;
        func.CurCost += 30;

        await RemoveBuff(buff.BuffId, RogueCommonActionResultSourceType.Reforge,
            RogueCommonActionResultDisplayType.None);
        await RollBuff(1, buff.BuffExcel.RogueBuffCategory switch
        {
            RogueBuffCategoryEnum.Common => 2000001,
            RogueBuffCategoryEnum.Rare => 2000002,
            RogueBuffCategoryEnum.Legendary => 2000003,
            _ => 2000001
        }, isReforge: true);

        return Retcode.RetSucc;
    }

    #endregion

    #region Serilization

    public RogueTournCurInfo ToProto()
    {
        return new RogueTournCurInfo
        {
            RogueTournCurGameInfo = ToCurGameInfo(),
            RogueTournCurAreaInfo = ToCurAreaInfo()
        };
    }

    public RogueTournCurGameInfo ToCurGameInfo()
    {
        return new RogueTournCurGameInfo
        {
            Buff = ToBuffInfo(),
            ItemValue = ToGameItemValueInfo(),
            Level = ToLevelInfo(),
            Lineup = ToLineupInfo(),
            MiracleInfo = ToMiracleInfo(),
            RogueTournGameAreaInfo = ToGameAreaInfo(),
            TournFormulaInfo = ToFormulaInfo(),
            UnlockValue = new KeywordUnlockValue(),
            GameDifficultyInfo = new RogueTournGameDifficultyInfo(),
            TournModuleInfo = new RogueTournModuleInfo
            {
                AllowFood = true
            },
            JMIDLLDKJBI = new()
        };
    }

    public ChessRogueBuffInfo ToBuffInfo()
    {
        return new ChessRogueBuffInfo
        {
            ChessRogueBuffInfo_ = new ChessRogueBuff
            {
                BuffList = { RogueBuffs.Select(x => x.ToCommonProto()) }
            }
        };
    }

    public RogueGameItemValue ToGameItemValueInfo()
    {
        return new RogueGameItemValue
        {
            VirtualItem = { { 31, (uint)CurMoney } }
        };
    }

    public RogueTournLineupInfo ToLineupInfo()
    {
        return new RogueTournLineupInfo
        {
            AvatarIdList = { Player.LineupManager!.GetCurLineup()!.BaseAvatars!.Select(x => (uint)x.BaseAvatarId) },
            RogueReviveCost = new ItemCostData
            {
                ItemList =
                {
                    new ItemCost
                    {
                        PileItem = new PileItem
                        {
                            ItemId = 31,
                            ItemNum = (uint)CurReviveCost
                        }
                    }
                }
            }
        };
    }

    public ChessRogueMiracleInfo ToMiracleInfo()
    {
        var proto = new ChessRogueMiracleInfo
        {
            ChessRogueMiracleInfo_ = new ChessRogueMiracle()
        };

        proto.ChessRogueMiracleInfo_.MiracleList.AddRange(RogueMiracles.Select(x => x.Value.ToGameMiracleProto())
            .ToList());

        return proto;
    }


    public RogueTournLevelInfo ToLevelInfo()
    {
        var proto = new RogueTournLevelInfo
        {
            Status = LevelStatus,
            CurLevelIndex = (uint)(CurLevel?.CurRoomIndex ?? 0),
            Reason = RogueTournSettleReason.None
        };

        foreach (var levelInstance in Levels.Values) proto.LevelInfoList.Add(levelInstance.ToProto());

        return proto;
    }

    public RogueTournFormulaInfo ToFormulaInfo()
    {
        var proto = new RogueTournFormulaInfo
        {
            FormulaTypeValue = new FormulaTypeValue()
        };

        foreach (var formula in RogueFormulas)
            proto.GameFormulaInfo.Add(formula.ToProto(RogueBuffs.Select(x => x.BuffId).ToList()));

        return proto;
    }

    public RogueTournGameAreaInfo ToGameAreaInfo()
    {
        var proto = new RogueTournGameAreaInfo
        {
            GameAreaId = (uint)AreaExcel.AreaID
        };

        return proto;
    }

    public RogueTournCurAreaInfo ToCurAreaInfo()
    {
        var proto = new RogueTournCurAreaInfo
        {
            RogueSubMode = (uint)RogueSubMode,
            SubAreaId = (uint)AreaExcel.AreaID,
            PendingAction = RogueActions.Count > 0
                ? RogueActions.First().Value.ToProto()
                : new RogueCommonPendingAction() // to serialize empty action
        };

        return proto;
    }

    public RogueTournCurSceneInfo ToCurSceneInfo()
    {
        return new RogueTournCurSceneInfo
        {
            Lineup = Player.LineupManager!.GetCurLineup()!.ToProto(),
            Scene = Player.SceneInstance!.ToProto(),
            //RotateInfo = new RogueMapRotateInfo
            //{
            //    IsRotate = CurLevel?.CurRoom?.Config?.RotateInfo.IsRotate ?? false,
            //    BJPBAJECKFO = (uint)(CurLevel?.CurRoom?.Config?.RotateInfo.RotateNum ?? 0)  // HDEHHKEMOCD
            //}
            RotateInfo = new RogueMapRotateInfo()
        };
    }

    #endregion
}