#[compute]
#version 450

layout( local_size_x = 8, local_size_y = 8, local_size_z = 1 ) in;

layout( rgba16f, set = 0, binding = 0 ) 
uniform image2D inputImage;
layout( rgba16f, set = 1, binding = 0 ) 
uniform image2D outputImage;


layout( push_constant, std430 ) 
uniform Parameters 
{
  vec4 h;
  vec4 s;
  vec4 l; 
  vec4 pad;
  float amount;
  float pad0;
  float pad1;
  float pad2;


} parameters;


vec3 RGBtoHSL( vec3 c )
{
  float h = 0.0;
  float s = 0.0; 
  float l = 0.0; 
  
  float cmin = min( min( c.r, c.g ), c.b );
  float cmax = max( max( c.r, c.g ), c.b );
  
  l = ( cmin + cmax ) / 2.0;
  
  if ( cmin == cmax ) 
  {
    s = 0.0;
    h = 0.0;
  } 
  else 
  {
    float delta = cmax - cmin;
    
    s = ( l <= 0.5 ) ? ( delta / ( cmax + cmin ) ) : ( delta / ( 2.0 - ( cmax + cmin ) ) );
    
    h = 0.0;
    
    if ( c.r == cmax ) 
    {
      h = ( c.g - c.b ) / delta;
    } 
    else if ( c.g == cmax ) 
    {
      h = 2.0 + ( c.b - c.r ) / delta;
    }
    else if ( c.b == cmax ) 
    {
      h = 4.0 + ( c.r - c.g ) / delta;
    }
    
    h = h / 6.0;

    if ( h > 1.0 )
    {
      h -= 1.0;
    }

  }

  return vec3( h, s, l );
}

vec3 hueToRGB( float hue )
{
  float R = abs( hue * 6.0 - 3.0 ) - 1.0;
  float G = 2.0 - abs( hue * 6.0 - 2.0 );
  float B = 2.0 - abs( hue * 6.0 - 4.0 );
  return clamp( vec3( R,G,B ), 0, 1 );
}

vec3 HSLtoRGB( vec3 hsl )
{
  if ( hsl.y == 0.0 )
  {
    return vec3( hsl.z, hsl.z, hsl.z );
  }

  vec3 rgb = hueToRGB( hsl.x );
  float C = ( 1.0 - abs( 2.0 * hsl.z - 1.0 )) * hsl.y;
  return ( rgb - 0.5 ) * C + hsl.z;
}

vec3 SRGBtoLINEAR( vec3 sRGB )
{
  return mix( pow( (sRGB + vec3( 0.055 )) * ( 1.0 / ( 1.0 + 0.055 )),vec3( 2.4 )),sRGB * ( 1.0 / 12.92 ),lessThan( sRGB,vec3( 0.04045 )) );
}

vec4 SRGBtoLINEAR( vec4 sRGB )
{
  return vec4( SRGBtoLINEAR( sRGB.rgb ), sRGB.a );
}

vec3 LINEARtoSRGB( vec3 linear )
{
  vec3 color = linear;
  const vec3 a = vec3(0.055f);
	return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}

vec4 LINEARtoSRGB( vec4 linear )
{
  return vec4( LINEARtoSRGB( linear.rgb ), linear.a );
}

void main( ) 
{
  ivec2 size = imageSize( inputImage );
  ivec2 texel_coord = ivec2( gl_GlobalInvocationID.xy );
  
  if ( any( greaterThanEqual( texel_coord, size ) )  ) 
  {
    return;
  }


  vec4 color = imageLoad( inputImage, texel_coord ); 
  
  vec4 sRGB = LINEARtoSRGB( color );
  float scale = max( 1.0, max( sRGB.r, max( sRGB.g, sRGB.b ) ) );
  vec4 scaledColor = sRGB / scale;

  vec3 hsl = RGBtoHSL( scaledColor.rgb );

  vec3 offsetX = vec3( parameters.h.x * parameters.h.w, parameters.s.x * parameters.s.w, parameters.l.x * parameters.l.w );
  vec3 offsetY = vec3( parameters.h.y * parameters.h.w, parameters.s.y * parameters.s.w, parameters.l.y * parameters.l.w );
  vec3 offsetZ = vec3( parameters.h.z * parameters.h.w, parameters.s.z * parameters.s.w, parameters.l.z * parameters.l.w );

  vec3 lerpedOffset = hsl.z < 0.5 ? mix( offsetX, offsetY, hsl.z * 2.0 ) :
                                 mix( offsetY, offsetZ, ( hsl.z - 0.5 ) * 2.0 );

  vec3 lerpedHSL = hsl;
  lerpedHSL.x = mod( lerpedHSL.x + lerpedOffset.x, 1.0 );
  lerpedHSL.y = clamp( lerpedHSL.y + lerpedOffset.y, 0.0, 1.0 );
  lerpedHSL.z = clamp( lerpedHSL.z + lerpedOffset.z, 0.0, 1.0 );

  vec4 processedColor = vec4( HSLtoRGB( lerpedHSL ) * scale, color.a );
  processedColor = SRGBtoLINEAR( processedColor );


  vec4 blended = mix( color, processedColor, parameters.amount );


  imageStore( outputImage, texel_coord, blended );
}