#version 150 compatibility

// fragment shader for map rendering

#define sp_map			0
#define sp_water		1
#define sp_light    	2
#define sp_cars			3
#define sp_actors		4
#define sp_particles	5
#define sp_triggers		6
#define sp_decals		7
#define sp_gui			10

// https://learnopengl.com/Lighting/Basic-Lighting

// TODO: light parameters.

uniform vec2 u_resolution;  	// Canvas size (width,height)
uniform vec2 u_mouse;       	// mouse position in screen pixels
uniform float u_time;       	// Time in seconds since load

uniform int ShadowsPass;		// Pipeline: shadow mapping shadows
uniform float AmbientLighting;	// minimum ambient brightness 0..1, even in shadows
uniform float ShadowSharpness;	// adjusting this gives more blurred shadows.. this value is in relation to how big shadow is (dimensions of world).

uniform int uRenderingPass;		// what is being rendered? 0 = map, 1 = water, 3 = cars, 4 = actors, 5 = particles, 10 = gui
uniform float uAddHue;			
uniform float uAddSaturation;	
uniform float uAddLightness;	
uniform bool uAddShadows;		// Should shadow mapping be taken into account during rendering or not
uniform float uShadowOpacity;	
uniform bool uBurnt_out;		
uniform bool uShadow_mode;		
uniform float uTransluscency;	

// shadow mapping
uniform sampler2D ShadowMap;
varying vec4 ShadowCoord_LightSpace; // passed from vertex shader

uniform sampler2D Texture1; // need
uniform vec3 PlayerPosition;
uniform vec3 CameraVector;
uniform int UndergroundMode;
uniform float UndergroundIntensity;

// Passed in from the vertex shader.
varying vec3 Playercheckcoords; // Player coordinates, for visual effects
varying vec2 v_texcoord;
varying vec4 VertexColor;
varying vec3 vertexcoords; // Vertex coordinates without modelview (so where in world it actually is)
varying vec4 WorldCoords;
varying vec3 normcoords; // unused



float Hue_2_RGB(float v1, float v2, float vH ) {
	
	float result;
	
	if (vH < 0.0)
		vH += 1.0;
	
	if (vH > 1.0)
		vH -= 1.0;
	
	if ((6.0 * vH) < 1.0) {
		result = (v1 + (v2 - v1) * 6.0 * vH); 
	} else if ((2.0 * vH) < 1.0) {
		result = ( v2 );
	} else if ((3.0 * vH) < 2.0) {
		result = (v1 + (v2 - v1) * ((2.0 / 3.0) - vH) * 6.0);
	} else {
		result = v1;
	}
	
	return result;
	
}

// convert to HSL
vec4 RGB2HSL(float R, float G, float B) {
	
	float H, S, L;
	
	float Cmax, Cmin;
	float D;
	
	Cmax = max (R, max (G, B));
	Cmin = min (R, min (G, B));
	
	// calculate lightness
	L = (Cmax + Cmin) / 2.0;
	
	if (Cmax == Cmin) { // it's grey
		H = 0.0; // it's actually undefined
		S = 0.0;
	} else {
		D = Cmax - Cmin; // we know D != 0 so we cas safely divide by it
	}
	
	// calculate Saturation
	if (L < 0.5) {
		S = D / (Cmax + Cmin);
	} else {
		S = D / (2.0 - (Cmax + Cmin));
	}
	
	// calculate Hue
	if (R == Cmax) {
		
		H = (G - B) / D;
		
	} else {
		
		if (G == Cmax) {
			H = 2.0 + (B - R) /D;
		} else {
			H = 4.0 + (R - G) / D;
		}
	}
	
	H = H / 6.0;
	
	if (H < 0.0) {
		H = H + 1.0;
	}
	
	return vec4(H, S, L, 0.0);
	
}

