#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "../common/objects.h"
#include "../common/vector.h"
#include "../render/intersect/intersect.h"
#include "../common/structs.h"
#include "../common/debug.h"
#include "phong.h"

#define SHADOW_RAYS 200
extern scene_data* main_scene;

int shadow_cache = -1;

// {{{ spherelight
double spherelight(int light_num, intersect_data *id)
{
	vector lightV;
	point lightP;
	object *ob;
	point tracePt;	//1st object on ray from object to light
	point *intersectP = &id->intersect;
	double shadow=1.0;
	double ray_scale=(1.0/SHADOW_RAYS);
	int i;
	double precalc_radius = main_scene->lights[light_num]->radius * 2;
	double radius = main_scene->lights[light_num]->radius;
	double old_shadow = shadow;
	double epsilon = 0.000000005;
	int next_check = 40;
	//double LdpL;

	printd(CRAZY, "shadow calcs\n");

		for(i = 0; i < SHADOW_RAYS; i++)
		{
			//shadow adaptively
			//keep going only if the shadow is getting darker
			if(i == next_check)
			{
				if(old_shadow < shadow + shadow * epsilon)
					return shadow;
				
				next_check += 30;
				old_shadow = shadow;
			}

			//random point close to the light
			lightP.x = main_scene->lights[light_num]->pos.x - 
				((double)rand()/RAND_MAX) *precalc_radius - radius;
			lightP.y = main_scene->lights[light_num]->pos.y - 
				((double)rand()/RAND_MAX) *precalc_radius - radius;
			lightP.z = main_scene->lights[light_num]->pos.z - 
				((double)rand()/RAND_MAX) *precalc_radius - radius;

			//from the object to the light
			make_vector(&lightP, intersectP, &lightV);
			normalize(&lightV);

			/* calculate shadow */
			ob = intersect(intersectP, &lightV, &tracePt, 1);

			if ( ob != NULL && distance_between(intersectP, &tracePt) <
					distance_between(intersectP, &lightP))
			{
				shadow -= ray_scale;
			}
		}
		
//		if(shadow == 1.0)
//			return 2.0;

		return shadow;
} //}}}

double pointlight_fast(int light_num, intersect_data *id, vector *lightV)
{
	object *ob;
	point closest_intersection;	//1st object on ray from object to light
	point *start_point = &id->intersect;
	double distance_to_light;
	double distance_closest_object;
	
	sub_vectors(lightV, &main_scene->lights[light_num]->pos, start_point);
	normalize(lightV);
	
	distance_to_light = distance_between(start_point, &main_scene->lights[light_num]->pos);
	
	if(shadow_cache > 0)
	{
		distance_closest_object = intersect_object(start_point, lightV, main_scene->models[shadow_cache], 1);
		if(distance_closest_object > EPSILON && distance_closest_object < distance_to_light)
			return 0.5;
	}
	
	//shadow cache didn't help, time to check all objects
	ob = intersect(start_point, lightV, &closest_intersection, 1);
	distance_closest_object = distance_between(start_point, &closest_intersection);

	//if something is in the way, there is a shadow
	if ( ob != NULL && distance_closest_object < distance_to_light)
	{
		shadow_cache = ob->id;
		return 0.5;
	}
	
	return 1;
}

