#include "../include/zf.h"

#define LOOP_STATE_SPLASH 0
#define LOOP_STATE_MENU 1
#define LOOP_STATE_CONTROLS 2
#define LOOP_STATE_HIGHSCORE  3
#define LOOP_STATE_CREDITS 4
#define LOOP_STATE_GAMEOVER 5
#define LOOP_STATE_LEVELS 6
#define NUM_LOOP_STATES 7

#define MENU_STATE_START 0
#define MENU_STATE_CONTROLS 1
#define MENU_STATE_HIGHSCORE 2
#define MENU_STATE_CREDITS 3
#define MENU_STATE_EXIT 4
#define NUM_MENU_STATES 5

typedef struct HighScore HighScore;

struct HighScore
{
    char pwner[11]; /*hack*/
    char score[8];    /* hack*/
};

static GList* highscores;
static char high_score_entry[11]; /*hack 10 character + \0*/
static int high_score_entry_index;
static char high_score_value[8]; /* hack 7 digit number + \0 */
static int score_position; /* where the high score fits */

static bool game_loop_started = false;
static bool done = false; /* boolean */

static float elapsed_time;

static int   menu_state;       /* for which option is highlighted */
static int   loop_state;       /* for what part of menu you're in - splash/menu/sub-menu */
static int   levels_state = 0; /* for which level in the levels sub-menu is highlighted */

static char** level_names = 0;
static char** level_filenames = 0;
static unsigned int num_levels = 0;

static ZfAudioStream* background_music;

static int menu_result;
static float select_sphere_rotate;

static unsigned int mouse_x;
static unsigned int mouse_y; /* 2D mouse coords from SDL */
static CLvertex mouse_position; /* 3D mouse position (unprojected from 2D) */

static ZfAudioSample* menu_change;
static ZfAudioSample* menu_select;
static ZfAudioSample* menu_back;

/* nick - use draw function pointers for each state */
typedef void _draw_func(void);

static void draw_splash(void);
static void draw_menu(void);
static void draw_controls(void);
static void draw_highscore(void);
static void draw_credits(void);
static void draw_high_score_entry(void);
static void draw_levels(void);


static _draw_func* draw_funcs[NUM_LOOP_STATES] = {draw_splash,
						  draw_menu,
						  draw_controls,
						  draw_highscore,
						  draw_credits,
                                                  draw_high_score_entry,
						  draw_levels};
						  

/* nick - timer function for animations */
static void timer_reset(void);
static float timer_get_time(void);


/* 
 * nick - timer functions
 */

static void
timer_reset(void)
{
    elapsed_time = 0;    
}

static float
timer_get_time(void)
{
    return elapsed_time;
}


/*
 * nick - graphics helper functions
 */

static float
butterworth(float r, float cutoff, float n)
{
    return 1.0f / (1.0f + pow((r/cutoff), 2*n));
}

static void
high_scores_write(void)
{
    unsigned int i;
    GList* li;
    
    FILE* stream;
    stream = fopen("../data/level/highscores.sc", "w");

    fprintf(stream, "HighScores\n{\n");

    li = highscores;

    for (i = 0; i < 10; i++)
    {
	HighScore *hs;
	hs = (HighScore*) li->data;
    
	fprintf(stream, "%s = %s\n", hs->pwner, hs->score);

	li = li->next; 
	
    }

    fprintf(stream, "}\n");
	
    fclose(stream);
}





static void
draw_quad(float size)
{
    glBegin(GL_QUADS);
    
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-size, size, 0.0f);
    
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(size, size, 0.0f);
    
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(size, -size, 0.0f);
    
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(-size, -size, 0.0f);
    
    glEnd();
}

static void
draw_spectrum(void)
{
    int i;

    float* spectrum;
    CLvertex lv, v; /* last vertex, vertex */

    spectrum = zf_audio_get_spectrum();

    glBegin(GL_LINES);

    cluSetVertex(&lv, 0.05f * (128 - 0), 100.0f * spectrum[0] / (0.1f * (256.0f - 0)), 0.0f);
    for (i = 1; i < 256; i++)
    {
	cluSetVertex(&v, 0.05f * (128 - i), 100.0f * spectrum[i] / (0.1f * (256.0f - i)), 0.0f);
	glVertex3fv((GLfloat*)&lv);
	glVertex3fv((GLfloat*)&v);
	clCopyVertex(&lv, &v);	
    }

    glEnd();
}

