uniform sampler2D depthtex0;
uniform sampler2D colortex6;
uniform sampler2D colortex11;
uniform mat4 gbufferProjectionInverse;
uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferModelView;
uniform vec3 cameraPosition;
uniform vec2 viewSize;
uniform float frameTimeCounter;
uniform float far;
uniform bool accum;
uniform float transitionDayAndNight;
uniform float dayAndNightBrightness;
uniform float raindropBrightness;

uniform sampler2D depthtex1;
uniform sampler2D colortex7;
uniform sampler2D colortex9;
uniform sampler2D colortex10;

vec2 texcoord = gl_FragCoord.xy / viewSize;

#include "../../includes/debug.glsl"
#include "../../BlockMappings.glsl"


uniform vec3 sunDirection;
//uniform float sunAngle;
uniform int worldTime;
uniform float wetness;

uniform usampler2D noisetex;
#define sky_tex colortex15
uniform usampler2D sky_tex;
//uniform usampler2D colortex14;
uniform vec3 sunPosition;
uniform int moonPhase;
uniform float brightnessRainy;
uniform float sunBrightRainy;
#include "../../includes/Sky.glsl"

uniform int isEyeInWater;
uniform ivec2 eyeBrightness;
uniform ivec2 eyeBrightnessSmooth;
#include "/lib/Water.glsl"
const vec3 WATER_COLOR = vec3(0.0, 0.5, 0.73);
const float WATER_COLOR_DIMMING = 0.08;
const vec3 WATER_COLOR_LIGHT = vec3(0.68, 0.91, 1.00);

#include "/lib/Pack.glsl"
uniform float rainStrength;

#define diagonal3(mat) vec3((mat)[0].x, (mat)[1].y, mat[2].z)
#define  projMAD(mat, v) (diagonal3(mat) * (v) + (mat)[3].xyz)
#define clamp01(x) clamp(x, 0.0, 1.0)

vec3 calculateViewSpace(vec3 screenSpace, mat4 projectionMatInverse) {
	screenSpace = screenSpace * 2.0 - 1.0;
	return projMAD(projectionMatInverse, screenSpace) / (screenSpace.z * projectionMatInverse[2].w + projectionMatInverse[3].w);
}

vec2 getWaterRefractedCoord(vec2 coord, vec3 normal, vec2 depthtex) {

    if(depthtex.x >= depthtex.y) return coord;

    vec3 pos1   = calculateViewSpace(vec3(coord, depthtex.x), gbufferProjectionInverse);
    vec3 pos2   = calculateViewSpace(vec3(coord, depthtex.y), gbufferProjectionInverse);

	vec3 normalFlat = vec3(0.0, 1.0, 0.0);

	float refractionDepth = min(distance(pos1, pos2), 0.5);

	vec2 refractedCoord = coord + (normal.xz - normalFlat.xz) * refractionDepth / pos1.z;

    vec4 gbuffers_refracted = texelFetch(colortex6, ivec2(refractedCoord * viewSize), 0);
    ivec2 gbuffers_refracted_w = Unpack2x16Int(gbuffers_refracted.w);
    int blockID = gbuffers_refracted_w.x;
    if (blockID != 200) return coord;  // as it is, except water

	float refractedDepth = texture2D(depthtex1, refractedCoord).r;
	return depthtex.x > refractedDepth || clamp01(refractedCoord) != refractedCoord ? coord : clamp01(refractedCoord);
}

float Luminance(vec3 x) {
    return dot(x, vec3(0.2126, 0.7152, 0.0722));
}

vec3 GetWorldSpacePosition(vec2 coord, float depth) {
    vec4 pos = vec4(vec3(coord, depth) * 2.0 - 1.0, 1.0);
    pos = gbufferProjectionInverse * pos;
    pos /= pos.w;
    pos.xyz = mat3(gbufferModelViewInverse) * pos.xyz;
    
    return pos.xyz;
}

/* RENDERTARGETS:11 */

