#[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 pad0;

  float kernelRadiusX;
  float kernelRadiusY;
  float pad1;
  float pad2;  

} parameters;

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 );
}

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 computeGaussianWeight( float x, float y, float sigma )
{
  return exp( - ( x * x + y * y ) / ( 2.0 * sigma * sigma ));
}



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

  vec4 color = vec4( 0.0 );

  int kernelRadiusX = int( parameters.kernelRadiusX );
  int kernelRadiusY = int( parameters.kernelRadiusY );

  float kernelOffset = parameters.kernelOffset;

  
  int xS = -kernelRadiusX; 
  int xE =  kernelRadiusX;
  
  int yS = -kernelRadiusY; 
  int yE =  kernelRadiusY;

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

  float sigma = float( max( kernelRadiusX, kernelRadiusY ) ) * 0.5;

  float sumWeights = 0.0;

  if ( parameters.noise > 0.0 )
  {
    for ( int x = xS; x <= xE; x++ ) 
    {
      for ( int y = yS; y <= yE; y++ ) 
      {      
        vec2 xy = vec2( x, y ) * kernelOffset;
        vec2 kernelUV = vec2( pixelUV + vec2( 0.5 ) ) + xy;

        float weight = computeGaussianWeight( float( x ), float( y ), sigma ); 

        kernelUV += vec2( random( kernelUV ) * parameters.noise );

        color += ( textureLod( inputImage, kernelUV * toUV , 0 ) ) * weight;

        sumWeights += weight;
      }
    }
  }
  else
  {
    for ( int x = xS; x <= xE; x++ ) 
    {
      for ( int y = yS; y <= yE; y++ ) 
      {      
        vec2 xy = vec2( x, y ) * kernelOffset;
        vec2 kernelUV = vec2( pixelUV + vec2( 0.5 ) ) + xy;

        float weight = computeGaussianWeight( float( x ), float( y ), sigma ); 

        color += ( textureLod( inputImage, kernelUV * toUV , 0 ) ) * weight;

        sumWeights += weight;
      }
    }
  }

  color /= sumWeights;

  color = ( color );

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

  vec4 originalColor = textureLod( inputImage, uv, 0 );
  vec4 mixedColor = mix( originalColor, color, parameters.intensity );

  imageStore( outputImage, pixelUV, mixedColor );
}