using System;
using System.Collections.Generic;
using KernelPanic.Core;
using KernelPanic.Data;
using KernelPanic.Meta;
using KernelPanic.Run;
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 string SharedScrollbarStyleResourcePath = "TerminalScrollbars";
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();
LoadSharedStyles();
BindCommandEntries();
RegisterCommandEntryCallbacks();
featuredUnitPanel.Bind(_root, monospaceFont);
collectionScreen.Bind(_root, monospaceFont, languageDeckDatabase, cardDatabase, _playerCollection, GetMergesBalance, UpgradeCollectionUnit);
BindScreenFrames();
starterSelection.Bind(_root, distroDatabase, HandleStarterConfirmed);
gachaScreen.Bind(_root, distroDatabase, monospaceFont, _gachaService, _playerCollection, _wallet, ResolvePulledDistros, 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