#include <stdio.h>
#include <time.h>
#include <stdlib.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 "ray_common.h"
#include "sub_sample.h"
#include "../shader/post_shaders.h"
#include "../simulation/simulation.h"
#include <math.h>

extern int interpolate_on;

extern scene_data* main_scene;
extern int intersects_calced;
double ***bilinear_table;

/* {{{ generate_subs */
void subsample_render(int min_height, int max_height)
{
	int distance;
	int i, j;
//	int k;
	
	distance = main_scene->sub_dis;
	
	/*
	bilinear_table = (double***) malloc(log2(distance) * sizeof(double**));
	for(k=0; k<log2(distance); k++)
	{
		bilinear_table[k] = (double**) malloc(distance * sizeof(double*));
		for(j=0; j<exp2(k+1); j++)
		{
			bilinear_table[k][j] = malloc(distance * sizeof(double));
			for(i=0; i<exp2(k+1); i++)
			{
				if(k == 1)
					bilinear_table[k][j][i] = 1;
				else
					bilinear_table[k][j][i] = ((double)i/(k-1) * (double)j/(k-1));
			}
		}
	}*/
	
	printd(DEBUG, "entering render loop\n");

	flush_color_buffer();
	clear_idata_buffer();

	// loop to generate the corners of the samples.
	for(j=min_height; j<max_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);
			calc_sub_sample(i, j, distance);
		}
	}

	intersects_calced = 0;
} // }}}

/* {{{ sub_sample */
//FIXME: should the distance be looked up from the scene struct instead of
// being passed to each call?
int calc_sub_sample(int x, int y, int dis)
{
	int dis2 = dis/2;
	//int object;
	color c;
	intersect_data *id;

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

	//calc corner values if needed
	//FIXME: this needs to be optimized badly
	id = get_idata(x, y);
	if(id->obj_num == NOTHING)
	{
		calc_ray(x, y);
		clear_color(&c);
		id->obj->shader(id, &c);
		set_color(x, y, &c);
	}

	id = get_idata(x+dis, y);
	if(id->obj_num == NOTHING)
	{
		calc_ray(x+dis, y);
		clear_color(&c);
		id->obj->shader(id, &c);
		set_color(x+dis, y, &c);
	}

	id = get_idata(x, y+dis);
	if(id->obj_num == NOTHING)
	{
		calc_ray(x, y+dis);
		clear_color(&c);
		id->obj->shader(id, &c);
		set_color(x, y+dis, &c);
	}

	id = get_idata(x+dis, y+dis);
	if(id->obj_num == NOTHING)
	{
		calc_ray(x+dis, y+dis);
		clear_color(&c);
		id->obj->shader(id, &c);
		set_color(x+dis, y+dis, &c);
	}

	// FIXME: move down to avoid 1 extra call to this function?
	// changed from 1 to 2
	if(dis < 2)
		return dis;

	//this stops sample when the max sample level is reached
	if(dis <= main_scene->sub_level)
	{
		if(interpolate_on)
			interpolate(x, y, id->obj_num, dis);
		return dis;
	}

	// quit if the we can interpolate
	if(is_close_match(x, y, dis) )
	{
		if(interpolate_on)
			interpolate(x, y, id->obj_num, dis);
		return dis;
	}

	//recursivly sub-sample
	calc_sub_sample(x, y, dis2);
	calc_sub_sample(x+dis2, y, dis2);
	calc_sub_sample(x, y+dis2, dis2);
	
	printd(CRAZY, "Exiting sub sampler\n");
	return calc_sub_sample(x+dis2, y+dis2, dis2);
} // }}}

/* {{{ sub_check */
/* this checks 4 colors (should be intersects) decides if they are close
 * enough to interpolate */
