#include <stdio.h>
#include <time.h>

#include "../common/objects.h"
#include "../common/debug.h"
#include "../common/structs.h"
#include "../common/buffer.h"
#include "../common/colors.h"
#include "../common/misc.h"
//#include "./shading/shader.h"
#include "../common/vector.h"
#include "tracer.h"
#include "deferred_renderer.h"
#include "ray_common.h"
#include "../shader/post_shaders.h"

extern scene_data* main_scene;
extern int intersects_calced;
void idata_interpolate(int x, int y, int dis);
void deferred_sub_sample(int x, int y, int dis);
int deferred_tile_check(int x, int y, int dis);

/* {{{ deferred_renderer */
void deferred_render()
{
	int distance;
	int i, j;
	color c;

	c.r = 0;
	c.g = 0;
	c.b = 0;

	distance = main_scene->sub_dis;

	printd(DEBUG, "entering render loop\n");

	//erase last intersect
	clear_idata_buffer();
	flush_color_buffer();

	//loop to generate the corners of the samples.
	for(j=0; j < main_scene->height-distance; j=j+distance)
	{
		for(i=0; i < main_scene->width-distance; i=i+distance)
		{
			printd(CRAZY, "Sub-sample @  %i %i\n", i, j);
			deferred_sub_sample(i, j, distance);
		}
	}

	//shade the image
	for(j=0; j < main_scene->height-distance; j++)
	{
		for(i=0; i < main_scene->width-distance; i++)
		{
			get_idata(i,j)->obj->shader(
					get_idata(i,j), &c);
			set_color(i, j, &c);
			clear_color(&c);
		}
	}


	//print_frame_info(startTime, finishTime, intersects_calced);
	intersects_calced = 0;

} // }}}

/* {{{ deferred_sub_sample */
//FIXME: should the distance be looked up from the scene struct instead of
// being passed to each call?
void deferred_sub_sample(int x, int y, int dis)
{
	int dis2 = dis/2;
	int obj;

	printd(CRAZY, "Entering sub sampler: %i, %i, %i\n", x, y, dis);

	//calc corner values if needed
	//FIXME: this needs to be optimized badly
	obj = get_idata(x, y)->obj_num;
	if(obj == NOTHING)
		calc_ray(x, y);
	obj = get_idata(x+dis, y)->obj_num;
	if(obj == NOTHING)
		calc_ray(x+dis, y);
	obj = get_idata(x, y+dis)->obj_num;
	if(obj == NOTHING)
		calc_ray(x, y+dis);
	obj = get_idata(x+dis, y+dis)->obj_num;
	if(obj == NOTHING)
		calc_ray(x+dis, y+dis);

	// FIXME: move down to avoid 1 extra call to this function?
	// changed from 1 to 2
	if(dis < 2)
		return;
	
	//this stops sample when the max sample level is reached
	if(dis < main_scene->sub_level)
	{
		idata_interpolate(x, y, dis);
		return;
	}

	// quit if the we can interpolate
	if(deferred_tile_check(x, y, dis) )
	{
		idata_interpolate(x, y, dis);
		return;
	}

	//recursivly sub-sample
	deferred_sub_sample(x, y, dis2);
	deferred_sub_sample(x+dis2, y, dis2);
	deferred_sub_sample(x, y+dis2, dis2);
	deferred_sub_sample(x+dis2, y+dis2, dis2);

	printd(CRAZY, "Exiting sub sampler\n");
} // }}}

/* {{{ deferred_tile_check */
/* this checks 4 colors (should be intersects) decides if they are close
 * enough to interpolate */
int deferred_tile_check(int x, int y, int dis)
{
	int obj1, obj2, obj3, obj4;

	printd(CRAZY, "{deferred_tile_check\n");

	obj1 = get_idata(x, y)->obj_num;
	obj2 = get_idata(x+dis, y)->obj_num;
	obj3 = get_idata(x+dis, y+dis)->obj_num;
	obj4 = get_idata(x, y+dis)->obj_num;
	
	// the object number check
	if( obj1 != obj2 || obj1 != obj3 || obj1 != obj4)
	{
		printd(CRAZY,"}deferred_tile_check: objs: %i %i %i %i\n", obj1, obj2, obj3, obj4);
		return 0;
	}

	printd(CRAZY, "}deferred_tile_check: match\n");
	return 1;
} // }}}

