using System;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.Serialization;
using UnityEngine.UI;
using RR.Common.Managers;
using RR.Common.Prizes;
namespace RR.Common.UI
{
///
/// Модальное окно настройки приза за конкретный уровень.
/// Полностью управляется с клавиатуры/геймпада через Input System.
/// Навигация: вверх/вниз — переключение строк (зацикленно), влево/вправо — изменение значения.
/// Настройки сохраняются автоматически при закрытии окна.
///
public class LevelPrizeSettingsModal : Modal
{
[Header("UI — Общее")]
[SerializeField] private TMP_Text titleText;
[SerializeField] private TMP_Text prizeTypeLabel;
[Header("UI — Параметры хоппера")]
[SerializeField] private GameObject hopperParamsGroup;
[SerializeField] private TMP_Text hopperCapsuleCountLabel;
[Header("UI — Параметры витрины")]
[FormerlySerializedAs("showcaseParamsGroup")]
[SerializeField] private GameObject prizeStandParamsGroup;
[FormerlySerializedAs("showcaseBoxCountLabel")]
[SerializeField] private TMP_Text prizeStandBoxCountLabel;
[Header("Строки (RectTransform)")]
[Tooltip("RectTransform строки выбора типа приза.")]
[SerializeField] private RectTransform prizeTypeRow;
[Header("Указатель")]
[Tooltip("Компонент UIRowPointer для визуальной стрелки-указателя (опционально).")]
[SerializeField] private UIRowPointer rowPointer;
[Header("Кнопка закрытия")]
[Tooltip("Кнопка закрытия окна. При фокусе на ней указатель прячется, но Submit срабатывает.")]
[SerializeField] private Button closeModalButton;
[Header("Навигация (Input System)")]
[Tooltip("InputAction навигации (Navigate, тип Value/Vector2)")]
[SerializeField] private InputActionReference navigateAction;
[Tooltip("Порог срабатывания по осям (0..1)")]
[SerializeField] private float deadZone = 0.5f;
[Header("Лимиты")]
[SerializeField] private int maxCapsules = 10;
[SerializeField] private int maxBoxes = 48;
// ── Строки ──
private const int ROW_TYPE = 0;
private const int ROW_PARAM = 1;
private const int ROW_CLOSE = 2;
private const int ROW_COUNT = 3;
// ── Текущее состояние ──
private string _levelName;
private LevelPrizeConfig _config;
private static readonly PrizeType[] PrizeTypes =
(PrizeType[])Enum.GetValues(typeof(PrizeType));
private int _currentTypeIndex;
private int _activeRow;
private bool _axisHeldX;
private bool _axisHeldY;
///
/// Вызывается после сохранения настроек (при закрытии окна). Параметр — имя уровня.
///
public event Action OnApplied;
// ────────────── Lifecycle ──────────────
protected override void OnEnable()
{
base.OnEnable();
if (navigateAction != null && navigateAction.action != null)
{
navigateAction.action.Enable();
navigateAction.action.performed += OnNavigatePerformed;
navigateAction.action.canceled += OnNavigateCanceled;
}
}
protected override void OnDisable()
{
if (navigateAction != null && navigateAction.action != null)
{
navigateAction.action.performed -= OnNavigatePerformed;
navigateAction.action.canceled -= OnNavigateCanceled;
}
_axisHeldX = false;
_axisHeldY = false;
base.OnDisable();
}
// ────────────── Ввод ──────────────
private void OnNavigatePerformed(InputAction.CallbackContext ctx)
{
if (!_isOpen) return;
Vector2 v = ctx.ReadValue();
// ── Вертикаль: переключение строк ──
if (Mathf.Abs(v.y) >= deadZone)
{
if (!_axisHeldY)
{
_axisHeldY = true;
MoveRow(v.y > 0f ? -1 : 1);
}
}
else
{
_axisHeldY = false;
}
// ── Горизонталь: изменение значения ──
if (Mathf.Abs(v.x) >= deadZone)
{
if (!_axisHeldX)
{
_axisHeldX = true;
HandleLeftRight(v.x > 0f ? 1 : -1);
}
}
else
{
_axisHeldX = false;
}
}
private void OnNavigateCanceled(InputAction.CallbackContext ctx)
{
_axisHeldX = false;
_axisHeldY = false;
}
// ────────────── Навигация строк ──────────────
private void MoveRow(int delta)
{
int next = _activeRow;
for (int i = 0; i < ROW_COUNT; i++)
{
next = (next + delta + ROW_COUNT) % ROW_COUNT;
if (IsRowVisible(next))
{
_activeRow = next;
break;
}
}
RefreshRowHighlights();
}
private bool IsRowVisible(int row)
{
if (row == ROW_PARAM)
return _config.type == PrizeType.Hopper || _config.type == PrizeType.PrizeStand;
return true; // ROW_TYPE и ROW_CLOSE видны всегда
}
private void HandleLeftRight(int direction)
{
switch (_activeRow)
{
case ROW_TYPE:
CyclePrizeType(direction);
break;
case ROW_PARAM:
if (_config.type == PrizeType.Hopper)
AdjustCapsules(direction);
else if (_config.type == PrizeType.PrizeStand)
AdjustBoxes(direction);
break;
// ROW_CLOSE — лево/право ничего не делает
}
}
// ────────────── Публичный API ──────────────
///
/// Открывает окно настройки приза для указанного уровня.
///
public void OpenForLevel(string levelName)
{
_levelName = levelName;
PrizeDatabase db = PrizeSystemManager.Instance?.Database;
_config = db != null
? CopyConfig(db.GetConfig(levelName))
: new LevelPrizeConfig();
_currentTypeIndex = 0;
for (int i = 0; i < PrizeTypes.Length; i++)
{
if (PrizeTypes[i] == _config.type)
{
_currentTypeIndex = i;
break;
}
}
_activeRow = ROW_TYPE;
RefreshUI();
RefreshRowHighlights();
Open();
}
///
/// Закрывает окно и сохраняет настройки.
///
public override void Close()
{
Save();
base.Close();
}
// ────────────── Внутреннее ──────────────
private void CyclePrizeType(int direction)
{
_currentTypeIndex = (_currentTypeIndex + direction + PrizeTypes.Length) % PrizeTypes.Length;
_config.type = PrizeTypes[_currentTypeIndex];
// Если строка параметров стала невидимой — перескочить
if (_activeRow == ROW_PARAM && !IsRowVisible(ROW_PARAM))
_activeRow = ROW_TYPE;
RefreshUI();
RefreshRowHighlights();
}
private void AdjustCapsules(int delta)
{
_config.hopperCapsuleCount = Mathf.Clamp(_config.hopperCapsuleCount + delta, 1, maxCapsules);
RefreshUI();
}
private void AdjustBoxes(int delta)
{
_config.prizeStandBoxCount = Mathf.Clamp(_config.prizeStandBoxCount + delta, 1, maxBoxes);
RefreshUI();
}
private void Save()
{
if (string.IsNullOrEmpty(_levelName) || _config == null) return;
PrizeDatabase db = PrizeSystemManager.Instance?.Database;
if (db == null)
{
Debug.LogError("[LevelPrizeSettingsModal] PrizeDatabase недоступна.");
return;
}
db.SetConfig(_levelName, _config);
Debug.Log($"[LevelPrizeSettingsModal] Уровень «{_levelName}» → приз «{_config.type}» сохранён.");
OnApplied?.Invoke(_levelName);
}
// ────────────── UI ──────────────
private void RefreshUI()
{
if (titleText)
titleText.text = $"Приз за уровень {_levelName}";
if (prizeTypeLabel)
prizeTypeLabel.text = GetPrizeTypeName(_config.type);
bool isHopper = _config.type == PrizeType.Hopper;
bool isPrizeStand = _config.type == PrizeType.PrizeStand;
if (hopperParamsGroup) hopperParamsGroup.SetActive(isHopper);
if (prizeStandParamsGroup) prizeStandParamsGroup.SetActive(isPrizeStand);
if (hopperCapsuleCountLabel)
hopperCapsuleCountLabel.text = _config.hopperCapsuleCount.ToString();
if (prizeStandBoxCountLabel)
prizeStandBoxCountLabel.text = _config.prizeStandBoxCount.ToString();
}
private void RefreshRowHighlights()
{
if (_activeRow == ROW_CLOSE)
{
// Прячем указатель, фокус переходит на кнопку закрытия
if (rowPointer) rowPointer.Hide();
if (closeModalButton && EventSystem.current)
EventSystem.current.SetSelectedGameObject(closeModalButton.gameObject);
}
else
{
if (rowPointer)
{
RectTransform target = GetActiveRowTransform();
if (target) rowPointer.SetTarget(target);
}
}
}
private RectTransform GetActiveRowTransform()
{
switch (_activeRow)
{
case ROW_TYPE:
return prizeTypeRow;
case ROW_PARAM:
if (_config.type == PrizeType.Hopper && hopperParamsGroup)
return hopperParamsGroup.transform as RectTransform;
if (_config.type == PrizeType.PrizeStand && prizeStandParamsGroup)
return prizeStandParamsGroup.transform as RectTransform;
return prizeTypeRow;
default:
return prizeTypeRow;
}
}
private static string GetPrizeTypeName(PrizeType type) => type switch
{
PrizeType.None => "Нет приза",
PrizeType.QRCode => "QR-код",
PrizeType.Hopper => "Хоппер",
PrizeType.PrizeStand => "Витрина",
_ => type.ToString()
};
private static LevelPrizeConfig CopyConfig(LevelPrizeConfig src) => new()
{
type = src.type,
hopperCapsuleCount = src.hopperCapsuleCount,
hopperDelayBetweenCapsules = src.hopperDelayBetweenCapsules,
prizeStandBoxCount = src.prizeStandBoxCount
};
}
}