#version 400 core

uniform struct Matrices
{
	mat4 projMatrix;
	mat4 modelViewMatrix; 
	mat4 modelMatrix; 
	mat4 normalMatrix;
} matrices;

/////#Light Structs and uniforms

struct Attentation{
	float c0;
	float c1;
	float c2;
};

struct PointLight
{ 
	int isOn;
	vec4 color;
	float ambient;
	float diffuse;
	float specular;
	vec3 position;
	Attentation attenuation;
	float range;
};

struct SpotLight
{ 
	int isOn;
	vec4 color;
	float ambient;
	float diffuse;
	float specular;
	vec3 position;
	Attentation attenuation;
	float range;
	vec3 direction;
	float cutOffAngle;
	float expFactor;
};

struct DirectionalLight
{ 
	int isOn;
	vec4 color;
	float ambient;
	float diffuse;
	float specular;
	vec3 direction;
};

#define MAX_POINT_LIGHTS 30
#define MAX_DIRECTIONAL_LIGHTS 5
#define MAX_SPOT_LIGHTS 30

uniform int pointLightCount;
uniform PointLight gPointLights[MAX_POINT_LIGHTS];
uniform int directionalLightCount;
uniform DirectionalLight gDirectionalLights[MAX_DIRECTIONAL_LIGHTS];
uniform int spotLightCount;
uniform SpotLight gSpotLights[MAX_SPOT_LIGHTS];

/////#Light Structs

uniform sampler2D sampler0;
uniform vec3 camPosition;

in vec2 uv;
in vec3 pixelPosition;	
in vec3 pixelNormal;
out vec4 vColor;

vec3 PhongPointLight(PointLight light, vec3 camPosition, vec3 pixelVec)
{
	if(light.isOn == 0){
		return vec3(0.0);
	}
	vec3 norm = normalize(pixelNormal);
	vec3 lightDir = -pixelVec + light.position;
	float sqrlightMag = dot(lightDir, lightDir);

	if(sqrlightMag > pow(light.range, 2)){ //light is too far away from pixel
		return vec3(0.0);
	}
	
	float lightMag = sqrt(sqrlightMag);
	lightDir = normalize(lightDir);
	float diff = max(dot(norm, lightDir), 0.0);
	float diffuse = diff * light.diffuse;

	vec3 viewDir = normalize(camPosition - pixelVec);
	vec3 reflectDir = reflect(-lightDir, norm);

	float spec = pow(max(dot(viewDir, reflectDir), 0.0), 2);
	float specular = spec * light.diffuse;  

	//return (light.ambient.xyz + diff + specular);
	float att = 1 / (light.attenuation.c0 + light.attenuation.c1 * lightMag + light.attenuation.c2 * sqrlightMag);
	return  att * (light.ambient + specular + diffuse) * light.color.xyz;
}

vec3 PhongSpotLight(SpotLight light, vec3 camPosition, vec3 pixelVec)
{
	if(light.isOn == 0){
		return vec3(0.0);
	}
	vec3 norm = normalize(pixelNormal);
	vec3 dir = normalize(light.direction);
	vec3 lightDir = -pixelVec + light.position;
	float sqrlightMag = dot(lightDir, lightDir);

	if(sqrlightMag > pow(light.range, 2)){ //light is too far away from pixel
		return vec3(0.0);
	}
	float cosTheta = cos(light.cutOffAngle);
	float lightMag = sqrt(sqrlightMag);
	lightDir = (1.0 / lightMag) * lightDir;

	float cosAngle = dot(-lightDir, dir);

	if(cosAngle < cosTheta){
		return vec3(0.0);
	}
	
	float angle = acos(cosAngle);
	float diff = max(dot(norm, lightDir), 0.0);
	float diffuse = diff * light.diffuse;

	vec3 viewDir = normalize(camPosition - pixelVec);
	vec3 reflectDir = reflect(-lightDir, norm);

	float spec = pow(max(dot(viewDir, reflectDir), 0.0), 2);
	float specular = spec * light.diffuse;  

	//return (light.ambient.xyz + diff + specular);
	float att = exp(-light.expFactor * angle) / (light.attenuation.c0 + light.attenuation.c1 * lightMag + light.attenuation.c2 * sqrlightMag);
	return  att * (light.ambient + specular + diffuse) * light.color.xyz;
}

vec3 PhongDirectionalLight(DirectionalLight light, vec3 camPosition, vec3 pixelVec)
{
	vec3 norm = normalize(pixelNormal);
	vec3 lightDir = normalize(light.direction);

	float diff = max(dot(norm, lightDir), 0.0);
	float diffuse = diff * light.diffuse;

	vec3 viewDir = normalize(camPosition - pixelVec);
	vec3 reflectDir = reflect(-lightDir, norm);

	float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
	float specular = spec * light.diffuse;  

	return (light.ambient + specular + diffuse) * light.color.xyz;
}

void main(){
	vec3 colorFactor = vec3(0);
	vec3 pixelVec = (matrices.modelMatrix * vec4(pixelPosition, 1)).xyz;

	for(int i = 0; i < directionalLightCount; i++){
		colorFactor += PhongDirectionalLight(gDirectionalLights[i], camPosition, pixelVec); 
	}

	for(int i = 0; i < pointLightCount; i++){
		colorFactor += PhongPointLight(gPointLights[i], camPosition, pixelVec); 
	}

	for(int i = 0; i < spotLightCount; i++){
		colorFactor += PhongSpotLight(gSpotLights[i], camPosition, pixelVec); 
	}

	vColor = vec4(colorFactor, 1) * texture(sampler0, uv);
}