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

namespace Rokojori
{
	public class Smoother
	{
    float overflowDelta = 0;

    public static float SmoothTimeInvariant( float value, float nextValue, float delta, float coefficient )
    {
      var lerpAmount = Mathf.Exp( -coefficient * delta );
      return Mathf.Lerp( nextValue, value, lerpAmount );
    }

    public static Vector2 SmoothTimeInvariant( Vector2 value, Vector2 nextValue, float delta, float coefficient )
    {
      var lerpAmount = Mathf.Exp( -coefficient * delta );
      return Math2D.Lerp( nextValue, value, lerpAmount );
    }

    public static Vector3 SmoothTimeInvariant( Vector3 value, Vector3 nextValue, float delta, float coefficient )
    {
      var lerpAmount = Mathf.Exp( -coefficient * delta );
      return Math3D.Lerp( nextValue, value, lerpAmount );
    }

    public float SmoothForDuration( float value, float nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
    {
      var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
      return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
    } 


    public float SmoothWithCoefficient( float value, float nextValue, float coefficient, float delta, float processDelta = MathX.fps120Delta )
    {
      overflowDelta += delta;

      while ( overflowDelta > processDelta )
      {
        value += coefficient * ( nextValue - value );
        overflowDelta -= processDelta;
      }

      return value;
    } 


    public Quaternion SmoothForDuration( Quaternion value, Quaternion nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
    {
      var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
      return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
    } 

    public Quaternion SmoothWithCoefficient( Quaternion value, Quaternion nextValue, float coefficient, float delta, float processDelta = MathX.fps120Delta )
    {
      overflowDelta += delta;

      while ( overflowDelta > processDelta )
      {
        value = value.Slerp( nextValue, coefficient );
        overflowDelta -= processDelta;
      }

      return value;
    }

    public Vector3 SmoothForDuration( Vector3 value, Vector3 nextValue, float duration, float delta, float processDelta = MathX.fps120Delta )
    {
      var coefficient = MathX.SmoothingCoefficient( duration * 1000f, processDelta );
      return SmoothWithCoefficient( value, nextValue, coefficient, delta, processDelta );
    } 

    public Vector3 SmoothWithCoefficient( Vector3 value, Vector3 nextValue, float coefficient, float delta, float processDelta = MathX.fps120Delta )
    {
      overflowDelta += delta;

      while ( overflowDelta > processDelta )
      {
        value = value.Lerp( nextValue, coefficient );
        overflowDelta -= processDelta;
      }

      return value;
    }  

    public void CopyPosition( Node3D source, Node3D target, float duration, float delta )
    {
      target.GlobalPosition = SmoothForDuration( target.GlobalPosition, source.GlobalPosition, duration, delta );
    }

    public void CopyPosition( Node3D source, Node3D target, float duration, double delta )
    {
      CopyPosition( source, target, duration, (float) delta );
    }

    public void CopyRotation( Node3D source, Node3D target, float duration, float delta )
    {
      target.SetGlobalQuaternion( SmoothForDuration( target.GetGlobalQuaternion(), source.GetGlobalQuaternion(), duration, delta ) );
    }

    public void CopyRotation( Node3D source, Node3D target, float duration, double delta )
    {
      CopyRotation( source, target, duration, (float) delta );
    }
  }
}