using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using RR.Common.Controls;
using RR.Common.Managers;
using RR.Common.Prizes;
using RR.Common.Settings;
using RR.Common.UI.FX;
using RR.Game2D.Core.Managers;
namespace RR.Common.UI
{
///
/// Контроллер баннера выигрыша.
/// Модальное окно, которое отображается при победе игрока.
///
public class WinBannerController : Modal
{
[Header("Элементы баннера выигрыша")]
[SerializeField] private TextMeshProUGUI winText;
[SerializeField] private Button mainMenuButton;
[Header("Опциональная разметка режимов")]
[Tooltip("Корневой объект обычной победной плашки. Если не задан, используется только winText.")]
[SerializeField] private GameObject winContentRoot;
[Tooltip("Корневой объект призовой плашки. Если не задан, используется только winText.")]
[SerializeField] private GameObject prizeContentRoot;
[SerializeField] private TextMeshProUGUI prizeTitleText;
[SerializeField] private TextMeshProUGUI prizeDetailsText;
[Header("QR-коды")]
[Tooltip("Контейнер для размещения QR-кодов. QR-коды создаются динамически.")]
[SerializeField] private RectTransform qrCodesContainer;
[Tooltip("Корневой объект секции QR. Если не задан, используется объект контейнера QR.")]
[SerializeField] private GameObject qrSectionRoot;
[Header("FX")]
[SerializeField] private bool pulsePrizeContent = true;
[SerializeField] private float prizePulseScale = 1.08f;
[SerializeField] private float prizePulseDuration = 1.2f;
///
/// Событие, вызываемое при закрытии баннера выигрыша.
///
public event Action WinBannerDismissed;
private readonly List _createdQRCodeObjects = new();
private Coroutine _prizePulseCoroutine;
private Transform _prizePulseTarget;
private Vector3 _prizePulseOriginalScale;
protected override void Awake()
{
base.Awake();
if (mainMenuButton)
{
mainMenuButton.onClick.AddListener(OnMainMenuClicked);
}
}
///
/// Показывает баннер выигрыша или призовой баннер для последнего уровня.
///
/// Время игры в секундах.
/// Конфиг приза последнего уровня.
/// Итоговый счёт за игру.
/// Нужно ли показывать итоговый счёт в баннере.
public void ShowWin(float gameTime, LevelPrizeConfig prizeConfig, int finalScore, bool showFinalScore)
{
ApplyBannerState(ResolveBannerState(gameTime, prizeConfig ?? new LevelPrizeConfig(), finalScore, showFinalScore));
Open();
}
private void ApplyBannerState(BannerState state)
{
if (winContentRoot != null)
winContentRoot.SetActive(!state.IsPrizeBanner);
if (prizeContentRoot != null)
prizeContentRoot.SetActive(state.IsPrizeBanner);
if (winText)
winText.text = state.FallbackWinText;
if (prizeTitleText)
prizeTitleText.text = state.PrizeTitle;
if (prizeDetailsText)
prizeDetailsText.text = state.PrizeDetails;
if ((prizeTitleText == null || prizeDetailsText == null) && winText)
winText.text = state.LegacyCombinedText;
DisplayQRCodes(state.ShowQRCodes);
StartPrizeHighlight(state.IsPrizeBanner);
}
private BannerState ResolveBannerState(float gameTime, LevelPrizeConfig prizeConfig, int finalScore, bool showFinalScore)
{
string formattedTime = TimeFormatHelper.FormatTime(gameTime);
string scoreLine = showFinalScore ? $"\nОчки за игру: {finalScore}" : string.Empty;
bool canShowQRCodes = prizeConfig.type == PrizeType.QRCode && CanDisplayBigPrizeQRCodes() && HasQRCodes(prizeConfig.qrTier);
string qrTierName = GetQRTierName(prizeConfig.qrTier);
string prizeStandSummary = GetPrizeStandSummary(finalScore, showFinalScore);
return prizeConfig.type switch
{
PrizeType.QRCode when canShowQRCodes => CreatePrizeState(
"Ваш приз",
$"Отсканируйте {qrTierName} QR-код, чтобы получить приз.\nВремя игры: {formattedTime}{scoreLine}",
$"Ваш приз: {qrTierName} QR-код\nВремя игры: {formattedTime}{scoreLine}",
showQRCodes: true),
PrizeType.Hopper => CreatePrizeState(
"Ваш приз",
$"Капсулы: {prizeConfig.hopperCapsuleCount}\nВремя игры: {formattedTime}{scoreLine}",
$"Ваш приз: капсулы\nКоличество: {prizeConfig.hopperCapsuleCount}\nВремя игры: {formattedTime}{scoreLine}"),
PrizeType.PrizeStand => CreatePrizeState(
"Ваш приз",
$"{prizeStandSummary}\nВремя игры: {formattedTime}{scoreLine}",
$"Ваш приз: витрина\n{prizeStandSummary}\nВремя игры: {formattedTime}{scoreLine}"),
_ => CreateWinState(formattedTime, finalScore, showFinalScore)
};
}
private static BannerState CreatePrizeState(string title, string details, string legacyCombinedText, bool showQRCodes = false)
{
return new BannerState
{
IsPrizeBanner = true,
PrizeTitle = title,
PrizeDetails = details,
LegacyCombinedText = legacyCombinedText,
ShowQRCodes = showQRCodes,
FallbackWinText = string.Empty
};
}
private static BannerState CreateWinState(string formattedTime, int finalScore, bool showFinalScore)
{
string winText = showFinalScore
? $"Время игры: {formattedTime}\nОчки за игру: {finalScore}"
: $"Время игры: {formattedTime}";
return new BannerState
{
IsPrizeBanner = false,
PrizeTitle = string.Empty,
PrizeDetails = string.Empty,
LegacyCombinedText = winText,
FallbackWinText = winText,
ShowQRCodes = false
};
}
///
/// Отображает QR-коды из PrizeSystemManager, если для финального уровня назначен QR-приз.
///
private void DisplayQRCodes(bool shouldDisplay)
{
ClearQRCodes();
if (qrCodesContainer == null)
return;
GetQRSectionRoot().SetActive(shouldDisplay);
if (!shouldDisplay || PrizeSystemManager.Instance == null)
return;
QRTier tier = PrizeSystemManager.Instance.GetPrizeConfigForCurrentLevel().qrTier;
Texture2D[] qrCodes = PrizeSystemManager.Instance.GetQRCodes(tier);
if (qrCodes == null || qrCodes.Length == 0)
{
GetQRSectionRoot().SetActive(false);
return;
}
foreach (Texture2D qrTexture in qrCodes)
{
if (qrTexture == null) continue;
GameObject qrObject = new GameObject("QRCode", typeof(RectTransform), typeof(Image));
qrObject.transform.SetParent(qrCodesContainer, false);
Sprite qrSprite = Sprite.Create(
qrTexture,
new Rect(0, 0, qrTexture.width, qrTexture.height),
new Vector2(0.5f, 0.5f)
);
Image image = qrObject.GetComponent();
image.sprite = qrSprite;
image.raycastTarget = false;
QRCodeTierHighlight.Apply(qrObject, tier);
_createdQRCodeObjects.Add(qrObject);
}
}
private void StartPrizeHighlight(bool isPrizeBanner)
{
StopPrizeHighlight();
if (!pulsePrizeContent || !isPrizeBanner)
return;
GameObject target = prizeContentRoot != null
? prizeContentRoot
: qrSectionRoot != null
? qrSectionRoot
: qrCodesContainer != null
? qrCodesContainer.gameObject
: null;
if (target == null || !target.activeInHierarchy)
return;
_prizePulseTarget = target.transform;
_prizePulseOriginalScale = _prizePulseTarget.localScale;
_prizePulseCoroutine = StartCoroutine(PulsePrizeContent());
}
private IEnumerator PulsePrizeContent()
{
float elapsed = 0f;
while (elapsed < prizePulseDuration && _prizePulseTarget != null)
{
elapsed += Time.unscaledDeltaTime;
float normalized = Mathf.Clamp01(elapsed / Mathf.Max(0.01f, prizePulseDuration));
float pulse = Mathf.Sin(normalized * Mathf.PI * 2f);
float scale = Mathf.Lerp(1f, prizePulseScale, Mathf.Abs(pulse));
_prizePulseTarget.localScale = _prizePulseOriginalScale * scale;
yield return null;
}
if (_prizePulseTarget != null)
_prizePulseTarget.localScale = _prizePulseOriginalScale;
_prizePulseCoroutine = null;
_prizePulseTarget = null;
}
private void StopPrizeHighlight()
{
if (_prizePulseCoroutine != null)
{
StopCoroutine(_prizePulseCoroutine);
_prizePulseCoroutine = null;
}
if (_prizePulseTarget != null)
_prizePulseTarget.localScale = _prizePulseOriginalScale;
_prizePulseTarget = null;
}
private static bool CanDisplayBigPrizeQRCodes()
{
return SettingsManager.Instance != null && SettingsManager.Instance.GetBool("prizes.giveBigPrize");
}
private bool HasQRCodes(QRTier tier)
{
if (PrizeSystemManager.Instance == null)
return false;
Texture2D[] qrCodes = PrizeSystemManager.Instance.GetQRCodes(tier);
if (qrCodes == null || qrCodes.Length == 0)
return false;
foreach (Texture2D qrCode in qrCodes)
{
if (qrCode != null)
return true;
}
return false;
}
private GameObject GetQRSectionRoot()
{
return qrSectionRoot != null ? qrSectionRoot : qrCodesContainer.gameObject;
}
///
/// Очищает созданные QR-код объекты.
///
private void ClearQRCodes()
{
foreach (GameObject obj in _createdQRCodeObjects)
{
if (obj != null)
{
Destroy(obj);
}
}
_createdQRCodeObjects.Clear();
}
private void OnMainMenuClicked()
{
Close();
WinBannerDismissed?.Invoke();
if (GameManager.Instance)
{
GameManager.Instance.SetPause(false);
GameManager.Instance.LoadScene("MainMenu", false);
}
}
protected override void OnDisable()
{
base.OnDisable();
StopPrizeHighlight();
ClearQRCodes();
if (qrCodesContainer != null)
GetQRSectionRoot().SetActive(true);
if (winContentRoot != null)
winContentRoot.SetActive(true);
if (prizeContentRoot != null)
prizeContentRoot.SetActive(true);
}
private static string GetQRTierName(QRTier tier)
{
return tier switch
{
QRTier.Bronze => "бронзовый",
QRTier.Silver => "серебряный",
QRTier.Gold => "золотой",
_ => "обычный"
};
}
private static string GetPrizeStandSummary(int finalScore, bool showFinalScore)
{
PrizeSystemManager manager = PrizeSystemManager.Instance;
if (manager == null)
return "Витрина недоступна";
PrizeStandConnectionInfo connection = manager.GetPrizeStandConnectionInfo();
PrizeStandSettings settings = manager.GetPrizeStandSettings();
int unlockedCount = 0;
if (settings?.boxes != null)
{
for (int i = 0; i < settings.boxes.Count; i++)
{
PrizeStandBoxConfig box = settings.boxes[i];
if (box == null)
continue;
bool isConnected = connection.IsConnected
&& (connection.ConnectedBoxesCount <= 0 || box.boxNumber <= connection.ConnectedBoxesCount);
bool isUnlocked = !showFinalScore || finalScore >= box.requiredScore;
if (isConnected && isUnlocked)
unlockedCount++;
}
}
string connectionText = connection.IsConnected
? $"Витрина подключена, ячеек на устройстве: {connection.ConnectedBoxesCount}"
: "Витрина не подключена";
return $"{connectionText}\nЯчеек доступно по очкам: {unlockedCount}";
}
private struct BannerState
{
public bool IsPrizeBanner;
public bool ShowQRCodes;
public string PrizeTitle;
public string PrizeDetails;
public string LegacyCombinedText;
public string FallbackWinText;
}
}
}