/********************************************************
    © 2020 Continuum Graphics LLC. All Rights Reserved
 ********************************************************/

#include "/../ContinuumLib/Syntax.glsl"

#define Composite3_glsl 33
#define DynamicShaderStage Composite3_glsl

varying vec2 texcoord;
varying float averageLuminance;

#if defined vsh

uniform sampler2D colortex4;
uniform sampler2D colortex0;

uniform vec2 viewDimensions;
uniform float frameTime;

#include "/../ContinuumLib/Debug.glsl"

#include "/../ContinuumLib/Utilities.glsl"

float SamplePreviousLuminance() {
    float part1 = texelFetch(colortex4, ivec2(0, 0), 0).a;
    float part2 = texelFetch(colortex4, ivec2(1, 0), 0).a;

    return DecodeME8(part1, part2);
}

float CalculateAverageLuminance() {
    const float res = 4.0;

    float totalWeight = 0.0;
    float currentAverage = 0.0;

    int LOD = int(log2(max(viewDimensions.x, viewDimensions.y) / res));

    for (float i = 0.0; i <= res; ++i){
        for (float j = 0.0; j <= res; ++j){
            vec2 samplePos = vec2(i, j) / res;
            float centerWeight = length(samplePos * 2.0 - 1.0);
            float weight = exp(-centerWeight * centerWeight);

            float avg = dot(DecodeRGBE8(textureLod(colortex0, samplePos, LOD)), vec3(0.2125, 0.7154, 0.0721));

            totalWeight += weight;
            currentAverage += log(clamp(avg, AUTO_EXPOSURE_MIN_LUM, AUTO_EXPOSURE_MAX_LUM)) * weight;
        }
    }

    float CurrentLuminanceAverage = exp(currentAverage / totalWeight);
    float previousAverage = SamplePreviousLuminance();

    return previousAverage + (CurrentLuminanceAverage - previousAverage) * (1.0 - exp(-frameTime * AUTO_EXPOSURE_TIME));
}

void main() {
    texcoord = gl_Vertex.xy;

    averageLuminance = CalculateAverageLuminance();

    gl_Position = ftransform();
}
#endif

#if defined fsh

uniform sampler2D colortex0;
uniform sampler2D colortex4;

#include "/../ContinuumLib/Debug.glsl"
#include "/../ContinuumLib/Utilities.glsl"

#include "/../ContinuumLib/Exit.glsl"
#include "/../ContinuumLib/Fragment/Camera.glsl"

const bool colortex0MipmapEnabled = true;

/* DRAWBUFFERS:034 */
layout (location = 0) out vec4 Buffer0;
layout (location = 1) out vec4 Buffer3;
layout (location = 2) out vec4 Buffer4;

float PackLuminance(float averageLuminance) {
    vec2 unpacked = EncodeME8(averageLuminance);
    float encoded = 0.0;

    if (gl_FragCoord.x < 1.0) {
        encoded = unpacked.x;
    } else if (gl_FragCoord.x < 2.0) {
        encoded = unpacked.y;
    }

    return encoded;
}

/*******************************************************************************
 - Lens Bokeh Generation
******************************************************************************/

vec3 LensBokeh(vec2 offset) {
    const float a  = TAU / CAMERA_BLADES;
    const mat2 rot = mat2(cos(a), -sin(a), sin(a), cos(a));

    const float hardness = LENS_HARDNESS;

    const vec3 size  = vec3(1.0 - vec2(LENS_SHIFT_AMOUNT, 0.5 * LENS_SHIFT_AMOUNT), 1.0);
    const vec3 size0 = size * hardness;
    const vec3 size1 = size0 * hardness * 0.5;

    float r = 0.0;
    const vec2 caddv = vec2(sin(BLADE_ROTATION), -cos(BLADE_ROTATION));
    vec2 addv = caddv;

    vec2 coord = (texcoord - offset) * 4.0;

    for(int i = 0; i < CAMERA_BLADES; ++i) {
        addv = rot * addv;
        r = max(r, dot(addv, coord));
    }

    r = mix(r, length(coord), BLADE_ROUNDING); // Brings the outter edges toward the center, rounding it out.
    r *= 1.5;
    if (r > 1.0) return vec3(0.0);
    float r2 = r * r;
    
    #if 1
        // Single Component - Uncorrected Spherical Lens
        vec3 bokeh = vec3(cos(r2 * 1.624835) * 0.767583 + sin(r2 * 1.624835) * 1.862321) * exp(-0.862325 * r2);
    #else
        #if 1
            const int numComponents = 1;
            // A, B, R, I,
            vec4 P[numComponents] = vec4[numComponents](vec4(-1.6, 0.8, 1.4, 1.4));
            float CP[numComponents] = float[numComponents](0.0);
        #elif 1
            const int numComponents = 2;
            // A, B, R, I,
            vec4 P[numComponents] = vec4[numComponents](vec4(5, 1.3, 0.3141, 0.21),
                                                        vec4(-1, 2.3, 4, 30));
            float CP[numComponents] = float[numComponents](0.0, 0.0);
        #endif

        for(int C = 0; C < numComponents; ++C) {
            float a = -P[C].x;
            float b = -P[C].y;
            float c = (sqrt(2.0) * (sqrt(P[C].z * P[C].z + P[C].w * P[C].w) + P[C].z) * sqrt(sqrt(P[C].z * P[C].z + P[C].w * P[C].w) - P[C].z)) / (2.0 * P[C].w);
            float d = (-sqrt(2.0) * sqrt(sqrt(P[C].z * P[C].z + P[C].w * P[C].w) - P[C].z)) / 2.0;

            float cosRRA = cos(r2 * a);
            float sinRRA = sin(r2 * a);
            float expRRB = exp(r2 * b);
            
            float real_coef = (c * cosRRA - d * sinRRA * expRRB;
            float imag_coef = (d * cosRRA + c * sinRRA * expRRB;

            CP[C] = real_coef + imag_coef;
            bokeh += vec3(CP[C]);

        }
    #endif

    return bokeh;
}

void main() {
    #ifdef DOF
	    Buffer0 = vec4(LensBokeh(vec2(0.5, 0.5)), 1.0);
    #endif

    vec3 color = DecodeRGBE8(texture(colortex0, texcoord));
    float EV = ComputeEV(averageLuminance * 2.0);

    color *= EV;

    vec3 buffer4Passthrough = texture(colortex4, texcoord).rgb;

    Buffer3 = vec4(EncodeColor(color), EV);
    Buffer4 = vec4(buffer4Passthrough, PackLuminance(averageLuminance));
}

#endif