using System.Collections;
using System.Collections.Generic;
using Godot;
using System;



namespace Rokojori
{
  [Tool]
  [GlobalClass]
  public partial class ProjectOnColliders:Scatterer
  { 
    
   

    [ExportGroup("Ray")]
    [Export]
    public Vector3 rayDirection = Vector3.Down;

    [Export]
    public float rayLength = 10;

    [Export]
    public Vector3 rayOffset = Vector3.Zero;

    [ExportGroup("Normal")]
    [Export(PropertyHint.Range, "0,1")]
    public float rotationAlignment = 1;

    [Export]  
    public bool discardSteepNormals = true;

    [Export]
    public Vector3 steepNormalDirection = Vector3.Down;
    
    [Export(PropertyHint.Range, "0,180")]
    public float steepNormalRemovalAngle = 0;

    [ExportGroup("Collisions")]
    [Export( PropertyHint.Layers3DPhysics)]
    public uint collisionLayer = 0;

    [Export]
    public bool collideWithAreas = true;

    [Export]
    public bool collideWithBodies = true;

    [Export]
    public bool hitFromInside = true;

    [Export]
    public bool hitBackFaces = true;

    protected override List<ScatterPoint> _Scatter( List<ScatterPoint> points, Scatterer root )
    {
      var world = GetWorld3D();
      var ray = new PhysicsRayQueryParameters3D();
      var direction = rayDirection.Normalized();
      var normalizedSteepNormalDirection = steepNormalDirection.Normalized();

      var steepNormalRemovalTreshold = Mathf.Cos( Mathf.DegToRad( steepNormalRemovalAngle ) );

      points.ForEach(
        p =>
        {
          if ( ! p.visible )
          {
            return;
          }

          ray.From = p.globalPosition;
          ray.To   = ray.From + direction * rayLength;

          var collisionData = CollisionData.FindCollision( world, ray,
            ( cd ) =>
            {
              if ( ! CollisionData.HasCollisionLayer( cd.collider ) )
              {
                return false;
              }              

              var colliderCollisionLayer = CollisionData.GetCollisionLayer( cd.collider ); 
              var collides = ( colliderCollisionLayer & collisionLayer ) != 0;
              // RJLog.Log( "Collision With:", "collides", collides, "colliderLayer:", colliderCollisionLayer, "own", collisionLayer, "name:", cd.collider.Name );
              return collides;

            }
          );

          p.visible = collisionData != null;

          if ( ! p.visible )
          {
            return;
          }

          if ( discardSteepNormals )
          {
            var dot = normalizedSteepNormalDirection.Dot( collisionData.normal );

            if ( dot >= steepNormalRemovalTreshold )
            {
              p.visible = false;
              return;
            }
          }

          p.position = collisionData.position;
          var alginedRotation =Math3D.AlignUp( p.rotation, collisionData.normal );
          p.rotation = p.rotation.Slerp( alginedRotation, rotationAlignment );
         
        } 

      );

      return points;
    }
  }
}