/*
================================ /// Super Duper Vanilla + PTGI /// ================================

    Enhanced complex shading with PTGI-style Global Illumination

================================ /// Super Duper Vanilla + PTGI /// ================================
*/

vec3 complexShadingDeferred(
    in vec3 sceneCol,
    in vec3 screenPos,
    in vec3 viewPos,
    in vec3 normal,
    in vec3 albedo,
    in vec3 dither,
    in float viewDotInvSqrt,
    in float metallic,
    in float smoothness,
    in bool realSky
) {
	#if defined ROUGH_REFLECTIONS || defined SSGI
		vec3 noiseUnitVector = generateUnitVector(dither.xy);
	#endif

	// ========================================================================
	// PTGI-STYLE GLOBAL ILLUMINATION
	// ========================================================================
	#if defined WORLD_LIGHT && defined SHADOW_MAPPING && PTGI_STYLE_GI == 1
		// Get world position and normal
		vec3 worldPos = mat3(gbufferModelViewInverse) * viewPos + gbufferModelViewInverse[3].xyz;
		vec3 worldNormal = mat3(gbufferModelViewInverse) * normal;
		
		// Get skylight (you might need to pass this as parameter if available)
		// For now using a default value
		float skylight = 0.8;
		
		// Calculate PTGI-style GI
		vec3 ptgiGI = CalculateGlobalIllumination(
			worldPos,
			worldNormal,
			viewPos,
			albedo,
			skylight,
			screenPos.xy,
			fragmentFrameTime  // From Super Duper Vanilla uniforms
		);
		
		// Add GI to scene with falloff based on distance
		float distFade = saturate(1.0 - length(viewPos) / (borderFar * 0.5));
		sceneCol += ptgiGI * distFade;
	#endif

	// ========================================================================
	// SCREEN SPACE GLOBAL ILLUMINATION (SSGI) - Original
	// ========================================================================
	#ifdef SSGI
		// Get SSGI screen coordinates
		vec3 SSGIcoord = rayTraceScene(screenPos, viewPos, generateCosineVector(normal, noiseUnitVector), dither.z);

		// If sky don't do SSGI
		#ifdef PREVIOUS_FRAME
			if(SSGIcoord.z > 0.5) sceneCol += albedo * textureLod(colortex5, getPrevScreenCoord(SSGIcoord.xy), 0).rgb * 0.3;
		#else
			if(SSGIcoord.z > 0.5) sceneCol += albedo * textureLod(colortex4, SSGIcoord.xy, 0).rgb * 0.3;
		#endif
	#endif

	// ========================================================================
	// SCREEN SPACE REFLECTIONS (SSR) - Original
	// ========================================================================
	
	// If smoothness is 0, return immediately (no reflections)
	if(smoothness < 0.005) return sceneCol;

	#ifdef ROUGH_REFLECTIONS
		// Rough the normals with noise
		normal = generateCosineVector(normal, noiseUnitVector * (squared(1.0 - smoothness) * 0.5));
	#endif

	vec3 nViewPos = viewPos * viewDotInvSqrt;

	// Get reflected view direction
	// reflect(direction, normal) = direction - 2.0 * dot(normal, direction) * normal
	float NV = dot(normal, -nViewPos);
	vec3 reflectViewDir = nViewPos + (2.0 * NV) * normal;

	// Calculate SSR and sky reflections
	#ifdef SSR
		// Get SSR screen coordinates
		vec3 SSRCoord = rayTraceScene(screenPos, viewPos, reflectViewDir, dither.z);

		#ifdef DISTANT_HORIZONS
			if(realSky) SSRCoord.z = 0.0;
		#endif

		// Fake reflections, also helps with improving reflection quality
		if(SSRCoord.z < 0.5){
			// Using the original ray direction, get the reflected ray and increase its length
			vec3 reflectDirF = viewPos + reflectViewDir * borderFar;

			// This masks only the reflections in view
			if(reflectDirF.z < viewPos.z){
				vec3 SSRDH = getScreenPos(gbufferProjection, reflectDirF);
				if(SSRDH.x >= 0 && SSRDH.y >= 0 && SSRDH.x <= 1 && SSRDH.y <= 1 && getDepthTex(SSRDH.xy) != 1) SSRCoord = vec3(SSRDH.xy, 1);
			}
		}

		#ifdef PREVIOUS_FRAME
			// Get reflections and check for sky
			vec3 reflectCol = SSRCoord.z < 0.5 ? getSkyReflection(reflectViewDir) : textureLod(colortex5, getPrevScreenCoord(SSRCoord.xy), 0).rgb;
		#else
			// Get reflections and check for sky
			vec3 reflectCol = SSRCoord.z < 0.5 ? getSkyReflection(reflectViewDir) : textureLod(colortex4, SSRCoord.xy, 0).rgb;
		#endif
	#else
		vec3 reflectCol = getSkyReflection(reflectViewDir);
	#endif

	// ========================================================================
	// PBR FRESNEL CALCULATION - Original
	// ========================================================================
	
	// Modified version of BSL's reflection PBR calculation
	// vec3 fresnel = (F0 + (1.0 - F0) * cosTheta) * smoothness
	// Fresnel calculation derived and optimized from this equation
	float smoothCosTheta = NV > 0 ? exp2(-9.28 * NV) * smoothness : smoothness;
	float oneMinusCosTheta = smoothness - smoothCosTheta;

	// Apply reflections based on material type
	if(metallic <= 0.9) return sceneCol + reflectCol * (smoothCosTheta + metallic * oneMinusCosTheta);
	return sceneCol + reflectCol * (smoothCosTheta + albedo * oneMinusCosTheta);
}
