using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
using Godot;

namespace Rokojori
{
  public class KeyFrameAnimation<T>
  {
    public List<KeyFrame<T>> keyFrames = new List<KeyFrame<T>>();

    int _currentIndex = -1;
    bool _sorted = false;

    public void FlagUnsorted()
    {
      _sorted = false;
    }

    public void FlagSorted()
    {
      _sorted = true;
    }
    

    public void Add( float time, T data )
    {
      var kf =new KeyFrame<T>();
      kf.time = time;
      kf.data = data;

      keyFrames.Add( kf );

    }

    public void Sort()
    {   
      if ( _sorted )
      {
        return;
      }

      keyFrames.Sort(  ( a, b ) => Mathf.Sign( a.time - b.time ) );
      
      _sorted = true;
    }

    public T GetLerpedValueAt( float time, System.Func<T,T,float,T> lerper )
    {
      var lerped = Lists.Lerp<KeyFrame<T>>( keyFrames, time, ( kf ) => kf.time, 
        ( result )=>
        {
          var keyFrameA = keyFrames[ result.closestIndex ];
          var keyFrameB = keyFrames[ result.secondIndex ];

          var keyFrame = new KeyFrame<T>();
          keyFrame.data = lerper( keyFrameA.data, keyFrameB.data, result.lerpAmount );
          keyFrame.time = time;

          return keyFrame;
        } 
      );

      return lerped.data;
    }

    public KeyFrame<T> GetKeyFrameAt( float time )
    {
      var index = GetKeyFrameIndexAt( time );

      return index == -1 ? null : keyFrames[ index ];
    }

    public int GetKeyFrameIndexAt( float time )
    {
      if ( keyFrames.Count == 0 )
      {
        return -1;
      } 

      Sort();

      for ( int i = 0; i < keyFrames.Count; i++ )
      {
        if ( keyFrames[ i ].time > time )
        {
          return Mathf.Max( 0, i - 1 );
        }
      }

      if ( _sorted )
      {
        return keyFrames.Count - 1;
      }
      
      if ( IsKeyFrameAt( time, _currentIndex ) )
      {
        return _currentIndex;
      } 

      _currentIndex = MathX.SafeIndex( _currentIndex, keyFrames );

      var kf = keyFrames[ _currentIndex ];

      if ( kf.time < time )
      {
        _currentIndex ++;

        while ( _currentIndex < keyFrames.Count )
        {
          if ( IsKeyFrameAt( time, _currentIndex ) )
          {
            return _currentIndex;
          }

          _currentIndex ++;
        }
      }
      else
      {
        _currentIndex --;

        while ( _currentIndex > -1 )
        {
          if ( IsKeyFrameAt( time, _currentIndex ) )
          {
            return _currentIndex;
          }

          _currentIndex --;
        }
      }

      return -1;
    }


    bool IsKeyFrameAt( float time, int index )
    {
      if ( index < 0 || index >= keyFrames.Count -1  )
      {
        return false;
      }

      var i = keyFrames[ index ];

      if ( i.time < time && index == keyFrames.Count - 1 )
      {
        return true; 
      }

      var n = keyFrames[ index + 1 ];

      return i.time < time && time < n.time;

    }

  }
}