#version 450

#define BASE_LIGHTS_SIZE 16
layout (constant_id = 0) const uint LIGHTS = BASE_LIGHTS_SIZE;

layout (input_attachment_index = 0, binding = 1) uniform subpassInput samplerAlbedo;
layout (input_attachment_index = 0, binding = 2) uniform subpassInput samplerNormal;
layout (input_attachment_index = 0, binding = 3) uniform subpassInput samplerPosition;
// layout (input_attachment_index = 0, binding = 4) uniform subpassInput samplerDepth;
layout (binding = 5) uniform sampler2D samplerShadows[LIGHTS];

layout (location = 0) in vec2 inUv;
layout (location = 1) in flat uint inPushConstantPass;

layout (location = 0) out vec4 outFragColor;


struct Light {
	vec3 position;
	float radius;
	vec3 color;
	float power;
	int type;
	int shadowed;
	mat4 view;
	mat4 projection;
};

struct Matrices {
	mat4 view[2];
	mat4 projection[2];
};

struct Space {
	vec3 eye;
	vec3 world;
} position, normal, view;

layout (binding = 0) uniform UBO {
	Matrices matrices;
	vec3 ambient;
	float kexp;
	Light lights[LIGHTS];
} ubo;

void fog( inout vec3 i ) {
	vec3 color = vec3( 0, 0, 0 );
	float inner = 8, outer = 64;
	float distance = length(-position.eye);
	float factor = (distance - inner) / (outer - inner);
	factor = clamp( factor, 0.0, 1.0 );

	i = mix(i.rgb, color, factor);
}

void phong( Light light, vec4 albedoSpecular, inout vec3 i ) {
	vec3 Ls = vec3(1.0, 1.0, 1.0); 		// light specular
	vec3 Ld = light.color; 				// light color
	vec3 La = vec3(1.0, 1.0, 1.0);

	vec3 Ks = vec3(albedoSpecular.a); 	// material specular
	vec3 Kd = albedoSpecular.rgb;		// material diffuse
	vec3 Ka = vec3(1.0, 1.0, 1.0); 

	float Kexp = ubo.kexp;

	vec3 L = light.position.xyz - position.eye;
	float dist = length(L);
	
	if ( light.radius < dist ) return;

	vec3 D = normalize(L);
	float d_dot = max(dot( D, normal.eye ), 0.0);

	vec3 R = reflect( -D, normal.eye );
	vec3 S = normalize(-position.eye);
	float s_factor = pow( max(dot( R, S ), 0.0), Kexp );
	
	float attenuation = light.radius / (pow(dist, 2.0) + 1.0);

	vec3 Ia = La * Ka;
	vec3 Id = Ld * Kd * d_dot * attenuation;
	vec3 Is = Ls * Ks * s_factor * attenuation;
	
	i += Id * light.power;
}

bool debugShadow( Light light, uint i ) {
//	return false;

	{
		outFragColor.rgb = texture(samplerShadows[i], inUv).rgb;
		outFragColor.a = 1;
		return true;
	}

	vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0);
	positionClip.xyz /= positionClip.w;
	float lightDepth = texture(samplerShadows[i], positionClip.xy * 0.5 + 0.5).r;

	float eyeDepth = positionClip.z;
//	eyeDepth = lightDepth;
	float bias = 0.0005;

		 if ( positionClip.x < -1 || positionClip.x >= 1 ) eyeDepth = 1;
	else if ( positionClip.y < -1 || positionClip.y >= 1 ) eyeDepth = 1;
	else if ( positionClip.z <  0 || positionClip.z >= 1 ) eyeDepth = 1;
	else eyeDepth /= 0.00526;

	outFragColor.rgb = vec3( 1 - eyeDepth );
	outFragColor.a = 1;
	
	return true;
}

float shadowFactor( Light light, uint i ) {
	vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0);
	positionClip.xyz /= positionClip.w;
	float lightDepth = texture(samplerShadows[(i*4)+3], positionClip.xy * 0.5 + 0.5).r;
	float eyeDepth = positionClip.z;
	float bias = 0.0005;
	// bias = max(0.05 * (1.0 - dot(normal.eye, light.position.xyz - position.eye)), bias);

//	eyeDepth /= 0.00526;
//	lightDepth /= 0.00526;

	eyeDepth = 1 - eyeDepth;
	lightDepth = 1 - lightDepth;

	if ( positionClip.x < -1 || positionClip.x >= 1 ) return 0.0;
	if ( positionClip.y < -1 || positionClip.y >= 1 ) return 0.0;
	if ( positionClip.z <  0 || positionClip.z >= 1 ) return 0.0;

	return eyeDepth - bias < lightDepth ? 1.0 : 0.0;
}

void main() {
	vec4 albedoSpecular = subpassLoad(samplerAlbedo);
	normal.eye = subpassLoad(samplerNormal).rgb;
	position.eye = subpassLoad(samplerPosition).rgb;

	{
		mat4 iView = inverse( ubo.matrices.view[inPushConstantPass] );
		vec4 positionWorld = iView * vec4(position.eye, 1);
		position.world = positionWorld.xyz;
	}
/*
	{
		for ( int i = 0; i < LIGHTS; ++i ) {
			outFragColor.rgb = texture(samplerShadows[i], inUv).rgb;
		}
		float depth = texture(samplerShadows[3], inUv).r;
		depth /= 0.00526;
		outFragColor.rgb = vec3(1 - depth);
	//	outFragColor.rgb = texture(samplerShadows[0], inUv).rgb;
		outFragColor.a = 1;
		return;
	}
*/
/*
	{
		mat4 iProj = inverse( ubo.matrices.projection[inPushConstantPass] );
		mat4 iView = inverse( ubo.matrices.view[inPushConstantPass] );

		float depth = subpassLoad(samplerDepth).r;

		if ( false ) {
			depth /= 0.00526;
			outFragColor.rgb = vec3( 1 - depth );
			outFragColor.a = 1;
			return;
		}
		
		vec4 positionClip = vec4(inUv * 2.0 - 1.0, depth, 1.0);
		vec4 positionEye = iProj * positionClip;
		positionEye /= positionEye.w;
		position.eye = positionEye.xyz;

		vec4 positionWorld = iView * positionEye;
		position.world = positionWorld.xyz;
	}
*/

	vec3 fragColor = albedoSpecular.rgb * ubo.ambient.rgb;
	bool lit = false;

	for ( uint i = 0; i < LIGHTS; ++i ) {
		Light light = ubo.lights[i];
		
		if ( light.power <= 0.001 ) continue;
		lit = true;
		
		light.position.xyz = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position.xyz, 1));
		if ( light.shadowed > 0 ) {
			float shadowFactor = shadowFactor( light, i );
			if ( shadowFactor <= 0.0001 ) continue;
		}
		phong( light, albedoSpecular, fragColor );
	}
	
	if ( !lit ) fragColor = albedoSpecular.rgb;
	
	fog(fragColor);

	outFragColor = vec4(fragColor,1);
/*
	if ( !false ) {
		float depth = texture(samplerShadows[0], inUv).r;
		depth /= 0.00526;
		outFragColor = vec4( vec3(1 - depth), 1 );
		return;
	} else if ( !false ) {
		outFragColor = vec4( texture(samplerShadows[0], inUv).rgb, 1 );
		return;
	} else {
		outFragColor = vec4( inUv.x, 0, inUv.y, 1 );
		return;
	}
*/
}