#include <stdlib.h>
#include <math.h>
#include "SDL/SDL.h"
#include "display_scene.h"
#include "../slim.h"
#include "../common/structs.h"
#include "../common/debug.h"
#include "../common/vector.h"
#include "../common/buffer.h"
#include "../common/colors.h"
#include "../render/ray_common.h"
#include "../load/setup_scene.h"
#include <string.h>
#include "../misc/save_frame.h"
#include "front_init.h"
#include "handle_input.h"
#include "globals.h"
#include "console.h"
#include "../parse/parser.h"
#include "../simulation/simulation.h"

#include "../load/setup_scene.h"
#include "../parse/scene_parser.h"

extern void *(*up)();
extern void *(*down)();
extern scene_data *main_scene;


extern double powder;
extern double air_friction;
extern double bullet_mass;
extern double cannon_azimuth;
extern double cannon_elevation;

extern double spring_mass;
extern double spring_constant;
extern double spring_damp;

extern double gravity;
extern double sim_step_size;

extern int current_sim;
extern int sim_type;

extern double tessellation_degree;
extern double max_radius;

extern char command_list[NUM_SAVED_COMMANDS][INPUT_COMMAND_LENGTH];
#define BUTTON_WIDTH GLYPH_WIDTH*5

char shift_on;
char ctrl_on;

void azimuth(int i)
{
	cannon_azimuth += i;
	recalc_cannon();
}

void elevation(int i)
{
	cannon_elevation += i;
	recalc_cannon();
}

int over_button(int x, int y)
{
	if(y > main_scene->height - LINE_HEIGHT-5)
		return 1;
	return 0;
}

int get_button_num(int x)
{
	int val;
	
	if(x> main_scene->width - BUTTON_WIDTH*8)
	{
		val = (x - (main_scene->width - BUTTON_WIDTH*8)) / (BUTTON_WIDTH);
		return val;
	}
	return -1;
}

void change_val(int dir, double *d, char *disc)
{
#define EPS 0.00001

	if(shift_on)
		*d = *d + fabs(*d) * 0.4 * (double)dir;
	else
		*d = *d + fabs(*d) * 0.05 * (double)dir;
		
	if(*d == 0)
		*d = dir * EPS;
	else if(*d < EPS && *d > 0)
		*d = 0;
	else if(*d > -EPS && *d < 0)
		*d = 0;

	printd(NORMAL, disc, *d);
}

void change_val_clamp(int dir, double *d, double min, double max, double delta, char *disc)
{

	*d = *d + delta * (double)dir;
	
	if(*d < min)
		*d = min;
	if(*d > max)
		*d = max;
	
	printd(NORMAL, disc, *d);
}

void update_num_spheres(int dir)
{
	if(dir == 1)
		add_ball();
	else
		remove_ball();
}

void handle_button_click(int dir, int button)
{
	if(button == -1)
		return;

	switch(sim_type)
	{
		case T_CANNON:
			if(button ==0)
				change_val(dir, &gravity, "gravity: %f\n");
			else if(button ==1)
				change_val(dir, &air_friction, "air fric: %f\n");
			else if(button ==2)
				change_val(dir, &powder, "tnt: %f\n");
			else if(button ==3)
				change_val(dir, &bullet_mass, "mass: %f\n");
			else if(button ==4)
				change_val(dir, &sim_step_size, "step: %f\n");
			break;
		case T_SPRING:
			if(button ==0)
				change_val(dir, &gravity, "gravity: %f\n");
			else if(button ==1)
				change_val(dir, &spring_constant, "k: %f\n");
			else if(button ==2)
				change_val(dir, &spring_mass, "mass: %f\n");
			else if(button ==3)
				change_val(dir, &sim_step_size, "step: %f\n");
			else if(button ==4)
				change_val(dir, &spring_damp, "damping: %f\n");
			break;
		case T_BALLS:
			if(button ==5)
				update_num_spheres(dir);
			else if(button ==6)
				adjust_tessellation(dir);
				//change_val_clamp(dir, &tessellation_degree, 3.0, 50.0, 1.0, "complexity: %.2f\n");
			else if(button ==7)
				change_val_clamp(dir, &max_radius, 0.1, 3.0, 0.3, "size: %.2f\n");
				break;
	}
}

