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

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

    [Export]
    public Selector selector;

    [Export]
    public Action onEntered;

    [Export]
    public Action onInside;

    [Export]
    public Action onExit;

    public readonly EventSlot<Node> onEnteredSlot = new EventSlot<Node>();
    public readonly EventSlot<Node> onInsideSlot = new EventSlot<Node>();
    public readonly EventSlot<Node> onExitSlot = new EventSlot<Node>();
    

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

    List<Node> _nodes = new List<Node>();

    public List<Node> GetNodesInside()
    {
      _nodes = _nodes.Filter( n => n != null && IsInstanceValid( n ) );

      return _nodes;
    }

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

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

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

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

    void TriggerOnEnter( Node n )
    {
      if ( ! Math3D.IsValid( area.GlobalPosition ) )
      {
        return;
      } 

      if ( ! Selector.IsSelecting( selector, n ) )
      {
        
        return;
      }

      if ( ! _nodes.Contains( n ) )
      {
        _nodes.Add( n );
      }
      

      

      // this.LogInfo( "Selecting Enter", area.GlobalPosition, HierarchyName.Of( n ), ( (Node3D)n ).GlobalPosition );
      Action.Trigger( onEntered );
      onEnteredSlot.DispatchEvent( n );

      if ( onInside == null )
      {
        return;
      }

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

      if ( tm == null )
      {
        return;
      }
      
      var callback = ()=>
      { 
        Action.Trigger( onInside ); 
        onInsideSlot.DispatchEvent( n );
      };

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

    void TriggerOnExited( Node n )
    {
      _nodes.Remove( n );

      if ( ! Selector.IsSelecting( selector, n ) )
      {
        return;
      }

      Action.Trigger( onExit );
      onExitSlot.DispatchEvent( n );
      

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

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

      if ( tm == null )
      {
        return;
      }
      
      var callback = _inside[ n ];
      tm.RemoveProcessCallback( callback );

      _inside.Remove( n );
    }
  }
}