static void
draw_pointer(void)
{
    glColor3f(1.0f, 0.0f, 0.0f);
    glPointSize(16.0f);
    glBegin(GL_POINTS);
    glVertex3fv((GLfloat*) &mouse_position);
    glEnd();
}


/*
 * nick - (fullscreen) drawing functions
 */

static void draw_splash(void)
{
    static float lifespan = 5.0f;
    static float fade;

    if (elapsed_time > lifespan)
    {
	loop_state = LOOP_STATE_MENU;
    }
    else
    {
	fade = 1.0f - 2.0f * (GLfloat)(lifespan - elapsed_time) / lifespan;
	fade = butterworth(fade, 0.5f, 8.0f);	
	
	glPushMatrix();
	glLoadIdentity();
	
	glColor3f(fade, fade, fade);
	glTranslatef(-5.0f, 0.0f, -10.0f - 50.0f * zf_audio_get_spectrum_average());
	glTranslatef(0.0f, 0.5f, 0.0f);
	glScalef(1.0f, fade, 1.0f);
	glTranslatef(0.0f, -0.5f, 0.0f);

	zf_text_draw("onetwenty");

	glPopMatrix();
    }
}


/*
 * nick - menu screen drawing variables and functions
 */

static const
float menu_position_start[3] = {1.2f, 0.8f, -17.5f};

static const
float menu_position_increment[3] = {0.0f, -1.6f, 0.0f};

static
float menu_position[3];

static void
draw_menu_reset(void)
{
    memcpy(menu_position, menu_position_start, 3 * sizeof(float));
}

static void
draw_menu_item(const char* name,
	       bool selected)
{
    glPushMatrix();

    glTranslatef(menu_position[0], menu_position[1], menu_position[2]);
    
    //raise text and change colour to red if it is the selected menu item
    if (selected)
    {
	glColor4f(1.0f, 1.0f, 1.0f, 0.6f);
	glPushMatrix();
	glTranslatef(-3.0f, 0.5f, 1.5f);
	glRotatef(select_sphere_rotate, 1.0f, 1.0f, 0.0f);
	glutWireSphere(0.6f, 8, 8);
	select_sphere_rotate += 0.1f;
	glPopMatrix();

	glTranslatef(0.0f, 0.0f, 1.5f);
	glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
    }
    else
	glColor3f(1.0f, 1.0f, 1.0f);
    
    zf_text_draw(name);
    
    glPopMatrix();

    //increment menu position
    cluVertexAdd((CLvertex*)menu_position, (CLnormal*)menu_position_increment);
}

static void
draw_menu_title(void)
{
    glPushMatrix();   

    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    glTranslatef(-5.4f, 3.6f, -10.0f - 20.0f * zf_audio_get_spectrum_average());
    zf_text_draw("zyber");

    glTranslatef(0.0f, 0.0f, 20.0f * zf_audio_get_spectrum()[128]);
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
    zf_text_draw("flux");

    glPopMatrix();
}

static void
draw_menu_spectrum(void)
{
    glPushMatrix();
    glTranslatef(0.0f, -3.6f, -7.5f);
    glColor3f(1.0f, 0.0f, 0.0f);
    draw_spectrum();
    glPopMatrix();
}

static void
draw_menu(void)
{   
    draw_menu_reset();

    draw_menu_title();

    draw_menu_item(game_loop_started ? "continue" : "start", menu_state == MENU_STATE_START);
    draw_menu_item("controls", menu_state == MENU_STATE_CONTROLS);
    draw_menu_item("highscore", menu_state == MENU_STATE_HIGHSCORE);
    draw_menu_item("credits", menu_state == MENU_STATE_CREDITS);
    draw_menu_item("exit", menu_state == MENU_STATE_EXIT);

    draw_menu_spectrum();

    //    draw_pointer();  /* put this back in when we can work in mouse selecting */
}


/*
 * nick - controls screen drawing variables and functions
 */

static const
float controls_position_start[3] = {-18.0f, 6.0f, -32.0f};

static const
float controls_position_increment[3] = {0.0f, -1.6f, 0.0f};

static
float controls_position[3];

static void
draw_controls_reset(void)
{
    memcpy(controls_position, controls_position_start, 3 * sizeof(float));
}


