using System; using System.Collections.Generic; using System.Linq; using BriarQueen.Framework.Managers.IO; using BriarQueen.Framework.Services.Game; using BriarQueen.UI.Menus.Components; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using VContainer; namespace BriarQueen.UI.Menus { /// /// Select Save Window: /// - Shows EXACTLY 3 slots (filled first, then empty). /// - Clicking a slot: /// - Filled slot => loads that save, then StartGame /// - Empty slot => opens NewSaveWindow (create + load), then StartGame /// - Delete is still supported (per-slot delete button + confirm modal). /// - Back closes this window (handled by MainMenuWindow). /// public class SelectSaveWindow : MonoBehaviour { private const int MAX_SLOTS = 3; [Header("UI")] [SerializeField] private RectTransform _listContentParent; [SerializeField] private SaveSlotUI _saveSlotPrefab; [Header("Buttons")] [SerializeField] private Button _backButton; [Header("New Save Window")] [SerializeField] private NewSaveWindow _newSaveWindow; [Header("Confirm Delete Window (optional but recommended)")] [SerializeField] private ConfirmDeleteWindow _confirmDeleteWindow; private readonly List _instantiatedSlots = new(); private int _currentSelectionIndex; private GameService _gameService; private bool _isBusy; private SaveManager _saveManager; private void Awake() { if (_backButton != null) _backButton.onClick.AddListener(OnBackClicked); if (_newSaveWindow != null) { _newSaveWindow.OnCloseWindow += HandleNewSaveClosed; _newSaveWindow.OnSaveCreated += HandleSaveCreatedAndStartGame; } if (_confirmDeleteWindow != null) { _confirmDeleteWindow.OnConfirmDelete += HandleConfirmDelete; _confirmDeleteWindow.OnCancel += HandleCancelDelete; _confirmDeleteWindow.Close(); } } private void OnDestroy() { if (_backButton != null) _backButton.onClick.RemoveListener(OnBackClicked); if (_newSaveWindow != null) { _newSaveWindow.OnCloseWindow -= HandleNewSaveClosed; _newSaveWindow.OnSaveCreated -= HandleSaveCreatedAndStartGame; } if (_confirmDeleteWindow != null) { _confirmDeleteWindow.OnConfirmDelete -= HandleConfirmDelete; _confirmDeleteWindow.OnCancel -= HandleCancelDelete; } ClearSlots(); } public event Action OnCloseWindow; [Inject] public void Construct(SaveManager saveManager, GameService gameService) { _saveManager = saveManager; _gameService = gameService; } /// Called by MainMenuWindow after enabling this GO. public void Refresh() { if (_newSaveWindow != null) _newSaveWindow.CloseImmediate(); if (_confirmDeleteWindow != null) _confirmDeleteWindow.Close(); EnsureThreeSlotsExist(); RefreshSlotsData(); SetBusy(false); } private void OnBackClicked() { if (_isBusy) return; // Check if Confirm Delete or New Save Window is open. OnCloseWindow?.Invoke(); } private void HandleNewSaveClosed() { // NewSaveWindow was closed (cancel/back) => return control to the list. SetBusy(false); RestoreSelection(); RefreshSlotsData(); } private void HandleSaveCreatedAndStartGame(string _) { // NewSaveWindow already created + loaded the save. OnCloseWindow?.Invoke(); _gameService?.StartGame().Forget(); } private void SetBusy(bool busy) { Debug.Log("[SelectSaveWindow] SetBusy: " + busy); _isBusy = busy; if (_backButton != null) _backButton.interactable = !busy; foreach (var slot in _instantiatedSlots) if (slot != null) slot.SetInteractable(!busy); Debug.Log($"[SelectSaveWindow] Finished set busy: {busy}"); } private void EnsureThreeSlotsExist() { if (_listContentParent == null || _saveSlotPrefab == null) return; if (_instantiatedSlots.Count == MAX_SLOTS) return; ClearSlots(); for (var i = 0; i < MAX_SLOTS; i++) { var slot = Instantiate(_saveSlotPrefab, _listContentParent); _instantiatedSlots.Add(slot); } } private void RefreshSlotsData() { // Always show 3 slots; if save system is missing, they’ll all appear empty/disabled. if (_saveManager == null) { for (var i = 0; i < _instantiatedSlots.Count; i++) _instantiatedSlots[i]?.SetEmpty(OnEmptySlotClicked); SelectBackButton(); return; } // Newest first, cap at 3 var saveFiles = _saveManager.GetAvailableSaves(); var infos = (saveFiles ?? new List<(string, DateTime)>()) .Select(sf => new SaveFileInfo(sf.FileName, sf.LastModified)) .OrderByDescending(i => i.LastModified) .Take(MAX_SLOTS) .ToArray(); for (var i = 0; i < MAX_SLOTS; i++) { var slot = _instantiatedSlots.ElementAtOrDefault(i); if (slot == null) continue; if (i < infos.Length) slot.SetFilled(infos[i], OnFilledSlotClicked, OnSlotDeleteClicked); else slot.SetEmpty(OnEmptySlotClicked); } _currentSelectionIndex = Mathf.Clamp(_currentSelectionIndex, 0, MAX_SLOTS - 1); SelectSlot(_currentSelectionIndex); } private void ClearSlots() { foreach (var slot in _instantiatedSlots) if (slot != null) Destroy(slot.gameObject); _instantiatedSlots.Clear(); _currentSelectionIndex = 0; } private void OnFilledSlotClicked(SaveFileInfo saveInfo) { if (_isBusy) return; if (_saveManager == null) return; if (string.IsNullOrWhiteSpace(saveInfo.FileName)) return; if (!_saveManager.DoesSaveExist(saveInfo.FileName)) { RefreshSlotsData(); return; } LoadAndStartGame(saveInfo.FileName).Forget(); } private void OnEmptySlotClicked() { Debug.Log("[SelectSaveWindow] Empty slot clicked."); if (_isBusy) return; if (_newSaveWindow == null) { Debug.LogWarning("[SelectSaveWindow] NewSaveWindow reference not set."); return; } SetBusy(true); _newSaveWindow.Open(); } private async UniTask LoadAndStartGame(string profileName) { SetBusy(true); try { await _saveManager.LoadGameData(profileName); OnCloseWindow?.Invoke(); _gameService?.StartGame().Forget(); } catch (Exception ex) { Debug.LogError($"[SelectSaveWindow] Failed to load profile '{profileName}': {ex}"); SetBusy(false); RefreshSlotsData(); RestoreSelection(); } } private void OnSlotDeleteClicked(SaveFileInfo saveInfo) { if (_isBusy) return; if (_saveManager == null) return; if (string.IsNullOrWhiteSpace(saveInfo.FileName)) return; // No confirm window wired? Do a direct delete. if (_confirmDeleteWindow == null) { TryDeleteAndRefresh(saveInfo.FileName); return; } SetBusy(true); _confirmDeleteWindow.Open(saveInfo); } private void HandleConfirmDelete(SaveFileInfo saveInfo) { try { TryDeleteAndRefresh(saveInfo.FileName); } finally { SetBusy(false); RestoreSelection(); } } private void HandleCancelDelete() { SetBusy(false); RestoreSelection(); } private void TryDeleteAndRefresh(string fileName) { if (string.IsNullOrWhiteSpace(fileName)) return; if (!_saveManager.DoesSaveExist(fileName)) { RefreshSlotsData(); return; } var deleted = _saveManager.Delete(fileName); if (!deleted) Debug.LogWarning($"[SelectSaveWindow] Failed to delete save '{fileName}'."); RefreshSlotsData(); } private void RestoreSelection() { _currentSelectionIndex = Mathf.Clamp(_currentSelectionIndex, 0, MAX_SLOTS - 1); SelectSlot(_currentSelectionIndex); } private void SelectBackButton() { if (_backButton == null) return; if (EventSystem.current != null) EventSystem.current.SetSelectedGameObject(_backButton.gameObject); else _backButton.Select(); } private void SelectSlot(int index) { if (_instantiatedSlots.Count == 0) { SelectBackButton(); return; } index = Mathf.Clamp(index, 0, _instantiatedSlots.Count - 1); _currentSelectionIndex = index; var go = _instantiatedSlots[index]?.GetSelectableGameObject(); if (go == null) return; if (EventSystem.current != null) EventSystem.current.SetSelectedGameObject(go); } } }