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

namespace Rokojori
{
  [Tool]
  [GlobalClass]
  public partial class MultiRayCaster:Caster
  {
    public enum UpdateMode
    { 
      Process,
      Physics_Process
    }

    [Export]
    public float rayLength = 10;

    [Export]
    public int maxHits = 3;

    List<CollisionData> collisions = new List<CollisionData>();
    int numCollisions = 0;

    [Export]
    public Vector3 to;

    [Export]
    public UpdateMode updateMode = UpdateMode.Physics_Process;

    [Export]
    public Action beforeProcess;

    [Export]
    public Action afterProcess;


    [ExportGroup( "Debugging")]
    [Export]
    public Node collider;

    [Export]
    public bool printColliderNames = false;



    

    PhysicsRayQueryParameters3D rayParameters = new PhysicsRayQueryParameters3D();

    public override void _PhysicsProcess( double delta )
    {      
      if ( UpdateMode.Physics_Process != updateMode )
      {
        return;
      }

      Update( delta );
    }

    public override void _Process( double delta )
    {
      if ( UpdateMode.Process != updateMode )
      {
        return;
      }

      Update( delta );

    } 

    [Export]
    public bool runInEditor = false;

    void Update( double delta )
    {
      if ( Engine.IsEditorHint() && ! runInEditor )
      {
        return;

      }

      Action.Trigger( beforeProcess );

      ResolveCollisions();
      SortCollisions(); 

      var nextCollider = NumColliders() == 0 ? null : GetCollider( 0 );

      if ( nextCollider != collider )
      { 
        collider = nextCollider;

        if ( printColliderNames )
        {
          this.LogInfo( "New Collider:", HierarchyName.Of( collider ) );
        }
      } 
      
      Action.Trigger( afterProcess );
    }
    

    CollisionData GetCollisionData( int i )
    {
      while ( collisions.Count <= i )
      {
        collisions.Add( new CollisionData() );
      }

      return collisions[ i ];
    }

    void ResolveCollisions()
    {
      var physics = GetWorld3D().DirectSpaceState;

      var excludes = new Array<Rid>();

      var from = GlobalPosition;
      var to   = from + this.GlobalForward().Normalized() * rayLength;

      this.to = to;

      rayParameters.From = from;
      rayParameters.To = to;

      numCollisions = 0;

      for ( int i = 0; i < maxHits; i++ )
      {        
        rayParameters.Exclude = excludes;

        var collisionData = GetCollisionData( numCollisions );
        collisionData.Get( rayParameters, physics );

        if ( ! collisionData.hasCollision || this.DistanceTo( collisionData.position ) > rayLength )
        {
          return;
        }
        
        excludes.Add( collisionData.rid );

        if ( IsSelected( collisionData ) )
        {          
          numCollisions ++;
        }

      }
    }

    


  }
}