namespace splat;

using Godot;

/// <summary>
/// Orbit/pan/zoom camera rig for a Godot <see cref="Camera3D"/>.
/// Controller is the primary input device (per design pillars): the right
/// stick rotates, triggers zoom, and the left stick pans. Keyboard/mouse maps
/// onto the same InputMap actions.
/// </summary>
public partial class CameraController : Node3D
{
    [Export] public float PanSpeed { get; set; } = 20f;
    [Export] public float ZoomSpeed { get; set; } = 8f;
    [Export] public float RotateSpeed { get; set; } = 2.5f;
    [Export] public float MinZoom { get; set; } = 8f;
    [Export] public float MaxZoom { get; set; } = 80f;
    [Export] public float MinPitch { get; set; } = 0.15f;
    [Export] public float MaxPitch { get; set; } = 1.4f;

    private Camera3D _camera = default!;

    // Spherical orbit state around the rig's origin (the focus point on the ground).
    private float _yaw;
    private float _pitch = 0.7f;
    private float _distance = 30f;

    public override void _Ready()
    {
        _camera = GetNode<Camera3D>("Camera3D");
        ApplyTransform();
    }

    public override void _Process(double delta)
    {
        var dt = (float)delta;

        // Pan: left stick / WASD, relative to current facing.
        var pan = Input.GetVector("cam_pan_left", "cam_pan_right", "cam_pan_forward", "cam_pan_back");
        if (pan != Vector2.Zero)
        {
            var forward = new Vector3(Mathf.Sin(_yaw), 0, Mathf.Cos(_yaw));
            var right = new Vector3(forward.Z, 0, -forward.X);
            var move = (right * pan.X + forward * -pan.Y) * PanSpeed * dt;
            Position += move;
        }

        // Rotate: right stick.
        var rotate = Input.GetVector("cam_rotate_left", "cam_rotate_right", "cam_rotate_up", "cam_rotate_down");
        _yaw -= rotate.X * RotateSpeed * dt;
        _pitch = Mathf.Clamp(_pitch - rotate.Y * RotateSpeed * dt, MinPitch, MaxPitch);

        // Zoom: triggers / scroll wheel.
        var zoom = Input.GetAxis("cam_zoom_out", "cam_zoom_in");
        _distance = Mathf.Clamp(_distance - zoom * ZoomSpeed * dt, MinZoom, MaxZoom);

        ApplyTransform();
    }

    private void ApplyTransform()
    {
        var offset = new Vector3(
            Mathf.Sin(_yaw) * Mathf.Cos(_pitch),
            Mathf.Sin(_pitch),
            Mathf.Cos(_yaw) * Mathf.Cos(_pitch)
        ) * _distance;
        _camera.Position = offset;
        _camera.LookAt(Vector3.Zero, Vector3.Up);
    }
}
