using System;
using System.Collections.Generic;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using Colosseum.Passives;
using Colosseum.Player;
namespace Colosseum.UI
{
///
/// 비전투 중 전체 화면으로 여는 패시브 트리 UI입니다.
///
public class PassiveTreeUI : MonoBehaviour
{
private sealed class NodeVisual
{
public RectTransform RectTransform;
public Button Button;
public Image Image;
public Image FillImage;
public Image InnerImage;
public Outline Outline;
}
[Header("Toggle")]
[Tooltip("패시브 UI를 여닫는 키")]
[SerializeField] private Key toggleKey = Key.P;
[Tooltip("토글 버튼에 표시할 텍스트")]
[SerializeField] private string toggleButtonLabel = "패시브";
[Tooltip("토글 버튼의 캔버스 기준 위치")]
[SerializeField] private Vector2 toggleButtonAnchoredPosition = new Vector2(-132f, 48f);
[Header("Debug")]
[Tooltip("플레이 모드 시작 시 패널을 자동으로 엽니다.")]
[SerializeField] private bool openOnStartInPlayMode = false;
[Header("Prefabs")]
[Tooltip("패시브 트리 메인 뷰 프리팹")]
[SerializeField] private PassiveTreeViewReferences viewPrefab;
[Tooltip("패시브 노드 프리팹")]
[SerializeField] private PassiveTreeNodeView nodePrefab;
[Tooltip("프로토타입 패시브 카탈로그")]
[SerializeField] private PassivePrototypeCatalogData passivePrototypeCatalog;
[Tooltip("일반 패시브 노드에 사용할 아이콘")]
[SerializeField] private Sprite normalNodeSprite;
[Tooltip("허브/브릿지/완성 패시브 노드에 사용할 아이콘")]
[SerializeField] private Sprite specialNodeSprite;
[Tooltip("노드 안쪽에 겹쳐서 사용할 보조 아이콘")]
[SerializeField] private Sprite innerNodeSprite;
[Header("Style")]
[SerializeField] private Color panelBackgroundColor = new Color(0.07f, 0.07f, 0.1f, 0.98f);
[SerializeField] private Color sectionBackgroundColor = new Color(0.12f, 0.12f, 0.16f, 0.96f);
[SerializeField] private Color sectionOverlayColor = new Color(0f, 0f, 0f, 0.12f);
[SerializeField] private Color attackColor = new Color(0.78f, 0.3f, 0.22f, 1f);
[SerializeField] private Color defenseColor = new Color(0.2f, 0.56f, 0.6f, 1f);
[SerializeField] private Color supportColor = new Color(0.56f, 0.67f, 0.24f, 1f);
[SerializeField] private Color bridgeColor = new Color(0.58f, 0.47f, 0.23f, 1f);
[SerializeField] private Color lockedNodeColor = new Color(0.15f, 0.15f, 0.17f, 0.96f);
[SerializeField] private Color selectedNodeTint = new Color(1f, 0.92f, 0.68f, 1f);
[SerializeField] private Color focusedOutlineColor = new Color(0.95f, 0.92f, 0.82f, 1f);
[SerializeField] private Color normalTextColor = new Color(0.9f, 0.88f, 0.82f, 1f);
[SerializeField] private Color mutedTextColor = new Color(0.72f, 0.71f, 0.66f, 0.95f);
[SerializeField] private Color statusErrorColor = new Color(1f, 0.52f, 0.45f, 1f);
[SerializeField] private Color lineColor = new Color(0.72f, 0.67f, 0.53f, 0.4f);
[SerializeField] private Color activeLineColor = new Color(0.98f, 0.9f, 0.7f, 0.92f);
[SerializeField] private float graphCenterYOffset = -118f;
private const float LeftPanelWidth = 292f;
private const float RightPanelWidth = 308f;
private const float GraphPadding = 24f;
private const float ConnectionThickness = 7f;
private readonly Dictionary nodeVisuals = new();
private PlayerNetworkController targetPlayer;
private PlayerMovement playerMovement;
private PlayerSkillInput playerSkillInput;
private Canvas parentCanvas;
private RectTransform canvasRectTransform;
private PassiveTreeViewReferences viewInstance;
private GameObject overlayRoot;
private RectTransform panelRectTransform;
private RectTransform graphRectTransform;
private RectTransform connectionLayer;
private RectTransform nodeLayer;
private RectTransform detailContent;
private TextMeshProUGUI pointsSummaryText;
private TextMeshProUGUI selectionSummaryText;
private TextMeshProUGUI detailText;
private TextMeshProUGUI statusText;
private Button nonePresetButton;
private Button defensePresetButton;
private Button supportPresetButton;
private Button attackPresetButton;
private Button clearButton;
private Button selectNodeButton;
private TextMeshProUGUI selectNodeButtonLabel;
private bool isPanelVisible;
private bool uiInitialized;
private bool previousCursorVisible;
private CursorLockMode previousCursorLockState;
private PassiveNodeData focusedNode;
private string lastStatusMessage = string.Empty;
private bool lastStatusIsError;
private void Awake()
{
EnsureUi();
HidePanelImmediate();
}
private void Start()
{
EnsureUi();
FindLocalPlayer();
RefreshAll();
if (Application.isPlaying && openOnStartInPlayMode)
{
SetPanelVisible(true);
}
}
private void Update()
{
if (targetPlayer == null)
{
FindLocalPlayer();
}
if (Keyboard.current == null)
return;
if (Keyboard.current[toggleKey].wasPressedThisFrame)
{
TogglePanelVisibility();
}
else if (isPanelVisible && Keyboard.current.escapeKey.wasPressedThisFrame)
{
SetPanelVisible(false);
}
if (isPanelVisible)
{
UpdatePanelSize();
}
}
private void OnDestroy()
{
UnsubscribeFromPlayer();
SetGameplayInputBlocked(false);
RestoreCursorState();
}
private void FindLocalPlayer()
{
PlayerNetworkController[] players = FindObjectsByType(FindObjectsSortMode.None);
for (int i = 0; i < players.Length; i++)
{
if (!players[i].IsOwner)
continue;
if (targetPlayer == players[i])
return;
SetTarget(players[i]);
return;
}
}
private void SetTarget(PlayerNetworkController target)
{
UnsubscribeFromPlayer();
targetPlayer = target;
playerMovement = targetPlayer != null ? targetPlayer.GetComponent() : null;
playerSkillInput = targetPlayer != null ? targetPlayer.GetComponent() : null;
if (targetPlayer != null)
{
targetPlayer.OnPassiveSelectionChanged += HandlePassiveSelectionChanged;
}
if (isPanelVisible)
{
SetGameplayInputBlocked(true);
}
RefreshAll();
}
private void UnsubscribeFromPlayer()
{
if (targetPlayer != null)
{
targetPlayer.OnPassiveSelectionChanged -= HandlePassiveSelectionChanged;
}
}
private void HandlePassiveSelectionChanged()
{
RefreshAll();
}
private void TogglePanelVisibility()
{
SetPanelVisible(!isPanelVisible);
}
private void SetPanelVisible(bool visible)
{
EnsureUi();
isPanelVisible = visible;
UpdatePanelSize();
if (overlayRoot != null)
{
overlayRoot.SetActive(visible);
}
if (visible)
{
RememberCursorState();
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
SetGameplayInputBlocked(true);
RefreshAll();
}
else
{
SetGameplayInputBlocked(false);
RestoreCursorState();
}
}
private void HidePanelImmediate()
{
isPanelVisible = false;
if (overlayRoot != null)
{
overlayRoot.SetActive(false);
}
}
private void RememberCursorState()
{
previousCursorVisible = Cursor.visible;
previousCursorLockState = Cursor.lockState;
}
private void RestoreCursorState()
{
Cursor.visible = previousCursorVisible;
Cursor.lockState = previousCursorLockState;
}
private void SetGameplayInputBlocked(bool blocked)
{
if (playerMovement != null)
{
playerMovement.SetGameplayInputEnabled(!blocked);
}
if (playerSkillInput != null)
{
playerSkillInput.SetGameplayInputEnabled(!blocked);
}
}
private void EnsureUi()
{
if (uiInitialized)
return;
parentCanvas = GetComponentInParent