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

namespace Rokojori
{
  [Tool]
  [GlobalClass]
  public partial class CharacterMovement:CharacterControllerAction
  {
    [Export]
    public float onFloorMultiply = 1f;

    [Export]
    public float inAirMultiply = 0.01f;

    [Export]
    public CameraTargetOffset cameraTargetOffset;

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

    public enum DirectionProcessing
    {
      None,
      Zero_Y_And_Normalize,
      Project_On_TransformPlane
    }    

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

    [Export]
    public CharacterMovementType controllerMovementType;

    [Export]
    public CharacterMovementType mouseKeyboardMovementType;

    [Export]
    public CharacterMovementType currentMovementType;

    [Export]
    public Smoothing onFloorMovementSmoothing = new FrameSmoothing();

    [Export]
    public Smoothing inAirMovementSmoothing = new FrameSmoothing();

    bool _onFloor = false;
    bool _moving = false;

    [Export]
    public float movingSpeedTreshold = 0.01f;

    [Export]
    public Action onStartedMoving;

    [Export]
    public Action onStoppedMoving;

    public bool isOnFloor => _onFloor;
    public bool isMoving => _moving;


    [ExportGroup( "Rotation" )]

    [Export]
    public bool adjustRotation = true;

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

    [Export(PropertyHint.Range, "0,600")]
    public int airRotationSmoothingFrames = 120;

    FrameSmoothing rotationSmoothing = new FrameSmoothing();

    protected CharacterMovementData characterMovementData = new CharacterMovementData();



    protected override void _OnTrigger()
    {
      var onFloor = body.IsOnFloor();
      _onFloor = onFloor;

      characterMovementData.Reset( this );

      if ( currentMovementType == null )
      {
        return;
      }

      currentMovementType.ProcessMovement( characterMovementData );

      if ( cameraTargetOffset != null )
      {
        cameraTargetOffset.SetMovingForward( characterMovementData.isMovingForward ? 1f : 0f );
      } 

      var movementMultiply = onFloor ? onFloorMultiply : inAirMultiply;

      characterMovementData.movement *= movementMultiply;
      

      if ( adjustRotation )
      {
        var nextRotation = Math3D.LookRotation( characterMovementData.forwardDirection );
        var speed = MathX.Clamp01( characterMovementData.movement.Length() / moveSpeed );

        if ( speed > 0 )
        {
          Quaternion rotation;

          if ( onFloor )
          {
            rotationSmoothing.frames = Mathf.Round( forwardToRotationSmoothingFrames.Sample( speed ) );                      
          }
          else
          {
            rotationSmoothing.frames = airRotationSmoothingFrames;
          }

          rotation = rotationSmoothing.Smooth( nextRotation, controller.delta );  
          body.SetGlobalQuaternion( rotation );
        }
      }

      var smoothedMovement = Vector3.Zero;

      if ( onFloor )
      {
        smoothedMovement = onFloorMovementSmoothing.Smooth( characterMovementData.movement, controller.delta );        
      }
      else
      {
        smoothedMovement = inAirMovementSmoothing.Smooth( characterMovementData.movement, controller.delta ); 
      }

      var wasMoving = _moving;
      _moving = smoothedMovement.Length() > movingSpeedTreshold;

      if ( wasMoving != _moving )
      {
        if ( _moving )
        {
          Trigger( onStartedMoving );
        }
        else
        {
          Trigger( onStoppedMoving );
        }
      }

      Velocity( smoothedMovement, onFloor );
      
    }
  }

}