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

namespace Rokojori
{
  [Tool]
  [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Grabber.svg")]
  public partial class Grabber:Node3D, SensorInputHandler
  {
    [Export]
    public Sensor button;

    [Export]
    public Pointer pointer;

    [Export]
    public Action onGrab;

     [Export]
    public Action onRelease;

    [Export]
    public TimeLine timeLine;
    
    [Export]
    public Node3D grabOffset;

    [Export]
    public bool disablePointableDuringGrab = true;

    [Export]
    public Smoothing positionSmoothing;

    [Export]
    public Smoothing rotationSmoothing;

    [ExportGroup("Read Only")]
    [Export]
    public Grabbable grabbable;


    

    public override void _Ready()
    {
      this.LogInfo( "Register" );
      SensorManager.Register( this, button );
    }

    public override void _ExitTree()
    {
      SensorManager.Unregister( this, button );
    }

    TimeLineCallback _callback = null;

    public void _OnSensor( SensorEvent se )
    {
      if ( se.isDown && _callback == null )
      {
        StartGrabbing();
      }
      else if ( se.isUp && _callback != null )
      {
        ReleaseGrabbing();
      }
    }

    void StartGrabbing()
    {
      var pointable = pointer.pointable;

      if ( pointable == null )
      {
        return;
      }

      grabbable = Nodes.Find<Grabbable>( pointable.GetParent() );

      if ( grabbable == null )
      {
        return;
      }

      grabbable.SetGrabber( this );

      _callback = TimeLineManager.ScheduleCallback(timeLine, 
        (System.Action<TimeLineCallback>)(( cb )=>
        {
          if ( cb != _callback)
          {
            return;
          }

          positionSmoothing.SetCurrent(grabbable.grabTarget.GlobalPosition );
          rotationSmoothing.SetCurrent(Node3DExtensions.GlobalQuaternion(grabbable.grabTarget));
          UpdateGrabbable();
        }) 
      );

      Action.Trigger( onGrab );
    }

    void ReleaseGrabbing()
    {
      if ( grabbable != null )
      {
        grabbable.SetGrabber( null );
        grabbable = null;
      }

      _callback.done = true;
      _callback = null;

      Action.Trigger( onRelease );
    }

    void UpdateGrabbable() 
    {
      // this.LogInfo( "Grabbing", HierarchyName.Of( grabbable ) );
      grabbable.grabTarget.GlobalPosition = Smoothing.Apply( positionSmoothing, grabOffset.GlobalPosition, timeLine.delta  );
      grabbable.grabTarget.SetGlobalQuaternion( Smoothing.Apply( rotationSmoothing, grabOffset.GlobalQuaternion(), timeLine.delta ) );
      grabbable.rigidBody3D.LinearVelocity = Vector3.Zero;
      grabbable.rigidBody3D.AngularVelocity = Vector3.Zero;
    }

  }
}