static void
draw_controls_item(const char* name,
		   const char* description)
{
    glPushMatrix();

    glTranslatef(controls_position[0], controls_position[1], controls_position[2]);
    
    //raise text and change colour to red if it is the selected controls item
    glTranslatef(0.0f, 0.0f, 1.5f);
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
    zf_text_draw(name);

    glColor3f(1.0f, 1.0f, 1.0f);
    zf_text_draw(" = ");
    zf_text_draw(description);
    
    glPopMatrix();

    //increment controls position
    cluVertexAdd((CLvertex*)controls_position, (CLnormal*)controls_position_increment);
}

static void
draw_controls(void)
{
    glPushMatrix();

    glTranslatef(-4.5f, 10.5f, -25.0f);
    glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
    zf_text_draw("controls");
    
    glPopMatrix();

    draw_controls_reset();

    draw_controls_item("w", "move up tier");
    draw_controls_item("s", "move down tier");
    draw_controls_item("a", "move anti-clockwise");
    draw_controls_item("d", "move clockwise");
    cluVertexAdd((CLvertex*)controls_position, (CLnormal*)controls_position_increment);
    draw_controls_item("mouse motion", "move crosshairs");
    draw_controls_item("left_mouse_button", "fire missle");
    draw_controls_item("right_mouse_button", "fire torpedo");
    cluVertexAdd((CLvertex*)controls_position, (CLnormal*)controls_position_increment);
    draw_controls_item("space_bar", "bomb");
}

static const
float highscore_position_start[3] = {-13.0f, 7.0f, -25.0f};

static const
float highscore_position_increment[3] = {0.0f, -1.6f, 0.0f};

static
float highscore_position[3];

static void
draw_highscore_reset(void)
{
    memcpy(highscore_position, highscore_position_start, 3 * sizeof(float));
}

static void
draw_highscore_item(const char* name,
		   const char* description)
{
    glPushMatrix();

    glTranslatef(highscore_position[0], highscore_position[1], highscore_position[2]);
    
    //raise text and change colour to red if it is the selected highscore item
    glTranslatef(0.0f, 0.0f, 1.5f);
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
    zf_text_draw(name);
    glPopMatrix();

    glPushMatrix();

    glTranslatef(highscore_position[0], highscore_position[1], highscore_position[2]);
    glTranslatef(15.0f, 0.0f, 1.5f);
    glColor3f(1.0f, 1.0f, 1.0f);
    zf_text_draw("x ");
    zf_text_draw(description);
    
    glPopMatrix();

    //increment highscore position
    cluVertexAdd((CLvertex*)highscore_position, (CLnormal*)highscore_position_increment);
}
static void
draw_highscore(void)
{
    GList* list;
    int i;
    glPushMatrix();

    glTranslatef(-5.5f, 11.0f, -25.0f);
    glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
    zf_text_draw("highscores");
    
    glPopMatrix();

    draw_highscore_reset();

    list = highscores;
    for(i = 0; i < 10; i++) /* hack - 10 high scores */
    {
	HighScore* hs;
	
	hs = (HighScore*)list->data;

	draw_highscore_item(hs->pwner, hs->score);
	list = list->next;
    }
    /*
      draw_highscore_item("jason", "9999999");
      draw_highscore_item("james", "9999999");
      draw_highscore_item("tone", "9999999");
      draw_highscore_item("minh", "9999999");
      draw_highscore_item("nick", "9876543");
      draw_highscore_item("terra", "0000005");
      draw_highscore_item("cloud", "0000004");
      draw_highscore_item("squall", "0000003");
      draw_highscore_item("zidane", "0000002");
      draw_highscore_item("vaan", "0000001");
    */
}

static void
draw_high_score_entry(void)
{
    glPushMatrix();

    glTranslatef(-12.5f, 10.5f, -25.0f);
    glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
    zf_text_draw("you got a high score!");
    
    glPopMatrix();

    glPushMatrix();

    glTranslatef(-4.0f, 0.8f, -10.0f);
    glColor4f(1.0f, 0.0f, .0f, 1.0f);
    zf_text_draw(high_score_value);
    
    glPopMatrix();

    glPushMatrix();

    glTranslatef(-10.5f, -2.0f, -25.0f);
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    zf_text_draw("name:");
    
    glPopMatrix();

    glPushMatrix();

    glTranslatef(-3.5f, -2.0f, -25.0f);
    glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
    zf_text_draw(high_score_entry);
    
    glPopMatrix();
 
}


