using Godot;
using RustyOptions;

namespace Movementtests.systems;

public partial class MantleSystem: Node3D
{
    [Export(PropertyHint.Range, "0,2,0.1,suffix:m,or_greater")]
    public float MantleEndLocationDistanceFromWall { get; set; } = 1f;
    [Export(PropertyHint.Range, "0,10,0.1,suffix:m,or_greater")]
    public float MantleHeightCastStart { get; set; } = 2f;
    [Export(PropertyHint.Range, "0,10,0.01,suffix:m,or_greater")]
    public float MaxStepHeight = 0.5f;
    
    private Node3D _head;
    private ShapeCast3D _wallInFrontCast3D;
    private ShapeCast3D _mantleCast3D;
    private RayCast3D _mantleCheckCast3D;
    
    private Option<Vector3> _mantleLocation;

    public void Init(Node3D head)
    {
        _head = head;
        _wallInFrontCast3D = GetNode<ShapeCast3D>("WallInFrontCast3D");
        _mantleCast3D = GetNode<ShapeCast3D>("MantleCast3D");
    }

    public override void _PhysicsProcess(double delta)
    {
        base._PhysicsProcess(delta);
        
        _wallInFrontCast3D.SetRotation(new Vector3(
            _wallInFrontCast3D.Rotation.X,
            _head.Rotation.Y,
            _wallInFrontCast3D.Rotation.Z));

        if (!_wallInFrontCast3D.IsColliding())
        {
            _mantleLocation = Option<Vector3>.None;
            return;
        }
        
        var collisionPoint = _wallInFrontCast3D.GetCollisionPoint(0);
        var collisionNormal = _wallInFrontCast3D.GetCollisionNormal(0);
        _mantleLocation = FindMantleLocationAtPoint(collisionPoint, collisionNormal);
    }

    public Option<Vector3> FindMantleInFrontOfPlayer()
    {
        return _mantleLocation;
    }

    public Option<Vector3> FindMantleLocationAtPoint(Vector3 point, Vector3 wallNormal)
    {
        var horizontalEndLocation = point - wallNormal * MantleEndLocationDistanceFromWall;
        var shapeCastStartLocation = horizontalEndLocation + Vector3.Up * MantleHeightCastStart;
        
        _mantleCast3D.SetGlobalPosition(shapeCastStartLocation);
        var targetLocation = Vector3.Down * MantleHeightCastStart + Vector3.Up * MaxStepHeight;
        _mantleCast3D.SetTargetPosition(targetLocation);

        if (_mantleCast3D.IsColliding() && _mantleCast3D.GetCollisionNormal(0).Y > 0.9f)
            return Option.Some(_mantleCast3D.GetCollisionPoint(0));
        return Option<Vector3>.None;
    }
}
