#[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 
{
  float amount;
  float h;
  float t;
  float s;

  float sp;
  float l;
  float lp;
  float hslMode;

  float tBlendRadius;
  float p0;
  float p1;
  float p2;

} 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 modPolarDegrees( float value )
{
  return mod( value + 180.0, 360.0 ) - 180.0;
}


vec3 changeHueOfRGB( vec3 rgb, float hueChange )
{
  vec3 hsl = RGBtoHSL( rgb );
  hsl.x = mod( hsl.x + hueChange, 1.0 ) ;

  return HSLtoRGB( hsl );
}

float hue360ToYB( float hue )
{
  return modPolarDegrees( hue - 240.0 );
}

float ybToHue360( float yb )
{
  return mod( yb + 240.0, 360.0 );
}

float changeHue360( float hue, float change )
{
  float yb = hue360ToYB( hue );

  float absYB = abs( yb );

  absYB = clamp( absYB + change, 0.0, 180.0 );

  float realYB = sign( yb ) * absYB;

  return ybToHue360( realYB );
}

float changeHue360Blended( float hue, float change, float blendRadius )
{
  float distanceToYellow = min( 1.0, abs( hue - 60.0 ) / blendRadius );
  float distanceToBlue   = min( 1.0, abs( hue - 240.0 ) / blendRadius );

  return changeHue360( hue, change * distanceToYellow * distanceToBlue );
}

vec3 shiftHSL360( vec3 hslWithH360, vec3 offset, float blendRadius )
{
  float distanceToYellow = min( 1.0, abs( hslWithH360.x - 60.0 ) / blendRadius );
  float distanceToBlue   = min( 1.0, abs( hslWithH360.x - 240.0 ) / blendRadius );

  hslWithH360.x = changeHue360( hslWithH360.x, offset.x * distanceToYellow * distanceToBlue );
  hslWithH360.y = clamp( hslWithH360.y + offset.y, 0, 1 );
  hslWithH360.z = clamp( hslWithH360.z + offset.z, 0, 1 );

  return hslWithH360;
}

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;

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

    int hslMode = int( round( parameters.hslMode ) );
    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 = mod( hsl.r + parameters.h, 1.0 );
    hsl.r = changeHue360Blended( hsl.r * 360.0, parameters.t, parameters.tBlendRadius) / 360.0; 
    hsl.g = clamp( pow( hsl.g, parameters.sp ) + parameters.s, 0.0, 1.0 );
    hsl.b = clamp( pow( hsl.b, parameters.lp ) + parameters.l, 0.0, 1.0 );

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


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


    imageStore( outputImage, texel_coord, blended );
}