#[compute]
#version 450

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

layout( set = 0, binding = 0 ) 
uniform sampler2D inputImage;

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


layout( push_constant, std430 ) 
uniform Parameters 
{
  float intensity;
  float noise;
  float kernelOffset;
  float kernelRadius;  

  float lumaTreshold;
  float lumaScale;
  float p2;
  float p3;

} parameters;


float randomFloat( vec2 uv ) 
{
  return fract( sin( dot( uv.xy, vec2( 12.9898,78.233 ) ) ) * 43758.5453123 );
}

vec2 random( vec2 uv ) 
{
	float x = randomFloat( uv );
  float y = randomFloat( uv + vec2( 10002, -23589 ) );
 
  return vec2( x, y ) * 2.0 - vec2( 1.0, 1.0 );
}


float fastGaussianWeight( vec2 p, float radius ) 
{
  float r = length( p ) / radius;
  return max( 0.0, 1.0 - r * r );
}

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

  vec4 color = vec4( 0.0 );

  int kernelRadius = int( parameters.kernelRadius );
  int kernelOffset = int( parameters.kernelOffset );

  vec2 toUV = vec2( 1.0 ) / vec2( size );

  float weights = 0.0;
  
  if ( parameters.noise > 0.0 )
  {
    for ( int x = -kernelRadius; x <= kernelRadius; x++ ) 
    {
      for ( int y = -kernelRadius; y <= kernelRadius; y++ ) 
      {      
        ivec2 kernelXYOffset = ivec2( x, y ) * kernelOffset;
        ivec2 kernelXY = xy + kernelXYOffset;

        kernelXY += ivec2( random( kernelXY ) * parameters.noise );

        kernelXY.x = clamp( kernelXY.x, 0, size.x - 1 );
        kernelXY.y = clamp( kernelXY.y, 0, size.y - 1 );

        float weight = fastGaussianWeight( vec2( kernelOffset ), kernelRadius );
        color += ( texture( inputImage, kernelXY * toUV ) ) * weight;
        weights += weight;

      }
    }
  }
  else
  {
    for ( int x = -kernelRadius; x <= kernelRadius; x++ ) 
    {
      for ( int y = -kernelRadius; y <= kernelRadius; y++ ) 
      {      
        ivec2 kernelXYOffset = ivec2( x, y ) * kernelOffset;
        ivec2 kernelXY = xy + kernelXYOffset;

        kernelXY.x = clamp( kernelXY.x, 0, size.x - 1 );
        kernelXY.y = clamp( kernelXY.y, 0, size.y - 1 );

        float weight = fastGaussianWeight( vec2( kernelOffset ), kernelRadius );
        color += ( texture( inputImage, kernelXY * toUV ) ) * weight;
        weights += weight;
      }
    }
  }


  color /= weights;

  vec4 originalColor = imageLoad( outputImage, xy );

  vec4 mixedColor = originalColor;   

  float luma = length( color.rgb );

  float lumaAmount = max( luma - parameters.lumaTreshold, 0.0 ) * parameters.lumaScale;

  mixedColor.rgb += color.rgb * lumaAmount * parameters.intensity;

  // mixedColor = color;

  imageStore( outputImage, xy, mixedColor );
}