
struct Light
{
	vec3 color;
	float linearAttenuation;
	vec3 positionDirection;
	float quadraticAttenuation;
};

struct Material
{
	float diffuseStrength;
	float specularPower;
	float specularStrength;
};

#define MAX_SPECULAR_POW 128
#define LIGHT_AMOUNT 9
uniform mat4 uViewMatrix;
uniform Light uLights[LIGHT_AMOUNT];

uniform samplerCube uCubeMap;
uniform mat4 uSkyboxRotationMatrix;
uniform vec3 uCameraPosition;

// DECLARATIONS ::::::::::::::::::::::::

float getAttenuation(float distanceToLight, float linear, float quadratic);

Material getMaterial(float metallicValue, float roughnessValue);

vec4 calculateDirLighting(
	  vec3 worldPos
	, vec3 viewPos
	, vec3 worldNormal
	, vec3 viewNormal
	, vec4 textureColor
	, Material mat);

vec4 calculateAllLighting(
	  vec3 worldPos
	, vec3 viewPos
	, vec3 worldNormal
	, vec3 viewNormal
	, vec4 textureColor
	, Material mat);

vec4 calculateLights(
	  vec3 viewPos
	, vec3 viewNormal
	, Material m);
	
vec4 getPointLight(
	  vec3 viewPos
	, vec3 viewNormal
	, Light l
	, Material m);
	
vec4 getDirectionalLight(
	  vec3 viewPos
	, vec3 viewNormal
	, Light l
	, Material m);
	
vec4 calculateReflectionColor(
	  vec3 worldPos
	, vec3 worldNormal
	, vec3 worldCameraPos
	, samplerCube skybox
	, mat4 skyboxRotation);
	
vec4 calculateRefractionColor(
	  vec3 worldPos
	, vec3 worldNormal
	, vec3 worldCameraPos
	, samplerCube skybox
	, mat4 skyboxRotation
	, float refractionIndex);

	
vec4 calculateIlluminationColor(
	  vec3 viewPos
	, vec3 viewNormal
	, vec4 innerLightColor
	, Material mat);

// FUNCTIONS ::::::::::::::::::::::::

vec4 calculateReflectionColor(
	  vec3 worldPos
	, vec3 worldNormal
	, vec3 worldCameraPos
	, samplerCube skybox
	, mat4 skyboxRotation)
{
	// When sampling skybox, need to flip x
	vec3 toFragment = normalize(worldPos - worldCameraPos);
	vec3 toCube = reflect(toFragment, worldNormal);
	toCube = vec3(skyboxRotation * vec4(toCube, 0.0f));
	vec4 sample = texture(skybox, vec3(-toCube.x, toCube.y, toCube.z));
	return sample;
}

// refract returns (0,0,0) if the light bounces.
vec4 calculateRefractionColor(
	  vec3 worldPos
	, vec3 worldNormal
	, vec3 worldCameraPos
	, samplerCube skybox
	, mat4 skyboxRotation
	, float refractionIndex)
{
	vec3 toFragment = normalize(worldPos - worldCameraPos);
	vec3 toCube = refract(toFragment, worldNormal, refractionIndex);
	toCube = vec3(skyboxRotation * vec4(toCube, 0.0f));
	vec4 sample = texture(skybox, vec3(-toCube.x, toCube.y, toCube.z));
	return sample;
}
	
vec4 getPointLight(
	  vec3 viewPos
	, vec3 viewNormal
	, Light l
	, Material m)
{
	// Transform light position to view space.
	vec3 viewLightPos = vec3(uViewMatrix * vec4(l.positionDirection, 1.0f));
	
	vec3 toLight = (viewLightPos - viewPos);
	float distanceToLight = length(toLight);
	toLight = normalize(toLight);
	vec3 toFragment = toLight * -1.0f;
	
	float attenuation = getAttenuation(distanceToLight, l.linearAttenuation, l.quadraticAttenuation);
	vec4 color4 = vec4(l.color, 1.0f);
	
	// Ambient color, no Ambient for pointlight
	vec4 ambientResult = vec4(0,0,0,1); //color4 * m.ambientStrength * attenuation;
	
	// Diffuse color
	vec3 norm = normalize(viewNormal);
	float diffuse = max(dot(norm, toLight), 0.0);
	vec4 diffuseResult = m.diffuseStrength * diffuse * color4 * attenuation;
	
	// Specular color
	// In view space the view position is (0,0,0)
	vec3 viewDir = normalize(-viewPos);
	vec3 reflectDir = reflect(toFragment, norm);
	float specular = pow(max(dot(viewDir, reflectDir), 0.0), m.specularPower);
	vec4 specularResult = m.specularStrength * specular * color4 * attenuation;
	
	return (ambientResult + diffuseResult + specularResult);
}