int is_close_match(int x, int y, int dis)
{
	double v = main_scene->sub_var;
	double rh, gh, bh;  //high variance
	double rl, gl, bl;  //low variance
	int obj1, obj2, obj3, obj4;
	color c1, c2, c3, c4;

	printd(CRAZY, "Entering sub 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
	// this is the fastest check, so let's do it first
	if( obj1 != obj2 || obj1 != obj3 || obj1 != obj4)
	{
		printd(CRAZY,"%i %i %i %i\n", obj1, obj2, obj3, obj4);
		return 0;
	}
	//else
	//		return 1;

	//start the color check

	get_color(x, y, &c1);
	get_color(x+dis, y, &c2);
	get_color(x+dis, y+dis, &c3);
	get_color(x, y+dis, &c4);

	// get the average
	rh = (c1.r + c2.r + c3.r + c4.r)/4;
	gh = (c1.g + c2.g + c3.g + c4.g)/4;
	bh = (c1.b + c2.b + c3.b + c4.b)/4;

	// set the lows
	rl = rh - v;
	gl = gh - v;
	bl = bh - v;

	// set the highs
	rh = rh + v;
	gh = gh + v;
	bh = bh + v;

	printd(CRAZY, "Leaving sub check\n");
	// check if in the range of low -> high
	if(c1.r < rl || c2.r < rl || c3.r < rl || c4.r < rl ||
			c1.r > rh || c2.r > rh || c3.r > rh || c4.r > rh)
		return 0;
	if(c1.b < bl || c2.b < bl || c3.b < bl || c4.b < bl ||
			c1.b > bh || c2.b > bh || c3.b > bh || c4.b > bh)
		return 0;
	if(c1.g < gl || c2.g < gl || c3.g < gl || c4.g < gl ||
			c1.g > gh || c2.g > gh || c3.g > gh || c4.g > gh)
		return 0;

	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 table_interpolate(int x, int y, int obj_num, int dis)
{
	int i,j, inv_i, inv_j;
	double tl_step, tr_step, bl_step, br_step;
	color c, tl, tr, bl, br;
	intersect_data *idata;
	int log_dis = log2(dis)-1;

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

	get_color(x, y, &tl);
	get_color(x+dis, y, &tr);
	get_color(x, y+dis, &bl);
	get_color(x+dis, y+dis, &br);

	for(j=0; j<dis; j++)
	{
		for(i=0; i<dis; i++)
		{
			inv_i = (dis-1) - i;
			inv_j = (dis-1) - j;
			//look up the interpolation values in a table
			tl_step = bilinear_table[log_dis][inv_j][inv_i];
			tr_step = bilinear_table[log_dis][inv_j][i];
			bl_step = bilinear_table[log_dis][j][inv_i];
			br_step = bilinear_table[log_dis][j][i];

			c.r = tl.r*tl_step + tr.r*tr_step +
				bl.r*bl_step + br.r*br_step;

			c.g = tl.g*tl_step + tr.g*tr_step +
				bl.g*bl_step + br.g*br_step;

			c.b = tl.b*tl_step + tr.b*tr_step +
				bl.b*bl_step + br.b*br_step;


			idata = get_idata(x+i, y+j);
			idata->obj_num = obj_num;
			// FIXME: this function call should be integrated into the loop
			set_color(x+i, y+j, &c);
		}
	}
	printd(INSANE, "}interpolate\n");
} //}}}



/* {{{ 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 interpolate(int x, int y, int obj_num, 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;
	color c, tl, tr, bl, br;
	intersect_data *idata;

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

	get_color(x, y, &tl);
	get_color(x+dis, y, &tr);
	get_color(x, y+dis, &bl);
	get_color(x+dis, y+dis, &br);

	//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;

	for(j=0; j<dis+1; j++)
	{
		for(i=0; i<dis+1; i++)
		{
			// these are the scales from each of the corners
			// they are pre-calced to make the loop faster
			// FIXME: precalc the scaling values in an array
			tl_step = horz_dec*vert_dec;
			tr_step = horz_inc*vert_dec;
			bl_step = horz_dec*vert_inc;
			br_step = horz_inc*vert_inc;

			c.r = tl.r*tl_step + tr.r*tr_step +
				bl.r*bl_step + br.r*br_step;

			c.g = tl.g*tl_step + tr.g*tr_step +
				bl.g*bl_step + br.g*br_step;

			c.b = tl.b*tl_step + tr.b*tr_step +
				bl.b*bl_step + br.b*br_step;


			idata = get_idata(x+i, y+j);
			idata->obj_num = obj_num;
			// FIXME: this function call should be integrated into the loop
			set_color(x+i, y+j, &c);

			horz_inc += step;
			horz_dec -= step;
		}

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


