/* Copyright 2009 HardHat
 * Licensed under the Revised BSD license
 * See licence.txt for details
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include <SDL/SDL_image.h>
#include <string>
#include <deque>
#include <algorithm>

#ifdef _WIN32
#include <windows.h>
#endif
#ifdef WIN32
#pragma comment(lib, "SDL.lib")
#pragma comment(lib, "SDLmain.lib")
#pragma comment(lib, "SDL_ttf.lib")
#pragma comment(lib, "SDL_image.lib")
#endif

enum {
	D_LEFT,
	D_UP,
	D_RIGHT,
	D_DOWN
};

int timer;
int timerActive=0;

void draw_text(int size,int x,int y,const char *msg);
/*
 PLEASE NOTE: the program will require SDL.dll which is located in
              dev-c++'s dll directory. You have to copy it to you
			  program's home directory or the path.
 */

/* The screen surface */
SDL_Surface *screen = NULL, *bgskin=NULL;
TTF_Font *headline, *subhead, *body, *prompt;

//static int fibonacci[]={ 3,5,8,13,21 };
class Mix {
public:
	std::string	text;
	int time;
	time_t date;
	Mix() { time=0; date=::time(NULL); }
};

std::deque<Mix> mix;
int count;

// Status message
std::string statusMsg;
// Statistics of recent solves
std::string stats;

std::deque<Mix>::iterator selected,deleteSelected;

TTF_Font *size_to_font(int size)
{
	TTF_Font *font=headline;
	
	switch(size) {
	case 18:
		font=subhead;
		break;
	case 12:
		font=body;
		break;
	case 8:
		font=prompt;
		break;
	}
	return font;
}

void draw_text_line(int size,int x,int y,const char *msg)
{
	SDL_Surface *surf;
	TTF_Font *font=headline;
	SDL_Rect rect;

	switch(size) {
	case 18:
		font=subhead;
		break;
	case 12:
		font=body;
		break;
	case 8:
		font=prompt;
		break;
	}
	SDL_Color fg={0xff,0xff,0xff,0xff};
	SDL_Color bg={0x00,0x7f,0x7f,0xff};
	if(bgskin && (size==8 || size>18)) {
		bg.r=0; bg.g=0; bg.b=0;
	} else if(bgskin) {
		bg.r=255; bg.g=255;	bg.b=255;
	}

	if(size==18) { fg.r=0xd0; fg.g=0xd0; fg.b=0xd0; }
	if(size==8) fg.b=0x80;

	if(bgskin && size==12) { fg.r=0; fg.b=0; fg.g=0; }
	if(bgskin && size==18) { fg.r=0x60; fg.g=0x60; fg.b=0x60; }

	surf=TTF_RenderText_Shaded(font,msg,fg,bg);
	rect.x=x;
	rect.y=y;
	rect.w=0;
	rect.h=0;
	SDL_BlitSurface(surf,NULL,screen,&rect);
	SDL_FreeSurface(surf);
}

// Adds in word wrapping
void draw_text(int size,int x,int y,const char *msg,int maxWidth)
{
	TTF_Font *font=size_to_font(size);
	int w,h;
	int offset=0;
	char *line=(char *)malloc(strlen(msg)+1);
	char *buf=(char *)malloc(strlen(msg)+1);

	offset=0;
	int i=0;
	do { 
		int len=strlen(msg);
		// Add words until too long.
		while(i<len && msg[i]!=' ') {
			buf[i]=msg[i];
			i++;
		}
		while(msg[i]==' ') {
			buf[i]=msg[i];
			i++;
		}
		buf[i]=0;
		w=0;
		h=0;
		TTF_SizeText(font,buf,&w,&h);
		if(w>=maxWidth || i==len) {
			strcpy(line,msg);
			if(w>=maxWidth) line[offset]=0;
			draw_text_line(size,x,y,line);
			y+=h;
			msg+=offset;
			if(i==len && w<maxWidth) msg+=i-offset;
			i=0;
			offset=0;
		} else {
			offset=i;
		}
	} while(strlen(msg)>0);

	free(line);
	free(buf);
}

void draw_text(int size,int x,int y,const char *msg)
{
	draw_text(size,x,y,msg,460-x);
}

