varying vec3 v_position;
varying vec3 v_normal;

uniform int num_lights = 2;
uniform bool enable_texture = false;
uniform bool enable_doubleside = false;
uniform bool enable_lighting = true;

uniform sampler2D tex_color;

uniform int shadowmap_pixels = 2048;
uniform int shadow_light_idx = 0;
uniform bool enable_shadow = true;
uniform bool enable_softshadow = true;
uniform float color_shadow_ratio = 0.6;

uniform sampler2D shadow_buffer;

varying vec4 v_shadow_pos;


vec4 GetPhongReflection(vec3 N, vec3 L, vec3 V, int light_idx)
{
	if(enable_doubleside)
	{
		if(!gl_FrontFacing)
			N = -N;
	}

	float LN = dot(L, N);
	if(LN <= 0.0)
		return vec4(0.0, 0.0, 0.0, 0.0);

	vec4 ref_diffuse = gl_FrontLightProduct[light_idx].diffuse * LN;

	vec4 br = ref_diffuse;

	vec3 H = normalize(L + V);
	float NH = dot(N, H);
	if(NH > 0.0)
	{
		float NHP = pow(NH, gl_FrontMaterial.shininess);

		vec4 ref_spec = gl_FrontLightProduct[light_idx].specular * NHP;
		br += ref_spec;
	}

	return br;
}


float get_depth(vec4 uv)
{
	return texture2DProj( shadow_buffer , uv ).x;
}

vec4 get_shadow_uv(void)
{
	vec4 shadow_uv = v_shadow_pos / v_shadow_pos.w;
	shadow_uv += 1.0;
	shadow_uv *= 0.5;
	shadow_uv.w = 1.0;

	return shadow_uv;
}

int GetNumSoftshadowLaysAround8(void)
{
	float thre = -0.01;

	vec4 shadow_uv = get_shadow_uv();
	float frag_depth = shadow_uv.z;

	vec4 c = shadow_uv;
	float d = 1.0 / shadowmap_pixels;
	float s[9];
	s[0] = get_depth( c );
	s[1] = get_depth( vec4(c.x-d, c.y, c.z, c.w) );
	s[2] = get_depth( vec4(c.x+d, c.y, c.z, c.w) );
	s[3] = get_depth( vec4(c.x, c.y-d, c.z, c.w) );
	s[4] = get_depth( vec4(c.x, c.y+d, c.z, c.w) );
	s[5] = get_depth( vec4(c.x-d, c.y-d, c.z, c.w) );
	s[6] = get_depth( vec4(c.x+d, c.y-d, c.z, c.w) );
	s[7] = get_depth( vec4(c.x-d, c.y+d, c.z, c.w) );
	s[8] = get_depth( vec4(c.x+d, c.y+d, c.z, c.w) );

	int num_lay = 0;
	for(int i = 0; i < 9; ++i)
	{
		if( ( s[i] - frag_depth ) >= thre )
			++num_lay;
	}

	return num_lay;
}

bool IsNotShadowed(void)
{
	float thre = -0.01;

	vec4 shadow_uv = get_shadow_uv();
	float frag_depth = shadow_uv.z;

	vec4 c = shadow_uv;
	float d = 1.0 / shadowmap_pixels;
	float s = get_depth( c );

	return ( ( s - frag_depth ) >= thre );
}

float GetLightInRatio(void)
{
	if(enable_softshadow)
	{
		int num_lay = GetNumSoftshadowLaysAround8();
		return (num_lay / 9.0);
	}
	else
	{
		if(IsNotShadowed())
			return 1.0;
		else
			return 0.0;
	}
}

vec4 GetPhongReflectionSumWithShadow(void)
{
	vec4 ret = vec4(0.0, 0.0, 0.0, 0.0);

	vec3 N = normalize(v_normal);

	for(int i = 0; i < num_lights; ++i)
	{
		vec3  light_xyz = gl_LightSource[i].position.xyz;
		float light_w   = gl_LightSource[i].position.w;
		vec3 L = normalize( light_xyz - v_position * light_w );
		vec3 V = normalize( -v_position );

		vec4 br = GetPhongReflection(N, L, V, i);

		if(enable_shadow && i == shadow_light_idx)
			br *= GetLightInRatio();

		ret += br;
	}

	vec4 ref_amb = gl_FrontMaterial.ambient * gl_LightModel.ambient;
	for(int i = 0; i < num_lights; ++i)
	{
		ref_amb += gl_FrontLightProduct[i].ambient;
	}

	ret += ref_amb;

	ret += gl_FrontMaterial.emission;

	ret.x = min(ret.x, 1.0);
	ret.y = min(ret.y, 1.0);
	ret.z = min(ret.z, 1.0);

	ret.w = gl_FrontMaterial.diffuse.w;

	if(enable_texture)
	{
		vec4 t_color = texture2DProj( tex_color , gl_TexCoord[0] );

		ret *= t_color;
	}

	return ret;
}

vec4 GetConstantColorWithShadow(void)
{
	vec4 ret = gl_Color;

	if(enable_texture)
		ret *= texture2DProj(tex_color, gl_TexCoord[0]);

	if(enable_shadow)
		ret.xyz *= (GetLightInRatio() * (1.0 - color_shadow_ratio) + color_shadow_ratio);

	return ret;
}



void main (void)
{
	gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);

	if(enable_lighting)
		gl_FragColor += GetPhongReflectionSumWithShadow();
	else
		gl_FragColor += GetConstantColorWithShadow();
}
