#version 430 core

#define MANY_LIGHTS 134

in VertexData {
	vec3 position_world;
	vec3 normal_world;
	vec2 uv;
} vert;

layout(location = 0) out vec4 color;
layout(location = 1) out vec4 normal;

uniform vec3 camera_world;

uniform vec3 materialCoefficients; // x = ambient, y = diffuse, z = specular 
uniform float specularAlpha;
uniform sampler2D diffuseTexture;

uniform struct DirectionalLight {
	vec3 color;
	vec3 direction;
} dirL;

uniform struct PointLight {
	vec3 color;
	vec3 position;
	vec3 attenuation;
} pointL;

uniform int POINTLIGHTCOUNT;
uniform PointLight pointLights[MANY_LIGHTS];

//floor multiplication from http://in2gpu.com/2014/06/23/toon-shading-effect-and-simple-contour-detection/
const int colorLevels = 6;
const float colorScaleFactor = 1.0 / colorLevels;
const int lightingLevels = 8;
const float lightingScaleFactor = 1.0 / lightingLevels;

// rgb to hsv from: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
vec3 rgb2hsv(vec3 c)
{
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c)
{
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

vec3 phong(vec3 n, vec3 l, vec3 v, vec3 diffuseC, float diffuseF, vec3 specularC, float specularF, float alpha, bool attenuate, vec3 attenuation) {
	float d = length(l);
	l = normalize(l);
	float att = 1.0;	
	if(attenuate) att = 1.0f / (attenuation.x + d * attenuation.y + d * d * attenuation.z);
	vec3 r = reflect(-l, n);
	float diffuse = max(0, dot(n, l));
	return (diffuseF * diffuseC * floor(diffuse * lightingLevels) * lightingScaleFactor + specularF * specularC * pow(max(0, dot(r, v)), alpha)) * att; 
}

void main() {	
	vec3 n = normalize(vert.normal_world);
	vec3 v = normalize(camera_world - vert.position_world);
	
	vec3 texColor = texture(diffuseTexture, vert.uv).rgb;
	//quantize color
	vec3 hsvColor = rgb2hsv(texColor);
	hsvColor.z = floor(hsvColor.z * colorLevels) * colorScaleFactor;
	texColor = hsv2rgb(hsvColor);
	color = vec4(texColor * materialCoefficients.x, 1); // ambient
	
	// add directional light contribution
	color.rgb += phong(n, -dirL.direction, v, dirL.color * texColor, materialCoefficients.y, dirL.color, materialCoefficients.z, specularAlpha, false, vec3(0));
	
	// add point light contribution
	for(int j; j<POINTLIGHTCOUNT; j++){
		color.rgb += phong(n, pointLights[j].position - vert.position_world, v, pointLights[j].color * texColor, materialCoefficients.y, pointLights[j].color, materialCoefficients.z, specularAlpha, true, pointLights[j].attenuation);
	}
	//output normals for post processing
	normal = vec4(n, 1.0);
}