#version 460 core

in vec2 FragTexCoords;
in vec3 FragNormal;
in vec3 FragPos;

out vec4 FinalColor;

uniform vec3 CameraPos;

// == Textures == //
uniform sampler2D ImageTexture0;
uniform sampler2D ImageTexture1;

// === FOG ==== //
struct LinearFog
{
	float start;
	float end;
	float maximum;
	vec4  color;
};

uniform int enableFog = 0;

uniform LinearFog fog = {
	25.0f,
	200.0f,
	0.97f,
	vec4(0.4f, 0.4f, 0.4f, 1.0f)
};

vec4 CalculateLinearFog(vec4 color)
{
	if (enableFog == 0)
	{
		return color;
	}
	float Dist = distance(FragPos, CameraPos);
	float FogFactor = (Dist - fog.start) / fog.end;
	FogFactor = clamp(FogFactor, 0.0f, fog.maximum);
	return mix(color, fog.color, FogFactor);
}
// === End Fog == //

// === Blinn-Phong Lighting === //

#define MAX_POINT_LIGHTS 4

struct PointLight
{
	vec3 Position;
	vec3 Color;
	float AmbientStrength;
	float SpecularStrength;
	float AttenuationConstant;
	float AttenuationLinear;
	float AttenuationExponent;
};

struct DirectionalLight 
{
	vec3  Direction;
	vec3  Color;
	float AmbientStrength;
	float SpecularStrength;
};

// uniform inputs
uniform int LightCount;
uniform float Shininess				= 128.0f;

uniform float RimExponent			= 2.5f;
uniform vec3 RimColor				= vec3(1.f, 0.f, 0.f);
uniform bool RimLightActive;

// multiple light uniforms
uniform PointLight PointLights[MAX_POINT_LIGHTS];
uniform DirectionalLight DirLight;

vec3 CalculateLight_Directional(DirectionalLight  dirLight)
{
	vec3 AmbientColor = vec3(1.f, 1.f, 1.f) * 0.01;
	vec3 light = vec3(0.0f,0.0f,0.0f);

	// Light Direction
	vec3 Normal = normalize(FragNormal);
	vec3 LightDir = normalize(dirLight.Direction);

	// Ambient Component
	vec3 Ambient = dirLight.AmbientStrength * AmbientColor;

	// Diffuse Component
	float DiffuseStrength = max(dot(Normal, -LightDir), 0.0f);
	vec3 Diffuse = DiffuseStrength * dirLight.Color;

	// Specular Component
	vec3 ReverseViewDir = normalize(CameraPos - FragPos);
	vec3 HalfwayVector = normalize(-LightDir + ReverseViewDir);
	float SpecularReflectivity = pow(max(dot(Normal, HalfwayVector), 0.0f), Shininess);
	vec3 Specular = dirLight.SpecularStrength * SpecularReflectivity * dirLight.Color;

	light = Ambient + Diffuse + Specular;
	return light;
}

vec3 CalculateLight_Point(PointLight light)
{
	// Light direction
	vec3 Normal = normalize(FragNormal);
	vec3 LightDir = normalize(FragPos - light.Position);

	// Ambient component
	//vec3 Ambient = AmbientStrength * AmbientColor;
	vec3 Ambient = light.AmbientStrength * light.Color;
	
	// Diffuse component
	float DiffuseStrength = max(dot(Normal, -LightDir), 0.0f);
	vec3 Diffuse = DiffuseStrength * light.Color;

	// Specular component
	vec3 ReverseViewDir = normalize(CameraPos - FragPos);
	vec3 HalfwayVector = normalize(-LightDir + ReverseViewDir);
	float SpecularReflectivity = pow(max(dot(Normal, HalfwayVector), 0.0f), Shininess);
	vec3 Specular = light.SpecularStrength * SpecularReflectivity * light.Color;

	// Rim component

	// Attenuation
	float Distance = length(light.Position - FragPos);
	float Attenuation = light.AttenuationConstant + (light.AttenuationLinear * Distance) + (light.AttenuationExponent * pow(Distance,2));


	// Rim component

	vec3 Rim = vec3(0.f,0.f,0.f);
	if (RimLightActive)
	{
		float RimFactor = 1.0f - dot(Normal, ReverseViewDir);
		RimFactor = smoothstep(0.f, 1.f, RimFactor);
		RimFactor = pow(RimFactor, RimExponent);
		Rim = RimFactor * RimColor;// * light.Color;
	}

	//Combine lighting components
	return Ambient + Diffuse + Specular + Rim;
	//return (Ambient + Diffuse + Specular) + Rim;
}

// === End Lighting === //

void main()
{
	vec3 LightOutput = vec3(0.f, 0.f, 0.f);
	for (int i=0; i < LightCount; ++i)
	{
		LightOutput += CalculateLight_Point(PointLights[i]);
	}

	LightOutput += CalculateLight_Directional(DirLight);

	vec4 LitTexture = vec4(LightOutput, 1.f) * texture(ImageTexture0, FragTexCoords);
	//Calculate final color
	FinalColor = CalculateLinearFog(LitTexture);
}