void main() {
    ivec2 coord = ivec2(gl_FragCoord.xy);
    
    vec2 depthset = vec2(texelFetch(depthtex0, coord, 0).x, texelFetch(depthtex1, coord, 0).x);
    
    vec3 worldPos = GetWorldSpacePosition(texcoord, depthset.x);
    
    vec3 worldDir = normalize(worldPos);
    vec3 absorb = vec3(1.0);
    
    vec4 gbufferEncode = texelFetch(colortex6, coord, 0);
    vec3 albedo = unpackUnorm4x8(floatBitsToUint(gbufferEncode.r)).rgb * 256.0 / 255.0;
    albedo = pow(albedo, vec3(2.2));

    float beyond_gbufferColor = texelFetch(colortex10 , coord, 0).a;
    vec3 beyond_albedo = unpackUnorm4x8(floatBitsToUint(beyond_gbufferColor)).rgb * 256.0 / 255.0;
    beyond_albedo = pow(beyond_albedo, vec3(2.2));
    
    //int blockID = int(gbufferEncode.w);
    ivec2 gbufferEncode_w = Unpack2x16Int(gbufferEncode.w);
    //int rainParticle = gbufferEncode_w.x;
    int blockID = gbufferEncode_w.x;
    int rainParticle = int(texelFetch(colortex11, coord, 0).a);
    
    float beyond_gbuffer_w = texelFetch(colortex9 , coord, 0).a;
    ivec2 beyond_bufferEncode_w = Unpack2x16Int(beyond_gbuffer_w);
    int beyond_blockID = beyond_bufferEncode_w.x;
    
    //float darkness = clamp(float(eyeBrightness.y) / 240.0, 0.0, 1.0);
        // If using eyeBrightness, the entire screen will be affected by the brightness at player's position.
    float lmcoord_y = (clamp(float(Unpack16uiTo2x8ui(gbufferEncode_w.y).y), 7.0, 247.0) - 7.0) / 240.0;
        // Note that brightness information from lmcoord.y may differ from actual brightness of object.
        // (even if lmcoord.y is high value outdoors during the day, it may actually be dark in the shadows, 
        // or even if lmcoord.y is 0 on the ocean floor, there may actually be some light shining and bright.)
    float skyDarkness = clamp(lmcoord_y, 0.0, 1.0);
    skyDarkness = min(skyDarkness, dayAndNightBrightness);
    vec4 color = texelFetch(colortex11 , coord, 0);  // rgb:terrain color  a:rain data
    float darkness = clamp(Luminance(color.rgb), 0.0, 1.0);
    float waterDarkness = darkness * 0.7;
    float rainDarkness = clamp(darkness * 30.0, 0.0, 1.0);

    vec4 screenPos = vec4(gl_FragCoord.xy / viewSize, gl_FragCoord.z, 1.0);
    vec4 viewPos = gbufferProjectionInverse * (screenPos * 2.0 - 1.0);
    viewPos /= viewPos.w;
    //vec3 skyColor = ComputeTotalSky(vec3(0.0), worldDir, absorb, true) + GetFogColor(worldDir);
    vec3 skyColor = ComputeTotalSky(vec3(0.0), worldDir, absorb, true, texcoord, viewPos.xyz) + GetFogColor(worldDir);
    
    if (depthset.x >= 1.0) {
        gl_FragData[0] = vec4(skyColor, 0.0);
        
        // Note: If don't set rain.depth=false in shaders.properties, the depth of the raindrops will be obtained and it will not be possible to process it as sky.
        if (rainStrength > 0.0 && rainParticle > 0) {
            gl_FragData[0].rgb = mix(gl_FragData[0].rgb, vec3(raindropBrightness), 0.05);
        }

        exit();
        return;
    }
    
    if (is_water(blockID)) {
        //gl_FragData[0] = vec4(texelFetch(colortex11, coord, 0).rgb * albedo, 0.0);
        gl_FragData[0] = vec4(texelFetch(colortex11, coord, 0).rgb, 0.0);
        gl_FragData[0].rgb *= beyond_albedo;

        if (isEyeInWater > 0.5) {
            if (depthset.y >= 1.0) {
                // sky seen through the water surface from underwater
                gl_FragData[0] = vec4(skyColor, 0.0);
                float poorVisibility = 1.0 - depthset.x;
                poorVisibility = clamp(poorVisibility, 0.0, 0.25) * 4.0;
                gl_FragData[0].rgb = mix(gl_FragData[0].rgb * 0.90, (WATER_COLOR * WATER_COLOR_DIMMING * skyDarkness), (1.0 - poorVisibility));
            } else {
                // terrain seen through the water surface from underwater
                float beyond_gbuffer_w = texelFetch(colortex9 , coord, 0).a;
                int beyond_blockID = Unpack2x16Int(beyond_gbuffer_w).x;
                if (is_emissive(beyond_blockID)) {
                    float emitterBrightness;
                    //if (beyond_blockID == 176 || beyond_blockID == 160 || beyond_blockID == 144) {  // torch types
                    if (is_burning(beyond_blockID)) {  // torch types, furnace types
                        float albedoBrightness = length(beyond_albedo);
                        emitterBrightness = albedoBrightness > 1.0 ? (emitterLevel(beyond_blockID) * albedoBrightness) : 0.01;
                    } else {
                        emitterBrightness = pow(emitterLevel(beyond_blockID), 2.0);
                    }
                    gl_FragData[0].rgb += beyond_albedo * emitterBrightness * 0.05;
                }
                
                float farApart = length(worldPos);
                float poorVisibility = exp(-0.1 * (farApart + 1.0));
                //poorVisibility = clamp(poorVisibility, 0.02, 1.0);
                poorVisibility = clamp(poorVisibility, 0.10, 1.0);
                gl_FragData[0].rgb = mix(gl_FragData[0].rgb * 0.80, (WATER_COLOR * WATER_COLOR_DIMMING * waterDarkness), (1.0 - poorVisibility));
            }
            exit();
            return;
        }
        
        vec3 normal_Transparent = getNormalWater(worldPos + cameraPosition, depthset.x * EPSILON_NRM);
        normal_Transparent.y = mix(normal_Transparent.y, -normal_Transparent.y, float(isEyeInWater > 0.5));
        vec2 refractedCoord = getWaterRefractedCoord(texcoord, normal_Transparent, depthset);
        ivec2 shiftCoord = ivec2(refractedCoord * viewSize);
        gl_FragData[0] = vec4(texelFetch(colortex11, shiftCoord, 0).rgb, 0.0);
        
        float beyond_gbufferColor = texelFetch(colortex10 , shiftCoord, 0).a;
        vec3 beyond_albedo = unpackUnorm4x8(floatBitsToUint(beyond_gbufferColor)).rgb * 256.0 / 255.0;
        beyond_albedo = pow(beyond_albedo, vec3(2.2));
            
        float shift_beyond_gbuffer_w = texelFetch(colortex9 , shiftCoord, 0).a;
        int shift_beyond_blockID = Unpack2x16Int(shift_beyond_gbuffer_w).x;
        if (is_emissive(shift_beyond_blockID)) {
            float emitterBrightness;
            // underwater, torch fire go out
            //if (beyond_blockID == 176 || beyond_blockID == 160 || beyond_blockID == 144) {  // torch types
            //    float albedoBrightness = length(beyond_albedo);
            //    emitterBrightness = albedoBrightness > 1.0 ? (emitterLevel(beyond_blockID) * albedoBrightness) : 0.01;
            //} else {
                emitterBrightness = pow(emitterLevel(beyond_blockID), 2.0);
            //}
            gl_FragData[0].rgb += beyond_albedo * emitterBrightness * 0.05;
        }
        
        //gl_FragData[0].rgb = mix(gl_FragData[0].rgb, (WATER_COLOR * 0.1 * darkness), 0.1);
        gl_FragData[0].rgb = mix(gl_FragData[0].rgb, (WATER_COLOR * 0.1 * darkness), 0.2);
        
        if (rainStrength > 0.0 && rainParticle > 0) {
            gl_FragData[0].rgb = mix(gl_FragData[0].rgb, vec3(rainDarkness), 0.02);
        }
        exit();
        return;
    }
    
    //gl_FragData[0].rgb = texelFetch(colortex11, coord, 0).rgb * albedo;
    if (depthset.x < depthset.y) {
        if (depthset.y >= 1.0) {
            gl_FragData[0] = vec4(skyColor, 0.0);
        } else {
            gl_FragData[0].rgb = texelFetch(colortex11, coord, 0).rgb;
            gl_FragData[0].rgb *= beyond_albedo;
        }
        
        if (is_emissive(beyond_blockID)) {
            //gl_FragData[0].rgb += albedo;
            float emitterBrightness;
            //if (beyond_blockID == 176 || beyond_blockID == 160 || beyond_blockID == 144) {  // torch types
            if (is_burning(beyond_blockID)) {  // torch types, furnace types
                float albedoBrightness = length(beyond_albedo);
                emitterBrightness = albedoBrightness > 1.0 ? (emitterLevel(beyond_blockID) * albedoBrightness) : 0.01;
            } else {
                emitterBrightness = pow(emitterLevel(beyond_blockID), 2.0);
            }
            gl_FragData[0].rgb += beyond_albedo * emitterBrightness;
        }
        
        // if apply colored glass albedo as is, the color will be too dark, so dilute it a little.
        gl_FragData[0].rgb *= (albedo * 0.95 + 0.05);
        
        gl_FragData[0].rgb += (blockID >= 192 && blockID <= 193) ? albedo * pow(0.75, 2.0) : vec3(0.0);  // nether portal
    } else {
        
        // main color
        gl_FragData[0].rgb = texelFetch(colortex11, coord, 0).rgb * albedo;
          //gl_FragData[0].rgb = texelFetch(colortex11, coord, 0).rgb;
        
        if (is_emissive(blockID)) {
            //gl_FragData[0].rgb += albedo;
            float emitterBrightness;
            //if (blockID == 176 || blockID == 160 || blockID == 144 || blockID == 114) {  // torch types
            if (is_burning(blockID)) {  // torch types, furnace types
                float albedoBrightness = length(albedo);
                //emitterBrightness = albedoBrightness > 1.0 ? (pow(emitterLevel(blockID), 2.0) * albedoBrightness) : pow(emitterLevel(blockID), 2.0);
                emitterBrightness = albedoBrightness > 1.0 ? (emitterLevel(blockID) * albedoBrightness) : 0.01;
            } else {
                emitterBrightness = pow(emitterLevel(blockID), 2.0);
            }
            gl_FragData[0].rgb += albedo * emitterBrightness;
        }
    }
    
    float fog_amount = clamp(length(worldPos) / max(16.0 * 16.0, far), 0.0, 1.0);
    
    if (is_nether)
        gl_FragData[0].rgb = mix(gl_FragData[0].rgb, GetFogColor(worldDir), fog_amount);
    else
        gl_FragData[0].rgb += GetFogColor(worldDir) * fog_amount;
    
    if (isEyeInWater == 1) {  // 1=water 2=lava 3=snow
        float playerBrightnessInWater = (depthset.x < 0.56) ? clamp(float(eyeBrightnessSmooth.y - 140) / 240.0, 0.1, 1.0) : 1.0;  // adjust brightness of hand or handheld item

        float farApart = length(worldPos);
        float poorVisibility = exp(-0.1 * (farApart + 1.0));
        poorVisibility = clamp(poorVisibility, 0.02, 1.0);
            /*
            float waterDarkness = clamp(darkness, max(0.008, transitionDayAndNight * 0.5), 1.0);
            // Underwater, the lightmap value decreases with depth, and quickly reaches the lower limit if you dive a little.
            // Even at a relatively bright depth, once the lightmap value reaches 0, darkness=0.
            // If using darkness as is, this causes the water color to become 0 in the mix() function, and as a result, only the normal color remains for the terrain at the bottom of the water 
            // (even though linear interpolation is performed, the water color is not added, so it appears to be just the normal terrain color).
            // Therefore, only when underwater, set a lower limit for the darkness value depending on the time of day (day or night), so that the water color is added appropriately.
            */
        gl_FragData[0].rgb = mix(gl_FragData[0].rgb * 0.90 * WATER_COLOR_LIGHT * playerBrightnessInWater, (WATER_COLOR * WATER_COLOR_DIMMING * waterDarkness), (1.0 - poorVisibility));
    }
    
    if (rainStrength > 0.0 && rainParticle > 0) {
        gl_FragData[0].rgb = mix(gl_FragData[0].rgb, vec3(rainDarkness), 0.02);
    }
    
    exit();
}