static const
float credits_position_start[3] = {-22.0f, 12.0f, -32.0f};

static const
float credits_position_increment[3] = {0.0f, -1.6f, 0.0f};

static
float credits_position[3];

static void
draw_credits_reset(void)
{
    memcpy(credits_position, credits_position_start, 3 * sizeof(float));
}

static void
draw_credits_item(const char* name,
		   const char* description)
{
    glPushMatrix();

    glTranslatef(credits_position[0], credits_position[1], credits_position[2]);
    
    //raise text and change colour to red if it is the selected credits item
    glTranslatef(0.0f, 0.0f, 1.4f);
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
    zf_text_draw(name);
    
    glPopMatrix();

    cluVertexAdd((CLvertex*)credits_position, (CLnormal*)credits_position_increment);

    glPushMatrix();
    glTranslatef(credits_position[0], credits_position[1], credits_position[2]);
    /* next line, rewind a bit */
    glTranslatef(5.0f, 0.0f, 0.9f);

    glColor3f(1.0f, 1.0f, 1.0f);
    zf_text_draw(" = ");
    zf_text_draw(description);
    
    glPopMatrix();

    //increment credits position.
    cluVertexAdd((CLvertex*)credits_position, (CLnormal*)credits_position_increment);
}

static void
draw_credits(void)
{
    glPushMatrix();

    glTranslatef(-10.0f, 12.5f, -25.0f);
    glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
    zf_text_draw("credits /  acks");
    
    glPopMatrix();

    draw_credits_reset();
    draw_credits_item("jason wong", "lead programmer / graphics");
    draw_credits_item("jamess strauss", "programmer / tool engineer");
    draw_credits_item("tone prior", "programmer / technical");  
    draw_credits_item("minh t tran", "modeller / artwork");
    cluVertexAdd((CLvertex*)credits_position, (CLnormal*)credits_position_increment);
    draw_credits_item("poya manoucherhi", "music");
    draw_credits_item("adam matera", "modeller");

    glPushMatrix();
    glTranslatef(credits_position[0], credits_position[1], credits_position[2]);
    glTranslatef(-7.5f, -5.5f, -10.0f);
    glColor3f(0.5f, 0.5f, 0.5f);
    zf_text_draw("acknowledgements:");
    glColor3f(0.6f, 0.6f, 0.5f);
    glTranslatef(-19.0f, -3.0f, -1.0f);
    zf_text_draw("nick lowe-engine design+opengl+sdl+fmod+gtk");
    glTranslatef(-38.0f, -2.0f, 0.0f);
    zf_text_draw("blender+thefreesoundproject+uwa:csse");
    glPopMatrix();
}


static const
float level_position_start[3] = {1.2f, 3.0f, -25.0f};

static const
float level_position_increment[3] = {0.0f, -1.6f, 0.0f};

static
float level_position[3];

static void
draw_level_reset(void)
{
    memcpy(level_position, level_position_start, 3 * sizeof(float));
}

static void
draw_level_item(const char* name,
	       bool selected)
{
    glPushMatrix();

    glTranslatef(level_position[0], level_position[1], level_position[2]);
    
    //raise text and change colour to red if it is the selected level item
    if (selected)
    {
	glColor4f(1.0f, 1.0f, 1.0f, 0.6f);
	glPushMatrix();
	glTranslatef(-3.0f, 0.5f, 1.5f);
	glRotatef(select_sphere_rotate, 1.0f, 1.0f, 0.0f);
	glutWireSphere(0.6f, 8, 8);
	select_sphere_rotate += 0.1f;
	glPopMatrix();

	glTranslatef(0.0f, 0.0f, 1.5f);
	glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
    }
    else
	glColor3f(1.0f, 1.0f, 1.0f);
    
    zf_text_draw(name);
    
    glPopMatrix();

    //increment level position
    cluVertexAdd((CLvertex*)level_position, (CLnormal*)level_position_increment);
}


static void
draw_levels_title(void)
{
    glPushMatrix();   

    glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
    glTranslatef(-8.0f, 10.0f, -25.0f);
    zf_text_draw("choose a level");

    glPopMatrix();
}

