using Godot;

namespace Rokojori
{  
  public static class ColorX 
  {
    

    public static float AlphaMultipliedR( this Color c )
    {
      return c.R * c.A;
    }

    public static float AlphaMultipliedG( this Color c )
    {
      return c.G * c.A;
    }

    public static float AlphaMultipliedB( this Color c )
    {
      return c.B * c.A;
    } 

    public static float pmR( this Color c ) => c.AlphaMultipliedR();
    public static float pmG( this Color c ) => c.AlphaMultipliedG();
    public static float pmB( this Color c ) => c.AlphaMultipliedB();
    public static Color PreMultiply( this Color c )
    {
      return new Color( c.pmR(), c.pmG(), c.pmB(), c.A );
    }
    
    static float Clamp( float f )
    {
      return MathX.Clamp01( f ); 
    }

    public static Color Blend( Color bottom, Color top )
    {
      var fade = ( 1f - top.A );
      var a = top.A + bottom.A * fade;

      var r = top.pmR() + bottom.pmR() * fade;
      var g = top.pmG() + bottom.pmG() * fade;
      var b = top.pmB() + bottom.pmB() * fade;
      
      return new Color( r, g, b, 1 ) / a;
    }

    public static Color PreMultipliedSingleComponentBlendMode( Color bottom, Color top, System.Func<float,float,float, float> blending )
    {
      var fade = ( 1f - top.A );
      var a = top.A + bottom.A * fade;

      var r = blending( bottom.pmR(), top.pmR(), fade );
      var g = blending( bottom.pmG(), top.pmG(), fade );
      var b = blending( bottom.pmB(), top.pmB(), fade );
      
      return new Color( r, g, b, 1 ) / a;
    }

    public static Color PreMultipliedRGBBlendMode( Color bottom, Color top, System.Func<Color,Color,float, Color> blending )
    {
      var fade = ( 1f - top.A );
      var a = top.A + bottom.A * fade;

      var rgb = blending( bottom.PreMultiply(), top.PreMultiply(), fade );
      
      return new Color( rgb.R, rgb.G, rgb.B, 1 ) / a;
    }

    

    public static Color BlendMultiply( Color bottom, Color top )
    {
      return PreMultipliedSingleComponentBlendMode( bottom, top, 
      ( a, b, f)=>
      {
        return a * b;
      }
      );
    }

    public static Color BlendScreen( Color bottom, Color top )
    {
      return PreMultipliedSingleComponentBlendMode( bottom, top, 
      ( a, b, f)=>
      {
        return 1f - ( 1f - a ) * ( 1f - b);
      }
      );
    }

    public static Color BlendOverlay( Color bottom, Color top )
    {
      return PreMultipliedSingleComponentBlendMode( bottom, top, 
      ( a, b, f)=>
      {
        return a < 0.5f ? (2.0f * a * b) : (1.0f - 2.0f * (1.0f - a) * (1.0f - b)); 
      }
      );
    }

    public static Color BlendHardLight( Color bottom, Color top )
    {
      return PreMultipliedSingleComponentBlendMode( bottom, top, 
      ( a, b, f)=>
      {
        return b < 0.5f ? (2.0f * a * b) : (1.0f - 2.0f * (1.0f - a) * (1.0f - b)) ; 
      }
      );
    }

    public static Color BlendSoftLight( Color bottom, Color top )
    {
      return PreMultipliedSingleComponentBlendMode( bottom, top, 
      ( a, b, f)=>
      {
        return b < 0.5f ? (2.0f * a * b) : (1.0f - 2.0f * (1.0f - a) * (1.0f - b)) ; 
      }
      );
    }

    public static Color BlendColor( Color bottom, Color top )
    {
      var hslA = HSLColor.FromRGBA( bottom );
      var hslB = HSLColor.FromRGBA( top );

      var combined = bottom.A == 0 ? bottom : (Color)( new HSLColor( hslB.h, hslB.s, hslA.l, top.A ) );

      return ColorX.Blend( bottom, combined );
    }

    public static Color BlendHue( Color bottom, Color top )
    {
      var hslA = HSLColor.FromRGBA( bottom );
      var hslB = HSLColor.FromRGBA( top );

      var combined = new HSLColor( hslB.h, hslA.s, hslA.l, bottom.A );

      return ColorX.Lerp( bottom, combined, top.A );
    }

    public static Color BlendSaturation( Color bottom, Color top )
    {
      var hslA = HSLColor.FromRGBA( bottom );
      var hslB = HSLColor.FromRGBA( top );

      var combined = new HSLColor( hslB.h, hslA.s, hslA.l, bottom.A );

      return ColorX.Lerp( bottom, combined, top.A );
    }

    public static Color BlendAdd( Color bottom, Color top )
    {
      return PreMultipliedSingleComponentBlendMode( bottom, top, 
      ( b, t, f)=>
      {
        return b + t;
      }
      );
    }

    

    public static Color Lerp( Color a, Color b, float amount )
    {
      return a.Lerp( b, amount ); 
    }

    public static Vector3 RGB( Color c )
    {
      return new Vector3( c.R, c.G, c.B );
    }

    public static Vector2 RA( Color c )
    {
      return new Vector2( c.R, c.A);
    }

    public static Color Create( Vector3 rgb, float a )
    {
      return new Color( rgb.X, rgb.Y, rgb.Z, a );
    }

    public static Vector4 ToVector4( Color c )
    {
      return new Vector4( c.R, c.G, c.B, c.A );
    }

    public static Color FromVector4( Vector4 c )
    {
      return new Color( c.X, c.Y, c.Z, c.W );
    }

    public static Color Fade( Color color, float fade )
    {
      color.A = color.A * fade;
      return color;
    }

    public static Color FadeAlpha( this Color color, float fade )
    {
      color.A = color.A * fade;
      return color;
    }

    public static Color SetAlpha( Color color, float alpha )
    {
      color.A = alpha;
      return color;
    }
  
  }
}