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

namespace Rokojori
{
  [Tool]
  [GlobalClass]
  public abstract partial class Caster:Node3D
  {
    [Export]
    public Selector includeSelector;

    [Export]
    public Selector excludeSelector;

    [Export]
    public bool sortByPointerPriority;
    
        
    public int NumColliders()
    {
      return collisions == null ? 0 : numCollisions;
    }
    
    public Node GetCollider( int index )
    {
      return collisions[ index ].collider;
    }

    public Vector3 GetCollisionNormal( int index )
    {
      return collisions[ index ].normal;
    }

    public Vector3 GetCollisionPosition( int index )
    {
      return collisions[ index ].position;
    }

    public Shape3D GetCollisionShape( int index )
    {
      return collisions[ index ].shape;
    }

    ValueSorter<CollisionData> singleSorter;
    MultiValueSorter<CollisionData> multiSorter;
    protected List<CollisionData> collisions = new List<CollisionData>();
    protected int numCollisions = 0;

    protected bool IsSelected( Node node )
    {
      if ( includeSelector != null && ! includeSelector.Selects( node ) )
      {
        return false;
      }

      if ( excludeSelector != null && excludeSelector.Selects( node ) )
      {
        return false;
      }

      return true;
    }

    protected bool IsSelected( CollisionData cd )
    {
      return IsSelected( cd.collider );
    }

    protected void SortCollisions()
    {
      if ( ! sortByPointerPriority )
      {
        if ( singleSorter == null )
        {
          singleSorter = ValueSorter<CollisionData>.Create( cd => GetDistance( cd ) );
        }

        singleSorter.Sort( 0, numCollisions, collisions );
      }
      else 
      {
        if ( multiSorter == null )
        {
          multiSorter = MultiValueSorter<CollisionData>.Create( 
            cd => GetPointablePriority( cd ),
            cd => GetDistance( cd )
          );
        }

        multiSorter.Sort( 0, numCollisions, collisions );
      }
    }

    protected float GetPointablePriority( CollisionData cd )
    {
      float priority = 0;

      var pointable = Nodes.Find<Pointable>( cd.collider );

      if ( pointable != null )
      {
        priority = pointable.pointingPriority;
      }

      return priority; 
    }

    protected float GetDistance( CollisionData cd )
    {
      return ( cd.position - GlobalPosition ).Length(); 
    }

  }
}