/**
 * Small OpenGL cube-demo 
 * (c) Daniel Hiepler, 2007-2008
 */

#include <stdio.h>
#include <getopt.h>
#include <math.h>
#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <SDL/SDL_image.h>

#include "include/main.h"
#include "include/object.h"
#include "include/ArcBall.h"
#include "include/blur.h"

#define FRAMERATE_MAX 100
#define DEFAULT_SCREEN_W 800
#define DEFAULT_SCREEN_H 600

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif





/* global info structure */
static struct MainLocal
{
	int width, height, depth;		/* screen dimensions */
	int old_x, old_y;
	int mouse_x;
	int mouse_y;
	int mouse_clicked;
	int mouse_dragged;
	int mouse_show;
	SDL_Surface *display;
	int sdl_flags;
	int draw_pyramid;				/* don't draw cube */
	int do_light;					/* do lighting */
	int do_blend;					/* do blending */
	int do_blur;					/* draw explode-effect */
	int do_moveBlur;				/* move blurred image like original */
	int do_flash;					/* if set, flash when clicked */
}local;


int m_getMoveExplosion()
{
	return local.do_moveBlur;
}
void m_setScreenWidth(int w)
{
	local.width = w;
}
void m_setScreenHeight(int h)
{
	local.height = h;
}
void m_setScreenDepth(int d)
{
	local.depth = d;
}
int m_getScreenWidth()
{
	return local.width;
}
int m_getScreenHeight()
{
	return local.height;
}
int m_getScreenDepth()
{
	return local.depth;
}



int m_getMouseClicked()
{
	return local.mouse_clicked;
}

int m_getMouseDragged()
{
	return local.mouse_dragged;
}

int m_getMouseX()
{
	return local.mouse_x;
}
int m_getMouseY()
{
	return local.mouse_y;
}

/**
 * print help-text
 */
static void m_usage(char *program_name)
{
 printf("Usage: %s [OPTION] [TEXTURES]\n" \
	"\t-r <wdith>x<height>\t--resolution\tResolution\n" \
	"\t-f\t\t\t--fullscreen\tRun fullscreen\n" \
	"\t-s\t\t\t--show\t\tShow mouse-pointer\n" \
	"\t-p\t\t\t--pyramid\tDraw pyramid instead of cube\n" \
	"\t-l\t\t\t--light\t\tEnable lighting\n" \
	"\t-a\t\t\t--alpha\t\tEnable alpha-blending \"glenz\" effect\n" \
	"\t-b\t\t\t--blitz\t\tFlash on click\n" \
	"\t-e\t\t\t--explode\tDraw \"exploding\" effect on click\n" \
	"\t-m\t\t\t--move\t\tMove explosion with rotating object\n" \
	"\t-h\t\t\t--help\t\tThis help\n\n" \
	"You can give up to 6 texture filenames.\n" \
	"While active press key to clear screen, ESC to exit.\n", program_name);
}
/**
 * parse commandline arguments
 */
static int m_parseArguments(int argc, char **argv) {
	int result = 0;
	int argument_index = 0;
	int argument;
	int width, height;
	struct Texture *new, *tmp, *s = NULL;
	static struct option loptions[] = 
	{
		{"help",0,0,'h'},
		{"resolution",1,0,'r'},
		{"fullscreen",0,0,'f'},
		{"pyramid",0,0,'p'},
		{"blitz",0,0,'b'},
		{"explode",0,0,'e'},
		{"light",0,0,'l'},
		{"show",0,0,'s'},
		{"alpha",0,0,'a'},
		{"move",0,0,'m'},
		{0,0,0,0}
	};
	
	while(TRUE) 
	{
		argument = getopt_long(argc, argv, "-hr:fpbmelas", loptions, &argument_index);
		if (argument == -1) break;
		
		switch(argument) 
		{	 
			/* enable blur-movement */
			case 'm':
				local.do_moveBlur = TRUE;
				break;
			
			/* enable blending */
			case 'a':
				local.do_blend = TRUE;
				break;
			
			/* draw explode-effect */
			case 'e':
				local.do_blur = TRUE;
				break;
			
			/* enable lighting */
			case 'l':
				local.do_light = TRUE;
				break;
			
			/* show mouse-pointer */
			case 's':
				local.mouse_show = TRUE;
				break;
			
			/* flash */
			case 'b':
				local.do_flash = TRUE;
				break;
			
			/* draw cube instead of pyramid */
			case 'p':
				local.draw_pyramid = TRUE;
				break;
			
			/* display help text */
			case 'h':
				m_usage(argv[0]);   
				result = -1;
				break;
			
			
			/* fullscreen */
			case 'f':
				local.sdl_flags |= SDL_FULLSCREEN;
				break;
			
			case 'r':
				if (!optarg) 
				{ 
					printf("** Please provide resolution (<WIDTH>x<HEIGHT>)\n");
					result = -1;
				}
				else {
					sscanf(optarg, "%dx%d", &width, &height);
					if(width <= 0 || height <= 0)
					{
						printf("Invalid resolution.\n");
						result = -1;		 
					}
					
					m_setScreenWidth(width);
					m_setScreenHeight(height);
				}
				break;
			
			/* treat unknown parameters as texture-filenames */
			case '?':
				fprintf(stderr,"**FATAL ERROR\n getopt returned character code 0%o\n",argument);
				result = -1;
				break;
			
			/* eek! this may never happen */
			default:
				/* init new texture definition */
				new = calloc(1, sizeof(struct Texture));
				new->filename = strdup(optarg);
				/* append to list */
				for(tmp = obj_getFirstTexture(); tmp; tmp = tmp->next)
				{
					s = tmp;
				}
				
				if(s)
					s->next = new;
				else
					obj_setFirstTexture(new);
				
				break;
		}
	}
	return result;
}


