// @name Versatile Noise Generator
// @param uType "Type (0:White, 1:Block, 2:Cloud, 3:Cells)" 0.0 0.0 3.0 1.0
// @param uScale "Scale/Density" 50.0 1.0 200.0 1.0
// @param uSpeed "Speed" 1.0 0.0 20.0 0.1
// @param uAlpha "Master Alpha" 1.0 0.0 1.0 0.01
// @param uContrast "Contrast" 1.0 0.5 5.0 0.1
// @param uThreshold "Threshold (Sparseness)" 0.0 0.0 0.95 0.01
// @param uR "Tint Red" 1.0 0.0 1.0 0.01
// @param uG "Tint Green" 1.0 0.0 1.0 0.01
// @param uB "Tint Blue" 1.0 0.0 1.0 0.01

// High quality hash function
float hash12(vec2 p) {
    vec3 p3  = fract(vec3(p.xyx) * .1031);
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.x + p3.y) * p3.z);
}

// 2D Noise
float noise(vec2 p) {
    vec2 i = floor(p);
    vec2 f = fract(p);
    f = f * f * (3.0 - 2.0 * f);
    float res = mix(
        mix(hash12(i), hash12(i + vec2(1.0, 0.0)), f.x),
        mix(hash12(i + vec2(0.0, 1.0)), hash12(i + vec2(1.0, 1.0)), f.x),
        f.y);
    return res;
}

// Fractal Brownian Motion for clouds
float fbm(vec2 p) {
    float total = 0.0;
    float amp = 0.5;
    for (int i = 0; i < 4; i++) {
        total += noise(p) * amp;
        p *= 2.0;
        amp *= 0.5;
    }
    return total;
}

// Voronoi/Cellular noise
float voronoi(vec2 x) {
    vec2 p = floor(x);
    vec2 f = fract(x);
    float res = 8.0;
    for(int j=-1; j<=1; j++)
    for(int i=-1; i<=1; i++) {
        vec2 b = vec2(float(i), float(j));
        vec2 r = vec2(b) - f + hash12(p + b);
        float d = dot(r, r);
        res = min(res, d);
    }
    return sqrt(res);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    // Setup UVs
    vec2 uv = fragCoord / iResolution.xy;
    vec2 aspectUV = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    
    float noiseVal = 0.0;
    float time = iTime * uSpeed;
    int type = int(uType);
    
    // --- Type 0: White Noise (Static) ---
    if (type == 0) {
        // We use fragCoord directly for pixel-perfect static
        // Scale controls "grain size" by floor dividing coords
        float grain = max(1.0, 10.0 - (uScale / 10.0)); // Invert scale for intuitive control
        vec2 pos = floor(fragCoord / grain);
        noiseVal = hash12(pos + vec2(time * 100.0, time * 50.0));
    }
    
    // --- Type 1: Block/Digital Noise ---
    else if (type == 1) {
        vec2 blockUV = floor(uv * uScale);
        noiseVal = hash12(blockUV + floor(time));
        
        // Add a subtle flicker
        noiseVal *= (0.8 + 0.2 * sin(time * 10.0));
    }
    
    // --- Type 2: Cloud/Smoke Noise (FBM) ---
    else if (type == 2) {
        vec2 p = aspectUV * (uScale / 10.0);
        vec2 motion = vec2(time * 0.1, time * 0.05);
        noiseVal = fbm(p + motion);
    }
    
    // --- Type 3: Cellular/Voronoi ---
    else if (type == 3) {
        vec2 p = aspectUV * (uScale / 5.0);
        // Animate cells slightly
        p += vec2(sin(time * 0.1), cos(time * 0.1));
        noiseVal = voronoi(p);
        noiseVal = 1.0 - noiseVal; // Invert so cells are bright
    }
    
    // --- Post-Processing the Noise ---
    
    // Apply contrast
    noiseVal = (noiseVal - 0.5) * uContrast + 0.5;
    
    // Apply threshold (make it sparse if needed)
    // Map [threshold, 1.0] to [0.0, 1.0]
    if (noiseVal < uThreshold) {
        noiseVal = 0.0;
    } else {
        noiseVal = (noiseVal - uThreshold) / (1.0 - uThreshold);
    }
    
    // Clamp result
    noiseVal = clamp(noiseVal, 0.0, 1.0);
    
    // --- Composition ---
    
    vec3 tint = vec3(uR, uG, uB);
    
    // Output format:
    // We output the color tint multiplied by the noise value.
    // The alpha channel is the master alpha * noise value.
    // This creates a layer that can be overlaid on other content.
    
    vec3 finalColor = tint * noiseVal;
    float finalAlpha = uAlpha * noiseVal;
    
    // Ensure we don't multiply alpha twice in pre-multiplied environments, 
    // but for general overlay shader logic:
    fragColor = vec4(finalColor, finalAlpha);
}