using Core;
using Core.Lifecycle;
using UI.Core;
using Cinematics;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.UI;
using System.Linq;
using System.Collections.Generic;
using System;
using DamianExperiments;
namespace UI
{
///
/// Manages player HUD elements and their visibility.
/// Works alongside UIPageController (must be on same GameObject).
/// Automatically hides HUD when pages open, shows when all pages close.
///
public class PlayerHudManager : ManagedBehaviour
{
public enum UIMode { Overworld, Puzzle, Minigame, HideAll };
///
/// Context object for managing temporary HUD element visibility.
/// Automatically restores previous visibility state when disposed.
/// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementTemporarily(myButton)) { ... }
///
public class HudVisibilityContext : IDisposable
{
private readonly GameObject _element;
private readonly bool _previousState;
private bool _disposed;
internal HudVisibilityContext(GameObject element, bool show)
{
_element = element;
_previousState = element.activeSelf;
_element.SetActive(show);
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
if (_element != null)
{
_element.SetActive(_previousState);
}
}
}
///
/// Multi-element visibility context for managing multiple HUD elements at once.
/// Automatically restores previous visibility states when disposed.
///
public class MultiHudVisibilityContext : IDisposable
{
private readonly List<(GameObject element, bool previousState)> _elements;
private bool _disposed;
internal MultiHudVisibilityContext(IEnumerable elements, bool show)
{
_elements = new List<(GameObject, bool)>();
foreach (var element in elements)
{
if (element != null)
{
_elements.Add((element, element.activeSelf));
element.SetActive(show);
}
}
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
foreach (var (element, previousState) in _elements)
{
if (element != null)
{
element.SetActive(previousState);
}
}
}
}
public UIMode currentUIMode;
[Header("HUD Management")]
[SerializeField] private Transform hudButtonsContainer; // Parent object containing all HUD buttons
[Header("Cinematic References")]
public GameObject landscapeObject;
public GameObject portraitObject;
public GameObject cinematicsParentObject;
public GameObject CinematicBackground;
[Header("HUD Elements")]
public GameObject eagleEye;
public GameObject ramaSjangButton;
public GameObject scrabBookButton;
public GameObject pauseButton;
[HideInInspector] public Image cinematicSprites;
[HideInInspector] public Image cinematicBackgroundSprites;
[HideInInspector] public GameObject currentCinematicPlayer;
[HideInInspector] public PlayableDirector playableDirector;
private static PlayerHudManager _instance;
public static PlayerHudManager Instance => _instance;
private UIPageController _uiPageController;
private AppSwitcher _appSwitcherComponent;
internal override void OnManagedAwake()
{
if (Instance != null)
{
Destroy(this);
return;
}
// Set instance immediately (early initialization)
_instance = this;
// Get UIPageController on same GameObject
_uiPageController = GetComponent();
if (_uiPageController == null)
{
UnityEngine.Debug.LogError("[PlayerHudManager] UIPageController not found on same GameObject!");
}
InitializeReferences();
}
internal override void OnManagedStart()
{
// Subscribe to UIPageController page changes for auto HUD management
if (_uiPageController != null)
{
_uiPageController.OnPageChanged += HandlePageStackChanged;
}
// Subscribe to CinematicsManager events for HUD management during cinematics
if (CinematicsManager.Instance != null)
{
CinematicsManager.Instance.OnCinematicStarted += HandleCinematicStarted;
CinematicsManager.Instance.OnCinematicStopped += HandleCinematicStopped;
// Check if a cinematic is already playing
if (CinematicsManager.Instance.IsCinematicPlaying)
{
HideAllHud();
Logging.Debug("[PlayerHudManager] Cinematic already playing on init, hiding HUD");
}
}
// Subscribe to scene load events to adjust HUD based on scene
if (SceneManagerService.Instance != null)
{
SceneManagerService.Instance.SceneLoadCompleted += NewSceneLoaded;
}
// If in editor - initialize HUD based on current scene
#if UNITY_EDITOR
if (SceneManagerService.Instance.CurrentGameplayScene == "StartingScene")
{
// TODO: Hide all UI until cinematics have played
NewSceneLoaded("AppleHillsOverworld");
}
else if (SceneManagerService.Instance.CurrentGameplayScene != null)
{
NewSceneLoaded(SceneManagerService.Instance.CurrentGameplayScene);
}
#endif
}
internal override void OnManagedDestroy()
{
// Unsubscribe from events
if (_uiPageController != null)
{
_uiPageController.OnPageChanged -= HandlePageStackChanged;
}
if (CinematicsManager.Instance != null)
{
CinematicsManager.Instance.OnCinematicStarted -= HandleCinematicStarted;
CinematicsManager.Instance.OnCinematicStopped -= HandleCinematicStopped;
}
if (SceneManagerService.Instance != null)
{
SceneManagerService.Instance.SceneLoadCompleted -= NewSceneLoaded;
}
}
private void InitializeReferences()
{
currentCinematicPlayer = landscapeObject;
playableDirector = cinematicsParentObject.GetComponent();
cinematicSprites = currentCinematicPlayer.GetComponent();
cinematicBackgroundSprites = CinematicBackground.GetComponent();
}
private void UpateCinematicReferences(GameObject newCinematicPlayer)
{
currentCinematicPlayer = newCinematicPlayer;
cinematicSprites = currentCinematicPlayer.GetComponent();
}
public void SetPortraitMode(bool portraitModeEnable)
{
if (portraitModeEnable)
{
UpateCinematicReferences(portraitObject);
}
else
{
UpateCinematicReferences(landscapeObject);
}
}
public void NewSceneLoaded(string sceneName)
{
switch (sceneName)
{
case "AppleHillsOverworld":
currentUIMode = UIMode.Overworld;
break;
case "Quarry":
currentUIMode = UIMode.Puzzle;
break;
case "DivingForPictures" or "CardQualityControl" or "BirdPoop" or "FortFight" or "ValentineNoteDelivery":
currentUIMode = UIMode.Minigame;
break;
case "StatueDecoration":
currentUIMode = UIMode.HideAll;
break;
}
ShowAllHud();
}
///
/// Hides all HUD button elements
///
public void HideAllHud()
{
SetHudVisibility(false);
}
///
/// Shows all HUD button elements
///
public void ShowAllHud()
{
SetHudVisibility(true);
}
///
/// Hides all HUD elements except the specified exceptions
///
/// GameObjects to keep visible
public void HideAllHudExcept(params GameObject[] exceptions)
{
if (hudButtonsContainer == null)
{
Logging.Warning("[PlayerHudManager] HUD buttons container not assigned");
return;
}
HashSet exceptionSet = new HashSet(exceptions);
foreach (Transform child in hudButtonsContainer)
{
bool shouldShow = exceptionSet.Contains(child.gameObject);
child.gameObject.SetActive(shouldShow);
}
Logging.Debug($"[PlayerHudManager] Hidden HUD except {exceptions.Length} exceptions");
}
///
/// Common method to set visibility of all HUD elements
///
private void SetHudVisibility(bool visible)
{
if (hudButtonsContainer == null)
{
Logging.Warning("[PlayerHudManager] HUD buttons container not assigned");
return;
}
// Set visibility for all HUD children
foreach (Transform child in hudButtonsContainer)
{
child.gameObject.SetActive(visible);
}
ApplyUIModeOverrides(visible);
Logging.Debug($"[PlayerHudManager] {(visible ? "Shown" : "Hidden")} all HUD elements");
}
///
/// Applies UI mode-specific visibility rules (e.g., hiding eagleEye in certain modes)
///
private void ApplyUIModeOverrides(bool visible)
{
// First, reset state to neutral
ResetStateBeforeLevelLoad();
switch (currentUIMode)
{
case UIMode.Overworld:
if (visible)
{
eagleEye.SetActive(false);
}
break;
case UIMode.Puzzle:
if (visible)
{
ramaSjangButton.SetActive(false);
}
break;
case UIMode.Minigame:
if (visible)
{
eagleEye.SetActive(false);
ramaSjangButton.SetActive(false);
scrabBookButton.SetActive(false);
}
break;
case UIMode.HideAll:
eagleEye.SetActive(false);
ramaSjangButton.SetActive(false);
scrabBookButton.SetActive(false);
pauseButton.SetActive(false);
break;
}
}
protected void ResetStateBeforeLevelLoad()
{
eagleEye.GetComponent()?.ResetEagleEye();
}
///
/// Automatically manages HUD visibility based on page stack state
///
private void HandlePageStackChanged(UIPage currentPage)
{
if (_uiPageController == null) return;
// Use LINQ Count() on IEnumerable PageStack property
int stackCount = _uiPageController.PageStack.Count();
if (stackCount == 1 && currentPage != null)
{
// First page just opened - hide HUD
HideAllHud();
Logging.Debug("[PlayerHudManager] Page opened, hiding HUD");
}
else if (stackCount == 0)
{
// Last page closed - show HUD
ShowAllHud();
Logging.Debug("[PlayerHudManager] All pages closed, showing HUD");
}
// If stackCount > 1, we're navigating between pages, keep HUD hidden
}
///
/// Called when a cinematic starts playing
///
private void HandleCinematicStarted()
{
HideAllHud();
Logging.Debug("[PlayerHudManager] Cinematic started, hiding HUD");
}
///
/// Called when a cinematic stops playing
///
private void HandleCinematicStopped()
{
ShowAllHud();
Logging.Debug("[PlayerHudManager] Cinematic stopped, showing HUD");
}
///
/// Convenience method to push a page from prefab using the UIPageController
///
public void PushPageFromPrefab(GameObject pagePrefab)
{
if (_uiPageController != null)
{
_uiPageController.PushPageFromPrefab(pagePrefab);
}
else
{
UnityEngine.Debug.LogError("[PlayerHudManager] Cannot push page - UIPageController not found!");
}
}
#region HUD Element Getters
public GameObject GetEagleEye() => eagleEye;
public GameObject GetRamaSjangButton() => ramaSjangButton;
public GameObject GetScrabookButton() => scrabBookButton;
#endregion
#region Context-Based Visibility Management
///
/// Temporarily shows a HUD element. Returns a context object that restores the previous state when disposed.
/// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementTemporarily(myButton)) { /* element is visible */ }
///
public HudVisibilityContext ShowElementTemporarily(GameObject element)
{
if (element == null)
{
Logging.Warning("[PlayerHudManager] Attempted to show null element");
return null;
}
return new HudVisibilityContext(element, true);
}
///
/// Temporarily hides a HUD element. Returns a context object that restores the previous state when disposed.
/// Usage: using (var ctx = PlayerHudManager.Instance.HideElementTemporarily(myButton)) { /* element is hidden */ }
///
public HudVisibilityContext HideElementTemporarily(GameObject element)
{
if (element == null)
{
Logging.Warning("[PlayerHudManager] Attempted to hide null element");
return null;
}
return new HudVisibilityContext(element, false);
}
///
/// Temporarily shows multiple HUD elements. Returns a context object that restores all previous states when disposed.
/// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementsTemporarily(button1, button2, button3)) { /* elements are visible */ }
///
public MultiHudVisibilityContext ShowElementsTemporarily(params GameObject[] elements)
{
return new MultiHudVisibilityContext(elements, true);
}
///
/// Temporarily hides multiple HUD elements. Returns a context object that restores all previous states when disposed.
/// Usage: using (var ctx = PlayerHudManager.Instance.HideElementsTemporarily(button1, button2, button3)) { /* elements are hidden */ }
///
public MultiHudVisibilityContext HideElementsTemporarily(params GameObject[] elements)
{
return new MultiHudVisibilityContext(elements, false);
}
#endregion
}
}