#include <errno.h>

#include "globals.h"
#include "../parse/scene_parser.h"
#include "../load/load.h"
#include "../load/setup_scene.h"
#include "../misc/save_frame.h"
#include "../frontend/front_init.h"
#include "../shader/shaders.h"
#include "../shader/post_shaders.h"
#include "../common/debug.h"
#include "../common/vector.h"
#include "parser.h"
#include "help_texts.h"

void *(*up)();
void *(*down)();

extern int interpolate_on;

//simple function used to parse out a shader from a "allshader=xxxx" string
void set_shaders(char *input)
{
	char shader_name[20];
	int i = 0;

	strncpy(shader_name, &input[16], 20);

	for(i = 0; main_scene->models[i]!=NULL; i++)
	{
		main_scene->models[i]->shader = (void* (*)(void*, color*))get_shader_ptr(shader_name);
	}
}

//bind object functions
void object_bind(char *input)
{
	char shader_name[20];
	int i = 0;

	strncpy(shader_name, &input[10], 20);

	for(i = 0; main_scene->models[i]!=NULL; i++)
	{
		main_scene->models[i]->shader = (void* (*)(void*, color*))get_shader_ptr(shader_name);
	}
}

//input functions
void units_up()
{main_scene->camera->units=+0.05; printf("units+1\n"); setup_camera();}
void units_down()
{main_scene->camera->units-=0.05; printf("units-1\n"); setup_camera();}

void variance_up()
{main_scene->sub_var = main_scene->sub_var +1; printf("var+1\n");}
void variance_down()
{main_scene->sub_var = main_scene->sub_var -1; printf("var-1\n");}

void level_up()
{main_scene->sub_level = main_scene->sub_level +1; printf("level+1\n");}
void level_down()
{main_scene->sub_level = main_scene->sub_level -1; printf("level-1\n");}

void cannon_up()
{main_scene->models[1]->norm.z +=0.2; normalize(&main_scene->models[1]->norm);}
void cannon_down()
{main_scene->models[1]->norm.z -=0.2; normalize(&main_scene->models[1]->norm);}

void null_up(){printf("null up\n");};
void null_down(){printf("null down\n");};

void parse_post_shader(char *input)
{
	shader_data* shader;
	
	strtok( input, " "); //addpostshader
	shader = get_shader( strtok( NULL, " ")); //get by name

	if(shader == NULL)
	{
		printd(MESSAGE, "shader not found %s\n");
		return;
	}
	add_post_shader(shader, strtok( NULL, "\""));
}


#define MAX_VARIABLES 200

char *keys[MAX_VARIABLES];
char *values[MAX_VARIABLES];





char strequal(const char *s1, const char *s2)
{
	if(strcmp(s1, s2) == 0)
		return 1;
	return 0;
}

char contains(const char *haystack, const char *needle)
{
	if(strstr(haystack, needle) == NULL)
		return 0;
	return 1;
}

//char is_assignment(char *input)
//{
//	if(contains(input, "=") == 1)
//		return 1;
//	return 0;
//}

int assign_dmatrix(void *container, char *value)
{
	dmatrix* matrix;
	double *elements;
	int i;
	
	matrix = (dmatrix*) container;

	matrix->width = -1;
	matrix->height = -1;

	assign_int(&matrix->width, (strtok(value, ",")) );
	assign_int(&matrix->height, (strtok(NULL, ",")) );

	if(matrix->width < 0 || matrix->height < 0)
		return 0;

	elements = (double*)malloc(matrix->width * matrix->height * sizeof(double));
 	matrix->elements = elements;

	for(i=0; i < matrix->width * matrix->height; i++)
	{
		if(assign_double(elements++, strtok(NULL, "\n{}, \0")) == 0)
			return 0;
	}

	return 1;
}


int assign_double(void *container, char *value)
{
	char *end;
	double val;
	
	if(value == NULL)
		return 0;
	
	val = strtod(value, &end);

	//if(errno == EINVAL || errno == ERANGE)
	//	return 0;

	if(strcmp(end, "") != 0)
		return 0;

	*(double*)container = val;
	
	return 1;
}

int assign_int(void *container, char *value)
{
	char *end;
	long val;
	
	if(value == NULL)
		return 0;

	val = strtol(value, &end, 10);
	
	//if(errno == EINVAL || errno == ERANGE)
	//	return 0;
		
	if(strcmp(end, "") != 0)
		return 0;

	*(int*)container = val;
	
	return 1;
}

