uniform vec3 fogColor;

uniform float blindness;
uniform float VFAmount;
uniform float darknessFactor;
uniform float darknessLightFactor;
uniform float eyeSmoothNorm;
uniform ivec2 eyeBrightnessSmooth;
#include "/lib/shadow_param.glsl"

const float dirtAmount = 0.005;
const vec3 waterEpsilon = vec3(0.25422, 0.03751, 0.01150);
const vec3 dirtEpsilon = vec3(0.65, 0.85, 1.05);
const vec3 totEpsilon = dirtEpsilon * dirtAmount + waterEpsilon;
const vec3 scatterCoef = dirtAmount * vec3(0.6);

float GetBorderFogMixFactor(in vec3 eyePlayerPos, in float far, in float z0) {
// Return 0 if the z0 value is 1.0
#ifndef waterfog
    if (z0 == 1.0) {
        return 0.0;
    }
#endif
    float eyeDist = length(eyePlayerPos);
    float borderFogFactor = smoothstep(far * 0.7, far, eyeDist);
    borderFogFactor *= borderFogFactor * borderFogFactor;
    return (eyeDist > far * 0.7) ? borderFogFactor : 0.0;
}
float phaseRayleigh(float cosTheta) {
    vec2 mul_add = vec2(0.18, 0.51);
    return cosTheta * mul_add.x + mul_add.y;
}
float phaseg(float x, float g) {
    float gg = g * g;
    return (gg * -0.25 + 0.25) * pow(-2.0 * (g * x) + (gg + 1.0), -1.5) / pi;
}
#define ATMOSPHERIC_DENSITY 1.0 //[0.0 0.5 1.0 1.5 2.0 2.5 3.0 4.0 5.0 7.5 10.0 12.5 15.0 20.]

float cloudVol(in float pos) {

    float unifCov = exp2(-max(pos - SEA_LEVEL, 0.0) / 50.0);
    float cloud = 1 * VFAmount * 600 + unifCov * 60. * fogAmount + (rainStrength * 0.05);
    return cloud;
}
#ifdef VOLUMETRIC_LIGHTING

uniform sampler2DShadow shadow;
uniform sampler2D noisetex;
uniform mat4 shadowModelView;
uniform mat4 shadowProjection;
#define diagonal3(m) vec3((m)[0].x, (m)[1].y, m[2].z)

#define VL_SAMPLES 8 //[4 6 8 10 12 14 16 20 24 30 40 50]
vec3 toShadowSpaceProjected(vec3 p3) {
    p3 = mat3(gbufferModelViewInverse) * p3 + gbufferModelViewInverse[3].xyz;
    p3 = mat3(shadowModelView) * p3 + shadowModelView[3].xyz;
    p3 = diagonal3(shadowProjection) * p3 + shadowProjection[3].xyz;

    return p3;
}
float blueNoise() {
    return fract(texelFetch2D(noisetex, ivec2(gl_FragCoord.xy) % 512, 0).a + 1.0 / 1.6180339887 * frameCounter);
}

