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

namespace Rokojori
{
  public static class Math2D
  {    
    public enum Axis 
    {
      X, Y
    }

    public static Vector2I RoundToInt( this Vector2 v )
    {
      return new Vector2I( Mathf.RoundToInt( v.X ), Mathf.RoundToInt( v.Y ) );
    }

    public static Vector2I FloorToInt( this Vector2 v )
    {
      return new Vector2I( Mathf.FloorToInt( v.X ), Mathf.FloorToInt( v.Y ) );
    }

    public static Vector2I CeilToInt( this Vector2 v )
    {
      return new Vector2I( Mathf.CeilToInt( v.X ), Mathf.CeilToInt( v.Y ) );
    }

    public static Vector2 SnapFloored( this Vector2 v, Vector2 snapping )
    {
      v.X = MathX.SnapFloored( v.X, snapping.X );
      v.Y = MathX.SnapFloored( v.Y, snapping.Y );

      return v;
    }

    public static Vector2 SnapCeiled( this Vector2 v, Vector2 snapping )
    {
      v.X = MathX.SnapCeiled( v.X, snapping.X );
      v.Y = MathX.SnapCeiled( v.Y, snapping.Y );

      return v;
    }

    public static float LookingAtEachOtherAngle( Vector2 lookDirectionA, Vector2 lookDirectionB )
    {
      return Dot( lookDirectionA, lookDirectionB );
    }

    public static bool LookingAtEachOther( Vector2 lookDirectionA, Vector2 lookDirectionB )
    {
      return LookingAtEachOtherAngle( lookDirectionA, lookDirectionB ) > 0;
    }

    public static Vector2 Lerp( Vector2 a, Vector2 b, float lerpAmount )
    {
      return a.Lerp( b, lerpAmount ); 
    }

    public static bool LookingTowards( Vector2 from, Vector2 fromDirection, Vector2 to )
    {
      return LookingAtEachOther( fromDirection.Normalized(), ( to - from ).Normalized() );
    }

    public static float Dot( Vector2 a, Vector2 b )
    {
      return a.Dot( b );
    }

    public static Vector2 XZ( Vector3 v )
    {
      return new Vector2( v.X, v.Z );
    }

    public static Vector3 To3DXZ( this Vector2 v, float y = 0 )
    {
      return new Vector3( v.X, y, v.Y );
    }

    public static Vector3 To3DXY( this Vector2 v, float z = 0 )
    {
      return new Vector3( v.X, v.Y, z );
    }

    public static Vector2 Map( Vector2 value, Vector2 inMin, Vector2 inMax, Vector2 outMin, Vector2 outMax )
    {
      return new Vector2( 
        MathX.Map( value.X, inMin.X, inMax.X, outMin.X, outMax.X ), 
        MathX.Map( value.Y, inMin.Y, inMax.Y, outMin.Y, outMax.Y ) 
      );
    }

    public static Vector2 Clamp01( Vector2 v )
    {
      return new Vector2(
        MathX.Clamp01( v.X ),
        MathX.Clamp01( v.Y )
      );
    }

    public static Vector2 Circle( float radians, float size = 1 )
    {
      var x = Mathf.Cos( radians ) * size;
      var y = Mathf.Sin( radians ) * size;

      return new Vector2( x, y );    
    }

    public static Vector2 Fract( Vector2 a )
    {
      return new Vector2( MathX.Fract( a.X ), MathX.Fract( a.Y ) );
    }

    public static Vector2 SmoothStep( Vector2 a, Vector2 b, Vector2 t )
    {
      var x = MathX.Smoothstep( a.X, b.X, t.X );
      var y = MathX.Smoothstep( a.Y, b.Y, t.Y ); 
      return new Vector2( x, y );
    }

    public static Vector2 Rotate90DegreesRight( Vector2 v )
    {
      // 1, 0.5 => -0.5, 1 : -y, x
      return new Vector2( -v.Y, v.X );
    }

    public static Vector2 Rotate90DegreesLeft( Vector2 v )
    {
      // -0.5, 1 => 1, 0.5 : y, -x
      return new Vector2( v.Y, -v.X );
    }

    public static Vector2 Rotate90Degrees( Vector2 v, bool clockwise )
    {
      return clockwise ? Rotate90DegreesRight( v ) : Rotate90DegreesLeft( v );
    }

    public static Vector2 ComputeAverage( List<Vector2> points )
    {
      if ( points == null || points.Count == 0 )
      {
        return Vector2.Zero;
      }

      var mean = Vector2.Zero;

      for ( int i = 0; i < points.Count; i++ )
      {
          mean += ( points[ i ] - mean ) / ( i + 1 );
      }

      return mean;

    }

    public static Vector2 ComputeAverage<T>( List<T> containers, Func<T,Vector2> getPoint )
    {
      if ( containers == null || containers.Count == 0 )
      {
        return Vector2.Zero;
      }

      var mean = Vector2.Zero;

      for ( int i = 0; i < containers.Count; i++ )
      {
        mean += ( getPoint( containers[ i ] ) - mean ) / ( i + 1 );
      }

      return mean;
    }
  }
}