#include <math.h>
#include <time.h>
#include <stdlib.h>
#include "shaders.h"
#include "../common/vector.h"
#include "../render/tracer.h"
#include "../common/structs.h"
#include "../common/colors.h"
#include "../common/debug.h"
#include "../common/misc.h"

extern int DEBUG_LEVEL;
extern clock_t current_time;
extern scene_data* main_scene;

void back_black(intersect_data *id, color *bg)
{
	printd(DEBUG, "{back_color\n");

	bg->r = 0;
	bg->g = 0;
	bg->b = 0;

	printd(DEBUG, "}back_color\n");
}

void back_gradiant(intersect_data *id, color *bg)
{
	printd(DEBUG, "{back_color\n");
	bg->r = (id->pos.x + id->pos.y) / 3;  //gradiant
	bg->g = (id->pos.x + id->pos.y) / 3;
	bg->b = (id->pos.x + id->pos.y) / 3;

	printd(DEBUG, "}back_color\n");
}


void purple_gradiant(intersect_data *id, color *bg)
{
	printd(DEBUG, "{back_color\n");
	bg->r = (id->pos.x + id->pos.y) / 3 * 0.1;  //gradiant
	bg->g = (id->pos.x + id->pos.y) / 3 * 0;
	bg->b = (id->pos.x + id->pos.y) / 3 * 0.3;

	printd(DEBUG, "}back_color\n");
}

void back_mountains(intersect_data *id, color *bg)
{
	#define num_mountains 20
	double cosY;
	double cosX;
	vector y_axis;
	vector x_axis;
	//int seed = 31337;
	double mountains[num_mountains] = {0.23, 0.87, 0.65, 0.11, 0.45, 0.185, 0.48, 0.8684,
		0.4183, 0.3285, 0.973, 0.371, 0.4, 0.7113, 0.99285, 0.37, 0.576, 0.48, 0.013, 0.8856};
	//int i;
	int first_mountain;
	int second_mountain;
	double projHeight = 0.20;
	double mountainHeight;
	double percent;
	
	printd(DEBUG, "{back_color\n");
	
	set_tri(&y_axis, 0., 1., 0.);
	set_tri(&x_axis, 1., 0., 0.);
	
	normalize(&id->proj);
	//nice sunset gradiant
	cosY = dot_product(&id->proj,&y_axis);
	bg->r = (1-cosY)*255;
	bg->g = (1-cosY)*100;
	bg->b = cosY*255;

	//mountains
	
	//srand(seed);  //random numbers are slowing down frameless rendering
	//for(i=0; i<num_mountains; i++)
	//	mountains[i] = (double)rand()/RAND_MAX;

	cosX = dot_product(&id->proj,&x_axis);
	cosX += 1; //adjust range to 0-2
	cosX = cosX * num_mountains / 2; //adjust range to 0 - num_mountains

	first_mountain = floor(cosX);
	second_mountain = first_mountain + 1;
	if(second_mountain >= num_mountains)
		second_mountain = 0;
	
	percent = cosX - first_mountain;
	mountainHeight = (1-percent)*mountains[first_mountain] + 
			(percent)*mountains[second_mountain];
	mountainHeight = cosY - mountainHeight * projHeight;
	
	if(mountainHeight < 0)
	{
		bg->r = 75*(1+mountainHeight*5);
		bg->g = 50*(1+mountainHeight*5);
		bg->b = 0;
	}

	//srand((unsigned) time(NULL));  //back to real 'random' numbers
	printd(DEBUG, "}back_color\n");
}

void refract_color(intersect_data *id, color *new_color)
{
	// this shader doesn't work right since the transmitted vector hits the same
	// object again from the inside
	intersect_data new_id;

	new_id.step = id->step + 1;
	CHECK_STEPS(new_id.step, main_scene->max_recurs);
	printd(DEBUG, "{refract_color\n");

	copy_vector(&id->intersect, &new_id.start);

	if(id->obj->refract <= 3.5 && id->obj->refract > 1) 
	{
		//print_vector(100, "proj1: ", &id->proj);
		refract_vector(&id->normal, &id->proj, id->obj->refract, &new_id.proj);
		//print_vector(100, "proj: ", &new_id.proj);
		new_id.obj_num = trace(&new_id.start, &new_id.intersect, &new_id.proj, new_id.step); 

		new_id.obj = main_scene->models[new_id.obj_num];
		new_id.obj->shader(&new_id, new_color);
	}
	printd(DEBUG, "}refract_color\n");
}

void reflect_color(intersect_data *i_data, color *new_color)
{
	intersect_data new_id;

	new_id.step = i_data->step + 1;
	CHECK_STEPS(new_id.step, main_scene->max_recurs);	

	printd(DEBUG, "{reflect_color\n");

	copy_vector(&i_data->intersect, &new_id.start);

	/* trace the reflection if needed */	
	if(i_data->obj->reflect > 0)
	{

		reflect_vector(&i_data->proj, &i_data->normal, &new_id.proj);
		new_id.obj_num = trace(&new_id.start, &new_id.intersect,
				&new_id.proj, new_id.step);
		new_id.obj = main_scene->models[new_id.obj_num];

		new_id.obj->shader(&new_id, new_color);
		//id->obj->shader(&new_id, new_color);
		//general_shader(&new_id, new_color);
	}
	printd(DEBUG, "}reflect_color\n");
} //}}}