/**
 * cleanup everything
 */
void m_cleanup()
{
	printf("Cleaning up...\n");
	
	blr_cleanup();
	
	obj_cleanup();
	
	SDL_ShowCursor(SDL_ENABLE);
}

/**
 * draw scene
 */
void m_drawScene()
{
	


	/* Clear The Screen And The Depth Buffer */
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	
	/* enable light? */
	if(local.do_light)
	{
		
		glEnable(GL_LIGHTING);
		GL_CHECKERROR;
		
		/* fade light? */
		if(obj_getLightAmbient()[0] > LIGHT_AMBIENT_R)
		{
			obj_getLightAmbient()[0] -= 0.2f;
			obj_getLightAmbient()[1] -= 0.2f;
			obj_getLightAmbient()[2] -= 0.2f;
			glLightfv(GL_LIGHT1, GL_AMBIENT, obj_getLightAmbient());
			GL_CHECKERROR;
		}
	}
	else
	{
		glDisable(GL_LIGHTING);
		GL_CHECKERROR;
	}
	
	/* blend ? */
	if(local.do_blend)
	{
		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
		GL_CHECKERROR;
		glEnable(GL_BLEND);
		GL_CHECKERROR;
		glDisable(GL_DEPTH_TEST);
		GL_CHECKERROR;
	}
	else
	{
		glDisable(GL_BLEND);
		GL_CHECKERROR;
		glEnable(GL_DEPTH_TEST);
		GL_CHECKERROR;
	}
	
	/* Move Left 1.5 Units And Into The Screen 6.0 */
	glLoadIdentity();
	glTranslatef( 0, 0.0f, -5.0f );
	
	/* rotate-object */
	obj_rotate();
	glPushMatrix();
	glMultMatrixf(obj_getRotMatrix()->M);
				
	/* draw object */
	if(local.draw_pyramid)
		obj_pyramidDraw();
	else
		obj_cubeDraw();
	
	glPopMatrix();
	
	
	/* blur */
	if(local.do_blur)
		blr_process();
	
	
	/* display frame */
	SDL_GL_SwapBuffers();
	
	glFlush();

		
}

/**
 * handle different keypresses
 */
static void m_handleKeypress(SDL_Event *event)
{
	switch(event->key.keysym.sym)
	{
		
		/* quit */
		case SDLK_q:
		case SDLK_ESCAPE:
			m_cleanup();
			exit(0);
			break;
		
		/* fullscreen */
		case SDLK_f:
			SDL_WM_ToggleFullScreen(local.display);
			break;
		
		/* light on/off */
		case SDLK_l:
			local.do_light = !local.do_light;
			obj_lightAmbientFlash();
			break;
		
		/* blend on/off */
		case SDLK_a:
			local.do_blend = !local.do_blend;
			break;
		
		/* flash on/off */
		case SDLK_b:
			local.do_flash = !local.do_flash;
			break;
		
		/* blur on/off */
		case SDLK_e:
			local.do_blur = !local.do_blur;
			break;
			
		/* move blur on/off */
		case SDLK_m:
			local.do_moveBlur = !local.do_moveBlur;
			break;
		
		default:
			break;
	}
}

/**
 * SDL Event handler
 */
