namespace splat;

using Godot;

/// <summary>
/// Central game state: tracks simulated time, pause state, and time scale.
/// Other systems (decay, weather, day/night) observe <see cref="TimeScale"/>
/// and <see cref="IsPaused"/> to advance their own simulations.
/// </summary>
public partial class GameManager : Node
{
    /// <summary>Discrete speed steps the player can cycle through.</summary>
    public static readonly float[] SpeedSteps = { 0f, 1f, 2f, 4f };

    /// <summary>Simulated seconds elapsed (scaled by time scale).</summary>
    public double SimTime { get; private set; }

    /// <summary>Whole in-game days elapsed since the city started.</summary>
    public int Day { get; private set; }

    /// <summary>Current time-of-day fraction in [0, 1): 0 = midnight.</summary>
    public float TimeOfDay { get; private set; }

    public bool IsPaused => CurrentSpeedIndex == 0;

    public float TimeScale => SpeedSteps[CurrentSpeedIndex];

    private int _currentSpeedIndex = 1;

    public int CurrentSpeedIndex
    {
        get => _currentSpeedIndex;
        private set => _currentSpeedIndex = Mathf.Clamp(value, 0, SpeedSteps.Length - 1);
    }

    /// <summary>Emitted when the player changes the pause/speed state.</summary>
    [Signal] public delegate void SpeedChangedEventHandler(float timeScale);

    /// <summary>Emitted at the start of each new in-game day.</summary>
    [Signal] public delegate void NewDayEventHandler(int day);

    public override void _Ready()
        => EmitSignal(SignalName.SpeedChanged, TimeScale);

    public override void _Process(double delta)
    {
        if (IsPaused)
            return;

        var scaled = delta * TimeScale;
        SimTime += scaled;
        TimeOfDay += (float)(scaled / DayLength);

        while (TimeOfDay >= 1f)
        {
            TimeOfDay -= 1f;
            Day++;
            EmitSignal(SignalName.NewDay, Day);
        }
    }

    public override void _UnhandledInput(InputEvent @event)
    {
        if (@event.IsActionPressed("game_cycle_speed"))
            CycleSpeedForward();
    }

    /// <summary>Advance to the next faster speed (wraps back to paused).</summary>
    public void CycleSpeedForward()
    {
        CurrentSpeedIndex = (CurrentSpeedIndex + 1) % SpeedSteps.Length;
        EmitSignal(SignalName.SpeedChanged, TimeScale);
    }

    public void SetPaused(bool paused)
    {
        CurrentSpeedIndex = paused ? 0 : Mathf.Max(1, _currentSpeedIndex);
        EmitSignal(SignalName.SpeedChanged, TimeScale);
    }

    /// <summary>One in-game day lasts this many real seconds at 1x speed.</summary>
    private const float DayLength = 120f;
}