static void
draw_levels(void)
{   
    unsigned int i;

    CL_FUNCENTRY();

    //printf("%s() : num_levels = %u\n", __FUNCTION__, num_levels);

    draw_level_reset();

    draw_levels_title();

    for (i = 0 ; i < num_levels ; i++)
    {
	draw_level_item(level_names[i], levels_state == i);
    }

    //draw_menu_spectrum();

    //draw_pointer();
}

static void
init_levels(void)
{
    /* clear the level names and num_levels since we are checking all over again */
    if(level_filenames)
	free(level_filenames);
    num_levels = 0;

    /* ask zf_level.c to search for level files and return a list */
    level_filenames = zf_level_search_directory(&num_levels, "../data/level");

#if 0
    /* print the levels found during the search above */
    printf("%s() : num_levels = %u\n", __FUNCTION__, num_levels);

    {
	unsigned int i;

	for (i = 0 ; i < num_levels ; i++)
	    printf("%u:%s\n", i, level_filenames[i]);
    }
#endif

    /* use the level_filenames to make an array of level names */
    {
      unsigned int i;

      /* malloc an array to store the level names */
      level_names = (char**) malloc (num_levels * sizeof(char*));

      for (i = 0 ; i < num_levels ; i++)
	{
	  level_names[i] = (char*) malloc (FILENAME_STRING_LENGTH * sizeof(char));

	  zf_level_get_level_name_from_file(level_names[i], level_filenames[i]);
	}
    }

}