/* This function draws to the screen; replace this with your own code! */
static void draw()
{
	SDL_Rect rect;

	SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0x00, 0x7f, 0x7f));
	if(bgskin) SDL_BlitSurface(bgskin,0,screen,0);

	//printf("Drawing::state=%d\n",state);
	rect.x=10;
	rect.y=10;
	rect.w=460;
	rect.h=40;
	if(bgskin) SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
	char dbg[256];
	static int frame=0;
	frame++;
	sprintf(dbg,"Trainer Time Trials");
	draw_text(24,20,10,dbg);
	rect.x=10;
	rect.y=120;
	rect.w=460;
	rect.h=80;
	if(bgskin) SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0xff, 0xff, 0xff));
	if(timerActive==0) {
		char buf[256];
		if(selected!=mix.end()) {
			buf[0]=0;
			if(selected->time>0) sprintf(buf,"%02d:%02.2f for ",selected->time/60000,(selected->time-(selected->time/60000)*60000)/1000.0f);
			strcat(buf,selected->text.c_str());
			draw_text(12,20,130,buf,440);
		}
		draw_text(12,20,160,stats.c_str(),440);
	} else {
		int now=SDL_GetTicks();
		int elapsed=now-timer;
		char buf[64];
		sprintf(buf,"Elapsed time: %02d:%02.2f",elapsed/60000,(elapsed-(elapsed/60000)*60000)/1000.0f);
		draw_text(18,20,130,buf,440);
	}
	rect.x=10;
	rect.y=238;
	rect.w=460;
	rect.h=34;
	if(bgskin) SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
	draw_text(8,20,240,statusMsg.c_str());
	draw_text(8,20,250,"X to scramble new.  Arrows for history. [] deletes.  Triangle to save");
	draw_text(8,20,260,"L+R to start timer. D-pad to stop timer. Press SELECT to quit.");

    /* Make sure everything is displayed on screen */
    SDL_Flip (screen);
    /* Don't run too fast */
    SDL_Delay (17);
}

void do_cross()
{
	if(timerActive) return;
	std::string scramble;
	
	deleteSelected=mix.end();
	char candidates[]="UDLRBF";
	// generate the next pattern.
	char last=-1;
	int i;
	
	for( i=0; i<25; i++) {
		int doublemove=0;
		int reversemove=0;
		int move=-1;
		while(1) {
			move=rand()/(RAND_MAX/6);
			if(move>5) continue;
			if(move==last) continue;
			if(move/2==last/2) continue;
			break;
		}
		if(1.0f*rand()/RAND_MAX>0.6666f) doublemove=1;
		if(1.0f*rand()/RAND_MAX>0.5f) reversemove=1;
		char buf[4];
		buf[0]=' ';
		buf[1]=candidates[move];
		buf[2]=doublemove?'2':reversemove?'\'':0;
		buf[3]=0;
		scramble+=i?buf:buf+1;
		last=move;
	}
	Mix mixNode;
	mixNode.text=scramble;
	mix.push_back(mixNode);
	selected=mix.end();
	selected--;
}

void do_square()
{
	if(timerActive) return;
	if(mix.size()==0) return;
	if(deleteSelected==selected) {
		mix.erase(deleteSelected);
		selected=mix.end();
		deleteSelected=mix.end();
		if(mix.size()>0) selected--;
		statusMsg="Deleted that scramble.";
	} else {
		deleteSelected=selected;
		statusMsg="Press [] again to delete.";
	}
}

void do_triangle()
{
	if(timerActive) return;
	FILE *file=fopen("history.txt","a");
	if(file) {
		std::deque<Mix>::iterator p;
		int i=0;
		int min=0,max=0,average=0;
		for(p=selected+1;p!=mix.begin() && i<12;) {
			p--;
			if(p->time==0) continue;
			if(p->time<min || min==0) min=p->time;
			if(p->time>max || max==0) max=p->time;
			average+=p->time;
			fprintf(file,"scramble=%s\n",p->text.c_str());
			fprintf(file,"scramble_date=%s",ctime(&p->date));
			fprintf(file,"scramble_solve=%02d:%02.2f\n",p->time/60000,(p->time-(p->time/60000)*60000)/1000.0f);
			i++;
		}
		average=average-min-max;
		if(i>2) average=average/(i-2);
		fprintf(file,"inner_average=%02d:%02.2f\n\n",average/60000,(average-(average/60000)*60000)/1000.0f);
		fclose(file);
		char msg[256];
		sprintf(msg,"Saved the last %d scores to history.txt",i);
		statusMsg=msg;
	} else {
		statusMsg="Couldn't write to history.txt for some reason.";
	}
}


void do_ltrigger()
{
	if(!timerActive) {
		timer=SDL_GetTicks();
		timerActive=1;
		statusMsg="Timer is now active! Go, go, go!";
	} else {
		timer=SDL_GetTicks();
		statusMsg="Timer is already active! Resetting. Go, go, go!";
	}
}