static void m_handleSDLEvents()
{
	SDL_Event event;
	
	
	/* check for SDL events */
	while(SDL_PollEvent(&event))
	{
		switch(event.type) 
		{

			/* window has been resized */
			case SDL_VIDEORESIZE:
				local.display = SDL_SetVideoMode(event.resize.w, event.resize.h,
									local.depth, local.sdl_flags);
				if(!local.display)
				{
					printf("Window resize error!\n");
					m_cleanup();
					exit(-1);
				}
				
				if(obj_gl_init(event.resize.w, event.resize.h) < 0)
				{
					printf("Window resize error (OpenGL)!\n");
					m_cleanup();
					exit(-1);
				}
				
				local.width = event.resize.w;
				local.height = event.resize.h;
				
				break;
			
			
			/* quit */
			case SDL_QUIT:
				m_cleanup();
				exit(0);
				break;
			
			
			/* keypress */
			case SDL_KEYDOWN:
				m_handleKeypress(&event);
				break; 
			
			
			/* mouse-motion */
			case SDL_MOUSEMOTION:
				/* save previous coordinates */
				local.old_x = local.mouse_x;
				local.old_y = local.mouse_y;
				local.mouse_x = (int) event.motion.x;
				local.mouse_y = (int) event.motion.y;
				
			
				if(local.mouse_clicked)
				{
					local.mouse_dragged = 1;
				}		 
				else
				{
					local.mouse_dragged = 0;
				}
				break;
			
				
			/* mouse-up */
			case SDL_MOUSEBUTTONUP:							
				local.mouse_clicked = 0;
				break;
			
			
			/* mouse-down */
			case SDL_MOUSEBUTTONDOWN:
				local.mouse_clicked = 1;
				
				/* explode */
				blr_do();
			
				/* flash? */
				if(local.do_flash)
				{
					obj_lightAmbientFlash();
				}
				break;
			
				
			/* oOops! */
			default:
				break;
		}
	}
}

/**
 * delay to respect framerate
 */
void m_fpsDelay()
{
	static GLint t, t0;
	
	/* get current time */
	t = SDL_GetTicks();
	
	/* sleep if we are beyond the framerate */
	if((t-t0) < (1000/FRAMERATE_MAX))
	{
		SDL_Delay((1000/FRAMERATE_MAX)-(t-t0));
	}
	
	t0 = t+(1000/FRAMERATE_MAX)-(t-t0);
}

/***
 * main()
 */
int main(int argc, char *argv[])
{
	const SDL_VideoInfo *videoInfo;
	/* These are to calculate our fps */
	struct Texture *tmp;
		
	
	/* initialize local structure */
	memset(&local, 0, sizeof(struct MainLocal));

	m_setScreenWidth(DEFAULT_SCREEN_W);
	m_setScreenHeight(DEFAULT_SCREEN_H);
	
	local.depth = 8;
	local.sdl_flags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER |
						SDL_HWPALETTE |SDL_RESIZABLE ;
	local.do_light = TRUE;
	local.do_flash = TRUE;
	local.do_moveBlur = TRUE;
	local.do_blur = FALSE;
	
	printf("laserTraq cube-demo - (c) D.Hiepler 2007-2008\n\n");
	
	/* parse arguments */
	if(m_parseArguments(argc, argv) < 0)
	{
		m_cleanup();
		exit(-1);
	}
	
	
	/* init SDL */
	if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) 
	{
		fprintf(stderr, "SDL-init error: \"%s\"\n", SDL_GetError());
		m_cleanup();
		exit(-1);
	}
		
	/* Quit SDL when we exit */
	atexit(SDL_Quit);

	videoInfo = SDL_GetVideoInfo();
	
	/* Fast? */
	if(videoInfo->hw_available)
	{
		local.sdl_flags |= SDL_HWSURFACE;
	}
	else
	{
		local.sdl_flags |= SDL_SWSURFACE;
	}
	
	/* Accelerated? */
	if(videoInfo->blit_hw)
		local.sdl_flags |= SDL_HWACCEL;
	
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	
	/* Set video mode */
	if(!(local.display = SDL_SetVideoMode( local.width, local.height, local.depth, local.sdl_flags)))
	{
		fprintf(stderr, "sdl-display open error: \"%s\"\n", SDL_GetError());
		m_cleanup();
		exit(-1);
	}
	
	
	/* load textures */
	for(tmp = obj_getFirstTexture(); tmp; tmp = tmp->next)
	{
		if(obj_textureLoad(tmp->filename, &(tmp->id)) < 0)
		{
			fprintf(stderr, "Failed to load \"%s\".\n",
						tmp->filename);
			m_cleanup();
			exit(-1);
		}
	}
	
			
	/* initialize openGL stuff */
	obj_init();
	
	/* initialize blur-effect */
	blr_init();
	
	
	/* hide mouse-pointer if necessary */
	if(!local.mouse_show)
		SDL_ShowCursor(SDL_DISABLE);
	
	
	SDL_WM_SetCaption("traq_cube (laserTraq demo) - D.Hiepler", NULL);
		
	
		

	/* main loop */
	while(1)
	{
		
		m_drawScene();

		
		m_handleSDLEvents();
		
		
		m_fpsDelay();
		
	}
	
	

	m_cleanup();

	return (0);
}
