#[compute]
#version 450

#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Colors.gdshaderinc"

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 noiseSeed;

  float pixelSizeX;
  float pixelSizeY;
  float amount;
  float channel;

  float noiseAmount;
  float noiseScale;
  float offsetX;
  float offsetY;

} parameters;

int quantize( int value, int quantization )
{
  return ( value / quantization ) * quantization; 
}

ivec2 quantize( ivec2 value, ivec2 quantization )
{
  return ivec2( quantize( value.x, quantization.x ), quantize( value.y, quantization.y ) );
}

int mirrorValue( int x, int size ) 
{
  int t = x % ( size * 2 ); 
  return t < size ? t : ( 2 * size )  - t;
}

ivec2 mirrorUV( ivec2 uv, ivec2 size ) 
{    
  return ivec2( mirrorValue( uv.x, size.x ), mirrorValue( uv.y, size.y ) );
}


float clamp01( float value )
{
  return clamp( value, 0.0, 1.0 );
}

float normalizeToRange( float value, float min, float max )
{
  return ( value - min ) / ( max - min );
}

float normalizeToRange01( float value, float min, float max )
{
  return clamp01( normalizeToRange( value, min, max ) );
}

float map( float value, float inMin, float inMax, float outMin, float outMax )
{
  return mix( outMin, outMax, normalizeToRange( value, inMin, inMax ) );
}

float mapClamped( float value, float inMin, float inMax, float outMin, float outMax )
{
  return mix( outMin, outMax, normalizeToRange01( value, inMin, inMax ) );
}

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

vec2 random_v2( vec2 uv ) 
{
  uv = vec2
  ( 
    dot(uv, vec2( 127.1,311.7 ) ),
    dot(uv, vec2( 269.5,183.3 ) ) 
  );
  
  return -1.0 + 2.0 * fract( sin( uv ) * 43758.5453123 );
}


vec3 random_v3( vec3 uvw )
{
	
  uvw = vec3( dot(uvw, vec3(127.1,311.7, 513.7) ),
              dot(uvw, vec3(269.5,183.3, 396.5) ),
        dot(uvw, vec3(421.3,314.1, 119.7) ) );
    
  return -1.0 + 2.0 * fract(sin(uvw) * 43758.5453123);
}

float perlin(vec2 uv) 
{
  vec2 uv_index = floor(uv);
  vec2 uv_fract = fract(uv);

  vec2 blur = smoothstep(0.0, 1.0, uv_fract);

  return mix( mix( dot( random_v2(uv_index + vec2(0.0,0.0) ), uv_fract - vec2(0.0,0.0) ),
                    dot( random_v2(uv_index + vec2(1.0,0.0) ), uv_fract - vec2(1.0,0.0) ), blur.x),
              mix( dot( random_v2(uv_index + vec2(0.0,1.0) ), uv_fract - vec2(0.0,1.0) ),
                    dot( random_v2(uv_index + vec2(1.0,1.0) ), uv_fract - vec2(1.0,1.0) ), blur.x), blur.y) + 0.5;
}

float mirrorValue( float x ) 
{
    float t = mod( x, 2.0 );
    return ( t <= 1.0 ) ? t : 2.0 - t;
}

vec2 mirrorUV( vec2 uv ) 
{    
  return vec2( mirrorValue( uv.x ), mirrorValue( uv.y ) );
}


void main() 
{
  ivec2 xy   = ivec2( gl_GlobalInvocationID.xy );
  ivec2 size = imageSize( outputImage );

  if ( any( greaterThanEqual( xy, size ) )  ) 
  {
    return;
  }

  vec4 originalColor = imageLoad( inputImage, xy );
  vec4 originalUnmodified = originalColor;
  
  vec2 uv = ( vec2( xy ) + 0.5 ) / vec2( size );

  int initialPixelsX = int( parameters.pixelSizeX );
  int initialPixelsY = int( parameters.pixelSizeY );

  ivec2 pixelXY = quantize( xy, ivec2( initialPixelsX, initialPixelsY ) );

  float noiseX = perlin( pixelXY * parameters.noiseScale + vec2( parameters.noiseSeed.x, parameters.noiseSeed.y ) );
  float noiseY = perlin( pixelXY * parameters.noiseScale + vec2( parameters.noiseSeed.z, parameters.noiseSeed.w ) );

  noiseX = mix( 1.0, noiseX, parameters.noiseAmount );
  noiseY = mix( 1.0, noiseY, parameters.noiseAmount );

  int pixelsX = int( parameters.pixelSizeX * noiseX );
  int pixelsY = int( parameters.pixelSizeY * noiseY );

  ivec2 off = ivec2( parameters.offsetX * pixelsX, parameters.offsetY * pixelsY );

  pixelXY = quantize( xy + off, ivec2( pixelsX, pixelsY ) );
  pixelXY = mirrorUV( pixelXY, size );

  vec4 color = imageLoad( inputImage, pixelXY );

  int colorChannel = int( parameters.channel );

  float colorScale = 1.0;
  float colorScale2 = 1.0;

  int editingChanngel = colorChannel;

  if ( colorChannel != 6 && colorChannel >= 3 )
  {
    editingChanngel -= 3;

    colorScale = getRGBScale( color );
    colorScale2 = getRGBScale( originalColor );

    color = RGBtoHSL( color, colorScale );
    originalColor = RGBtoHSL( originalColor, colorScale2 );
  }

  if ( colorChannel != 6 )
  { 
    float value = getRGBByIndex( color.rgb, editingChanngel );  
    color.rgb = setRGBByIndex( originalColor.rgb, editingChanngel, value );
  }
  
  if ( colorChannel != 6 && colorChannel >= 3 )
  {
    color = HSLtoRGB( color, colorScale );
    originalColor = HSLtoRGB( originalColor, colorScale2 );
  }

  

  color = mix( originalUnmodified, color, parameters.amount );


  imageStore( outputImage, xy, color);
}
