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);
}
}
}