vec4 HueShiftColor(vec4 color_input, float ChangeHue, float ChangeSat, float ChangeLit, bool burnt_out, bool shadow_mode, float ShadowOpacity, float in_transluscency) {
	
	float ModifyHue = ChangeHue;
	float ModifySat = ChangeSat;
	float ModifyLit = ChangeLit;
	
	if (shadow_mode) // shadow mode
		return vec4(0,0,0, (color_input.a * ShadowOpacity) );
	
	if ( burnt_out ) { // exploded burnt out rendering mode
		
		ModifyHue = 0;
		ModifySat = -1;
		ModifyLit = -0.2;
		
	}
	
	float H, S, L;
	float R, G, B;
	
	R = color_input.r;
	G = color_input.g;
	B = color_input.b;
	
	// properly process white color which cannot be properly converted to hsl and back (ends up black)
	if ((R == 1.0) && (G == 1.0) && (B == 1.0))
		return color_input;
	
	vec4 hsl = RGB2HSL(R, G, B);
	
	H = hsl.x;
	S = hsl.y;
	L = hsl.z;
	
	// modify H/S/L values
	H += ModifyHue;
	S += ModifySat;
	L += ModifyLit;
	
	/*if (VertexColor.r < 1.0)
		L -= (1 - VertexColor.r);
	*/
	
	if (H < 0.0) {
		H = H + 1.0;
	}
	
	// clamp H,S and L to [0..1]
	
	if (burnt_out) // exploded burnt out rendering mode
		L = L * 0.5; // clamp lightness so it removes highlights, TODO: maybe just multiply everything by 0.5 ?
	
	H = clamp(H, 0.0, 1.0);
	S = clamp(S, 0.0, 1.0);
	L = clamp(L, 0.0, 1.0);
	
	// convert back to RGB
	float var_2, var_1;
	
	if (S == 0.0) {
		
		R = L;
		G = L;
		B = L;
		
	} else {
		
		if ( L < 0.5 ) { 
			var_2 = L * ( 1.0 + S );
		} else {
			var_2 = ( L + S ) - ( S * L );
		}
		
		var_1 = 2.0 * L - var_2;
		
		R = Hue_2_RGB( var_1, var_2, H + ( 1.0 / 3.0 ) );
		G = Hue_2_RGB( var_1, var_2, H );
		B = Hue_2_RGB( var_1, var_2, H - ( 1.0 / 3.0 ) );
		
	}
	
	return vec4(R, G, B, color_input.a * in_transluscency) * VertexColor;
	
}