void trans_color(intersect_data *id, color *new_color)
{
	intersect_data new_id;
	printd(DEBUG, "{trans_color\n");

	new_id.pos.x = id->pos.x;
	new_id.pos.y = id->pos.y;
	new_id.step = id->step + 1;
	CHECK_STEPS(new_id.step, main_scene->max_recurs);

	copy_vector(&id->intersect, &new_id.start);
	copy_vector(&id->proj, &new_id.proj);

	/* trace the reflection if needed */	
	if(id->obj->trans > 0)
	{
		new_id.obj_num = trace(&new_id.start, &new_id.intersect,
				&new_id.proj, new_id.step);
		new_id.obj = main_scene->models[new_id.obj_num];

		new_id.obj->shader(&new_id, new_color);
	}
	printd(DEBUG, "}trans_color\n");
} //}}}

void black_white_filter(intersect_data *id, color *new_color)
{
	intersect_data new_id;
	double bw_color = 0.0;

	//DEBUG_LEVEL = 0;
	printd(DEBUG, "{black_white_filter\n");

	new_id.step = id->step + 1;
	CHECK_STEPS(new_id.step, main_scene->max_recurs);
	
	copy_vector(&id->intersect, &new_id.start);
	copy_vector(&id->proj, &new_id.proj);
	/* trace the reflection if needed */	
	//if(id->obj->reflect > 0)
	{
		new_id.start.x -= id->normal.x*0.01;
		new_id.start.y -= id->normal.y*0.01;
		new_id.start.z -= id->normal.z*0.01;

		//reflect_vector(&id->normal, &id->proj, &new_id.proj);
		new_id.obj_num = trace(&new_id.start, &new_id.intersect,
				&new_id.proj, new_id.step);
		new_id.obj = main_scene->models[new_id.obj_num];

		new_id.obj->shader(&new_id, new_color);

		bw_color = new_color->r + new_color->g + new_color->b;
		bw_color = bw_color / 3;
		new_color->r = bw_color;
		new_color->g = bw_color;
		new_color->b = bw_color;
	}
	printd(DEBUG, "}\n");
	//DEBUG_LEVEL = 5;
} //}}}

void portal_filter(intersect_data *id, color *new_color)
{
	intersect_data new_id;
	vector bend;
	point portal;

	//DEBUG_LEVEL = 0;
	printd(DEBUG, "{portal_filter\n");

	new_id.step = id->step + 1;
	CHECK_STEPS(new_id.step, main_scene->max_recurs);
	
	portal.x = id->obj->amb.r;
	portal.y = id->obj->amb.g;
	portal.z = id->obj->amb.b;
	
	//vector from portal to track point (will be new proj)
	make_vector(&main_scene->camera->lookP, &portal, &new_id.proj);
	normalize(&new_id.proj);

	//next start point
	copy_vector(&portal, &new_id.start);

	//amount the current proj differs from track point vector
	make_vector(&id->proj, &main_scene->camera->look, &bend);
//	normalize(&bend);

	//add amount of differ to new projection
	add_vectors(&bend, &new_id.proj, &bend);
//	normalize(&new_id.proj);
	
	new_id.step = id->step++;

	new_id.obj_num = trace(&new_id.start, &new_id.intersect,
			&new_id.proj, new_id.step);
	new_id.obj = main_scene->models[new_id.obj_num];

	new_id.obj->shader(&new_id, new_color);

	printd(DEBUG, "}\n");
	//DEBUG_LEVEL = 5;
} //}}}

void lens_filter(intersect_data *id, color *new_color)
{
	intersect_data new_id;
	vector bend;
	
	//DEBUG_LEVEL = 0;
	printd(DEBUG, "{lens_filter\n");
	
	new_id.step = id->step + 1;
	copy_vector(&id->intersect, &new_id.start);
	 
	copy_vector(&id->proj, &new_id.proj);
	make_vector(&id->intersect, &id->pos, &bend);
	multiply_vector(&bend, &bend, 0.01);
	add_vectors(&new_id.proj, &new_id.proj, &bend);
	 
	new_id.start.x -= id->normal.x*0.01;
	new_id.start.y -= id->normal.y*0.01;
	new_id.start.z -= id->normal.z*0.01;
	 
	new_id.obj_num = trace(&new_id.start, &new_id.intersect,
			&new_id.proj, new_id.step);
	new_id.obj = main_scene->models[new_id.obj_num];
	
	new_id.obj->shader(&new_id, new_color);
	 
	printd(DEBUG, "}\n");
	//DEBUG_LEVEL = 5;
} //}}}

void sin_filter(intersect_data *id, color *new_color)
{
	intersect_data new_id;
	vector bend;
	double dist = 0.0;

	printd(DEBUG, "{sine_filter\n");

	new_id.step = id->step + 1;
	CHECK_STEPS(new_id.step, main_scene->max_recurs);
	
	if(new_id.step > main_scene->max_recurs)
	{
		printd(DEBUG, "}__FUNCTION__: max recurs\n");
		return;
	}

	copy_vector(&id->intersect, &new_id.start);

	copy_vector(&id->proj, &new_id.proj);
	dist = distance_between(&id->intersect, &id->obj->pos);
	make_vector(&id->intersect, &id->obj->pos, &bend);

	multiply_vector(&bend, &bend, sin(dist+current_time/TIME_SCALE));
	multiply_vector(&bend, &bend, 0.2);
	add_vectors(&new_id.proj, &new_id.proj, &bend);

	new_id.start.x -= id->normal.x*0.01;
	new_id.start.y -= id->normal.y*0.01;
	new_id.start.z -= id->normal.z*0.01;

	new_id.obj_num = trace(&new_id.start, &new_id.intersect,
			&new_id.proj, new_id.step);
	new_id.obj = main_scene->models[new_id.obj_num];

	new_id.obj->shader(&new_id, new_color);

	printd(DEBUG, "}\n");
} //}}}

