#include "../slim.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../common/objects.h"
#include "./scene_parser.h"
#include "../common/debug.h"
#include "../shader/shaders.h"
#include "../render/intersect/sphere.h"
#include "../render/intersect/cylinder.h"
#include "../render/intersect/plane.h"
#include "../render/intersect/disc.h"
#include "../render/intersect/rectangle.h"
#include "../shader/lighting.h"
#include "parser.h"
#include "../load/setup_scene.h"
#include "../simulation/simulation.h"

/* loops through the scene file, building scene data structures as it does
 * this function could use a little splitting up
 */

extern int current_sim;
extern int sim_type;
extern int DEBUG_LEVEL;

void* get_shader_ptr(char* shader_name);

int parse_scene(scene_data* scene)
{
	int obj_type = 0;
	double posX = 0;
	double posY = 0;
	double posZ = 0;
	double rotX = 0;
	double rotY = 0;
	double rotZ = 0;
	double ambR = 0;
	double ambG = 0;
	double ambB = 0;
	double specR = 0;
	double specG = 0;
	double specB = 0;
	double diffR = 0;
	double diffG = 0;
	double diffB = 0;
	double upvX = 0;
	double upvY = 0;
	double upvZ = 0;
	double normX = 0;
	double normY = 0;
	double normZ = 0;
	double lookX = 0;
	double lookY = 0;
	double lookZ = 0;
	double radius = 0;
	double dist = 0;
	double spec_alpha = 0;
	double no_shadow = 0;
	double trans = 0;
	double refract = 0;
	double reflect = 0;
	int width = 0;
	int height = 0;
	int units = 0;
	int max_recurs = 3;
	int sub_dis = 16;
	int antialiasing = 0;
	int sub_var = 10;
	int sub_level = 1;
	char command_string[255];
	void* shader_ptr;
	char *token;
	
	int linenumber = 0;

	///TERRIBLE HACK
	//allows the background shader to be set while parsing scene file
	setup_background_object();
	///
	
#define MODELS scene->models
#define LIGHTS scene->lights
#define CAMERA1 scene->camera
	//object* models[] = &(scene->models);
	//object* lights[] = scene->lights;
	//camera* scene->camera1;

	char* str;
	int m = 1;  //FIXME: quick hack so 0 can be for the background
	int l = 0;

	str = strtok_r( (char *) scene->file_content, "\n", &token);

	/* while the string is not null parse it */
	do
	{
		shader_ptr = (void*) &general_shader;
		//shader_ptr = &normal_shader;
		/* skip line if it starts with 2 slashes */
		if( strncmp("//", str, 2)==0 || strncmp("#", str, 1)==0)
		{
			continue;
		}

		/* parse for star comments */
		else if( strncmp("/*", str, 2) == 0)
			while( strncmp("*/", strtok_r(NULL, " \t\n", &token), 2)!=0 )
				linenumber++;

		/* process objects */
		else if( strcmp(str, "obj_start") == 0)
		{
			while( (str = strtok_r(NULL, " \n\t", &token)) != NULL &&
				strcmp(str, "obj_end") != 0)
			{
				linenumber++;

				/* object type */
				if( strcmp(str, "obj_type") == 0 )
				{
					str = strtok_r(NULL, " \t\n", &token);
					linenumber++;

					if(strcmp(str, "sphere") == 0)
						obj_type = SPHERE;
					if(strcmp(str, "cylinder") == 0)
						obj_type = CYLINDER;
					else if(strcmp(str, "plane") == 0)
						obj_type = PLANE;
					else if(strcmp(str, "rectangle") == 0)
						obj_type = RECTANGLE;
					else if(strcmp(str, "disc") == 0)
						obj_type = DISC;
					else if(strcmp(str, "light") == 0)
						obj_type = LIGHT_POINT;
					else if(strcmp(str, "light_spot") == 0)
						obj_type = LIGHT_SPOT;
					else if(strcmp(str, "light_sphere") == 0)
						obj_type = LIGHT_SPHERE;
					else if(strcmp(str, "light_infinite") == 0)
						obj_type = LIGHT_INFINITE;
					else if(strcmp(str, "camera") == 0)
						obj_type = CAMERA;
				}
				/* position */
				else if( strcmp(str, "pos") == 0 )
				{
					posX = atof( strtok_r(NULL, " \t\n", &token));
					posY = atof( strtok_r(NULL, " \t\n", &token));
					posZ = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* rotation */
				else if( strcmp(str, "rotation") == 0)
				{
					rotX = atof( strtok_r(NULL, " \t\n", &token));
					rotY = atof( strtok_r(NULL, " \t\n", &token));
					rotZ = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* normal vector */
				else if( strcmp(str, "normal") == 0)
				{
					normX = atof( strtok_r(NULL, " \t\n", &token));
					normY = atof( strtok_r(NULL, " \t\n", &token));
					normZ = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* look point */
				else if( strcmp(str, "look") == 0)
				{
					lookX = atof( strtok_r(NULL, " \t\n", &token));
					lookY = atof( strtok_r(NULL, " \t\n", &token));
					lookZ = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}

				/* up vector */
				else if(strcmp(str, "up_vector") == 0)
				{
					upvX = atof( strtok_r(NULL, " \t\n", &token));
					upvY = atof( strtok_r(NULL, " \t\n", &token));
					upvZ = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* translucancy */
				else if( strcmp(str, "trans") == 0)
				{
					trans = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* refraction */
				else if( strcmp(str, "refract") == 0)
				{
					refract = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* reflection */
				else if( strcmp(str, "reflect") == 0)
				{
					reflect = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* ambient light */
				else if( strcmp(str, "ambient") == 0)
				{
					ambR = atof( strtok_r(NULL, " \t\n", &token));
					ambG = atof( strtok_r(NULL, " \t\n", &token));
					ambB = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* specular light */
				else if( strcmp(str, "specular") == 0)
				{
					specR = atof( strtok_r(NULL, " \t\n", &token));
					specG = atof( strtok_r(NULL, " \t\n", &token));
					specB = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* diffuse light */
				else if( strcmp(str, "diffuse") == 0)
				{
					diffR = atof( strtok_r(NULL, " \t\n", &token));
					diffG = atof( strtok_r(NULL, " \t\n", &token));
					diffB = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				/* misc items */
				else if( strcmp(str, "radius") == 0)
				{
					radius = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "dist") == 0)
				{
					dist = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "spec_alpha") == 0)
				{
					spec_alpha = atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "no_shadow") == 0)
				{
					no_shadow = atoi( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "width") == 0)
				{
					width = (int) atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "height") == 0)
				{
					height = (int) atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "max_recurs") == 0)
				{
					max_recurs = (int) atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "antialiasing") == 0)
				{
					antialiasing = (int) atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "sub_dis") == 0)
				{
					sub_dis = (int) atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "sub_var") == 0)
				{
					sub_var = (int) atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "sub_level") == 0)
				{
					sub_level = (int) atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "units") == 0)
				{
					units = (int) atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "parse") == 0)
				{
//					strtok_r(NULL, "\n");
					strncpy((char*)&command_string, strtok_r(NULL, "\n", &token), 255);
					process_command_string((char*)&command_string);
					linenumber++;
					continue;
				}
				else if( strcmp(str, "debug_level") == 0)
				{
					DEBUG_LEVEL = (int) atof( strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "shader") == 0)
				{
					shader_ptr = get_shader_ptr(strtok_r(NULL, " \t\n", &token));
					linenumber++;
				}
				else if( strcmp(str, "sim_type") == 0 )
				{
					str = strtok_r(NULL, " \t\n", &token);
					linenumber++;

					if(strcmp(str, "cannon") == 0)
						sim_type = T_CANNON;
					if(strcmp(str, "spring") == 0)
						sim_type = T_SPRING;
					if(strcmp(str, "balls") == 0)
						sim_type = T_BALLS;
				}
				else if( strncmp("/*", str, 2) == 0)
					while( strncmp("*/", strtok_r(NULL, " \t\n", &token), 2)!=0 )
						linenumber++;
			}

			/* based on the object type, the data is transfered to the 
			 * scene data structure */
			switch(obj_type)
			{
				case SPHERE:
					scene->models[m] = make_object(SPHERE, m);
					set_tri(&MODELS[m]->pos, posX, posY, posZ);
					make_color(&MODELS[m]->amb, ambR, ambG, ambB);
					make_color(&MODELS[m]->spec, specR, specG, specB);
					make_color(&MODELS[m]->diff, diffR, diffG, diffB);
					set_tri(&MODELS[m]->norm, normX, normY, normZ);
					set_tri(&MODELS[m]->up, upvX, upvY, upvZ);
					set_misc(MODELS[m], trans, reflect, refract,
							radius, spec_alpha);
					MODELS[m]->shader = (void* (*)(void*, color*)) shader_ptr;
					MODELS[m]->intersect =  (double (*)(point*, vector*, void*, point*)) &intersect_sphere;
					MODELS[m]->normal =  (vector* (*)(point*, void*, vector*)) &normal_sphere;
					MODELS[m]->inverse_map =  (vector* (*)(point*, void*, vector*)) &inverse_map_sphere;
					MODELS[m]->no_shadow = no_shadow;

					print_object(MESSAGE, "", m);

					m++;
					break;
				case CYLINDER:
					scene->models[m] = make_object(CYLINDER, m);
					set_tri(&MODELS[m]->pos, posX, posY, posZ);
					set_tri(&MODELS[m]->norm, normX, normY, normZ);
					normalize(&MODELS[m]->norm);
					make_color(&MODELS[m]->amb, ambR, ambG, ambB);
					make_color(&MODELS[m]->spec, specR, specG, specB);
					make_color(&MODELS[m]->diff, diffR, diffG, diffB);
					MODELS[m]->dist = dist;
					set_misc(MODELS[m], trans, reflect, refract,
							radius, spec_alpha);
					MODELS[m]->shader = (void* (*)(void*, color*))shader_ptr;
					MODELS[m]->intersect =  (double (*)(point*, vector*, void*, point*))&intersect_cylinder;
					MODELS[m]->normal =  (vector* (*)(point*, void*, vector*))&normal_cylinder;
					MODELS[m]->no_shadow = no_shadow;

					print_object(MESSAGE, "", m);

					m++;
					break;
				case DISC:
					MODELS[m] = make_object(DISC, m);
					set_tri(&MODELS[m]->pos, posX, posY, posZ);
					set_tri(&MODELS[m]->norm, normX, normY, normZ);
					make_color(&MODELS[m]->amb, ambR, ambG, ambB);
					make_color(&MODELS[m]->spec, specR, specG, specB);
					make_color(&MODELS[m]->diff, diffR, diffG, diffB);
					set_misc(MODELS[m], trans, reflect, refract, radius,
							spec_alpha);
					MODELS[m]->shader = (void* (*)(void*, color*))shader_ptr;
					MODELS[m]->intersect =  (double (*)(point*, vector*, void*, point*))&intersect_disc;
					MODELS[m]->normal =  (vector* (*)(point*, void*, vector*))&normal_disc;
					MODELS[m]->no_shadow = no_shadow;

					print_object(MESSAGE, "", m);
					m++;
					break;
				case PLANE:
					MODELS[m] = make_object(PLANE, m);
					set_tri(&MODELS[m]->pos, posX, posY, posZ);
					set_tri(&MODELS[m]->norm, normX, normY, normZ);
					make_color(&MODELS[m]->amb, ambR, ambG, ambB);
					make_color(&MODELS[m]->spec, specR, specG, specB);
					make_color(&MODELS[m]->diff, diffR, diffG, diffB);
					set_misc(MODELS[m], trans, reflect, refract, radius,
							spec_alpha);
					MODELS[m]->shader = (void* (*)(void*, color*))shader_ptr;
					MODELS[m]->intersect =  (double (*)(point*, vector*, void*, point*))&intersect_plane;
					MODELS[m]->normal =  (vector* (*)(point*, void*, vector*))&normal_plane;
					MODELS[m]->no_shadow = no_shadow;

					print_object(MESSAGE, "", m);
					m++;
					break;
				case RECTANGLE:
					MODELS[m] = make_object(RECTANGLE, m);
					set_tri(&MODELS[m]->pos, posX, posY, posZ);
					set_tri(&MODELS[m]->norm, normX, normY, normZ);
					set_tri(&MODELS[m]->up, upvX, upvY, upvZ);
					make_color(&MODELS[m]->amb, ambR, ambG, ambB);
					make_color(&MODELS[m]->spec, specR, specG, specB);
					make_color(&MODELS[m]->diff, diffR, diffG, diffB);
					set_misc(MODELS[m], trans, reflect, refract, radius,
							spec_alpha);
					MODELS[m]->shader = (void* (*)(void*, color*))shader_ptr;
					MODELS[m]->intersect =  (double (*)(point*, vector*, void*, point*))&intersect_rectangle;
					MODELS[m]->normal =  (vector* (*)(point*, void*, vector*))&normal_rectangle;
					MODELS[m]->no_shadow = no_shadow;
					
					rectangle_initialize(MODELS[m]);

					print_object(MESSAGE, "", m);
					m++;
					break;
				case CAMERA:
//					CAMERA1 = (camera_data*) malloc( sizeof(camera_data));
					set_tri(&CAMERA1->pos, posX, posY, posZ);
					set_tri(&CAMERA1->rot, rotX, rotY, rotZ);
					set_tri(&CAMERA1->look, lookX, lookY, lookZ);
					set_tri(&CAMERA1->lookP, lookX, lookY, lookZ);
					set_tri(&CAMERA1->up, upvX, upvY, upvZ);
					printd(MESSAGE, "setting up camera\n");
					CAMERA1->units = units;
					scene->width = width;
					scene->height = height;
					scene->max_recurs = max_recurs;
					scene->sub_dis = sub_dis;
					scene->antialiasing = antialiasing;
					scene->sub_var = sub_var;
					scene->sub_level = sub_level;

					printd(MESSAGE, "Camera- ok\n");
					break;
				case LIGHT_POINT:
					LIGHTS[l] = make_object(LIGHT_POINT, l);
					set_tri(&LIGHTS[l]->pos, posX, posY, posZ);
					make_color(&LIGHTS[l]->amb, ambR, ambG, ambB);
					make_color(&LIGHTS[l]->spec, specR, specG, specB);
					make_color(&LIGHTS[l]->diff, diffR, diffG, diffB);
					set_misc(LIGHTS[l], trans, reflect, refract, radius,
							spec_alpha);

					printd(MESSAGE, "Light[%i], type[%i-light] - ok\n",
							l,LIGHTS[l]->obj_type);

					l++;
					break;
				case LIGHT_SPHERE:
					LIGHTS[l] = make_object(LIGHT_SPHERE, l);
					set_tri(&LIGHTS[l]->pos, posX, posY, posZ);
					make_color(&LIGHTS[l]->amb, ambR, ambG, ambB);
					make_color(&LIGHTS[l]->spec, specR, specG, specB);
					make_color(&LIGHTS[l]->diff, diffR, diffG, diffB);
					set_misc(LIGHTS[l], trans, reflect, refract, radius,
							spec_alpha);

					printd(MESSAGE, "Light[%i], type[%i-light] - ok\n",
							l,LIGHTS[l]->obj_type);

					l++;
					break;
				case LIGHT_SPOT:
					LIGHTS[l] = make_object(LIGHT_SPOT, l);
					set_tri(&LIGHTS[l]->pos, posX, posY, posZ);
					set_tri(&LIGHTS[l]->norm, normX, normY, normZ);
					make_color(&LIGHTS[l]->amb, ambR, ambG, ambB);
					make_color(&LIGHTS[l]->spec, specR, specG, specB);
					make_color(&LIGHTS[l]->diff, diffR, diffG, diffB);
					set_misc(LIGHTS[l], trans, reflect, refract, radius,
							spec_alpha);

					printd(MESSAGE, "Light[%i], type[%i-light] - ok\n",
							l,LIGHTS[l]->obj_type);
					l++;
					break;

				case LIGHT_INFINITE:
					LIGHTS[l] = make_object(LIGHT_INFINITE, l);
					set_tri(&LIGHTS[l]->pos, posX, posY, posZ);
					normalize(&LIGHTS[l]->pos);
					make_color(&LIGHTS[l]->amb, ambR, ambG, ambB);
					make_color(&LIGHTS[l]->spec, specR, specG, specB);
					make_color(&LIGHTS[l]->diff, diffR, diffG, diffB);
					printd(MESSAGE, "Light[%i], type[%i-light] - ok\n",	l,LIGHTS[l]->obj_type);
					l++;
					break;
			}
		}

		else
		{
			printd(NORMAL, "Error in scene code at line %i: %s\n",
					linenumber,  str);
			return 0;
		}
	}
	while( str != NULL && (str = strtok_r(NULL, "\n", &token)) != NULL );
	
	main_scene->num_objects = m;
	return 1;
}



