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

namespace Rokojori
{
  [Tool]
  [GlobalClass]
	public partial class Smoothing: Resource
	{
    float _currentFloat = 0;
    public void SetCurrent( float value )
    {
      _currentFloat = value;
    }

    Vector2 _currentVector2 = Vector2.Zero;
    Vector3 _currentVector3 = Vector3.Zero;
    Vector4 _currentVector4 = Vector4.Zero;

    public void SetCurrent( Vector2 value )
    {
      _currentVector2 = value;
    }

    public void SetCurrent( Vector3 value )
    {
      _currentVector3 = value;
    }

    public void SetCurrent( Vector4 value )
    {
      _currentVector4 = value;
    }


    Quaternion _currentQuaternion = Quaternion.Identity;

    public void SetCurrent( Quaternion quaternion )
    {
      _currentQuaternion = quaternion;
    }

    Color _currentColor = Colors.Black;

    public void SetCurrent( Color color )
    {
      _currentColor = color;
    }

    public float Smooth( float nextValue, float delta )
    {
      _currentFloat = Mathf.Lerp( _currentFloat, nextValue, _ComputeInterpolationAmount( delta ) ); 

      return _currentFloat;
    }

    public static float Apply( Smoothing sm, float value, float delta )
    {
      if ( sm == null ){ return value; }
      return sm.Smooth( value, delta );
    }

    public static float ApplyWith( Smoothing sm, float oldValue, float nextValue, float delta )
    {
      if ( sm == null ){ return nextValue; }

      sm._currentFloat = oldValue;
      
      return sm.Smooth( nextValue, delta );
    }

     public Vector2 Smooth( Vector2 nextValue, float delta )
    {
      _currentVector2 = _currentVector2.Lerp( nextValue, _ComputeInterpolationAmount( delta ) ); 

      return _currentVector2;
    }

    public static Vector2 Apply( Smoothing sm, Vector2 value, float delta )
    {
      if ( sm == null ){ return value; }
      return sm.Smooth( value, delta );
    }

    public static float ApplyDegrees( Smoothing sm, float value, float delta )
    {
      var newV = MathX.RadiansToVector2( MathX.DegreesToRadians * value );

      return MathX.RadiansToDegrees * MathX.Vector2ToRadians( Apply( sm, newV, delta ) );
    }

    public static float ApplyDegreesWith( Smoothing sm, float oldValue, float nextValue, float delta )
    {
      var oldV = MathX.RadiansToVector2( MathX.DegreesToRadians * oldValue );
      var newV = MathX.RadiansToVector2( MathX.DegreesToRadians * nextValue );

      return MathX.RadiansToDegrees * MathX.Vector2ToRadians( ApplyWith( sm, oldV, newV, delta ) ); 
    }

    public static Vector2 ApplyWith( Smoothing sm, Vector2 oldValue, Vector2 nextValue, float delta )
    {
      if ( sm == null ){ return nextValue; }

      sm._currentVector2 = oldValue;
      
      return sm.Smooth( nextValue, delta );
    }

    public Vector3 Smooth( Vector3 nextValue, float delta )
    {
      _currentVector3 = _currentVector3.Lerp( nextValue, _ComputeInterpolationAmount( delta ) ); 

      return _currentVector3;
    }

    public static Vector3 Apply( Smoothing sm, Vector3 value, float delta )
    {
      if ( sm == null ){ return value; }
      return sm.Smooth( value, delta );
    }

    public Vector4 Smooth( Vector4 nextValue, float delta )
    {
      _currentVector4 = _currentVector4.Lerp( nextValue, _ComputeInterpolationAmount( delta ) ); 

      return _currentVector4;
    }

    public static Vector4 Apply( Smoothing sm, Vector4 value, float delta )
    {
      if ( sm == null ){ return value; }
      return sm.Smooth( value, delta );
    }

    public Quaternion Smooth( Quaternion nextValue, float delta )
    {
      _currentQuaternion = _currentQuaternion.GetNormalized();
      _currentQuaternion = _currentQuaternion.Slerp( nextValue.GetNormalized(), _ComputeInterpolationAmount( delta ) ); 

      return _currentQuaternion;
    }

    public static Quaternion Apply( Smoothing sm, Quaternion value, float delta )
    {
      if ( sm == null ){ return value; }
      return sm.Smooth( value, delta );
    }

    public Color Smooth( Color nextValue, float delta )
    {
      _currentColor = _currentColor.Lerp( nextValue, _ComputeInterpolationAmount( delta ) ); 

      return _currentColor;
    }

    public static Color Apply( Smoothing sm, Color value, float delta )
    {
      if ( sm == null ){ return value; }
      return sm.Smooth( value, delta );
    }

    protected virtual float _ComputeInterpolationAmount( float delta )
    {
      return 0;
    }

    public virtual float ComputeDuration( float delta = 1f/480f, float tresholdValue = 0.01f )
    {
      var cached = _currentFloat;      

      var value = 1f; 
      _currentFloat = 1f;

      var duration = 0f;
      var lastValue = value;
      
      var maxDuration = 120;

      Safe.While(
        ()=> value > tresholdValue && duration < maxDuration,
        ()=>
        {
          lastValue = value;
          value = Smooth( 0, delta );

          duration += delta;
        }
      );

      _currentFloat = cached;

      return MathX.RemapClamped( tresholdValue, lastValue, value, duration - delta, duration ); 

    }
  }
}