#version 330 core

in vec2 vTex;
in float vFade;

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

uniform mat4 viewMatrix;		// View matrix

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 = used_material.diffuse * light.diffuse;
	float NdotL = max(dot(frag_normal, L), 0.0);
	vec3 diffuse_reflect = used_material.diffuse * light.diffuse * NdotL;
	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;
	
	float distance = length(light.position - frag_3D_position);
	float intensity = 1.0 / (1.0 + 0.7*distance + 1.8*distance*distance);
	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 =  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 = 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);
}

out vec4 fragColor;

uniform sampler2D leafTexture;
uniform sampler2D petalTexture;
uniform float uTexMix;
uniform vec4 uTintColor;


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

	vec3 globalAmbientLight = used_material.ambient;

    vec4 leaf = texture(leafTexture, vTex);
    vec4 petal = texture(petalTexture, vTex);
    
    vec4 col = mix(leaf, petal, uTexMix);

	float leafBoost  = 1.2;
	float petalBoost = 1.0;

	float boost = mix(leafBoost, petalBoost, uTexMix);
	col.rgb *= boost;
	col.rgb = clamp(col.rgb, 0.0, 1.0);
    //make it transparent
    if (col.a < 0.1)
        discard;

	used_material.diffuse = col.rgb;
	used_material.ambient = col.rgb * 0.2;

	globalAmbientLight = vec3(0.08); 
	//fragColor = vec4(used_material.ambient * globalAmbientLight, 1.0f);
	fragColor = vec4(used_material.ambient * globalAmbientLight, 1.0);
	for (int i = 0; i < light_amount; i++) {
		fragColor += calculateLight(light_group[i]);
	}
	fragColor.a = col.a;
    fragColor.a *= vFade;
}