void do_rtrigger()
{
	if(!timerActive) {
		timer=SDL_GetTicks();
		timerActive=1;
		statusMsg="Timer is now active! Go, go, go!";
	} else {
		timer=SDL_GetTicks();
		statusMsg="Timer is already active! Resetting. Go, go, go!";
	}
}

void updateStats()
{
	stats="";
	std::deque<Mix>::iterator p;
	int i=0;
	int min=0,max=0,average=0;
	for(p=selected+1;p!=mix.begin() && i<12;) {
		p--;
		if(p->time==0) continue;
		if(p->time<min || min==0) min=p->time;
		if(p->time>max || max==0) max=p->time;
		average+=p->time;
		char buf[64];
		sprintf(buf,"%s%02d:%02.2f%s ",p==selected?">":"",p->time/60000,(p->time-(p->time/60000)*60000)/1000.0f,p==selected?"<":"");
		stats+=buf;
		i++;
	}
	average=average-min-max;
	if(i>2) average=average/(i-2);
	char avg[256];
	avg[0]=0;
	if(i>2) sprintf(avg,"  Inner Average (%d of %d): %02d:%02.2f",i-2,i,average/60000,(average-(average/60000)*60000)/1000.0f);
	stats+=avg;
}

void move_character(int dir)
{
	if(timerActive && dir>=0) {
		char buf[256];
		int i;
		
		int now=SDL_GetTicks();
		int elapsed=now-timer;
		if(selected==mix.end()) {
			Mix mixNode;
			mix.push_back(mixNode);
			selected=mix.end();
			selected--;
		}
		selected->time=now-timer;
		sprintf(buf,"New time: %02d:%02.2f",elapsed/60000,(elapsed-((elapsed/60000)*60000))/1000.0f);
		statusMsg=buf;
		updateStats();
		timerActive=0;
		do_cross();
		return;
	}
	if(mix.size()==0) return;
	if(dir==D_DOWN || dir==D_RIGHT) {
		selected++;
		if(selected==mix.end()) {
			selected=mix.begin();
		}
	} else if(dir==D_UP || dir==D_LEFT) {
		if(selected==mix.begin()) {
			selected=mix.end();
		}
		selected--;
	}
	updateStats();
	deleteSelected=mix.end();
	statusMsg="";
}

int landr=0;

void handleEvent(SDL_Event &event,int &done,int &dir)
{
    switch (event.type)
    {
    case SDL_KEYDOWN:
//printf("Key Down\n");
        if(event.key.keysym.sym == SDLK_ESCAPE) done=1;
        if(event.key.keysym.sym == SDLK_SPACE) do_cross();
        if(event.key.keysym.sym == SDLK_LALT) do_square();
        if(event.key.keysym.sym == SDLK_LCTRL) do_triangle();
		if(event.key.keysym.sym == SDLK_LEFT) dir=0;
		if(event.key.keysym.sym == SDLK_UP) dir=1;
		if(event.key.keysym.sym == SDLK_RIGHT) dir=2;
		if(event.key.keysym.sym == SDLK_DOWN) dir=3;
		if(event.key.keysym.sym == ',') do_ltrigger();
		if(event.key.keysym.sym == '.') do_rtrigger();
        if(dir>=0) move_character(dir);
		dir=-1;
        break;
    case SDL_KEYUP:
		if(event.key.keysym.sym == SDLK_LEFT && dir==0) dir=-1;
		if(event.key.keysym.sym == SDLK_UP && dir==1) dir=-1;
		if(event.key.keysym.sym == SDLK_RIGHT && dir==2) dir=-1;
		if(event.key.keysym.sym == SDLK_DOWN && dir==3) dir=-1;
		break;
    case SDL_JOYBUTTONDOWN:
        if(event.jbutton.button == 10 /* SELECT */) done=1;
        if(event.jbutton.button == 0 /* TRIANGLE */) do_triangle();
        if(event.jbutton.button == 2 /* CROSS */) do_cross();
        if(event.jbutton.button == 3 /* SQUARE */) do_square();
        if(event.jbutton.button == 4 /* LTRIGGER */) {
		landr|=1;
	}
        if(event.jbutton.button == 5 /* RTRIGGER */) {
		landr|=2;
	}
		if(event.jbutton.button == 7 /* LEFT */) dir=0;
		if(event.jbutton.button == 8 /* UP */) dir=1;
		if(event.jbutton.button == 9 /* RIGHT */) dir=2;
		if(event.jbutton.button == 6 /* DOWN */) dir=3;
        if(dir>=0) move_character(dir);
		dir=-1;
		break;
    case SDL_JOYBUTTONUP:
        if(event.jbutton.button == 4 /* LTRIGGER */) {
		if(landr==3) do_ltrigger();
		landr&=~1;
	}
        if(event.jbutton.button == 5 /* RTRIGGER */) {
		if(landr==3) do_rtrigger();
 		landr&=~2;
	}
		if(event.jbutton.button == 7 /* LEFT */ && dir==0) dir=-1;
		if(event.jbutton.button == 8 /* UP */ && dir==1) dir=-1;
		if(event.jbutton.button == 9 /* RIGHT */ && dir==2) dir=-1;
		if(event.jbutton.button == 6 /* DOWN */ && dir==3) dir=-1;
		break;
    case SDL_QUIT:
        done = 1;
        break;
    default:
        break;
    }
	
}

