#version 330 core
const int numLights=1;
const float PI = 3.14159;

in vec3 fragPos;
in vec3 tan;
in vec3 biTan;
in vec3 norm;
in vec2 texCoords;

out vec4 FragColor;
//layout (location = 1) out vec4 BrightColor;

//0-POINT,1-DIRECTIONAL,2-SPOT

struct Light{
	int type;
	vec3 pos, color, direction;
	float innerAngle, outerAngle;
	float a, b, c, near, far;
	sampler2D depthMap;
	samplerCube depthMapCube;
	mat4 lightMat;
};

struct FlatTexture{
	float mixRatio;
	sampler2D pastTexture, nextTexture;
	bool animated;
};

uniform Light lights[numLights];
uniform FlatTexture textures[5];
uniform samplerCube irradianceMap, postFilterMap;
uniform sampler2D brdfIntegrationMap;

uniform bool albedoMapEnabled;
uniform bool normalMapEnabled;
uniform bool roughnessMapEnabled;
uniform bool metalnessMapEnabled;
uniform bool ambientOcclutionMapEnabled;
uniform bool environmentMapEnabled;
uniform vec4 albedoColor;
uniform float roughnessVal;
uniform float metalnessVal;
uniform float ambientOcclusion;
uniform vec3 camPos;

float trowbridgeReitz(vec3 n, vec3 h, float a){
	return a * a / (PI * pow(pow(max(dot(n, h), 0), 2) * (a * a - 1) + 1, 2));
}

float schlickGGX(vec3 vec1, vec3 vec2, float k){
	float dotProd = max(dot(vec1, vec2), 0);
	return dotProd / max(dotProd * (1 - k) + k, .000001);
}

float geoSmith(vec3 n, vec3 v, vec3 l, float k){
	float ggx1 = schlickGGX(n, v, k);
	float ggx2 = schlickGGX(n, l, k);
	return ggx1 * ggx2;
}

vec3 fresnelSchlick(vec3 v, vec3 h, vec3 f0){
	return f0 + (vec3(1) - f0) * pow(1 - max(dot(v, h), 0), 5);
}

vec3 fresnelSchlickRoughness(vec3 n, vec3 v, vec3 f0, float roughness) {
	return f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - max(dot(n, v), 0), 5.0);
}   

vec3 lambertDiffuse(vec4 color){
	return (color / PI).xyz;
}

void main() {
	vec3 normal = norm;

	if(normalMapEnabled) {
		mat3 normMat = mat3(tan, biTan, norm);
		vec3 n = normalize(texture(textures[1].pastTexture, texCoords).rgb * 2 - 1);
		normal = normMat * n;
	}

	vec3 viewDir = normalize(camPos - fragPos);

	vec4 albedo = albedoColor;

	if(albedoMapEnabled)
		albedo = texture(textures[0].pastTexture, texCoords);

	float roughness = roughnessVal;

	if(roughnessMapEnabled)
		roughness = texture(textures[2].pastTexture, texCoords).r;

	float metalness = metalnessVal;

	if(metalnessMapEnabled)
		metalness = texture(textures[3].pastTexture, texCoords).r;

	vec3 f0 = mix(vec3(.04), albedo.rgb, metalness);
	vec3 totalLightColor = vec3(0);

	for(int i = 0; i < numLights; ++i){
		vec3 lightDir;
		float dist = length(lights[i].pos - fragPos), attenuation = 1, a = lights[i].a, b = lights[i].b, c = lights[i].c;

		if(lights[i].type == 0){
			lightDir = normalize(lights[i].pos - fragPos);
			attenuation = 1 / (dist * dist);
		}
		else if(lights[i].type == 1){
			lightDir = -normalize(lights[i].direction);
		}
		else if(lights[i].type == 2){
			lightDir = normalize(lights[i].pos - fragPos);
			attenuation = 1 / (dist * dist);
		}

		vec3 halfVec = normalize(lightDir + normal);

		float D = trowbridgeReitz(normal, halfVec, roughness * roughness);
		float k = pow(roughness + 1, 2) / 8.0;
		float G = geoSmith(normal, viewDir, lightDir, k);
		vec3 F = fresnelSchlick(viewDir, halfVec, f0);
		float nDotV = max(dot(viewDir, normal), 0);
		float nDotL = max(dot(lightDir, normal), 0);
		vec3 cookTor = D * G * F / max(4 * nDotV * nDotL, .000001);

		vec3 fDiff = lambertDiffuse(albedo);
		vec3 kDiff = (vec3(1) - F) * (1 - metalness);

		totalLightColor += (kDiff * fDiff + cookTor) * lights[i].color * attenuation * nDotL;
	}

	vec3 specular = vec3(.03), F = vec3(0);

	if(environmentMapEnabled){
    // sample both the pre-filter map and the BRDF lut and combine them together as per the Split-Sum approximation to get the IBL specular part.
		vec3 reflVec = reflect(-viewDir, normalize(fragPos + normal));
    const float MAX_REFLECTION_LOD = 4.0;
    vec3 prefilteredColor = textureLod(postFilterMap, reflVec, roughness * MAX_REFLECTION_LOD).rgb;    
    vec2 brdf  = texture(brdfIntegrationMap, vec2(max(dot(normal, viewDir), 0.0), roughness)).rg;
		F = fresnelSchlickRoughness(normal, viewDir, f0, roughness);
    specular = prefilteredColor;
    specular = prefilteredColor * (F * brdf.x + brdf.y);
	}

	float ao = ambientOcclusion;

	vec3 irradiance = texture(irradianceMap, normal).rgb;
	vec3 diffuse = irradiance * albedo.rgb;
	vec3 kD = (vec3(1) - F) * (1 - metalness);
	vec3 totalAmbientColor = (kD * diffuse + specular) * ao;

	vec4 finalColor = vec4(totalLightColor + totalAmbientColor, 1);

	float brightness = dot(finalColor.rgb, vec3(0.2126, 0.7152, 0.0722));

/*
	if(brightness > 1)
		BrightColor = vec4(finalColor.rgb, 1);
*/

	finalColor.rgb /= finalColor.rgb + vec3(1.0);
	finalColor.rgb = pow(finalColor.rgb, vec3(1.0 / 2.2));

	FragColor = finalColor;
}