/*!
  \todo Magic string length 80!
*/
static void
handle_key_down(SDL_keysym* keysym)
{
    int game_result;

    switch(loop_state)
    {
	/*
	  splash screen
	  - any key switches state to the menu screen
	*/
    case LOOP_STATE_SPLASH:
	//glClear(GL_COLOR_BUFFER_BIT);
	loop_state = LOOP_STATE_MENU; /* commenting this will make splash unskippable */
	break;
	
	/*
	  menu screen
	  - up, down manipulate menu
	  - return selects an option
	  - escape finishes
	*/
    case LOOP_STATE_MENU:
	switch(keysym->sym)
	{
	case SDLK_ESCAPE:
	    zf_audio_sample_play(menu_back);
	    menu_result = 0;
	    done = true;
	    break;
	case SDLK_UP:
	case SDLK_w:
	    zf_audio_sample_play(menu_change);
	    menu_state = (menu_state + NUM_MENU_STATES - 1) % NUM_MENU_STATES;
	    break;
	case SDLK_DOWN:
	case SDLK_s:
	    zf_audio_sample_play(menu_change);
	    menu_state = (menu_state + 1) % NUM_MENU_STATES;
	    break;
	case SDLK_RETURN:
	case SDLK_SPACE:

	    zf_audio_sample_play(menu_select);

	    switch(menu_state)
	    {
		/* start */
	    case MENU_STATE_START:
		//zf_game_loop_init();
		if (!game_loop_started)
		{
		    init_levels();
		    
		    loop_state = LOOP_STATE_LEVELS;
		}
		else
		{
		    zf_audio_stream_stop(background_music);
		    game_result = zf_game_loop_run();
		    zf_audio_stream_play(background_music);
		    /* returning from the pause menu */
		    switch(game_result)
		    {
		    case 0:
		    case 1:
			{
			    bool high_enough = false;
			    GList* li = highscores;
			    unsigned int score = zf_hud_query_score();
			    int s;
			    score_position = 10;
			    for(s = 0; s < 10; s++) 
			    {
				HighScore* hs;
				hs = (HighScore*) li->data;

				if(score >= atoi(hs->score) && s < score_position)
				{
				    score_position = s;		
				    high_enough = true;
				}

				li = li->next;
			    }

			    if(high_enough)
			    {
				loop_state = LOOP_STATE_GAMEOVER;
				
				if(score < 10)
				    sprintf(high_score_value, "000000%d\0",score); 
				else if (score < 100)
				    sprintf(high_score_value, "00000%d\0",score); 
				else if (score < 1000)
				    sprintf(high_score_value, "0000%d\0",score); 
				else if (score < 10000)
				    sprintf(high_score_value, "000%d\0",score); 
				else if (score < 100000)
				    sprintf(high_score_value, "00%d\0",score); 
				else if (score < 1000000)
				    sprintf(high_score_value, "0%d\0",score); 
				else if (score < 10000000)
				    sprintf(high_score_value, "%d\0",score); 
				else
				    sprintf(high_score_value, "9999999\0"); 
			    }
			    else 
				loop_state = LOOP_STATE_MENU;
			    
			    menu_result = 1;
			    game_loop_started = false;
			    done = true;
			}
			break;
		    case 2: /*quit */
			menu_result = 0;
			done = true;
		    default:
			/*do nothing  - game is paused */
			break;
		    }
		    
		}
	
		//zf_game_loop_close();

		break;
	    case MENU_STATE_CONTROLS:  /*controls*/
		loop_state = LOOP_STATE_CONTROLS;
		break;

	    case MENU_STATE_HIGHSCORE:  /* highscore */
		loop_state = LOOP_STATE_HIGHSCORE;
		break;

	    case MENU_STATE_CREDITS:  /* credits */
		loop_state = LOOP_STATE_CREDITS;
		break;

	    case MENU_STATE_EXIT:  /* exit */
		menu_result = 0;
		done = true;
		break;
	    }
	
	default:
	    break;
	}
	break;

    case LOOP_STATE_GAMEOVER:
	switch(keysym->sym)
	{
	case SDLK_RETURN:
	    {
		int counter = 0;
		unsigned int high_score = zf_hud_query_score();
		HighScore* hs = g_new(HighScore, 1);
		//printf("high_score index %d\n", high_score_entry_index);
		high_score_entry[high_score_entry_index] = '\0';
			
		while(high_score_entry[counter]!='\0')
		{
		    hs->pwner[counter] = high_score_entry[counter];
		    counter++;
		  }

		hs->pwner[counter] = '\0';

		counter = 0;
		if(hs->pwner[counter] == '\0')
		    sprintf(hs->pwner,"onetwenty\0");


		while(high_score_value[counter]!='\0')
		{
		    hs->score[counter] = high_score_value[counter];
		    counter++;
		  }
		hs->score[counter] = '\0';
					
		highscores = g_list_insert(highscores, hs, score_position);
		high_scores_write();

		high_score_entry_index = 0;
		for(counter = 0; counter < 10; counter++)
		    high_score_entry[counter] = '\0';

		score_position = 11;
		loop_state = LOOP_STATE_HIGHSCORE;
	    }
	    break;
	case SDLK_BACKSPACE:
	    high_score_entry_index--;
	    if(high_score_entry_index < 0)
		high_score_entry_index = 0;

	    high_score_entry[high_score_entry_index] = '\0';
	    break;
	case SDLK_ESCAPE:
	case SDLK_SPACE:
	case SDLK_LSHIFT: 
	case SDLK_RSHIFT:
	case SDLK_CAPSLOCK:
	case SDLK_TAB:
	case SDLK_LCTRL:
	case SDLK_RCTRL:
	case SDLK_LMETA:
	case SDLK_RMETA:
 	    /*do nothing*/
	    break;
	default:  /* other keys */
	    if(high_score_entry_index < 10)
		high_score_entry[high_score_entry_index] = keysym->sym;

	    high_score_entry_index++;
	    if(high_score_entry_index > 10)
		high_score_entry_index = 10;
	    //printf("%c\n", keysym->sym);
	    break;
	}
	break;
    case LOOP_STATE_LEVELS:
	switch(keysym->sym)
	{
	    
	case SDLK_h:
	    glClear(GL_COLOR_BUFFER_BIT);
	    loop_state = LOOP_STATE_GAMEOVER;
	    break;
	    
	case SDLK_ESCAPE:
	    zf_audio_sample_play(menu_back);
	    glClear(GL_COLOR_BUFFER_BIT);
	    loop_state = LOOP_STATE_MENU;
	    break;
	    
	case SDLK_UP:
	case SDLK_w:
	    zf_audio_sample_play(menu_change);
	    levels_state = (levels_state + num_levels - 1) % num_levels;
	    break;
	    
	case SDLK_DOWN:
	case SDLK_s:
	    zf_audio_sample_play(menu_change);
	    levels_state = (levels_state + 1) % num_levels;
	    break;

	case SDLK_RETURN:
	case SDLK_SPACE:
	    zf_audio_sample_play(menu_select);

	    zf_audio_stream_stop(background_music);
	    glClear(GL_COLOR_BUFFER_BIT);
	    
	    {
		char     full_filename[FILENAME_STRING_LENGTH];

		/* add the path to the front of the level filename */
		sprintf(full_filename, "../data/level/");
		strcat(full_filename, level_filenames[levels_state]);
		
		/* check filename */
		//printf("%s() : full_filename = %s\n", __FUNCTION__, full_filename);

		zf_game_loop_init(full_filename);
	    }		

	    game_loop_started = true;
	    game_result = zf_game_loop_run();
	    zf_audio_stream_play(background_music);
	    loop_state = LOOP_STATE_MENU;
	    switch(game_result)
	    {
	    case 0:
	    case 1:
		{
		    bool high_enough = false;
		    GList* li = highscores;
		    unsigned int score = zf_hud_query_score();
		    int s;
		    score_position = 10;
		    for(s = 0; s < 10; s++) 
		    {
			HighScore* hs;
			hs = (HighScore*) li->data;

			if(score >= atoi(hs->score) && s < score_position)
			{
			    score_position = s;	
			    high_enough = true;
			}
			   
			li = li->next;
		    }
		 
		    if(high_enough)
		    {
			loop_state = LOOP_STATE_GAMEOVER;
				
			if(score < 10)
			    sprintf(high_score_value, "000000%d\0",score); 
			else if (score < 100)
			    sprintf(high_score_value, "00000%d\0",score); 
			else if (score < 1000)
			    sprintf(high_score_value, "0000%d\0",score); 
			else if (score < 10000)
			    sprintf(high_score_value, "000%d\0",score); 
			else if (score < 100000)
			    sprintf(high_score_value, "00%d\0",score); 
			else if (score < 1000000)
			    sprintf(high_score_value, "0%d\0",score); 
			else if (score < 10000000)
			    sprintf(high_score_value, "%d\0",score); 
			else
			    sprintf(high_score_value, "9999999\0"); 
		    }
		    else 
			loop_state = LOOP_STATE_MENU;
	
		    menu_result = 1;
		    game_loop_started = false;
		    done = true;
		}
		break;
	    case 2:
		menu_result = 0;
		done = true;
	    default:
		/*do nothing  - game is paused */
		break;
	    }
	}
	
	break;
	
    default: 
	zf_audio_sample_play(menu_back);
	glClear(GL_COLOR_BUFFER_BIT);
	loop_state = LOOP_STATE_MENU;
	break;
    }
}