void main() {
	
	// Get fragment color
	vec4 color = texture2D(Texture1, gl_TexCoord[0].st);
	
	if (uRenderingPass == sp_map) {
		
		// Produce cone shaped holes in ground, to see thru ceilings
		if (UndergroundIntensity > 0.0) {
			
			vec3 comparison = vertexcoords.xyz;
			float maxdist = 3.0;
			vec3 checkpcoords = Playercheckcoords;
			checkpcoords.z += maxdist;
			
			float Dist2D = distance(checkpcoords.xy, comparison.xy);
			float dist3d = distance(checkpcoords.xyz, comparison.xyz);
			
			// discard based on sort of cone shape, 0.4 is minimum height at which the effect starts.
			
			float offset = 0.0;
			
			if (UndergroundMode > 1) // /* + 0.4 USE THIS WHEN STANDING ON angled surfaces. */
				offset = 0.4;
			
			if ( (Dist2D < maxdist) && (comparison.z > (Playercheckcoords.z + offset) + (Dist2D * 0.25)) ) {
				
				maxdist = exp2(dist3d);
				
				float how_much_alpha = (Dist2D * 0.35 /*(1.0 /maxdist)*/);
				
				color.a = color.a * (how_much_alpha + (1.0 - UndergroundIntensity) );
				
			}
			
		}
		
	}
	
	float shadow = 1.0;
	
	if (ShadowsPass != 0) {
		
		vec4 shadowCoordinateWdivide = ShadowCoord_LightSpace; // dividing - / ShadowCoord_LightSpace.w  is needed only for projected lights?
		
		float bias = 0.001 * 0.4;
		
		// Debugging if shadow mapping projection is correct.
		// gl_FragColor = texture2D(ShadowMap, shadowCoordinateWdivide.st); //  + (color * 0.5)
		
		/*
		
		// primitive technique:
		// Used to lower moiré pattern and self-shadowing
		// shadowCoordinateWdivide.z -= bias;
		float distanceFromLight = texture2D(ShadowMap, shadowCoordinateWdivide.st).r; // was .z
		
	 	if (ShadowCoord_LightSpace.w > 0.0) // This is when we try to sample value behind the light's view frustrum.
	 		shadow = distanceFromLight < shadowCoordinateWdivide.z ? 0.5 : 1.0;
		
		*/
		
		// poisson disk, small:
		/*
		vec2 poissonDisk[4] = vec2[](
			vec2( -0.94201624, -0.39906216 ),
			vec2( 0.94558609, -0.76890725 ),
			vec2( -0.094184101, -0.92938870 ),
			vec2( 0.34495938, 0.29387760 )
		);
		*/
		
		// poisson disk, large:
		int num_samples = 16;
		
		vec2 poissonDisk[16] = vec2[]( 
		   vec2( -0.94201624, -0.39906216 ), 
		   vec2( 0.94558609, -0.76890725 ), 
		   vec2( -0.094184101, -0.92938870 ), 
		   vec2( 0.34495938, 0.29387760 ), 
		   vec2( -0.91588581, 0.45771432 ), 
		   vec2( -0.81544232, -0.87912464 ), 
		   vec2( -0.38277543, 0.27676845 ), 
		   vec2( 0.97484398, 0.75648379 ), 
		   vec2( 0.44323325, -0.97511554 ), 
		   vec2( 0.53742981, -0.47373420 ), 
		   vec2( -0.26496911, -0.41893023 ), 
		   vec2( 0.79197514, 0.19090188 ), 
		   vec2( -0.24188840, 0.99706507 ), 
		   vec2( -0.81409955, 0.91437590 ), 
		   vec2( 0.19984126, 0.78641367 ), 
		   vec2( 0.14383161, -0.14100790 ) 
		);
		
		float poisson_divider = 700.0 * ShadowSharpness; // adjusting this gives more blurred shadows.. this value is in relation to how big shadow is (dimensions of world).
		
		for (int i = 0; i < num_samples; i++) {
			if ( texture( ShadowMap, shadowCoordinateWdivide.st + poissonDisk[i] / poisson_divider ).r  < shadowCoordinateWdivide.z - bias ){
				shadow -= (1.0 / num_samples);
			}
		}
		
		// Take ambient brightness into account
	  	shadow = AmbientLighting + (shadow * (1.0 - AmbientLighting));
	}
	
	/*
	#define sp_map			0
	#define sp_water		1
	#define sp_cars			3
	#define sp_actors		4
	#define sp_particles	5
	#define sp_triggers		6
	#define sp_decals		7
	#define sp_gui			10
	*/
	
  	// color is fetched from texture, alpha is already modified by transluscency
  	
  	// sp_water
  	
  	
  	if (uRenderingPass == sp_light) {
  		
  		color = texture2D(Texture1, gl_TexCoord[0].st);
		
		//color = color * VertexColor; // take vertex color
		
		//color = vec4(1, 1, 1, 1);
		
		gl_FragColor = vec4(color.r, color.g, color.b, color.a);
		
  	} else if (uRenderingPass == sp_decals) {
  		
  		color = texture2D(Texture1, gl_TexCoord[0].st);
		
		color = color * VertexColor; // take vertex color
		
		gl_FragColor = vec4(color.r * shadow, color.g * shadow, color.b * shadow, color.a);
		
  	} else if (uRenderingPass == sp_particles) {
  		
		color = color * VertexColor; // take vertex color
		
		if (uAddShadows)
			gl_FragColor = vec4(color.r * shadow, color.g * shadow, color.b * shadow, color.a);
		else
			gl_FragColor = vec4(color.r, color.g, color.b, color.a);
		
  	} else if (uRenderingPass == sp_actors) { // actors pass
  		
  		color = HueShiftColor(color, 0.0, 0.0, 0.0, uBurnt_out, uShadow_mode, uShadowOpacity, uTransluscency);
		gl_FragColor = vec4(color.r * shadow, color.g * shadow, color.b * shadow, color.a);
		
	} else if (uRenderingPass == sp_cars) { // vehicles pass
		
		//if (uShadow_mode) {
		//	
		//	float original_alpha = texture2D(Texture1, gl_TexCoord[0].st).a;
		//	
		//	vec4 color_only = HueShiftColor(color, uAddHue, uAddSaturation, uAddLightness, uBurnt_out, uShadow_mode, uShadowOpacity, uTransluscency);
		//	
		//	vec4 shadow_only = vec4(shadow, shadow, shadow, original_alpha - (1 - shadow));
		//	
		//	gl_FragColor = shadow_only;
		//	
		//	/*
		//	if (shadow < 1) {
		//		
		//		if (color.a = 1) {
		//			
		//			color = vec4(0, 0, 0, shadow - color.a);
		//			
		//			gl_FragColor = vec4(color.r, color.g, color.b, color.a);
		//			
		//		}
		//		
		//	} else {
		//		
		//	}
		//	*/
		//	
		//} else {
			
			color = color * VertexColor; // take VertexColor into account so things like EM lights and car damage can work.
			
			color = HueShiftColor(color, uAddHue, uAddSaturation, uAddLightness, uBurnt_out, uShadow_mode, uShadowOpacity, uTransluscency);
			
			if (uAddShadows)
				gl_FragColor = vec4(color.r * shadow, color.g * shadow, color.b * shadow, color.a);
			else
				gl_FragColor = vec4(color.r, color.g, color.b, color.a);
			
		//}
		
	} else {
		
		gl_FragColor = vec4(color.r * shadow, color.g * shadow, color.b * shadow, color.a);
		
	}
	
	
	
}
