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

namespace Rokojori
{
  [Tool]
  [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Collider.svg")]
  public partial class Collider:Node, iNodeState
  {
    [Export]
    public Area3D area;

    [Export]
    public CollisionFlag collisionType;

    [Export]
    public Action onEntered;

    [Export]
    public Action onInside;

    [Export]
    public Action onExit;

    [Export]
    public bool activateAlwaysOnInside = false;


    Dictionary<Collidable,System.Action> _inside = new Dictionary<Collidable,System.Action>();

    public void OnNodeStateChanged()
    {
      if ( ! IsProcessing() || ! IsPhysicsProcessing() || Node.ProcessModeEnum.Disabled == this.ProcessMode )
      {
        // this.LogInfo( "Clearing nodes" );
        _inside.Clear();
      }
    }

    public override void _Ready()
    {
      if ( area == null )
      { 
        return;
      }

      area.AreaEntered += TriggerOnEnter;
      area.BodyEntered += TriggerOnEnter;

      area.AreaExited  += _TriggerOnExited;
      area.BodyExited  += _TriggerOnExited;
    }     

    public void TriggerOnCollisionEnter( Projectile p, KinematicCollision3D collision )
    {
      // this.LogInfo( "Collider:", collision.GetCollider( 0 ), "Shape:", collision.GetColliderShape( 0 ) );
      _TriggerOnEnter( (Node) collision.GetCollider( 0 ) );
    }

    public void TriggerOnCollisionExit( Projectile p, KinematicCollision3D collision )
    {
      // this.LogInfo( "Collider:", collision.GetCollider( 0 ), "Shape:", collision.GetColliderShape( 0 ) );
      _TriggerOnExited( (Node) collision.GetCollider( 0 ) );
    }

    public void TriggerOnEnter( Node3D node3D )
    {
      // this.LogInfo( "Collider:", collision.GetCollider( 0 ), "Shape:", collision.GetColliderShape( 0 ) );
      _TriggerOnEnter( node3D );
    }

    public void TriggerOnExited( Node3D node3D )
    {
      // this.LogInfo( HierarchyName.Of( node3D ) );
      _TriggerOnExited( node3D );
    }

    Collidable GetCollidable( Node n )
    {
      if ( n is Collidable c && c.IsCollidingWith( this ) )
      {
        return c;
      }

      return n.FindDirectChild<Collidable>( c => c.IsCollidingWith( this ) );

    }

    void _TriggerOnEnter( Node n )
    {
      if ( n == null )
      {
        return;
      }
      
      if ( area != null && ! Math3D.IsValid( area.GlobalPosition ) )
      {
        return;
      } 


      var collidable = GetCollidable( n );

      if ( collidable == null )
      {
        return;
      }

      
      collidable.Collide( this, CollisionPhase.Entered );

      if ( ( ! activateAlwaysOnInside ) && onInside == null )
      {
        return;
      }

      var tm = Unique<TimeLineManager>.Get();

      if ( tm == null )
      {
        return;
      }
      
      var callback = ()=>
      { 
        collidable.Collide( this, CollisionPhase.Inside );
      };

      _inside[ collidable ] = callback;
      tm.AddProcessCallback( callback );
      
    }

    void _TriggerOnExited( Node n )
    {
      var collidable = GetCollidable( n );

      if ( collidable == null )
      {
        return;
      }

      collidable.Collide( this, CollisionPhase.Exit );

      if ( ! _inside.ContainsKey( collidable ) )
      {
        return;
      }

      var tm = Unique<TimeLineManager>.Get();

      var callback = _inside[ collidable ];
      _inside.Remove( collidable );

      if ( tm == null )
      {
        return;
      }      
      
      tm.RemoveProcessCallback( callback );

      
    }
  }
}