static void
handle_mouse_select_state(void)
{
}

static void
handle_mousebutton_down(void)
{
} 

static void
unproject_mouse(CLvertex* v, GLdouble mouse_x, GLdouble mouse_y, GLdouble mouse_z)
{
    GLdouble projection[16];   /* opengl projection matrix */
    GLdouble model_view[16];   /* opengl modelview matrix */
    GLint    view_port[4];     /* opengl viewport */
    GLdouble point[3];

    /* get projection and modelview matrices and viewport */
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetDoublev(GL_MODELVIEW_MATRIX, model_view);
    glGetIntegerv(GL_VIEWPORT, view_port);

    /* unproject */
    gluUnProject(mouse_x,
		 view_port[3] - mouse_y,
		 mouse_z,
		 model_view,
		 projection,
		 view_port,
		 &point[0],
		 &point[1],
		 &point[2]);

    /* set return value */
    cluSetVertex(v, point[0], point[1], point[2]);
}

static void
handle_mouse_state(void)
{
    Uint8 mouse_state;

    /* update 2d mouse pos */
    mouse_state = SDL_GetMouseState(&mouse_x, &mouse_y);

    /* get 3d mouse pos */
    unproject_mouse(&mouse_position, (GLdouble) mouse_x, (GLdouble) mouse_y, (GLdouble) 1.0);

    /* change menu state? */
    switch(loop_state)
    {
    case LOOP_STATE_MENU:
#if 0
	clPrintVertex(&mouse_position);
	printf("%f %f %f\n", menu_position_start[0],menu_position_start[1],menu_position_start[2]);
	printf("%f %f %f\n", menu_position_increment[0],menu_position_increment[1],menu_position_increment[2]);
#endif
	break;
    }
}

static void
process_events(void)
{
    SDL_Event event;
   
    while(SDL_PollEvent(&event))
    {
	switch(event.type)
	{
	case SDL_KEYDOWN:
	    /* Handle key presses. */
	    handle_key_down(&event.key.keysym);
	    break;

	case SDL_QUIT:
	    /* Handle quit requests (like Ctrl-c). */
	    done = true;
	    break;
	}
    }

    handle_mouse_state();
}

