using System; using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; public class SongDetailPanel : MonoBehaviour { [Header("곡 정보")] [SerializeField] private TMP_Text titleText; [SerializeField] private TMP_Text artistText; [SerializeField] private TMP_Text infoText; [Header("난이도 버튼")] [SerializeField] private Button btnNormal; [SerializeField] private Button btnHard; [SerializeField] private Button btnExpert; [SerializeField] private Button btnExpertPlus; [Header("액션 버튼")] [SerializeField] private Button downloadButton; [SerializeField] private Button deleteButton; [SerializeField] private Button playButton; [SerializeField] private Button closeButton; [Header("진행률")] [SerializeField] private GameObject progressGroup; [SerializeField] private Slider progressSlider; [SerializeField] private TMP_Text progressText; [Header("씬 이름")] [SerializeField] private string gameSceneName = "Game"; private static readonly Color SelectedColor = new Color(0.2f, 0.78f, 0.4f); private static readonly Color DeselectedColor = Color.white; private SongInfo currentSong; private string selectedDifficulty; private DownloadManager downloadManager; private SongSelectManager selectManager; private readonly (string key, Func btn)[] diffSlots = { ("normal", p => p.btnNormal), ("hard", p => p.btnHard), ("expert", p => p.btnExpert), ("expertplus", p => p.btnExpertPlus), }; // ── Public API ─────────────────────────────────────────── public void Show(SongInfo song, DownloadManager dm, SongSelectManager sm) { currentSong = song; downloadManager = dm; selectManager = sm; selectedDifficulty = null; titleText.text = song.title; artistText.text = song.artist; infoText.text = song.duration > 0 ? $"BPM {Mathf.RoundToInt(song.bpm)} | {FormatDuration(song.duration)}" : $"BPM {Mathf.RoundToInt(song.bpm)}"; RefreshUI(); } // ── UI 갱신 ────────────────────────────────────────────── private void RefreshUI() { bool downloaded = SongLibrary.Instance.IsSongDownloaded(currentSong.id); foreach (var (key, getBtn) in diffSlots) { Button btn = getBtn(this); bool exists = currentSong.difficulties.Get(key) != null; btn.interactable = downloaded && exists; btn.onClick.RemoveAllListeners(); if (downloaded && exists) { string captured = key; btn.onClick.AddListener(() => SelectDifficulty(captured)); } } UpdateDiffColors(); downloadButton.gameObject.SetActive(!downloaded); deleteButton.gameObject.SetActive(downloaded); playButton.interactable = downloaded && selectedDifficulty != null; progressGroup.SetActive(false); downloadButton.onClick.RemoveAllListeners(); downloadButton.onClick.AddListener(OnDownloadClicked); deleteButton.onClick.RemoveAllListeners(); deleteButton.onClick.AddListener(OnDeleteClicked); playButton.onClick.RemoveAllListeners(); playButton.onClick.AddListener(OnPlayClicked); if (closeButton != null) { closeButton.onClick.RemoveAllListeners(); closeButton.onClick.AddListener(() => gameObject.SetActive(false)); } } private void SelectDifficulty(string difficulty) { selectedDifficulty = difficulty; playButton.interactable = true; UpdateDiffColors(); } private void UpdateDiffColors() { foreach (var (key, getBtn) in diffSlots) { Button btn = getBtn(this); var colors = btn.colors; colors.normalColor = (key == selectedDifficulty) ? SelectedColor : DeselectedColor; btn.colors = colors; } } // ── 다운로드 ────────────────────────────────────────────── private void OnDownloadClicked() { StartCoroutine(DownloadAllCoroutine()); } private IEnumerator DownloadAllCoroutine() { var diffs = new List(); foreach (var (key, _) in diffSlots) if (currentSong.difficulties.Get(key) != null) diffs.Add(key); if (diffs.Count == 0) yield break; SetInteractable(false); progressGroup.SetActive(true); downloadButton.gameObject.SetActive(false); deleteButton.gameObject.SetActive(false); playButton.gameObject.SetActive(false); int totalSteps = diffs.Count; int doneSteps = 0; bool failed = false; foreach (string diff in diffs) { bool stepDone = false; downloadManager.DownloadSong( currentSong, diff, onProgress: p => { float overall = (doneSteps + p) / totalSteps; progressSlider.value = overall; progressText.text = $"{diffs[Mathf.Min(doneSteps, diffs.Count - 1)].ToUpper()} {(int)(overall * 100)}%"; }, onComplete: () => { SongLibrary.Instance.MarkDownloaded(currentSong.id, diff); doneSteps++; stepDone = true; }, onError: err => { Debug.LogError($"[SongDetailPanel] {err}"); failed = true; stepDone = true; }); yield return new WaitUntil(() => stepDone); if (failed) break; } SetInteractable(true); progressGroup.SetActive(false); playButton.gameObject.SetActive(true); selectManager.RefreshCards(); RefreshUI(); if (!failed) Debug.Log($"[SongDetailPanel] '{currentSong.title}' 전체 다운로드 완료"); } // ── 삭제 ───────────────────────────────────────────────── private void OnDeleteClicked() { downloadManager.DeleteSong(currentSong.id); SongLibrary.Instance.MarkSongRemoved(currentSong.id); selectedDifficulty = null; selectManager.RefreshCards(); RefreshUI(); } // ── 플레이 ─────────────────────────────────────────────── private void OnPlayClicked() { GameSession.SelectedSong = currentSong; GameSession.SelectedDifficulty = selectedDifficulty; SceneManager.LoadScene(gameSceneName); } // ── 유틸 ───────────────────────────────────────────────── private void SetInteractable(bool value) { downloadButton.interactable = value; deleteButton.interactable = value; playButton.interactable = value && selectedDifficulty != null; foreach (var (_, getBtn) in diffSlots) getBtn(this).interactable = value; } private static string FormatDuration(int seconds) => $"{seconds / 60}:{seconds % 60:D2}"; }