using Godot;

namespace Rokojori
{
  
  public struct HSLColor 
  {
    public static readonly HSLColor white = new HSLColor( new Color( 1, 1, 1, 1 ) );
    public static readonly HSLColor red   = new HSLColor( new Color( 1, 0, 0, 1 ) );
    public static readonly HSLColor green = new HSLColor( new Color( 0, 1, 0, 1 ) );
    public static readonly HSLColor blue  = new HSLColor( new Color( 0, 0, 1, 1 ) );
    public static readonly HSLColor black = new HSLColor( new Color( 0, 0, 0, 1 ) );

    // hue in degrees 0 - 360 
    public float h;
    // saturation 0 - 1
    public float s;
    // luminance 0 - 1 
    public float l;
    // alpha 0 - 1 
    public float a;
    
    
    public HSLColor( float h, float s, float l, float a ) 
    {
      this.h = h;
      this.s = s;
      this.l = l;
      this.a = a;
    }
    
    public HSLColor( float h, float s, float l ) 
    {
      this.h = h;
      this.s = s;
      this.l = l;
      this.a = 1f;
    }

    public float lumaBT601
    {
      get 
      {
        var rgb = ToRGBA();
        
        var R = rgb.R;
        var G = rgb.G;
        var B = rgb.B;

        return 0.2989f * R + 0.5870f *  G + 0.1140f * B;
      }
    }

    public float lumaBT709
    {
      get 
      {
        var rgb = ToRGBA();
        
        var R = rgb.R;
        var G = rgb.G;
        var B = rgb.B;

        return 0.2126f * R + 0.7152f * G + 0.0722f * B;
      }
    }
    
    public float normalizedHue
    {
      get { return h / 360f;}
    }

    public HSLColor( Color c ) 
    {
      HSLColor temp = FromRGBA( c );
      h = temp.h;
      s = temp.s;
      l = temp.l;
      a = temp.a;
    }
    
    public void ClampToLimits()
    {
      h = MathX.EnsureValidFloat( h );
      h = MathX.Repeat( h, 360 );
      s = MathX.Clamp01( s );
      l = MathX.Clamp01( l );
      a = MathX.Clamp01( a );
    }

    public static HSLColor FromRGBA( Color c ) 
    {		
      float h = 0;
      float s = 0; 
      float l = 0; 
      float a = c.A;

      
      float cmin = Mathf.Min( Mathf.Min( c.R, c.G ), c.B );
      float cmax = Mathf.Max( Mathf.Max( c.R, c.G ), c.B );
      
      l = ( cmin + cmax ) / 2f;
      
      if ( cmin == cmax ) 
      {
        s = 0;
        h = 0;
      } 
      else 
      {
        float delta = cmax - cmin;
        
        s = ( l <= 0.5f ) ? ( delta / ( cmax + cmin ) ) : 
                            ( delta / ( 2f - ( cmax + cmin ) ) );
        
        h = 0;
        
        if ( c.R == cmax ) 
        {
          h = ( c.G - c.B ) / delta;
        } 
        else if ( c.G == cmax ) 
        {
          h = 2f + ( c.B - c.R ) / delta;
        }
        else if ( c.B == cmax ) 
        {
          h = 4f + ( c.R - c.G ) / delta;
        }
        
        h = MathX.Repeat( h * 60f, 360f );
      }
      
      return new HSLColor( h, s, l, a );
    } 

    public static HSLColor Lerp( HSLColor x, HSLColor y, float t )
    {
      return Lerp( x, y, Vector4.One * t );
    }
    
    public static HSLColor Lerp( HSLColor x, HSLColor y, Vector4 t )
    {
      float h = MathX.LerpAngleDegrees( x.h, y.h, t.X );
      float s = Mathf.Lerp( x.s, y.s, t.Y );
      float l = Mathf.Lerp( x.l, y.l, t.Z );
      float a = Mathf.Lerp( x.a, y.a, t.W);

      return new HSLColor( h, s, l, a );
    }

    public static HSLColor LerpWithHueInRGB( HSLColor x, HSLColor y, float t )
    {
      return LerpWithHueInRGB( x, y, Vector4.One * t );
    }

    public static float BlendHueInRGB( HSLColor x, HSLColor y, float weight )
    {
      var rgbLerped = ColorX.Lerp( x.ToRGBA(), y.ToRGBA(), weight );
      rgbLerped = rgbLerped.Clamp();

      var hsl = HSLColor.FromRGBA( rgbLerped );

      return MathX.EnsureValidFloat( hsl.h );
    }

    public static HSLColor LerpWithHueInRGB( HSLColor x, HSLColor y, Vector4 v )
    {
      float h = BlendHueInRGB( x, y, v.X );
      float s = Mathf.Lerp( x.s, y.s, v.W );
      float l = Mathf.Lerp( x.l, y.l, v.Z );
      float a = Mathf.Lerp( x.a, y.a, v.W );

      return new HSLColor( h, s, l, a );
    }

    public static Color Lerp( Color x, Color y, float t )
    {
      var hx = new HSLColor( x );
      var hy = new HSLColor( y );

      var h = Lerp( hx, hy, t );
      
      return h.ToRGBA(); 
    }


    public Color ToRGBA()
    {
      var r = 0f;
      var g = 0f; 
      var b = 0f; 
      
      var m1 = 0f; 
      var m2 = 0f;

      m2 = ( l <= 0.5f ) ? ( l * ( 1f + s ) ) : ( l + s - l * s );
      m1 = 2f * l - m2;
      
      if ( s <= 0f ) 
      {
        r = g = b = l;
      } 
      else 
      {
        r = Value( m1, m2, h + 120f );
        g = Value( m1, m2, h );
        b = Value( m1, m2, h - 120f );
      }
      
      return new Color( r, g, b, a );
    }
    
    static float Value( float n1, float n2, float hue )
    {
      hue = MathX.Repeat( hue, 360f );
      
      if ( hue < 60f ) 
      {
        return n1 + ( n2 - n1 ) * hue / 60f;
      } 
      else if ( hue < 180f ) 
      {
        return n2;
      } 
      else if ( hue < 240f ) 
      {
        return n1 + ( n2 - n1 ) * ( 240f - hue ) / 60f;
      } 
      else 
      {
        return n1;
      }
    }
    
    public static implicit operator HSLColor( Color src ) 
    {
      return FromRGBA( src );
    }
    
    public static implicit operator Color( HSLColor src ) 
    {
      return src.ToRGBA( );
    }

  }

}