/* {{{ interpolate */
/* bi-linearly interpolates the color of 4 points */
/* this function should be changed to use 4 intersect structs instead of 
 * 4 colors.  that way, normals, objects, and such can be considered */
void idata_interpolate(int x, int y, int dis)
{
	int i,j;
	double horz_dec, vert_dec;   // these values range from one end to zero
	double horz_inc, vert_inc;
	double step;
	double tl_step, tr_step, bl_step, br_step;
	intersect_data* out;
	intersect_data* tl;
	intersect_data* tr;
	intersect_data* bl;
	intersect_data* br;

	tl = get_idata(x, y);
	tr = get_idata(x+dis, y);
	bl = get_idata(x, y+dis);
	br = get_idata(x+dis, y+dis);

	//initializing these to 1.0 and 0.0 instead of 1 and 0 improves
	//rendering time by a noticable amount, due to the fact that 2
	//memory accesses are eliminated for each initialization
	step = 1.0 / (double) dis;
	horz_dec = 1.0;
	vert_dec = 1.0;
	horz_inc = 0.0;
	vert_inc = 0.0;

	printd(CRAZY, "{interpolate: %i\n", dis);

	for(j=0; j<dis; j++)
	{
		for(i=0; i<dis; i++)
		{
			// these are the scales from each of the corners
			// they are pre-calced to make the loop faster
			tl_step = horz_dec*vert_dec;
			tr_step = horz_inc*vert_dec;
			bl_step = horz_dec*vert_inc;
			br_step = horz_inc*vert_inc;
			
			out = get_idata(x+i, y+j);

			out->obj = tl->obj;
			out->obj_num = tl->obj_num;
			out->step = tl->step;
			out->pos.x = x+i;
			out->pos.y = y+j;
			copy_vector(&tl->start, &out->start);
			out->distance = main_scene->intrsct.distance;

			//intersect point
			out->intersect.x = 
				tl->intersect.x*tl_step + tr->intersect.x*tr_step +
				bl->intersect.x*bl_step + br->intersect.x*br_step;
			out->intersect.y = 
				tl->intersect.y*tl_step + tr->intersect.y*tr_step +
				bl->intersect.y*bl_step + br->intersect.y*br_step;
			out->intersect.z = 
				tl->intersect.z*tl_step + tr->intersect.z*tr_step +
				bl->intersect.z*bl_step + br->intersect.z*br_step;

//			out->intersect.x = 
//				tl->intersect.x;
//			out->intersect.y = 
//				tl->intersect.y;
//			out->intersect.z = 
//				tl->intersect.z;

			//pojection vector
			out->proj.x = tl->proj.x*tl_step + tr->proj.x*tr_step +
				bl->proj.x*bl_step + br->proj.x*br_step;
			out->proj.y = tl->proj.y*tl_step + tr->proj.y*tr_step +
				bl->proj.y*bl_step + br->proj.y*br_step;
			out->proj.z = tl->proj.z*tl_step + tr->proj.z*tr_step +
				bl->proj.z*bl_step + br->proj.z*br_step;
				
//			out->proj.x = tl->proj.x;
//			out->proj.y = tl->proj.y;
//			out->proj.z = tl->proj.z;

			//normal vector
			out->normal.x = tl->normal.x*tl_step + tr->normal.x*tr_step +
				bl->normal.x*bl_step + br->normal.x*br_step;
			out->normal.y = tl->normal.y*tl_step + tr->normal.y*tr_step +
				bl->normal.y*bl_step + br->normal.y*br_step;
			out->normal.z = tl->normal.z*tl_step + tr->normal.z*tr_step +
				bl->normal.z*bl_step + br->normal.z*br_step;

//			out->normal.x = tl->normal.x;
//			out->normal.y = tl->normal.x;
//			out->normal.z = tl->normal.x;

			horz_inc += step;
			horz_dec -= step;
		}

		horz_inc = 0.0;
		horz_dec = 1.0;
		vert_inc += step;
		vert_dec -= step;
	}
	printd(INSANE, "}interpolate\n");
} //}}}