extern "C" {
int main (int argc, char *argv[]);
}

int main (int argc, char *argv[])
{
    int done;
	int start=0;
	int dir=-1;
#ifdef _WIN32
	char msg[256];
#endif

    /* Initialize SDL */
    if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_JOYSTICK) < 0)
    {
#ifdef _WIN32
        sprintf (msg, "Couldn't initialize SDL: %s\n", SDL_GetError ());
        MessageBox (0, msg, "Error", MB_ICONHAND); 
#endif
        exit (1);
    }
    atexit (SDL_Quit);

	TTF_Init();

    /* Set 640x480 16-bits video mode */
    screen = SDL_SetVideoMode (480, 272, 32, SDL_SWSURFACE);
    if (screen == NULL)
    {
#ifdef _WIN32
        sprintf (msg, "Couldn't set 480x272x16 video mode: %s\n",
          SDL_GetError ());
        MessageBox (0, msg, "Error", MB_ICONHAND); 
#endif
        exit (2);
    }
    SDL_WM_SetCaption ("TimeTrainer SDL by HardHat", NULL);

	SDL_Joystick *joy=0;
	if(SDL_NumJoysticks()>0)
	   joy=SDL_JoystickOpen(0);

	SDL_Surface *splash=IMG_Load("composplash.png");
	if(splash) {
		SDL_BlitSurface(splash,0,screen,0);
		SDL_Flip (screen);
		SDL_Delay(1000);	
		SDL_FreeSurface(splash);
	}


    done = 0;
	SDL_Rect rect;
//	SDL_Delay(3000);	
	SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 255, 255));
	headline=TTF_OpenFont("Arial Black.TTF",24);
	subhead=TTF_OpenFont("Arial Black.TTF",18);
	SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 255));
	body=TTF_OpenFont("Bookman Old Style.TTF",12);
	SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 0, 255));
	prompt=TTF_OpenFont("BoomBox.TTF",9);
    SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0x7f, 0x7f));
	bgskin=SDL_LoadBMP("bg.bmp");

	if(bgskin) SDL_BlitSurface(bgskin,0,screen,0);
	/* Check for events */
	if(bgskin) {
		rect.x=10;
		rect.y=5;
		rect.w=460;
		rect.h=40;
		SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
		draw_text(24,40,10,"TimeTrainer by HardHat");
	} else {
		draw_text(24,150,10,"TimeTrainer");
		draw_text(18,150,45,"by HardHat");
	}

	rect.x=10;
	rect.y=255;
	rect.w=460;
	rect.h=20;
	if(bgskin) SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
	draw_text(8,20,260,"Press X to start.");
    SDL_Flip (screen);

	//printf("Waiting...\n");
	SDL_Event event;
    while (start==0 && SDL_WaitEvent (&event))
    {
        switch (event.type)
        {
        case SDL_KEYDOWN:
			start=1;
            break;
        case SDL_JOYBUTTONDOWN:
			start=1;
			break;
        case SDL_QUIT:
            done = 1;
            break;
        default:
            break;
        }
        if(start || done) break;
    }
	//printf("Starting game...\n");
	srand(SDL_GetTicks());
	do_cross();
	selected=mix.begin();

	while (!done) {
        SDL_Event event;

#define USE_WAIT 1
#ifdef USE_WAIT
		if(timerActive==0) {
	        /* Check for events */
			draw();
	        while (!done && SDL_WaitEvent (&event) ) {
				if(timerActive) break;
				/* Draw to screen */
				handleEvent(event,done,dir);
				draw();
	        }
		} else {
#endif
			/* Draw to screen */
			draw();
#ifndef _PSP
				SDL_Delay(1);
#endif
	        /* Check for events */
	        while (!done && SDL_PollEvent (&event) ) {
				handleEvent(event,done,dir);
	        }
		}
#ifdef USE_WAIT
    }
#endif

    return 0;
}
