#[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 r;
  vec4 g;
  vec4 b;
  vec4 rgb;

  vec4 h;
  vec4 s;
  vec4 l;
  vec4 settings;

} 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;
    float both = cmax + cmin;
    
    s = ( l <= 0.5 ) ? 
        ( both == 0.0 ? 0.0 : ( delta / both ) ) : 
        ( delta / ( 2.0 - both ) );
    
    h = 0.0;
    
    if ( c.r == cmax ) 
    {
      h = delta == 0.0 ? 0.0 : ( ( c.g - c.b ) / delta );
    } 
    else if ( c.g == cmax ) 
    {
      h = delta == 0.0 ? 0.0 : ( 2.0 + ( c.b - c.r ) / delta );
    }
    else if ( c.b == cmax ) 
    {
      h = delta == 0.0 ? 0.0 : ( 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;
}

float applyGamma( float x, float power )
{
  if ( x <= 0.0 )
  {
    return 0.0;
  }

  return pow( x, power );
}

float apply( float x, vec4 parameters )
{
  float amount = parameters.x;
  float steps = parameters.y;
  float power = parameters.z;
  float off = parameters.w;

  float y = applyGamma( x + off, power );
  y = round( y * steps ) / steps;
  return mix( x, applyGamma( y, 1.0/ power ) - off, amount );
}

vec4 applyRGB( vec4 v, vec4 parameters )
{
  v.x = apply( v.x, parameters );
  v.y = apply( v.y, parameters );
  v.z = apply( v.z, parameters );

  return v;
}


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

    vec2 uv = ( vec2( texel_coord ) + 0.5 ) / vec2( size );    

    vec4 color = imageLoad( inputImage, ivec2( uv * size ) ); 

    vec4 rgb = color;
    rgb.r = apply( rgb.r, parameters.r );
    rgb.g = apply( rgb.g, parameters.g );
    rgb.b = apply( rgb.b, parameters.b );

    rgb = applyRGB( rgb, parameters.rgb ); 

    int NONE = 0;
    int SCALE = 1;
    int CLAMP = 2;

    int hslMode = int( round( parameters.settings.y ) );
    float scaler = 1.0;
    
    if ( hslMode == SCALE ) 
    {
      scaler = max( max( max( rgb.r, rgb.g ), rgb.b ), 1.0 );
    }

    if ( hslMode == CLAMP )
    {
      rgb.rgb = clamp( rgb.rgb, 0.0, 1.0 );
    }

    vec3 hsl = RGBtoHSL( rgb.rgb / scaler );
    hsl.r = apply( hsl.r, parameters.h );
    hsl.g = apply( hsl.g, parameters.s );
    hsl.b = apply( hsl.b, parameters.l );

    vec4 posterColor = vec4( HSLtoRGB( hsl ) * scaler, color.a );


    vec4 blended = mix( color, posterColor, parameters.settings.x );


    imageStore( outputImage, texel_coord, blended );
}