/*!
  \todo Menu needs mouse support.
*/
static void
draw_screen(void)
{
    /*
      if (loop_state == LOOP_STATE_MENU) //pseudo-blur when in menu mode
      {
      
      glMatrixMode(GL_PROJECTION);
      glPushMatrix();
      glLoadIdentity();
      glColor4f(0.0f, 0.0f, 0.0f, 0.02f);
      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      draw_quad(1.0f);
      glDisable(GL_BLEND);
      glPopMatrix();
	
      glMatrixMode(GL_MODELVIEW);
      }
      else
    */
    glClear(GL_COLOR_BUFFER_BIT);

    draw_funcs[loop_state]();

    glFlush();
    glFinish();

    SDL_GL_SwapBuffers();
}

/*!
  \todo clear up this warning, happens during tool level load, maybe the
  function that fills the highscores list isnt called during normal tool
  execution
 */
unsigned int
zf_menu_loop_query_highscore(void)
{
    GList* list;
    HighScore* hs;
    unsigned score;

    if (!highscores)
    {
	printf("%s() : WARNING : highscores list not initialised\n", __FUNCTION__);
	return (unsigned int) 0;
    }

    list = highscores;
    hs = (HighScore *)list->data;
    
    score = atoi(hs->score);
   
    /*printf("%s\n", hs->score);
      printf("%d\n", score);*/
}


/* currently initial configuration is hardcoded, change this to read
   data from disk (ie. level file) */
void
zf_menu_loop_init(void)
{
    menu_state = MENU_STATE_START;

    /* load background music */
    background_music = zf_audio_stream_load("../data/music/Dry Land.mp3");

    /* load menu sounds */
    menu_change = zf_audio_sample_load("../data/sound_effects/menu_change.mp3");
    zf_audio_sample_set_volume(menu_change, 128);
    menu_select = zf_audio_sample_load("../data/sound_effects/menu_select.mp3");
    zf_audio_sample_set_volume(menu_select, 128);
    menu_back = zf_audio_sample_load("../data/sound_effects/menu_back.mp3");
    zf_audio_sample_set_volume(menu_back, 128);
    

    menu_result = 0;
    select_sphere_rotate = 0.0f;

    highscores = 0;
    high_score_entry_index = 0;
    
    /* load highscores*/
    {
	unsigned int i;
	
	FILE* stream;
	stream = fopen("../data/level/highscores.sc", "r");

	/* read */
	fscanf(stream, "HighScores\n{\n");

	for (i = 0; i < 10; i++)
	{
	    HighScore *hs;
	    hs = g_new(HighScore, 1);
    
	    fscanf(stream, "%s = %s\n", &hs->pwner, &hs->score);

	    //printf("%s = %s\n", hs->pwner, hs->score);
	    highscores = g_list_append(highscores, hs);
	}

	fscanf(stream, "}\n");
	
	fclose(stream);
    }

    glDisable(GL_DEPTH_TEST);
}

void
zf_menu_loop_close(void)
{
    zf_audio_stream_destroy(background_music);

    zf_audio_sample_destroy(menu_change);
    zf_audio_sample_destroy(menu_select);
    zf_audio_sample_destroy(menu_back);

    glEnable(GL_DEPTH_TEST);

}



/* returns 0 to quit program. otherwise will restart menu loop */
int
zf_menu_loop_run(void)
{
    Uint32 game_time;
    Uint32 lt;
    Uint32 t;

    double viewport[2] = {800.0, 600.0}; /* WARNING: GROSS HAXOR! */

    lt = SDL_GetTicks();
    done = 0;

    /* load a projection matrix that matches the window aspect ratio */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (double)viewport[0]/(double)viewport[1], 0.1, 512.0);

    /* reset the modelview matrix */
    glMatrixMode(GL_MODELVIEW);

    game_time = SDL_GetTicks();

    /* haxor */
    elapsed_time = 0.0f;
    
    /* start music */
    zf_audio_stream_play(background_music);

    while(!done)
    {
	/* time for game slice */
	t = SDL_GetTicks();
	lt = t;

	draw_screen();  /* before process events, so the menu doesn't show after game over screens*/

	process_events();
	
	while ((t - game_time) > ZF_TIME_STEP)
	{
	    game_time += ZF_TIME_STEP;
	    elapsed_time += ZF_TIME_STEP_SECS;
	}


    }

    glPopAttrib();
  
    return menu_result;
}
