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

namespace Rokojori
{
  [GlobalClass]
  public partial class CharacterMovement:CharacterControllerAction
  {
    [Export]
    public float onFloorMultiply = 0f;
    
    [Export]
    public bool overwriteVelocity = true;

    [ExportGroup( "Actions" )]
    [Export]
    public Action onMoving;
    [Export]
    public Action onIdle;

    [Export]
    public Action onForward;
    [Export]
    public Action onBackwards;
    [Export]
    public Action onStrafeLeft;
    [Export]
    public Action onStrafeRight;


    [ExportGroup( "Direction Source" )]
    [Export]
    public Node3D directionSource;

    public enum DirectionProcessing
    {
      None,
      Zero_Y_And_Normalize,
      Project_On_TransformPlane
    }

    [Export]
    public DirectionProcessing directionProcessing;

    [ExportGroup( "Moving" )]
    
    [Export]
    public float moveSpeed;

    [Export]
    public Sensor forward;

    [Export]
    public Sensor backwards;

    [ExportGroup( "Strafing" )]
    [Export]
    public float strafeSpeed;

    [Export]
    public Sensor strafeLeft;

    [Export]
    public Sensor strafeRight;   

    [Export]
    public bool useBodyDirection = true;

    [ExportGroup( "Rotation" )]

    [Export]
    public bool adjustRotation = true;

    [Export]
    public Curve forwardToRotation = MathX.Curve( 0, 1 );

    [Export( PropertyHint.Range, "0,1")]
    public float rotationSmoothingCoefficient = 0.1f;
    Smoother rotationSmoother = new Smoother(); 


    protected override void _OnTrigger()
    {
      if ( ! body.IsOnFloor() )
      {
        return;
      }

      var movement = Vector3.Zero;

      var fw = Sensors.GetValue( forward );
      var bw = Sensors.GetValue( backwards );

      var fbAxis = Sensors.PolarAxis( backwards, forward );

      var dir = directionSource != null ? directionSource : body;
      var forwardDirection = dir.GlobalForward();
      var rightDirection   = useBodyDirection ? body.GlobalRight() : dir.GlobalRight();

      if ( DirectionProcessing.Zero_Y_And_Normalize == directionProcessing ) 
      {
        forwardDirection.Y = 0;
        rightDirection.Y = 0;        
      } 
      else if ( DirectionProcessing.Project_On_TransformPlane == directionProcessing ) 
      {
        var bodyPlane = Plane3.CreateFromNode3D( body );
        forwardDirection = bodyPlane.ConstrainToPlane( forwardDirection + body.GlobalPosition ) - body.GlobalPosition;
        rightDirection   = bodyPlane.ConstrainToPlane( rightDirection + body.GlobalPosition ) - body.GlobalPosition;
      }
     
      forwardDirection = forwardDirection.Normalized();
      rightDirection   = rightDirection.Normalized();

      movement += forwardDirection * Sensors.PolarAxis( backwards, forward ) * moveSpeed;

      movement += rightDirection * Sensors.PolarAxis( strafeLeft, strafeRight ) * strafeSpeed ; 

      

      if ( body.IsOnFloor() )
      {
        movement *= onFloorMultiply;
      }

      if ( adjustRotation )
      {
        var currentRotation = body.GetGlobalQuaternion();
        var nextRotation = Math3D.LookRotation( forwardDirection );

        var sm = 1f - rotationSmoothingCoefficient;
        sm = sm * sm;

        var speed = MathX.Clamp01( movement.Length() / moveSpeed ) * Mathf.Max( fw, bw );
        
        sm *= forwardToRotation.Sample( speed );

        var rotation = rotationSmoother.SmoothWithCoefficient( currentRotation, nextRotation, sm, controller.delta );
        body.SetGlobalQuaternion( rotation );
      }

      Velocity( movement, overwriteVelocity );
      
    }
  }

}