using System;
using System.Collections.Generic;
using KernelPanic.Core;
using KernelPanic.Data;
using KernelPanic.Meta;
using UnityEngine;
using UnityEngine.TextCore.Text;
using UnityEngine.UIElements;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace KernelPanic.UI
{
///
/// Binds the main menu terminal UI document and routes command activation between panels.
///
[RequireComponent(typeof(UIDocument))]
public sealed class MainMenuController : MonoBehaviour
{
private const string HiddenClassName = "hidden";
private const string SelectedClassName = "selected";
private const string CursorOnClassName = "cursor-on";
private const float BootIntroSeconds = 1.5f;
private static bool bootIntroPlayed;
// Static fields don't reset between Play sessions when the Editor's "Reload Domain"
// option is disabled. RuntimeInitializeOnLoadMethod runs once per player/Play-mode
// startup regardless of that setting, so this keeps bootIntroPlayed session-scoped.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetSessionState()
{
bootIntroPlayed = false;
}
[SerializeField] private FontAsset monospaceFont; // TODO: Assign a real monospace FontAsset when typography assets exist.
[SerializeField] private string motdBody = "unstable userspace detected; keep a rollback shell open.";
[SerializeField] private DistroDatabase distroDatabase;
[SerializeField] private CardDatabase cardDatabase;
[SerializeField] private LanguageDeckDatabase languageDeckDatabase;
[SerializeField] private FeaturedUnitPanel featuredUnitPanel = new();
[SerializeField] private CollectionScreenController collectionScreen = new();
[SerializeField] private StarterSelectionController starterSelection = new();
[SerializeField] private GachaScreenController gachaScreen = new();
private readonly List commandEntries = new();
private readonly List packageRows = new();
private readonly List loadoutRows = new();
private readonly List selectedRunLanguages = new();
private UIDocument document;
private VisualElement root;
private VisualElement shellRoot;
private VisualElement bootIntroPanel;
private VisualElement mainMenuPanel;
private VisualElement collectionPanel;
private VisualElement runSetupPanel;
private VisualElement gachaPanel;
private VisualElement settingsPanel;
private VisualElement eventBanner;
private VisualElement backgroundLogLayer;
private ScrollView runSetupList;
private VisualElement runSetupDetail;
private ScrollView runSetupPackageScroll;
private Label appIdLabel;
private Label entropyLabel;
private Label pullTokensLabel;
private Label titleCursorLabel;
private Label promptCursorLabel;
private Label bootIntroLogLabel;
private Label motdBodyLabel;
private VisualElement motdBlock;
private Button rootCreditsToEntropyButton;
private VisualElement rootCreditExchangeModal;
private Label rootCreditExchangeBalanceLabel;
private Label rootCreditExchangePreviewLabel;
private Label rootCreditExchangeMessageLabel;
private TextField rootCreditExchangeInput;
private Button rootCreditExchangeMinusHundredButton;
private Button rootCreditExchangeMinusOneButton;
private Button rootCreditExchangeNoneButton;
private Button rootCreditExchangePlusOneButton;
private Button rootCreditExchangePlusHundredButton;
private Button rootCreditExchangeMaxButton;
private Button rootCreditExchangeConfirmButton;
private Button rootCreditExchangeCancelButton;
private Button rootCreditExchangeCloseButton;
private Button collectionUnitsButton;
private Button collectionLanguagesButton;
private readonly ScreenFrameController runSetupFrame = new();
private readonly ScreenFrameController collectionFrame = new();
private readonly ScreenFrameController gachaFrame = new();
private readonly ScreenFrameController settingsFrame = new();
private SaveService saveService;
private SaveData saveData;
private EntropyWallet wallet;
private GachaService gachaService;
private PlayerCollection playerCollection;
private CardLoadout cardLoadout;
private BackgroundLogRingBuffer backgroundLog;
private IEventBannerSource eventBannerSource;
private int selectedCommandIndex;
private int selectedPackageIndex;
private string runSetupNotice;
private bool collectionShowingLanguages;
private float bootIntroElapsed;
private int bootIntroCharacterCount;
private bool cursorVisible;
private bool suppressNextClick;
private bool warnedUnresolvedSaveId;
private bool rootCreditExchangeOpen;
private string bootIntroCopy;
private Action rootCreditExchangeMinusHundredClicked;
private Action rootCreditExchangeMinusOneClicked;
private Action rootCreditExchangePlusOneClicked;
private Action rootCreditExchangePlusHundredClicked;
private IVisualElementScheduledItem blinkSchedule;
private IVisualElementScheduledItem bootIntroSchedule;
public void Initialize(EntropyWallet initializedWallet)
{
wallet = initializedWallet ?? new EntropyWallet();
RefreshCurrencyReadouts();
}
private void Awake()
{
document = GetComponent();
saveService = new SaveService();
gachaService = new GachaService();
playerCollection = new PlayerCollection(); // TODO: Replace with persistent player-collection service composition.
cardLoadout = new CardLoadout(playerCollection.OwnedUnits);
Initialize(new EntropyWallet()); // TODO: Replace with persistent wallet service composition.
BindElements();
BindCommandEntries();
RegisterCommandEntryCallbacks();
featuredUnitPanel.Bind(root, monospaceFont);
collectionScreen.Bind(root, monospaceFont, languageDeckDatabase, cardDatabase, playerCollection);
BindScreenFrames();
starterSelection.Bind(root, distroDatabase, HandleStarterConfirmed);
gachaScreen.Bind(root, distroDatabase, monospaceFont, gachaService, playerCollection, wallet, HandleMetaStateChanged, OpenRootCreditExchange);
LoadMetaState();
playerCollection.Changed += HandleMetaStateChanged;
gachaService.Changed += HandleMetaStateChanged;
ApplyOptionalFont();
}
private void OnEnable()
{
RegisterCallbacks();
root.Focus();
shellRoot.EnableInClassList("reduced-motion", UIPreferences.ReducedMotion);
RefreshStaticText();
RefreshCurrencyReadouts();
RefreshEventBanner();
featuredUnitPanel.Refresh(playerCollection.OwnedUnits, playerCollection.FeaturedUnit);
collectionScreen.RefreshUnits(playerCollection.OwnedUnits);
gachaScreen.Refresh();
SelectCommand(0);
ShowMainMenu();
StartAmbientSchedules();
PlayBootIntroIfNeeded();
}
private void OnDisable()
{
UnregisterCallbacks();
blinkSchedule?.Pause();
backgroundLog?.Stop();
bootIntroSchedule?.Pause();
starterSelection.PauseSchedules();
}
private void BindElements()
{
root = document.rootVisualElement;
shellRoot = root.Q("ShellRoot");
bootIntroPanel = root.Q("BootIntroPanel");
mainMenuPanel = root.Q("MainMenuPanel");
collectionPanel = root.Q("CollectionPanel");
runSetupPanel = root.Q("RunSetupPanel");
gachaPanel = root.Q("GachaPanel");
settingsPanel = root.Q("SettingsPanel");
eventBanner = root.Q("EventBanner");
motdBlock = root.Q("MotdBlock");
backgroundLogLayer = root.Q("BackgroundLogLayer");
runSetupList = root.Q("RunSetupList");
runSetupDetail = root.Q("RunSetupDetail");
backgroundLog = new BackgroundLogRingBuffer(backgroundLogLayer, BootLogCopy.Lines);
appIdLabel = root.Q