/*
====================================================================================================

    Copyright (C) 2020 RRe36

    All Rights Reserved unless otherwise explicitly stated.


    By downloading this you have agreed to the license and terms of use.
    These can be found inside the included license-file
    or here: https://rre36.com/copyright-license

    Violating these terms may be penalized with actions according to the Digital Millennium
    Copyright Act (DMCA), the Information Society Directive and/or similar laws
    depending on your country.

====================================================================================================
*/

vec2 computeVariance(sampler2D tex, ivec2 pos) {
    float sum_msqr  = 0.0;
    float sum_mean  = 0.0;

    for (int i = 0; i<9; i++) {
        ivec2 deltaPos     = kernelO_3x3[i];
        //float weight        = kernelW_5x5[i];

        vec3 col    = texelFetch(tex, pos + deltaPos, 0).rgb;
        float lum   = getLuma(col);

        sum_msqr   += sqr(lum);
        sum_mean   += lum;
    }
    sum_msqr /= 9.0;
    sum_mean /= 9.0;

    return vec2(abs(sum_msqr - sqr(sum_mean)) * rcp(max(sum_mean, 1e-20)), sum_mean);
}

#define atrousLumaExp 6.0
#define atrousLumaOffset 0.66

#define atrousDepthTreshold 0.001
#define atrousDepthOffset 0.5
#define atrousDepthExp 64.0

#define atrousNormalExponent 16

vec4 atrous3x3(sampler2D tex, sampler2D depth, sampler2D normals, vec2 coord, const int size) {
    if (clamp(coord, 0.0, indirectResScale) != coord) return vec4(0.0);
    ivec2 pixelPos  = ivec2(floor(coord * viewSize));

    float expDepthCenter = texelFetch(depth, ivec2(pixelPos / indirectResScale), 0).x;

    vec4 colorCenter    = texelFetch(tex, pixelPos, 0);
    //return colorCenter;
    float centerLuma    = getLuma(colorCenter.rgb);

    if (!landMask(expDepthCenter)) return colorCenter;

    float depthCenter   = depthLinear(expDepthCenter) * far;
    vec3 normalCenter   = texelFetch(normals, ivec2(pixelPos / indirectResScale), 0).xyz * 2.0 - 1.0;

    #define kernelMidIndex 4

    vec4 totalColor     = colorCenter /* kernelW_3x3[kernelMidIndex]*/;
    float totalWeight   = 1.0;

    vec2 variance       = computeVariance(tex, pixelPos);

    //float sigmaL        = rcp(atrousLumaExp * variance.x + atrousLumaOffset);

    float sigmaL        = rcp(0.5 + atrousLumaExp * variance.x);
    float maxLumDelta   = halfPi;

    vec3 viewDir        = normalize(screenToViewSpace(vec3(coord, 1.0)));
    vec3 viewNorm       = mat3(gbufferModelView) * normalCenter;

    float depthExp      = 10.0 * (cube(saturate(-dot(viewNorm, -viewDir))) * 0.75 + 0.25);

    for (int i = 0; i < 9; i++) {
        if (i == kernelMidIndex) continue;
        ivec2 deltaPos     = kernelO_3x3[i] * size;
    //for (int i = 0; i<25; i++) {
    //    ivec2 deltaPos      = kernelO_5x5[i] * size;
    //    if (deltaPos.x == 0 && deltaPos.y == 0) continue;

        ivec2 currPos   = pixelPos + deltaPos;
        if (clamp(currPos, ivec2(0), ivec2(viewSize)-1) != currPos) continue;

        vec3 normalCurr = texelFetch(normals, ivec2(currPos / indirectResScale), 0).xyz * 2.0 - 1.0;

        float normalWeight = pow(saturate(dot(normalCurr, normalCenter)), 16.0);

        if (normalWeight < 1e-5) continue;

        float weight    = normalWeight;

        float depthCurr = texelFetch(depth, ivec2(currPos / indirectResScale), 0).x;
            depthCurr   = depthLinear(depthCurr) * far;

        float depthDelta = abs(depthCurr - depthCenter) * depthExp;

        float depthWeight = expf(-depthDelta);

        if ((depthWeight < 1e-5 || depthCurr == 1.0)) continue;

        vec4 currentColor  = texelFetch(tex, currPos, 0);

        //float weight = normalWeight * depthWeight;

        float currentLuma   = getLuma(currentColor.rgb);

        float lumaDelta     = abs(centerLuma - currentLuma) / max(variance.y, 1e-12);

        weight         *= exp(-depthDelta - sigmaL * clamp(lumaDelta, 0.1, 1.0));

        totalColor.rgb += currentColor.rgb * weight;
        totalWeight += weight;
    }

    totalColor.rgb *= rcp(totalWeight);
    totalColor.a    = colorCenter.a;

    //return colorCenter;
    return vec4(totalColor);
}