#version 430

smooth in vec3 camspace_position; // position for per fragment lighting
smooth in vec3 camspace_normal;   // normal for per fragment lighting
smooth in vec2 frag_uv_coordinate;// UV coordinate
smooth in vec3 frag_3D_position;  // 3D position of fragment in wolrd coordinates
vec3 frag_normal;				  // normalized camspace_normal

uniform mat4 ViewMatrix;		// View matrix

out vec4 frag_color; // fragment color

struct Material {
	vec3  ambient;       // ambient component
	vec3  diffuse;       // diffuse component
	vec3  specular;      // specular component
	float shininess;     // sharpness of specular reflection
};

struct Light {
	vec3 ambient;			// intensity & color of the ambient component
	vec3 diffuse;			// intensity & color of the diffuse component
	vec3 specular;			// intensity & color of the specular component
	vec3 position;			// light position, direction for directional (not in camera view)
	vec3 spot_direction;	// direction for spotlight (not in camera view)
	float spot_cos_cutoff;	// cosine of cutoff of spotlight half-angle
	float spot_exponent;	// intensity distribution within spotlight cone
	int light_type;			// 0 -> is directional light, 1 -> is spotlight, 2 -> is point light
};

Material used_material;
uniform Material material;	// material used by object
//uniform Light light;		// debug only, one hardcoded light
#define MAX_LIGHT_AMOUNT 100// max amount of lights used for light array
uniform int light_amount; // amount of lights
uniform Light[MAX_LIGHT_AMOUNT] light_group; // light array

uniform sampler2D sampler;
uniform bool use_texture;

//uniform vec4 fog_color;
//uniform float fog_density;

vec4 calculateSpot(Light light) {
	vec3 ret = vec3(0.0); // prepare black return value

	// transform light to camera view
	vec3 camspace_light_pos = vec3(ViewMatrix * vec4(light.position, 1.0));
	vec3 camspace_light_dir = vec3(ViewMatrix * vec4(light.spot_direction, 0.0));

	// get LRV
	vec3 L = normalize(camspace_light_pos - camspace_position);
	vec3 R = reflect(-L, frag_normal);
	vec3 V = normalize(-camspace_position);

	//if (dot(frag_normal, L) < 0) return vec4(ret, 1.0);

	// calculate component effects
	vec3 diffuse_reflect = max(dot(L, frag_normal), 0) * used_material.diffuse * light.diffuse;
	vec3 specular_reflect = pow(max(dot(R, V), 0), material.shininess) * light.specular * used_material.specular;
	vec3 ambient_reflect = light.ambient * used_material.ambient;

	// get spotlight intesity
	float spotlight_effect = 0;
	float alpha = dot(camspace_light_dir, -L);

	if (alpha >= light.spot_cos_cutoff) {
		spotlight_effect = pow(max(alpha, 0), light.spot_exponent);
	}

	// calculate distance loss
	float distance = distance(light.position, frag_3D_position);
	float intensity = 1;
	if (distance != 0) {
		intensity = 1 / (distance * 0.05);
	}

	// get color
	ret += intensity * spotlight_effect * (diffuse_reflect + specular_reflect + ambient_reflect);
	return vec4(ret, 1.0);
}

vec4 calculatePoint(Light light){
	vec3 ret = vec3(0.0); // prepare black return value

	// transform light to camera view
	vec3 camspace_light_pos = vec3(ViewMatrix * vec4(light.position, 1.0));

	// get LRV
	vec3 L = normalize(camspace_light_pos - camspace_position);
	vec3 R = reflect(-L, frag_normal);
	vec3 V = normalize(-camspace_position);

	// calculate component effects
	vec3 diffuse_reflect = max(dot(L, frag_normal), 0) * used_material.diffuse * light.diffuse;
	vec3 specular_reflect = pow(max(dot(R, V), 0), material.shininess) * light.specular * used_material.specular;
	vec3 ambient_reflect = light.ambient * used_material.ambient;

	// calculate distance loss
	float distance = distance(light.position, frag_3D_position);
	float intensity = 1;
	if (distance != 0) {
		intensity = 1 / (distance * 0.05);
	}

	// get color
	ret += intensity * (diffuse_reflect + specular_reflect + ambient_reflect);
	return vec4(ret, 1.0);
}

vec4 calculateDirectional(Light light) {
	vec3 ret = vec3(0.0); // prepare black return value

	// transform light to camera view
	vec3 camspace_light_pos = vec3(ViewMatrix * vec4(light.position, 0.0));

	// get LRV
	vec3 L = normalize(camspace_light_pos);
	vec3 R = reflect(-L, frag_normal);
	vec3 V = normalize(-camspace_position);

	//if (dot(frag_normal, L) < 0) return vec4(ret, 1.0);

	// calculate component effects
	vec3 diffuse_reflect = max(dot(L, frag_normal), 0.0) * used_material.diffuse * light.diffuse;
	vec3 specular_reflect = pow(max(dot(R, V), 0.0), material.shininess) * light.specular * used_material.specular;
	vec3 ambient_reflect = light.ambient * used_material.ambient;

	// get color
	ret += diffuse_reflect + specular_reflect + ambient_reflect;
	return vec4(ret, 1.0);
}

vec4 calculateLight(Light light) {
	if (light.light_type == 0) return calculateDirectional(light);
	else if(light.light_type == 1) return calculateSpot(light);
	else return calculatePoint(light);
}

//void fog(){
//	float f = exp(fog_density * camspace_position.z);
//	frag_color = mix(fog_color, frag_color, clamp(f, 0, 1));
//}

void main(){
	used_material = material;
	frag_normal = normalize(camspace_normal);
	float alpha = 1.0;

	vec3 globalAmbientLight = used_material.ambient;
	//frag_color = vec4(0.0, 0.0, 0.0, 0.0);
	if (use_texture) {
		used_material.diffuse = vec3(texture(sampler, frag_uv_coordinate).rgb);
		used_material.ambient = vec3(texture(sampler, frag_uv_coordinate).rgb);
		//used_material.shininess = (1.0f - ((1.0 - used_material.specular.x) * (1.0 - used_material.specular.y))) * 20.0f;
		alpha = texture(sampler, frag_uv_coordinate).a;
		if (alpha < 0.3f) discard;
	}
	frag_color = vec4(used_material.ambient * globalAmbientLight, 1.0);
	for (int i = 0; i < light_amount; i++) {
		frag_color += calculateLight(light_group[i]);
	}
	frag_color = vec4(frag_color.rgb, alpha);
	//frag_color += calculateLight(light);
	//fog();
}