void handle_front_end_keys(SDL_Event *event)
{
//	static char shift = 0;
	Uint8 *keystate = SDL_GetKeyState(NULL);

	if(event->key.keysym.sym == SDLK_PAGEDOWN)
	{
		history_pos--;
		if(history_pos < 0)
			history_pos = 0;
		return;
	}

	if(event->key.keysym.sym == SDLK_PAGEUP)
	{
		history_pos++;
		if(history_pos > CONSOLE_HISTORY_LINES - 1)
			history_pos = CONSOLE_HISTORY_LINES - 1;
		return;
	}

	if(event->key.keysym.sym == SDLK_UP)
	{
		current_command++;
		if(current_command > NUM_SAVED_COMMANDS - 1)
			current_command = NUM_SAVED_COMMANDS - 1;

		frontend->current_char = strlen(command_list[current_command]);
		return;
	}

	if(event->key.keysym.sym == SDLK_DOWN)
	{
		current_command--;
		if(current_command < 0)
			current_command = 0;

		frontend->current_char = strlen(command_list[current_command]);
		return;
	}		

	if(event->key.keysym.sym == SDLK_BACKQUOTE)
	{
		frontend->console_on = 0;
		return;
	}

	if ( keystate[SDLK_BACKSPACE] || event->key.keysym.sym == SDLK_BACKSPACE)
	{
		frontend->current_char--;
		if(frontend->current_char < 0)
			frontend->current_char = 0;
		command_list[current_command][frontend->current_char] = '\0';
	}

	if(event->key.keysym.sym == SDLK_RETURN)
	{
		//process input
		update_command_list();
		update_console_history(command_list[current_command]);
		printd(ALERT, "%s\n", command_list[current_command]);
		
		process_command_string((char*)&command_list[0]);
		frontend->current_char = 0;
		clear_input_string();
//			input_command[frontend->current_char] = '\0';
	}

	if(event->key.keysym.unicode >= 32 && event->key.keysym.unicode <= 126)
	{
//			if(shift)
//				input_command[current_char] = shifted_char(event->key.keysym.sym);
//			else
			command_list[current_command][frontend->current_char] = tolower(event->key.keysym.unicode);

		frontend->current_char++;
		command_list[current_command][frontend->current_char] = '\0';
		//printd(ALERT, "%i\n", event.key.keysym.sym);
	}
}

void handle_keys(SDL_Event *event)
{
	//static char shift = 0;
	//Uint8 *keystate = SDL_GetKeyState(NULL);
	
	if(event->key.keysym.sym == SDLK_ESCAPE)
		frontend->running = 0;
	
	if(frontend->console_on)
	{
		handle_front_end_keys(event);
		return;
	}

	//shift_on = 0;
	//if ( keystate[SDLK_LSHIFT] )
	//	shift_on = 1;
	//if ( keystate[SDLK_RSHIFT] )
	//	shift_on = 1;


	switch(event->key.keysym.sym)
	{
//		case SDLK_5:
//			main_scene->lights[0]->pos.x = 
//				main_scene->lights[0]->pos.x+.2;
//			break;
//
//		case SDLK_6:
//			main_scene->lights[0]->pos.x = 
//				main_scene->lights[0]->pos.x-.2;
//			break;
//		case SDLK_2:
//			up();
//			break;
//		case SDLK_1:
//			down();
//			break;	

		case SDLK_UP:
			elevation(2);
			break;
		case SDLK_DOWN:
			elevation(-2);
			break;
		case SDLK_LEFT:
			azimuth(-2);
			break;
		case SDLK_RIGHT:
			azimuth(2);
			break;

		case SDLK_BACKSPACE:
		case SDLK_RSHIFT:
		case SDLK_LSHIFT:
			break;
		
		case SDLK_BACKQUOTE:
			frontend->console_on = 1;
			break;
		
		case SDLK_p:
			save_frame(main_scene);
			break;
		
		default:
			break;
	}
}