vec4 getDirectionalLight(
	  vec3 viewPos
	, vec3 viewNormal
	, Light l
	, Material m)
{
	// On direction vector the w component must be 0.0f
	vec3 viewLightDir = vec3(uViewMatrix * vec4(l.positionDirection, 0.0f));
	
	float ambientStrength = l.linearAttenuation;
	float attenuation = l.quadraticAttenuation; // Intensity
	vec3 toFragment = viewLightDir;
	vec3 toLight = toFragment * -1.0f;
	// Ambient color
	vec4 lightColor4 = vec4(l.color, 1.0f);
	vec4 ambientResult = lightColor4 * ambientStrength;
	
	// Diffuse color
	vec3 norm = normalize(viewNormal);
	float diffuse = max(dot(norm, toLight), 0.0);
	vec4 diffuseResult = m.diffuseStrength * diffuse * lightColor4 * attenuation;
	
	// Specular color
	// In view space the view position is (0,0,0)
	vec3 viewDir = normalize(-viewPos);
	vec3 reflectDir = reflect(toFragment, norm);
	float specular = pow(max(dot(viewDir, reflectDir), 0.0), m.specularPower);
	vec4 specularResult = m.specularStrength * specular * lightColor4 * attenuation;
	
	return (ambientResult + diffuseResult + specularResult);
}

vec4 calculateIlluminationColor(
	  vec3 viewPos
	, vec3 viewNormal
	, vec4 innerLightColor
	, Material mat)
{
	vec3 viewDir = normalize(-viewPos);
	vec3 norm = normalize(viewNormal);
	float specular = pow(max(dot(viewDir, norm), 0.0), mat.specularPower);
	return specular * innerLightColor;
}

float getAttenuation(float distanceToLight, float linear, float quadratic)
{
	return 1.0f / (1.0f + linear * distanceToLight + quadratic * (distanceToLight * distanceToLight));
}

Material getMaterial(float metallicValue, float roughnessValue)
{
	// Metallic: Diffuse 0.2 Spec 0.8 , Non-metallic Diffuse 0.8 Spec 0.2
	Material mat;
	float diffuseS = 0.8f - step(0.5f, metallicValue) * 0.6f;
	mat.diffuseStrength = diffuseS * (1.0 - roughnessValue);
	float specularS = 0.2f + step(0.5f, metallicValue) * 0.6f;
	mat.specularStrength = specularS * roughnessValue;
	mat.specularPower = 2 + roughnessValue * MAX_SPECULAR_POW;
	return mat;
}

vec4 calculateLights(
	  vec3 viewPos
	, vec3 viewNormal
	, Material mat)
{
	vec4 lightColor = vec4(0,0,0,1);
		// Light 0 is directional
	lightColor += getDirectionalLight(viewPos, viewNormal, uLights[0], mat);
	
	for (int i = 1; i < LIGHT_AMOUNT; i++)
	{
		lightColor += getPointLight(viewPos, viewNormal, uLights[i], mat);
	}
	
	return lightColor;
}

vec4 calculateDirLighting(
	  vec3 worldPos
	, vec3 viewPos
	, vec3 worldNormal
	, vec3 viewNormal
	, vec4 textureColor
	, Material mat)
{
	vec4 skyColor = vec4(0,0,0,1);
	vec4 lightColor = vec4(0,0,0,1);
	// Light 0 is directional
	lightColor += getDirectionalLight(viewPos, viewNormal, uLights[0], mat);
	
	skyColor = calculateReflectionColor(worldPos, worldNormal, uCameraPosition, uCubeMap, uSkyboxRotationMatrix);

	vec4 lightComponent = lightColor;
	vec4 reflectionComponent = skyColor * mat.specularStrength;

	vec4 combined =  textureColor * (lightComponent + reflectionComponent);
	combined.a = 1.0f;

	return combined;		
}

vec4 calculateAllLighting(
	  vec3 worldPos
	, vec3 viewPos
	, vec3 worldNormal
	, vec3 viewNormal
	, vec4 textureColor
	, Material mat)
{
	
	vec4 skyColor = vec4(0,0,0,1);
	
	vec4 lightColor = calculateLights(viewPos, viewNormal, mat);
	skyColor = calculateReflectionColor(worldPos, worldNormal, uCameraPosition, uCubeMap, uSkyboxRotationMatrix);
	
	vec4 lightComponent = lightColor;
	vec4 reflectionComponent = skyColor * mat.specularStrength;
	
	vec4 combined =  textureColor * (lightComponent + reflectionComponent);
	combined.a = 1.0f;
	
	return combined;
}