double pointlight(int light_num, intersect_data *id)
{
	vector lightV;
	point lightP;
	object *ob;
	point tracePt;	//1st object on ray from object to light
	point *intersectP = &id->intersect;
	double distance_to_light;
	double distance_closest_object;
	//double shadow=1;
	//int i;
	//double LdpL;


	printd(CRAZY, "shadow calcs\n");
	lightV.x = main_scene->lights[light_num]->pos.x - intersectP->x;
	lightV.y = main_scene->lights[light_num]->pos.y - intersectP->y;
	lightV.z = main_scene->lights[light_num]->pos.z - intersectP->z;

	normalize(&lightV);
	lightP.x = main_scene->lights[light_num]->pos.x;
	lightP.y = main_scene->lights[light_num]->pos.y;
	lightP.z = main_scene->lights[light_num]->pos.z;
	
	distance_to_light = distance_between(intersectP, &lightP);
		   
	if(shadow_cache > 0)
	{
		distance_closest_object = intersect_object(intersectP, &lightV, main_scene->models[shadow_cache], 1);
		if(distance_closest_object > EPSILON && distance_closest_object < distance_to_light)
			return 0;
	}

   //intersect towards light
   ob = intersect(intersectP, &lightV, &tracePt, 1);
   
   distance_closest_object = distance_between(intersectP, &tracePt);
   //if something is in the way, there is a shadow
   if ( ob != NULL && distance_closest_object < distance_to_light)
   {
	   shadow_cache = ob->id;
	   return 0;
   }
	return 1;
}// }}}



double infinite_light(int light_num, intersect_data *id)
{
	object *ob;
	point tracePt;	//1st object on ray from object to light
	point *intersectP = &id->intersect;

	//light is at infinity, so the pos is the light vector
	//the position should already be normalized

	// see if there is an object from intersection to light
	ob = intersect(intersectP, &main_scene->lights[light_num]->pos, &tracePt, 1);

	//if there is any object, it block the light
	if ( ob != NULL ) 
		return 0;
	
	return 1;
}


// {{{ spotlight
double spotlight(int light_num, intersect_data *id)
{
	vector lightV;
	point lightP;
	object *ob;
	vector spotV;
	point tracePt;	//1st object on ray from object to light
	point *intersectP = &id->intersect;
	double shadow=1;
	//double dis = 0.5;
	//int i;
	double LdpL;


	/* light vector */
	lightV.x = main_scene->lights[light_num]->pos.x - intersectP->x;
	lightV.y = main_scene->lights[light_num]->pos.y - intersectP->y;
	lightV.z = main_scene->lights[light_num]->pos.z - intersectP->z;
	normalize(&lightV);
	
	make_vector(&main_scene->lights[light_num]->pos, &main_scene->lights[light_num]->norm, &spotV);
	normalize(&spotV);
	LdpL = dot_product(&lightV, &spotV);

	if( LdpL <= 0 || LdpL < 0 )
	{
		shadow = 0;
		return shadow;
	}
	
	//scale
	LdpL = 1 - distance_between(&spotV, &lightV) / main_scene->lights[light_num]->radius;
	if(LdpL > 1)
		return 0;

	LdpL = LdpL * main_scene->lights[light_num]->radius;

	lightP.x = main_scene->lights[light_num]->pos.x;
	lightP.y = main_scene->lights[light_num]->pos.y;
	lightP.z = main_scene->lights[light_num]->pos.z;


	/* calculate shadow */
	ob = intersect(intersectP, &lightV, &tracePt, 1);

	/* if we are in a shadow, try the next light (ambient is the only
	 * lighting needed and it is already done */
	if ( ob != NULL && distance_between(intersectP, &tracePt) <
			distance_between(intersectP, &lightP))
		shadow = 0;
	else
		shadow = LdpL * 1;

	return shadow;
} // }}}

// {{{get_shadow
double get_shadow(int light_num, intersect_data *id)
{
	//vector lightV;
	//point lightP;
	//object *ob;
	//point tracePt;	//1st object on ray from object to light
	//point *intersectP = &id->intersect;
	double shadow=1;
	//int i;
	//double LdpL;

	printd(CRAZY, "shadow calcs\n");
	if (main_scene->lights[light_num]->obj_type == LIGHT_INFINITE)
	{
		shadow = pointlight(light_num, id);
	}
	else if( main_scene->lights[light_num]->obj_type == LIGHT_SPOT)
	{
		shadow = spotlight(light_num, id);
	}
	else if( main_scene->lights[light_num]->obj_type == LIGHT_POINT)
	{
		shadow = pointlight(light_num, id);
	}
	else if (main_scene->lights[light_num]->obj_type == LIGHT_SPHERE)
	{
		shadow = spherelight(light_num, id);
	}

	return shadow;
} //}}}