void handle_mouse_button(SDL_Event *event)
{
	intersect_data *id;
	color c;
	vector new_camera_pos;

	int old_debug = DEBUG_LEVEL;
	switch(event->button.button)
	{
		case SDL_BUTTON_RIGHT:
			clear_color(&c);
			DEBUG_LEVEL = 0;
			printd(ALERT, "\ndebugging pixel: %i,%i\n", 
					event->button.x, event->button.y);

			id = get_idata(event->button.x, event->button.y);
			calc_ray(event->button.x, event->button.y);
			id->obj->shader(id, &c);

			printd(ALERT, "\n");
			DEBUG_LEVEL = old_debug;
			break;

		case SDL_BUTTON_LEFT:
			if(over_button(event->motion.x, event->motion.y))
			{
				/* if(event->motion.x > main_scene->width - BUTTON_WIDTH)
					start_sim();
				else if(event->motion.x > main_scene->width - BUTTON_WIDTH*2)
					reset_sim();
				else if(event->motion.x > main_scene->width - BUTTON_WIDTH*3)
					change_solver(); */
			}
			else
				frontend->look_on = 1;
			break;
			
		case SDL_BUTTON_WHEELDOWN:
			if(over_button(event->motion.x, event->motion.y))
			{
				handle_button_click(-1, get_button_num(event->motion.x));
			}
			else
			{
				sub_vectors(&new_camera_pos, &main_scene->camera->pos, &main_scene->camera->lookP);
				multiply_vector(&new_camera_pos, &new_camera_pos, 1.1);
				add_vectors(&main_scene->camera->pos, &new_camera_pos, &main_scene->camera->lookP);
			}
			break;

		case SDL_BUTTON_WHEELUP:
			if(over_button(event->motion.x, event->motion.y))
			{
				handle_button_click(1, get_button_num(event->motion.x));
			}
			else
			{
				sub_vectors(&new_camera_pos, &main_scene->camera->pos, &main_scene->camera->lookP);
				multiply_vector(&new_camera_pos, &new_camera_pos, 0.9);
				add_vectors(&main_scene->camera->pos, &new_camera_pos, &main_scene->camera->lookP);
			}
			break;
	}
}

void handle_mouse_motion(SDL_Event *event)
{
	vector xmotion;
	vector ymotion;
	double dis;
	
	//stop camera from going too high
//	if(dot_product(&main_scene->camera->vert, &main_scene->camera->up) < 0.1)
//		return;
	
	//replace with real rotation later
	dis = distance_between(&main_scene->camera->lookP, 
			&main_scene->camera->pos);

	printd(DEBUG, "mouse pos: %i,%i\n mouse rel: %i,%i\n",
			event->motion.x, event->motion.y,
			event->motion.xrel, event->motion.yrel);

	copy_vector(&main_scene->camera->horz, &xmotion);
	copy_vector(&main_scene->camera->vert, &ymotion);
	multiply_vector(&xmotion, &xmotion, (double)event->motion.xrel/20);
	multiply_vector(&ymotion, &ymotion, (double)event->motion.yrel/20);

	add_vectors(&main_scene->camera->pos, &main_scene->camera->pos, &xmotion);
	add_vectors(&main_scene->camera->pos, &main_scene->camera->pos, &ymotion);
	
	setup_camera();
	
	copy_vector(&main_scene->camera->look, &main_scene->camera->pos);
	normalize(&main_scene->camera->pos);
	invert_vector(&main_scene->camera->pos);
	multiply_vector(&main_scene->camera->pos, &main_scene->camera->pos, dis);
	
	add_vectors(&main_scene->camera->pos, &main_scene->camera->pos, &main_scene->camera->lookP);

//	cross_product(&main_scene->camera->look,
//			&main_scene->camera->horz, 
//			&main_scene->camera->up);

}

int handle_input()
{
	SDL_Event event;
	Uint8 *keystate = SDL_GetKeyState(NULL);
	
	shift_on = 0;
	if(keystate[SDLK_RSHIFT] || keystate[SDLK_LSHIFT])
		shift_on = 1;

	while(SDL_PollEvent(&event))
	{
		if(event.type == SDL_QUIT)
			frontend->running = 0;

		if(event.type == SDL_MOUSEBUTTONDOWN)
		{
			handle_mouse_button(&event);
		}

		if(event.type == SDL_MOUSEBUTTONUP)
		{
			frontend->look_on = 0;
		}
		
		if(event.type == SDL_MOUSEMOTION && frontend->look_on)
		{
			handle_mouse_motion(&event);
		}

		if(event.type == SDL_KEYDOWN)
		{
			handle_keys(&event);
		}
	}
	return 1;
}