#endif
vec4 getVolumetricRays(vec4 color, vec3 fragpos, vec3 ambientUp, vec3 scatterCoef, vec3 totEpsilon, vec4 sunColor2, float z, vec3 sky) {

    vec3 absorbance = vec3(1.0);
    vec3 vL = vec3(0.0);
    //vec3 dVWorld = ((mat3(gbufferModelViewInverse) * fragpos + gbufferModelViewInverse[3].xyz) - gbufferModelViewInverse[3].xyz);
    vec3 dVWorld = mat3(gbufferModelViewInverse) * fragpos;

    float SdotV = max(dot(sunPosition, normalize(fragpos)), 0.0) * 0.01 * sunColor2.a;
    vec3 sunColor3 = sunColor2.rgb * 10 * eyeSmoothNorm;
    vec3 skyCol0 = ambientUp * eyeSmoothNorm;
    float dfactor = 1 - (blindness + darknessFactor);
    float dfactor2 = blindness + darknessFactor * 0.75;

    sunColor3 *= dfactor;
    skyCol0 *= dfactor;
    float borderFogFactor = GetBorderFogMixFactor(dVWorld, far, z);

    dVWorld *= min(length(dVWorld), far) / length(dVWorld);

    float dL = length(dVWorld);
    float rayL = phaseRayleigh(SdotV);
    vec3 mC = vec3(0.0000030);
    vec3 fcolor = fogColor;
    #if defined END
    fcolor = vec3(1.0, 0.65, 0.43);
#endif
    switch (isEyeInWater) {
        case 0:

            float densityMod = ATMOSPHERIC_DENSITY + (40000 * dfactor2);
            float sh = 1.0;
            vec3 scolor = skyColor;
            float lumC = luma(scolor);
            vec3 diff = scolor - lumC;
            scolor = scolor + diff * (-lumC * 0.5 + 0.5);
            vec3 rC = mix(vec3(5.8 * 1e-6, 1.35 * 1e-5, 3.31 * 1e-5), vec3(toLinear(scolor) * 1e-5), 0.5);

#ifdef VOLUMETRIC_LIGHTING
            float multiplier = 6;
            float dither = blueNoise();
            dither = mix(dither, 0.5, float(z >= 1));

            vec3 wpos = mat3(gbufferModelViewInverse) * fragpos + gbufferModelViewInverse[3].xyz;
            vec3 fragposition = mat3(shadowModelView) * wpos + shadowModelView[3].xyz;
            fragposition = diagonal3(shadowProjection) * fragposition + shadowProjection[3].xyz;

            vec3 start = toShadowSpaceProjected(vec3(0.0));
            vec3 dV = (fragposition - start);
            vec3 progress = start.xyz;
            float samples = mix(VL_SAMPLES, 1, float(z >= 1));
            for (int i = 0; i < samples; i++) {
                float d = (pow(11.0, float(i + dither) / float(samples)) / 11.0 - 1.0 / 11.0) / (1 - 1.0 / 11.0);
                float dd = pow(11.0, float(i + dither) / float(samples)) * log(11.0) / float(samples) / (11.0 - 1.0);
                progress = start.xyz + d * dV;
                float progressW = gbufferModelViewInverse[3].y + cameraPosition.y + d + dVWorld.y;

			//project into biased shadowmap space
                float distortFactor = calcDistort(progress.xy);
                vec3 pos = vec3(progress.xy * distortFactor, progress.z);
                float densityVol = cloudVol(progressW) * 2000.0;
                if (abs(pos.x) < 1.0 - 0.5 / shadowMapResolution && abs(pos.y) < 1.0 - 0.5 / shadowMapResolution) {
                    pos = pos * vec3(0.5, 0.5, 0.5 / 6.0) + 0.5;
                    sh = mix(shadow2D(shadow, pos).x, 1, float(z >= 1));

                }
#else
                float multiplier = 1.5;
                float progressW = gbufferModelViewInverse[3].y + cameraPosition.y + dVWorld.y;
                float densityVol = cloudVol(progressW) * 400.0;

#endif             
                vec2 airCoef = exp2(-max(progressW - SEA_LEVEL, 0.0) / vec2(48000, 8400)) * multiplier;

                float density = densityVol * densityMod;

                vec3 rL = rC * airCoef.x;
                vec3 m = (airCoef.y + density) * mC;
                vec3 rLm = rL + m;
                vec3 vL0 = (sunColor3 * sh * (rayL * rL + m * max(phaseg(SdotV, 0.4), 0.076)) + skyCol0 * rLm);

#ifdef VOLUMETRIC_LIGHTING
                vL += clamp01(vL0 - vL0 * exp(-(rL + m) * dd * dL)) / (rLm + 0.00000001) * absorbance;
                absorbance *= clamp(exp(-(rL + m) * dd * dL), 0.0, 1.0);
            }
#else
            vL = (vL0 - vL0 * exp(-rLm * 2.6 * dL)) / rLm;
            absorbance = clamp(exp(-rLm * 2.6 * dL), 0.0, 1.0);
#endif

#if defined NETHER || defined END
            float dist = smoothstep(-far, far, dL);
            vL = mix(vec3(0.0), vec3(0.15 * clamp(fcolor * pow(luma(fcolor), -0.75), 0.0, 1.0)), dist);
            absorbance = mix(vec3(2.0), vec3(1.0, 0.2, 0.5), dist);
#endif

            color.rgb *= absorbance;
            color.rgb += clamp(toLinear(vL), 0, 1);

            break;

        case 1:

            sunColor3 = sunColor2.rgb * 10;
            skyCol0 = ambientUp * 2;
            rayL = length(fragpos);

            float dd = 6;

            float maxZ = min(rayL, 32.0) / (1e-8 + rayL);
            rayL *= maxZ;

            float estEyeDepth = 1.0 - (eyeBrightnessSmooth.y / 255.0 * 16.0) / 14.0;

            estEyeDepth *= estEyeDepth * estEyeDepth * 24.0;
            estEyeDepth = max(estEyeDepth, 0.0);

            vec3 ambientMul = exp(-estEyeDepth * totEpsilon * 1.1);
            vec3 sunMul = exp(-estEyeDepth / abs(sunElevation) * totEpsilon);
            vec3 light = (sunColor3.rgb * phaseg(SdotV, 0.4) * sunMul + ambientMul * (skyCol0)) * scatterCoef;

            vL = (light - light * exp(-totEpsilon * dd * rayL)) / totEpsilon;
            absorbance = exp(-dd * rayL * totEpsilon);
            color.rgb = mix(color.rgb, (luma(ambientUp) * fogColor), borderFogFactor);

            color.rgb *= absorbance;
            color.rgb += cout(toLinear(vL));

            break;
        case 2:
            color.rgb = vec3(1, 0.1, 0);
            break;
        case 3:
            color.rgb = vec3(0.4, 0.8, 1);
            break;
    }
    color.a = dot(absorbance, vec3(0.22, 0.71, 0.07));

    return color;
}