void* get_shader_ptr(char* shader_name)
{
	void* ptr = (void*)&general_shader;
	printd(ALERT,"shader: %s\n",shader_name);

	if(strcmp(shader_name, "?") == 0)
	{
		ptr = (void*)&general_shader;
		printd(NORMAL, "general\nportalfilter\nphong\ndistance\nobject\nsinfilter\n\
background\nwave\nnormal\n");
	}

	if(strcmp(shader_name, "general") == 0)
	{ ptr = (void*)&general_shader; }
	
	else if(strcmp(shader_name, "lensfilter") == 0)
	{ ptr = (void*)&lens_filter; }

	else if(strcmp(shader_name, "portalfilter") == 0)
	{ ptr = (void*)&portal_filter; }

	else if(strcmp(shader_name, "bwfilter") == 0)
	{ ptr = (void*)&black_white_filter; }

	else if(strcmp(shader_name, "phong") == 0)
	{ ptr = (void*)&phong; }

	else if(strcmp(shader_name, "distance") == 0)
	{ ptr = (void*)&distance_shader; }
	
	else if(strcmp(shader_name, "objnum") == 0)
	{ ptr = (void*)&object_shader; }
	
	else if(strcmp(shader_name, "reflection") == 0)
	{ ptr = (void*)&reflect_color; }

	else if(strcmp(shader_name, "sinfilter") == 0)
	{ ptr = (void*)&sin_filter; }
	
	else if(strcmp(shader_name, "refraction") == 0)
	{ ptr = (void*)&refract_color; }
	
	else if(strcmp(shader_name, "transparency") == 0)
	{ ptr = (void*)&trans_color; }

	//else if(strcmp(shader_name, "diffuse") == 0)
	//{ ptr = &diffuse_lighting; }

	else if(strcmp(shader_name, "wave") == 0)
	{ ptr = (void*)&wave_shader; }

	else if(strcmp(shader_name, "normal") == 0)
	{ ptr = (void*)&normal_shader; }
	
	else if(strcmp(shader_name, "fastdiff") == 0)
	{ ptr = (void*)&fast_shade; }
	
	else if(strcmp(shader_name, "mountains") == 0)
	{ ptr = (void*)&back_mountains; }
	
	else if(strcmp(shader_name, "gradiant") == 0)
	{ ptr = (void*)&back_gradiant; }
	
	else if(strcmp(shader_name, "back_black") == 0)
	{ ptr = (void*)&back_black; }

	else if(strcmp(shader_name, "purple_gradiant") == 0)
	{ ptr = (void*)&purple_gradiant; }

	return ptr;
}