int assign_vector(void *container, char *value)
{
	vector *v;
	v = (vector*)container;

	if(assign_double(&v->x, strtok(value, "{ ,}")) == 0)
		return 0;
	if(assign_double(&v->y, strtok(NULL, "{ ,}"))  == 0)
		return 0;
	if(assign_double(&v->z, strtok(NULL, "{ ,}"))  == 0)
		return 0;
	
	return 1;
}

char* add_post(char *args)
{
	int num;
	char *name;
	char *options;
	shader_data* shader;

	name = strtok(args, " ,\"");
	options  = strtok(NULL, "\0");

	if(name == NULL)
		return NULL;

	if(strequal(name, "?"))
		return add_post_shader_help;//print help
	
	shader = get_shader(name); //get by name

	if(shader == NULL)
	{
		printd(MESSAGE, "shader not found %s\n", name);
		return NULL;
	}
	
	if(options != NULL && strequal(options, "?"))
		return shader->help_text;

	num = add_post_shader(shader, options);

	if(num < 0)
		printd(NORMAL, "error setting up shader\n");
	else
		printd(NORMAL, "shader num: %i\n", num);

	return NULL;
}

char* remove_post(char *args)
{
	int num = -1;
	char *num_string;
	
	num_string = strtok(args, " ,\"");
	assign_int(&num, num_string);
	if(num < 0)
		return NULL;

	remove_post_shader(num);

	return NULL;
}

char* list_shader()
{
	list_shaders();
	return NULL;
}

char* set_render(char *args)
{
	char *key;

	key = strtok(args, " ,\"");

	if(key == NULL) return NULL;

	if(strequal(key, "?"))
		return set_render_help_text;//print help

	if(strequal(key, "subsample"))
		frontend->renderer = 0;
	else if(strequal(key, "normal"))
		frontend->renderer = 1;
	else if(strequal(key, "deferred"))
		frontend->renderer = 2;
	else if(strequal(key, "old_tracer"))
		frontend->renderer = 3;
	else if(strequal(key, "frameless"))
		frontend->renderer = 4;
	else if(strequal(key, "frameless_sub"))
		frontend->renderer = 5;
	else if(strequal(key, "random_sub"))
		frontend->renderer = 6;

	return key;
}

char* set_camera(char *args)
{
	char *key;
	char *value;

	key = strtok(args, " ,\"");
	value  = strtok(NULL, " \"\n\0");

	if(key == NULL) return NULL;

	if(strcmp(key, "?") == 0)
		return set_camera_help_text;//print help
		
	if(value == NULL) return NULL;

	if(strcmp(key, "units") == 0)
		assign_int(&main_scene->camera->units, value);
	else if(strcmp(key, "look") == 0)
		assign_vector(&main_scene->camera->lookP, value);
	else if(strcmp(key, "pos") == 0)
		assign_vector(&main_scene->camera->pos, value);
	else if(strcmp(key, "up") == 0)
		assign_vector(&main_scene->camera->up, value);
		
	setup_camera();

	return value;
}

char* set_object(char *args)
{
	int objnum = -1;
	char *num_string;
	char *key;
	char *value;

	num_string = strtok(args, " ,\"");
	
	if(num_string == NULL) return NULL;
	
	if(strequal(num_string, "?"))    //print help
		return set_object_help_text;
	
	assign_int(&objnum, num_string);
	if(objnum < 0 || objnum > NUM_OBJECTS)
		return NULL;

	key = strtok(NULL, " ,\"");
	value  = strtok(NULL, " \"\n\0");
	
	if(key == NULL || value == NULL)
		return NULL;

	if(strcmp(key, "radius") == 0)
		assign_double(&main_scene->models[objnum]->radius, value);
	else if(strcmp(key, "pos") == 0)
		assign_vector(&main_scene->models[objnum]->pos, value);
	else if(strcmp(key, "normal") == 0)
		assign_vector(&main_scene->models[objnum]->norm, value);
	else if(strcmp(key, "shader") == 0)
		main_scene->models[objnum]->shader = (void* (*)(void*, color*))get_shader_ptr(value);

	return value;
}

