using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Random = System.Random;

/// <summary>
///     gameplay behaviour class
/// </summary>
public class Gameplay
{
    /// <summary>
    ///     singleton instance of the gameplay class
    /// </summary>
    public const int RoundsPerGame = 5;

    /// <summary>
    ///     seconds per round
    /// </summary>
    // ReSharper disable once MemberCanBePrivate.Global
    public const double SecondsPerRound = 12d;

    /// <summary>
    ///     countdown text label for showing the countdown
    /// </summary>
    private readonly Label _countdownText;

    /// <summary>
    ///     list of played rounds
    /// </summary>
    private readonly List<RoundInfo> _playedRounds = new(5);

    /// <summary>
    ///     response colour for the player to match
    /// </summary>
    private readonly VisualElement _responseColour;

    /// <summary>
    ///     round text label for showing the current round
    /// </summary>
    private readonly Label _roundText;

    /// <summary>
    ///     template colour for the player to match
    /// </summary>
    private readonly VisualElement _templateColour;

    /// <summary>
    ///     game countdown timer
    /// </summary>
    private DateTime _countdownDatetime;

    /// <summary>
    ///     game round counter
    /// </summary>
    public int Round = -1;

    /// <summary>
    ///     constructor for the gameplay class
    /// </summary>
    /// <param name="ui">the visual element object for the ui document with the GameView</param>
    public Gameplay(VisualElement ui)
    {
        _roundText = ui.Q<Label>("RoundText");
        _countdownText = ui.Q<Label>("TimeText");
        _templateColour = ui.Q<VisualElement>("TemplateColour");
        _responseColour = ui.Q<VisualElement>("ResponseColour");
    }

    /// <summary>
    ///     function for starting the game
    /// </summary>
    public void StartGame()
    {
        Round = 0;
        AdvanceToNextRound();
    }

    /// <summary>
    ///     (to be) called once per frame
    /// </summary>
    public void Update()
    {
        if (Round < 1) return;
        if (_countdownDatetime < DateTime.Now) AdvanceToNextRound();
        Render();
    }

    /// <summary>
    ///     function for advancing to the next round
    /// </summary>
    private void AdvanceToNextRound()
    {
        if (Round > 0) StoreRoundInfo();
        GenerateNewTemplateColour();

        if (Round < RoundsPerGame)
        {
            Debug.Log("round advance");
            Round++;
            _countdownDatetime = DateTime.Now.AddSeconds(SecondsPerRound);
        }
        else
        {
            // end game
            Round = -1;
            GameManager.Instance.SignalGameEnd(_playedRounds);
        }
    }

    /// <summary>
    ///     function for storing the round information
    /// </summary>
    private void StoreRoundInfo()
    {
        _playedRounds.Add(new RoundInfo
        {
            TemplateColour = _templateColour.style.backgroundColor.value,
            ResponseColour = _responseColour.style.backgroundColor.value
        });
        Debug.Log(
            $"stored round info (Template is {_templateColour.style.backgroundColor.value}, Response is {_responseColour.style.backgroundColor.value})");
    }

    /// <summary>
    ///     function for generating a new template colour
    ///     (a random colour that isn't hard to visually match)
    /// </summary>
    private void GenerateNewTemplateColour()
    {
        // - lightness: 30-90
        // - chroma: 0.03-0.20
        // - hue: all (0-360)

        var r = new Random();
        var l = Math.Clamp(r.NextDouble() * 60d + 30d, 30d, 90d);
        var c = Math.Clamp(r.NextDouble() * 0.17d + 0.03d, 0.03d, 0.20d);
        var h = Math.Clamp(r.NextDouble() * 360d, 0d, 360d);
        var colour = Colorimetry.RawLchToColor(l, c, h);
        Debug.Log($"generated new template colour LCh({l:F}, {c:F}, {h:F}) -> {colour}");
        _templateColour.style.backgroundColor = colour;
    }

    /// <summary>
    ///     function for rendering the game state
    /// </summary>
    private void Render()
    {
        var remaining = (_countdownDatetime - DateTime.Now).TotalSeconds;
        _roundText.text = $"{Round}/{RoundsPerGame}";
        _countdownText.text = $"{remaining:F2}";
    }

    /// <summary>
    ///     struct for storing round information
    /// </summary>
    public struct RoundInfo
    {
        public Color TemplateColour;
        public Color ResponseColour;
    }
}