char* set_scene(char *args)
{
	char *key;
	char *value;

	key = strtok(args, " ,\"");
	value  = strtok(NULL, " \"\n\0");
	if(key == NULL) return NULL;

	if(strcmp(key, "?") == 0)
		return set_scene_help_text;//print help
		
	if(value == NULL) return NULL;

	if(strcmp(key, "sub_dis") == 0)
		assign_int(&main_scene->sub_dis, value);
	else if(strcmp(key, "sub_level") == 0)
		assign_int(&main_scene->sub_level, value);
	else if(strcmp(key, "sub_var") == 0)
		assign_int(&main_scene->sub_var, value);

	else if(strcmp(key, "width") == 0)
		assign_int(&main_scene->width, value);
	else if(strcmp(key, "height") == 0)
		assign_int(&main_scene->height, value);

	else if(strcmp(key, "max_recurs") == 0)
		assign_int(&main_scene->max_recurs, value);
	else if(strcmp(key, "debug_level") == 0)
		assign_int(&DEBUG_LEVEL, value);
	else if(strcmp(key, "interpolate") == 0)
		assign_int(&interpolate_on, value);

	return value;
}

char* invoke_function(char *input)
{
	char *name;
	char *args;

	if(input == NULL || strlen(input) < 1)
		return NULL;

	name = strtok(input, "( \0\n)");
	args = strtok(NULL, "\0\n)");

	if(strcmp(name, "set_object") == 0)
		return set_object(args);
	else if(strcmp(name, "set_scene") == 0)
		return set_scene(args);
	else if(strcmp(name, "render") == 0)
		return set_render(args);
	else if(strcmp(name, "set_camera") == 0)
		return set_camera(args);
	else if(strcmp(name, "add_post") == 0)
		return add_post(args);
	else if(strcmp(name, "remove_post") == 0)
		return remove_post(args);
	else if(strcmp(name, "list_shaders") == 0)
		return list_shader();
	else if(strcmp(name, "?") == 0)
		return main_help_text;

	return NULL;
}

char* parse_expression(char *input)
{
	char *ret;
	//trim white space
	ret = invoke_function(input);

	if(ret != NULL)
		printd(NORMAL, ret);

	//FIXME: finish the parser later
//	if(is_function(input))
//		invoke function(input);
	//if in register
		//return register
	//if

	return NULL;
}



























//parses the input string from the frontend
void process_command_string(char *input)
{
	char path[255];

	if(strncmp(input, "cannon", 40) == 0)
		{ up = (void* (*)())cannon_up; down = (void* (*)())cannon_down;}
//	else if(strncmp(input, "level", 40) == 0)
//		{up = (void*)level_up; down = (void*)level_down;}
//	else if(strncmp(input, "variance", 40) == 0)
//		{up = (void*)variance_up; down = (void*)variance_down;}
//	else if(strncmp(input, "object[", 7) == 0)
//		{ object_bind(input); }
//	else if(strncmp(input, "object", 40) == 0)
//		{}
//	else if(strncmp(input, "light", 40) == 0)
//		{}

	if(strncmp(input, "set_all_shaders ", 16) == 0)
		{ set_shaders(input); }
	else if(strncmp(input, "load ", 5) == 0)
	{
		strncpy( (char*)&path, input + sizeof(char)*5, 255);
		load_scene( (char*)&path, main_scene);
		frontend_reinit();
	}
	else if(strncmp(input, "save_frame", 40) == 0)
		{ save_frame(main_scene); }
	else if(strncmp(input, "p", 40) == 0)
		save_frame(main_scene);
//	else if(strncmp(input, "subsample", 40) == 0)
//		{frontend->renderer = 0;}
//	else if(strncmp(input, "normal", 40) == 0)
//		{frontend->renderer = 1;}
//	else if(strncmp(input, "deferred", 40) == 0)
//		{frontend->renderer = 2;}
//	else if(strncmp(input, "old_tracer", 40) == 0)
//		{frontend->renderer = 3;}
//	else if(strncmp(input, "frameless", 40) == 0)
//		{frontend->renderer = 4;}
//	else if(strncmp(input, "frameless_sub", 40) == 0)
//		{frontend->renderer = 5;}
		

	else
	{
		parse_expression(input);
	}
//		{up = (void*)null_up; down = (void*)null_down;}
}
