/* 
**  RedSquare for Wii - Created by wplaat
**
**  RedSquare is an classic 2D action game.  Click and hold the red square. 
**  Now, move so that you neither touch the walls nor get hit by any of the 
**  blue blocks. If you make it to 31 seconds, you are doing brilliantly!
**
**  Copyright (C) 2009
**  ==================
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, version 2.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**
**  Release Notes:
**  ==============
**
**  06/01/2009 Version 0.72
**  - update urls on intro screen to www.plaatsoft.nl (My new domain on internet)
**  - Bugfix google analysic erquest are now working again.
**  - Bugfix: Score cheating with 1 and 2 button is now not posible anymore.
**
**  03/01/2009 Version 0.71
**  - Bugfix: Global highscore xml data is now always processed right.
**  - Network thread status is now showed on the welcome screen.
**  - Improve sound effects (Thanks nowhereman)
**  - Updated credits screen information.
**  - Updated help screen information.
**  - Build game with libogc 1.7.1 and devkitPPC r16 compiler.
**
**  01/01/2009 Version 0.70
**  - Added global highscore screen.
**  - Global highscore information is fetch from internet.
**  - Improve button graphics.
**  - Improve release notes screen.
**  - Added new cool background music.
**  - Network thread status is displayed on welcome screen.
**  - Bugfix: Local highscore is now always loaded correctly.
**  - Build game with libogc 1.7.1 and devkitPPC r16 compiler.
**
**  20/12/2008 Version 0.60
**  - Added SDHC card support.
**  - Added power button support.
**  - Default effect volume adapted.
**  - Improve screen title design.
**  - Bugfix: IR pointer doesn't disappear anymore.
**  - Build game with libogc 1.7.1 and devkitPPC r16 compiler.
**
**  08/12/2008 Version 0.50
**  - Added sound setting screen.
**  - Added release notes screen.
**  - Added functionality to fetch release notes from internet.
**  - Added send highscore to google analytic.
**  - Added 9 music tracks.
**  - Build game with libogc 1.6.0 and devkitPPC r15 compiler.
**
**  05/12/2008 Version 0.40
**  - Added welcome screen.
**  - Added help screen.
**  - Added credits screen.
**  - Added exit to HBC functionality.
**  - Added reset Wii functionality.
**  - Build game with libogc 1.6.0 and devkitPPC r15 compiler.
**
**  04/12/2008 Version 0.30
**  - Added highScore screen.
**  - Store highscore (in xml format) on SD Card .
**  - Added google analytic http request.
**  - Build game with libogc 1.6.0 and devkitPPC r15 compiler.
**
**  02/12/2008 Version 0.20
**  - Added new version http request check.
**  - Remove some minor reported bugs.
**  - Complete redesign of gameboard graphics.
**  - Add background music.
**  - Add some sound effects.
**  - Build game with libogc 1.6.0 and devkitPPC r15 compiler.
**
**  29/11/2008 Version 0.10
**  - First release for Wii homebrew scene.
**  - Started programming.
**  - Gameboard design.
**  - Added WiiRemote IR control.
**  - Added three intro screens.
**  - Basic game engine ready.
**  - Build game with libogc 1.6.0 and devkitPPC r15 compiler.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <ogcsys.h>
#include <gccore.h>
#include <gcmodplay.h> 
#include <wiiuse/wpad.h>
#include <jpeg/jpgogc.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h> 
#include <snd.h>
#include <fat.h>
#include <mxml.h>
 
#include "http.h"
#include "GRRLIB.h"
#include "effect1_pcm.h"
#include "effect2_pcm.h"
#include "effect3_pcm.h"
#include "track1_mod.h"
#include "track2_mod.h"
#include "track3_mod.h"
#include "track4_mod.h"
#include "track5_mod.h"
#include "track6_mod.h"
#include "track7_mod.h"
#include "track8_mod.h"
#include "track9_mod.h"

// -----------------------------------------------------------
// DEFINES
// -----------------------------------------------------------

#define PROGRAM_NAME	    "RedSquare"
#define PROGRAM_VERSION     "0.72"
#define RELEASE_DATE        "06-01-2009" 

// Get Latest released version 
#define URL1                "http://members.tele2.nl/wplaat/releasenotes3.html"
#define DOMAIN1				"members.tele2.nl/wplaat/"
#define ID1			        "UA-6447255-1"

// Get Release notes
#define URL2                "http://members.tele2.nl/wplaat/releasenotes3.html"
#define DOMAIN2				"members.tele2.nl/wplaat/"
#define ID2				    "UA-6447255-1"

// Get Best High Score
#define URL3                "http://wplaat.comze.com/game_store_result.php"
#define DOMAIN3				"wplaat.comze.com"
#define ID3				    "UA-6810183-1"

#define URL_TOKEN           " Version "
#define HIGHSCORE_FILENAME  "sd:/apps/RedSquare/highscore.xml"

#define WSP_POINTER_X       200
#define WSP_POINTER_Y       250

#define COLOR_LIGHTBLUE     0xEBCE87
#define COLOR_WHITESMOKE    0xFFFFFF
#define COLOR_DARKBLACK     0x000000
#define COLOR_DARKRED       0x0000FF

#define MAX_BUTTONS         10
#define MAX_RUMBLE			4
#define MAX_SQUARES   		5
#define MAX_HORZ_PIXELS		640
#define MAX_VERT_PIXELS		480
#define MAX_HIGHSCORE 		13
#define MAX_BEST	 		13
#define MAX_LEN			    256
#define MAX_ANGLE			360
#define MAX_SOUND_VOLUME	10
#define MAX_MUSIC_TRACK     9
#define MAX_LINES		    200
#define MAX_BUFFER_SIZE		4096

#define MIN_X_CURSOR		35
#define MIN_Y_CURSOR		25
#define MAX_X_CURSOR		627
#define MAX_Y_CURSOR		450

#define MUSIC_MULTIPLER		5
#define EFFECT_MULTIPLER	20

#define ICON_X				460
#define ICON_Y				-70
#define ICON_ZOOM			0.3

#define BUTTON_A            (WPAD_BUTTON_A     | WPAD_CLASSIC_BUTTON_A)
#define BUTTON_HOME         (WPAD_BUTTON_HOME  | WPAD_CLASSIC_BUTTON_HOME)
#define BUTTON_1            (WPAD_BUTTON_1     | WPAD_CLASSIC_BUTTON_X)
#define BUTTON_2            (WPAD_BUTTON_2     | WPAD_CLASSIC_BUTTON_Y)

// -----------------------------------------------------------
// PROTOTYPES
// -----------------------------------------------------------

void loadHighScoreFile(char* filename);
void drawText(int x, int y, int type, char *text,...);
void buttonExit(int index);
void convertBestHighScore(char *buffer);

// -----------------------------------------------------------
// TYPEDEFS 
// -----------------------------------------------------------
 
typedef struct 
{
  // Png Image index
  u8 *redsquare;
  u8 *bluesquare1;
  u8 *bluesquare2;
  u8 *bluesquare3;
  u8 *bluesquare4;
  u8 *gameboard1;
  u8 *gameboard2;
  u8 *pointer;
  u8 *button1;
  u8 *button1select;  
  u8 *button2;
  u8 *button2select;  
  u8 *background1;
  u8 *info;
  u8 *question;
  u8 *bar;
  u8 *barCursor;
  u8 *sound;
} 
image;

image images;

typedef struct
{
   u8 *image;
   int x;
   int y;
   int width;
   int height;
   bool directionX;
   bool directionY;
} 
square;

square squares[MAX_SQUARES];

typedef struct
{
   time_t localTime;
   time_t playTime; 
}
highscore;

highscore highscores[MAX_HIGHSCORE+1];

typedef struct
{
   time_t dt;
   char name[MAX_LEN];
   time_t score;
}
best;

best bests[MAX_BEST+1];

typedef struct
{
   u8 *image;
   u8 *imageSelect;
   char name[MAX_LEN]; 
   int x;
   int y;
   int width;
   int height;
} 
button;

button buttons[MAX_BUTTONS+1];

// -----------------------------------------------------------
// ENUMS
// -----------------------------------------------------------
 
enum
{
   stateWelcome=0,
   stateGame=1,
   stateGameOver=2,
   stateHighScore1=3,
   stateHighScore2=4,
   stateHelp=5,
   stateCredits=6,
   stateSound=7,
   stateReleaseNotes=8,
   stateNone=9
};

enum
{
   fontTitle1=0,
   fontTitle2=1,
   fontSubTitle=2,
   fontParagraph=3,
   fontNormal=4,
   fontSpecial=5,
   fontNew=6,
   fontButton=7,
   fontWelcome=8
};

// -----------------------------------------------------------
// VARIABLES
// -----------------------------------------------------------

u32     *frameBuffer[1] = {NULL};
GXRModeObj *rmode = NULL;
Mtx     GXmodelView2D;

time_t  localTime;
time_t  playTime;
time_t  prevPlayTime;

int     yOffset;
int     yjpegOffset;
int     angle;

bool    selected;
bool    selected1;
bool    selected2;

int     stateMachine;
int     prevStateMachine;
static  MODPlay snd1;
int     speed;
int     maxHighScores;
char    appl_user3[MAX_LEN];
int     maxButtons;
int     rumbleCounter;

int     selectedMusic;
int     musicVolume;
int     effectVolume;
int     notesStartLine;
int     noteDelay;

// Poweroff flag
bool bPowerOff = false;	

// Intro1 Image
extern char     pic1data[];
extern int      pic1length;

// Intro2 Image
extern char     pic2data[];
extern int      pic2length;

// Intro3 Image
extern char     pic3data[];
extern int      pic3length;

// RedSquare1 Image
extern char     pic4data[];
extern int      pic4length;

// BlueSquare1 Image
extern char     pic5data[];
extern int      pic5length;

// BlueSquare2 Image
extern char     pic6data[];
extern int      pic6length;

// BlueSquare3 Image
extern char     pic7data[];
extern int      pic7length;

// BlueSquare4 Image
extern char     pic8data[];
extern int      pic8length;

// Pointer Image
extern char     pic9data[];
extern int      pic9length;

// Gameboard1 Image
extern char     pic10data[];
extern int      pic10length;

// Gameboard2 Image
extern char     pic11data[];
extern int      pic11length;

// Button1 Image
extern char     pic12data[];
extern int      pic12length;

// Button1select Image
extern char     pic13data[];
extern int      pic13length;

// info Image
extern char     pic15data[];
extern int      pic15length;

// question Image
extern char     pic16data[];
extern int      pic16length;

// bar Image
extern char     pic17data[];
extern int      pic17length;

// barCursor Image
extern char     pic18data[];
extern int      pic18length;

// Button2 Image
extern char     pic19data[];
extern int      pic19length;

// Button2select Image
extern char     pic20data[];
extern int      pic20length;

// Sound Image
extern char     pic21data[];
extern int      pic21length;

// -----------------------------------------------------------
// GAME LOGIC
// -----------------------------------------------------------

void initMusicTrack(void)
{
   switch (selectedMusic)
   {
      case 1 : MODPlay_SetMOD(&snd1, track1_mod);
			   MODPlay_Start(&snd1);	
			   break;	
			   
      case 2 : MODPlay_SetMOD(&snd1, track2_mod);
			   MODPlay_Start(&snd1);	
			   break;
			   
      case 3 : MODPlay_SetMOD(&snd1, track3_mod);
			   MODPlay_Start(&snd1);	
			   break;
			   
      case 4 : MODPlay_SetMOD(&snd1, track4_mod);
			   MODPlay_Start(&snd1);	
			   break;
			   
      case 5 : MODPlay_SetMOD(&snd1, track5_mod);
			   MODPlay_Start(&snd1);	
			   break;	
		   
	  case 6 : MODPlay_SetMOD(&snd1, track6_mod);
			   MODPlay_Start(&snd1);	
			   break;	
			   
	  case 7 : MODPlay_SetMOD(&snd1, track7_mod);
			   MODPlay_Start(&snd1);	
			   break;	
			   
	  case 8 : MODPlay_SetMOD(&snd1, track8_mod);
			   MODPlay_Start(&snd1);	
			   break;	
			   
	  case 9 : MODPlay_SetMOD(&snd1, track9_mod);
			   MODPlay_Start(&snd1);	
			   break;
   } 
   MODPlay_SetVolume( &snd1, musicVolume*MUSIC_MULTIPLER,musicVolume*MUSIC_MULTIPLER); 
}

void moveSquares(void)
{ 
   int i;
   
   for (i=1; i<MAX_SQUARES; i++)
   {
       if (squares[i].directionX)
	   {
           if (squares[i].x+squares[i].width<MAX_HORZ_PIXELS)
	       {
	          squares[i].x=squares[i].x+speed;	   
	       }
	       else
	       {
	          squares[i].directionX=false;
		      squares[i].x=squares[i].x-speed;	
			  if (stateMachine==stateGame) SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 8000, 0, (u8 *) effect1_pcm, effect1_pcm_size, effectVolume*EFFECT_MULTIPLER, effectVolume*EFFECT_MULTIPLER, NULL);

	       }	
	   }
       else  	   
	   {
	       if (squares[i].x<=0)
	       {
	          squares[i].directionX=true;
		      squares[i].x=squares[i].x+speed;	
			  if (stateMachine==stateGame) SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 8000, 0, (u8 *) effect1_pcm, effect1_pcm_size, effectVolume*EFFECT_MULTIPLER, effectVolume*EFFECT_MULTIPLER, NULL);
		   }
		   else
		   {
		      squares[i].x=squares[i].x-speed;	
		   }
	   }
	   
	   
	   if (squares[i].directionY)
	   {
           if (squares[i].y+squares[i].height<MAX_VERT_PIXELS)
	       {
	          squares[i].y=squares[i].y+speed;	   
	       }
	       else
	       {
	          squares[i].directionY=false;
		      squares[i].y=squares[i].y-speed;	
			  if (stateMachine==stateGame) SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 8000, 0, (u8 *) effect1_pcm, effect1_pcm_size, effectVolume*EFFECT_MULTIPLER, effectVolume*EFFECT_MULTIPLER, NULL);
	       }	
	   }
       else  	   
	   {
	       if (squares[i].y<=0)
	       {
	          squares[i].directionY=true;
		      squares[i].y=squares[i].y+speed;	
			  if (stateMachine==stateGame) SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 8000, 0, (u8 *) effect1_pcm, effect1_pcm_size, effectVolume*EFFECT_MULTIPLER, effectVolume*EFFECT_MULTIPLER, NULL);
		   }
		   else
		   {
		      squares[i].y=squares[i].y-speed;	
		   }
	   }
   }
}

bool collisionDetection(void)
{
     int i,x,y;
	  	 
	 // Wall collision detection
     if (squares[0].x<=MIN_X_CURSOR)
	 {
	    return true;     	 
	 }
	 
	 if (squares[0].x+squares[0].width+20>MAX_X_CURSOR)
	 {
	    return true;     	 
	 }
	 
	 if (squares[0].y<=MIN_Y_CURSOR)
	 {
	    return true;     	 
	 }
	 
     if (squares[0].y+squares[0].height>=MAX_Y_CURSOR)
	 {
	    return true;     	 
	 }
	 	 
	  // blue square collesion detection
	 for (i=1; i<MAX_SQUARES; i++)
	 { 		
	     for (x=squares[i].x; x<squares[i].x+squares[i].width; x++)
		 {
	        if (x>=squares[0].x) 
		    {
		       if (x<=squares[0].x+squares[0].width)			
               {			  
			      if (squares[i].y>=squares[0].y) 
			      {
				     if (squares[i].y<=squares[0].y+squares[0].height)
				     {
		               return true;		 
				     }
			      }
 
			      if (squares[i].y+squares[i].height>=squares[0].y) 
			      {
				     if (squares[i].y+squares[i].height<=squares[0].y+squares[0].height)
				     {
		                return true;		 
				     }
				  }
			   }
			}			
		 }
		 				 

         for (y=squares[i].y; y<squares[i].y+squares[i].height; y++)						 
		 {
   	        if (y>=squares[0].y) 
			{
			   if (y<=squares[0].y+squares[0].height)
			   {
			   	  if (squares[i].x>=squares[0].x) 
		          {
		              if (squares[i].x<=squares[0].x+squares[0].width)			
                      { 	
		                 return true;		 
					  }
				  }
				  if (squares[i].x+squares[i].width>=squares[0].x) 
		          {
		              if (squares[i].x+squares[i].width<=squares[0].x+squares[0].width)			
                      { 	
		                 return true;		 
					  }
				  }
			   }
			}
		 }		
	 }
	 
	 return false;
}

void initSound(void)
{
   // Init Sound engine	
   SND_Init(INIT_RATE_48000); 
   MODPlay_Init(&snd1);
   SND_Pause(0);
   
   initMusicTrack();	
}

void initThreads(void)
{ 
   char userData1[MAX_LEN];
   char userData2[MAX_LEN];

   // Set userData1   		 
   memset(userData1,0x00,sizeof(userData1));
   sprintf(userData1,"%s=%s",PROGRAM_NAME,PROGRAM_VERSION);
		
   // Get userData2 
   memset(userData2,0x00,sizeof(userData2));
   sprintf(userData2,"appl=%s%s",PROGRAM_NAME, PROGRAM_VERSION);
	   
   tcp_start_thread(PROGRAM_NAME, PROGRAM_VERSION, ID1, URL1, DOMAIN1, ID2, URL2, DOMAIN2, ID3, URL3, DOMAIN3, URL_TOKEN, userData1, userData2);
}

void initButtons(void)
{
   angle=0;
   
   switch (stateMachine)
   {
      case stateWelcome:
      {
        maxButtons=8;

		// Play button 
		buttons[0].image=images.button1;
		buttons[0].imageSelect=images.button1select;
		strcpy(buttons[0].name,"Play");
		buttons[0].x=370;
		buttons[0].y=10+yOffset;
		buttons[0].width=240;
		buttons[0].height=36;
   
		// High Score button 
		buttons[1].image=images.button1;
		buttons[1].imageSelect=images.button1select;
		strcpy(buttons[1].name,"High Score");   
		buttons[1].x=370;
		buttons[1].y=55+yOffset;
		buttons[1].width=240;
		buttons[1].height=36;
   
		// Help button 
		buttons[2].image=images.button1;
		buttons[2].imageSelect=images.button1select;
		strcpy(buttons[2].name,"Help" );
		buttons[2].x=370;
		buttons[2].y=100+yOffset;
		buttons[2].width=240;
		buttons[2].height=36;
   
		// Credits button 
		buttons[3].image=images.button1;
		buttons[3].imageSelect=images.button1select;
		strcpy(buttons[3].name,"Credits" );
		buttons[3].x=370;
		buttons[3].y=145+yOffset;
		buttons[3].width=240;
		buttons[3].height=36;
   
        // Sound button 
		buttons[4].image=images.button1;
		buttons[4].imageSelect=images.button1select;
		strcpy(buttons[4].name,"Sound Settings" );
		buttons[4].x=370;
		buttons[4].y=190+yOffset;
		buttons[4].width=240;
		buttons[4].height=36;
		
		// Sound button 
		buttons[5].image=images.button1;
		buttons[5].imageSelect=images.button1select;
		strcpy(buttons[5].name,"Release Notes" );
		buttons[5].x=370;
		buttons[5].y=235+yOffset;
		buttons[5].width=240;
		buttons[5].height=36;
		
		// Exit HBC button 
		buttons[6].image=images.button1;
		buttons[6].imageSelect=images.button1select;
		strcpy(buttons[6].name,"Exit HBC" );
		buttons[6].x=370;
		buttons[6].y=345+yOffset;
		buttons[6].width=240;
		buttons[6].height=36;
      
		// Reset Wii button 
		buttons[7].image=images.button1;
		buttons[7].imageSelect=images.button1select;
		strcpy(buttons[7].name,"Reset Wii" );
		buttons[7].x=370;
		buttons[7].y=390+yOffset;
		buttons[7].width=240;
		buttons[7].height=36;
      }
      break;
	  
    case stateHighScore1:
	  {
	    maxButtons=1;

		// Next button 
		buttons[0].image=images.button1;
		buttons[0].imageSelect=images.button1select;
		strcpy(buttons[0].name,"Next");
		buttons[0].x=200;
		buttons[0].y=400+yOffset;
		buttons[0].width=240;
		buttons[0].height=36;
	  }
	  break;
	  
	case stateHighScore2:
	  {
	    maxButtons=1;

		// Back button 
		buttons[0].image=images.button1;
		buttons[0].imageSelect=images.button1select;
		strcpy(buttons[0].name,"Back");
		buttons[0].x=200;
		buttons[0].y=400+yOffset;
		buttons[0].width=240;
		buttons[0].height=36;
	  }
	  break;
	  
	  
	case stateHelp:
	  {
	    maxButtons=1;

		// Back button 
		buttons[0].image=images.button1;
		buttons[0].imageSelect=images.button1select;
		strcpy(buttons[0].name,"Back");
		buttons[0].x=200;
		buttons[0].y=400+yOffset;
		buttons[0].width=240;
		buttons[0].height=36;
	  }
	  break;
	  
	case stateCredits:
	  {
	    maxButtons=1;

		// Back button 
		buttons[0].image=images.button1;
		buttons[0].imageSelect=images.button1select;
		strcpy(buttons[0].name,"Back");
		buttons[0].x=200;
		buttons[0].y=400+yOffset;
		buttons[0].width=240;
		buttons[0].height=36;
	  }
	  break;
	  
	  
	case stateSound:
      {
        maxButtons=7;

		// Music Volume - button 
		buttons[0].image=images.button2;
		buttons[0].imageSelect=images.button2select;
		strcpy(buttons[0].name,"-");
		buttons[0].x=20;
		buttons[0].y=130+yOffset;
		buttons[0].width=80;
		buttons[0].height=36;
   
		// Music Volume + button 
		buttons[1].image=images.button2;
		buttons[1].imageSelect=images.button2select;
		strcpy(buttons[1].name,"+");   
		buttons[1].x=540;
		buttons[1].y=130+yOffset;
		buttons[1].width=80;
		buttons[1].height=36;
		
		// Effect Volume - button 
		buttons[2].image=images.button2;
		buttons[2].imageSelect=images.button2select;
		strcpy(buttons[2].name,"-");
		buttons[2].x=20;
		buttons[2].y=230+yOffset;
		buttons[2].width=80;
		buttons[2].height=36;
   
		// Effect Volume + button 
		buttons[3].image=images.button2;
		buttons[3].imageSelect=images.button2select;
		strcpy(buttons[3].name,"+");   
		buttons[3].x=540;
		buttons[3].y=230+yOffset;
		buttons[3].width=80;
		buttons[3].height=36;
		
		// Music track - button 
		buttons[4].image=images.button2;
		buttons[4].imageSelect=images.button2select;
		strcpy(buttons[4].name,"-");
		buttons[4].x=140;
		buttons[4].y=310+yOffset;
		buttons[4].width=80;
		buttons[4].height=36;
   
		// Music track + button 
		buttons[5].image=images.button2;
		buttons[5].imageSelect=images.button2select;
		strcpy(buttons[5].name,"+");   
		buttons[5].x=420;
		buttons[5].y=310+yOffset;
		buttons[5].width=80;
		buttons[5].height=36;
		
		// Back button 
		buttons[6].image=images.button1;
		buttons[6].imageSelect=images.button1select;
		strcpy(buttons[6].name,"Back");
		buttons[6].x=200;
		buttons[6].y=400+yOffset;
		buttons[6].width=240;
		buttons[6].height=36;
	  }
	  break;
   
      case stateReleaseNotes:
	  {
	    maxButtons=1;
	  		
		// Back button 
		buttons[0].image=images.button1;
		buttons[0].imageSelect=images.button1select;
		strcpy(buttons[0].name,"Back");
		buttons[0].x=200;
		buttons[0].y=400+yOffset;
		buttons[0].width=240;
		buttons[0].height=36;
	  }
   }
}

void initGame()
{
   // Init variables
   localTime=time(NULL);
   prevPlayTime=0;
   speed=1;
   selected=false;   
   selected1=false;
   selected2=false;
   angle=0; 
   notesStartLine=4;
}

void initImages(void)
{
   // Buttons & Panels
   images.redsquare=GRRLIB_LoadTexture((unsigned char*) pic4data);
   images.bluesquare1=GRRLIB_LoadTexture((unsigned char*) pic5data);
   images.bluesquare2=GRRLIB_LoadTexture((unsigned char*) pic6data);
   images.bluesquare3=GRRLIB_LoadTexture((unsigned char*) pic7data);
   images.bluesquare4=GRRLIB_LoadTexture((unsigned char*) pic8data);
   images.pointer=GRRLIB_LoadTexture((unsigned char*) pic9data);
   images.gameboard1=GRRLIB_LoadTexture((unsigned char*) pic10data);
   images.gameboard2=GRRLIB_LoadTexture((unsigned char*) pic11data);
   images.button1=GRRLIB_LoadTexture((unsigned char*) pic12data);
   images.button1select=GRRLIB_LoadTexture((unsigned char*) pic13data);     
   images.info=GRRLIB_LoadTexture((unsigned char*) pic15data); 
   images.question=GRRLIB_LoadTexture((unsigned char*) pic16data);
   images.bar=GRRLIB_LoadTexture((unsigned char*) pic17data); 
   images.barCursor=GRRLIB_LoadTexture((unsigned char*) pic18data);
   images.button2=GRRLIB_LoadTexture((unsigned char*) pic19data);
   images.button2select=GRRLIB_LoadTexture((unsigned char*) pic20data);    
   images.sound=GRRLIB_LoadTexture((unsigned char*) pic21data);    
}

void initSquares(void)
{
   // Red Square
   squares[0].image=images.redsquare;
   squares[0].width=64;
   squares[0].height=64;
   squares[0].x=(MAX_HORZ_PIXELS/2)-(squares[0].width/2);
   squares[0].y=(MAX_VERT_PIXELS/2)-(squares[0].height/2);
   
   // Blue Square 1
   squares[1].image=images.bluesquare1;
   squares[1].width=120;
   squares[1].height=64;
   squares[1].x=10;
   squares[1].y=10;
   squares[1].directionX=true;
   squares[1].directionY=true;
   
   // Blue Square 2
   squares[2].image=images.bluesquare2;
   squares[2].width=120;
   squares[2].height=32;
   squares[2].x=MAX_HORZ_PIXELS-squares[2].width-10;
   squares[2].y=10;
   squares[2].directionX=false;
   squares[2].directionY=true;
      
   // Blue Square 3
   squares[3].image=images.bluesquare3;
   squares[3].width=64;
   squares[3].height=120;
   squares[3].x=10;
   squares[3].y=MAX_VERT_PIXELS-squares[3].height-10;
   squares[3].directionX=false;
   squares[3].directionY=true;
      
   // Blue Square 4
   squares[4].image=images.bluesquare4;
   squares[4].width=64;
   squares[4].height=64;
   squares[4].x=MAX_HORZ_PIXELS-squares[4].width-10;
   squares[4].y=MAX_VERT_PIXELS-squares[4].height-10;   
   squares[4].directionX=false;
   squares[4].directionY=false;
}


void initStateMachine(void)
{
    switch(stateMachine)
    {
		case stateWelcome:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		   
		   moveSquares();
		}
		break;
		
        case stateGame:
		{  		 
		   playTime = time(NULL)-localTime;   
		   if (prevPlayTime!=playTime)
		   {
		      if ((playTime % 10)==9) 
			  {
			     speed++;			  
		         prevPlayTime=playTime;
			  }
		   }
		
           if (collisionDetection())
	       {				  
	          SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 8000, 0, (u8 *) effect2_pcm, effect2_pcm_size, effectVolume*EFFECT_MULTIPLER, effectVolume*EFFECT_MULTIPLER, NULL);
			  stateMachine=stateGameOver;
			  if (stateMachine!=prevStateMachine) initButtons();
			  
			  if ((int)playTime>20)
			  {
			     sprintf(appl_user3,"appl=%s%s&level=%d&score=%d&dt=%d",PROGRAM_NAME,PROGRAM_VERSION,0,(int)playTime,(int)localTime);
			     tcp_set_state(TCP_REQUEST3a, appl_user3);			  
			  }
		   }	
	   
		   moveSquares();				   
		}
		break;
		
		case stateGameOver:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
		
		case stateHighScore1:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
		
		case stateHighScore2:
		{
		   if (stateMachine!=prevStateMachine) 
		   {		   
		   	  char *buffer=tcp_get_highscore();
		      convertBestHighScore(buffer);		     
		      initButtons();
		   }
		}
		break;
		
		case stateHelp:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
		
		case stateCredits:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
		
		case stateSound:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
		
		case stateReleaseNotes:
		{
		   if (stateMachine!=prevStateMachine) 
		   {
		      initButtons();
			  noteDelay=0;
		   }
		}
		break;
	} 
	prevStateMachine=stateMachine;
}

//	PowerOff callback function.
void doPowerOff( void )
{
	bPowerOff = true;
	return;
}

//	PowerOff callback function for the Wii Remote power button.
void doPadPowerOff( s32 chan )
{
	if ( chan == WPAD_CHAN_0 )
	{
		bPowerOff = true;
	}
	return;
}


void saveHighScoreFile(char* filename)
{
   int i;
   bool store=false;
   int selectedHighScore=0;
   mxml_node_t *xml;
   mxml_node_t *group;
   mxml_node_t *data;   
   char tmp[MAX_LEN];
      
   xml = mxmlNewXML("1.0");
   
   group = mxmlNewElement(xml, "RedSquare");
   
   for(i=0; i<MAX_HIGHSCORE; i++)
   {
      if ((playTime>=highscores[i].playTime) && !store)
	  {
	     sprintf(tmp, "entry%d", i);
         data = mxmlNewElement(group, tmp);
  
	     sprintf(tmp, "%d", (int) localTime);
         mxmlElementSetAttr(data, "localTime", tmp);
	  
	     sprintf(tmp, "%d", (int) playTime);
	     mxmlElementSetAttr(data, "playTime", tmp);
		 
		 store=true;
	  }
	  else
	  {	  
        sprintf(tmp, "entry%d", i);
        data = mxmlNewElement(group, tmp);
  
	    sprintf(tmp, "%d", (int) highscores[selectedHighScore].localTime);
        mxmlElementSetAttr(data, "localTime", tmp);
	  
  	    sprintf(tmp, "%d", (int) highscores[selectedHighScore].playTime);
	    mxmlElementSetAttr(data, "playTime", tmp);
		
		selectedHighScore++;		
	  }	  
   }
  
   /* now lets save the xml file to a file! */
   FILE *fp;
   fp = fopen(filename, "w");

   mxmlSaveFile(xml, fp, MXML_NO_CALLBACK);
   
   /*Time to clean up!*/
   fclose(fp);
   mxmlDelete(data);
   mxmlDelete(group);
   mxmlDelete(xml);
}

void convertBestHighScore(char *buffer)
{
   int i;
   mxml_node_t *tree=NULL;
   mxml_node_t *data=NULL;
   const char *tmp;
   char temp[MAX_LEN];
   
   // Clear memory
   for(i=0; i<MAX_BEST; i++)
   {
      	bests[i].score=0;
		bests[i].dt=0;
		bests[i].name[0]=0x00;
   } 
	 
   // If xml data available, parse it....
   if ((buffer!=NULL) && (strlen(buffer)>0))
   {  
      tree = mxmlLoadString(NULL, buffer, MXML_NO_CALLBACK);

      for(i=0; i<MAX_BEST; i++)
      {		
	    sprintf(temp, "item%d", i+1);
        data = mxmlFindElement(tree, tree, temp, NULL, NULL, MXML_DESCEND);

        tmp=mxmlElementGetAttr(data,"name");   
        if (tmp!=NULL) strcpy(bests[i].name,tmp); else strcpy(bests[i].name,"");

        tmp=mxmlElementGetAttr(data,"score");   
        if (tmp!=NULL) bests[i].score=atoi(tmp); else bests[i].score=0;
	 
        tmp=mxmlElementGetAttr(data,"dt");   
        if (tmp!=NULL) bests[i].dt=atoi(tmp); else bests[i].dt=0; 
      }
   
      mxmlDelete(data);
      mxmlDelete(tree);
   }
}


void loadHighScoreFile(char* filename)
{
   int i;
   FILE *fp;
   mxml_node_t *tree=NULL;
   mxml_node_t *data=NULL;
   const char *tmp;
   char temp[MAX_LEN];
   
   /*Load our xml file! */
   fp = fopen(filename, "r");
   if (fp!=NULL)
   {
     tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);
     fclose(fp);

     for(i=0; i<MAX_HIGHSCORE; i++)
     {
	    sprintf(temp, "entry%d", i);
        data = mxmlFindElement(tree, tree, temp, NULL, NULL, MXML_DESCEND);
  
        tmp=mxmlElementGetAttr(data,"localTime");   
        if (tmp!=NULL) highscores[i].localTime=atoi(tmp); else highscores[i].localTime=0;
		
		tmp=mxmlElementGetAttr(data,"playTime"); 
		if (tmp!=NULL) highscores[i].playTime=atoi(tmp); else highscores[i].playTime=0;
	 } 
   }
   else
   {
     // If file not found, create empty highscore list.
     for(i=0; i<MAX_HIGHSCORE; i++)
     {
       	highscores[i].localTime=0;
		highscores[i].playTime=0;
	 } 
   }
   
   mxmlDelete(data);
   mxmlDelete(tree);
}
 
// -----------------------------------------------------------
// BUTTON LOGIC
// -----------------------------------------------------------

int buttonSelected(int x, int y)
{
   int i;
   
   for (i=0; i<maxButtons; i++)
   {
       if ( (x>=buttons[i].x) && (x<=buttons[i].x+buttons[i].width) && 
	        (y>=buttons[i].y) && (y<=buttons[i].y+buttons[i].height) )
	   {
          SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_16BIT, 22050, 0, (char *) effect3_pcm, effect3_pcm_size, effectVolume*EFFECT_MULTIPLER, effectVolume*EFFECT_MULTIPLER, NULL);
	      return i;
	   }	   
   }
   return -1;
}

void button1x(void)
{
   if (!selected1)
   {
      selected1=true;
	  
      MODPlay_Stop(&snd1);  
      if (selectedMusic<MAX_MUSIC_TRACK) selectedMusic++; else selectedMusic=1;   
      initMusicTrack();
   }        
}

void button2y(void)
{
   if (!selected2)
   { 
      selected2=true;   
      MODPlay_Stop(&snd1);
      if (selectedMusic>1) selectedMusic--; else selectedMusic=MAX_MUSIC_TRACK;   
      initMusicTrack();
   }
}

void buttonMinusVolume(int index)
{
   if (index==0)
   {
       // Music volume
	   if (musicVolume>0) musicVolume--;   
	   MODPlay_SetVolume( &snd1, musicVolume*MUSIC_MULTIPLER,musicVolume*MUSIC_MULTIPLER);
   }
   else if (index==1)
   {
       // Effect volume
       if (effectVolume>0) effectVolume--; 
   }
   else if (index==2)
   {
      MODPlay_Stop(&snd1);
      if (selectedMusic>1) selectedMusic--; else selectedMusic=MAX_MUSIC_TRACK;
	  initMusicTrack();
   }
}

void buttonPlusVolume(int index)
{
   if (index==0)
   {
       // Music volume
	   if (musicVolume<MAX_SOUND_VOLUME) musicVolume++;   
	   MODPlay_SetVolume( &snd1, musicVolume*MUSIC_MULTIPLER,musicVolume*MUSIC_MULTIPLER); 
   }
   else if (index==1)
   {
       // Effect volume
       if (effectVolume<MAX_SOUND_VOLUME) effectVolume++; 
   }
   else if (index==2)
   {
      MODPlay_Stop(&snd1);
      if (selectedMusic<MAX_MUSIC_TRACK) selectedMusic++; else selectedMusic=1;
	  initMusicTrack();
   }
}


void buttonA(int x, int y)
{
  rumbleCounter=MAX_RUMBLE;
	 
  switch (stateMachine)
  {
     case stateWelcome:
     {
        switch (buttonSelected(x,y))
	    {
          case 0: // play button	
 		  	      initSquares();
                  initGame();  		
				  stateMachine=stateGame;		
				  WPAD_Rumble(0,0);		  
			      break;
	  
	      case 1: // High Score button 
				  stateMachine=stateHighScore1;
			      break;
				  
		  case 2: // Help button 
				  stateMachine=stateHelp;
			      break;
	  
	      case 3: // Credits button 		 
				  stateMachine=stateCredits;
			      break;
		  
		  case 4: // Sound button 		 
				  stateMachine=stateSound;
			      break;
				  
		  case 5: // Release Notes button 		 
				  stateMachine=stateReleaseNotes;
			      break;
				  
		  case 6: // Exit HBC button 
			      buttonExit(0);
			      break;	
				  
		  case 7: // Reset Wii button 
				  buttonExit(1);
			      break; 
	    }
     }
	 break;

     case stateSound:
	 {     
  	      switch (buttonSelected(x,y))
	      {
			 case 0: // - music volume button event	           
			         buttonMinusVolume(0);
			         break;	   
			 
			 case 1: // + music volume button event	           
			         buttonPlusVolume(0);
			         break;	 
					 
			 case 2: // - effect volume button event	           
			         buttonMinusVolume(1);
			         break;	   
			 
			 case 3: // + effect volume button event	           
			         buttonPlusVolume(1);
			         break;	 
					 
			 case 4: // - music track button event	           
			         buttonMinusVolume(2);
			         break;	   
			 
			 case 5: // + music track  button event	           
			         buttonPlusVolume(2);
			         break;
					 
			 case 6: // Back button event          
			         stateMachine=stateWelcome;
			         break;	   
					 
		  }
	 }
	 break;


     case stateReleaseNotes:
	 {     
  	      switch (buttonSelected(x,y))
	      {		
			 case 0: // Back button event          
			         stateMachine=stateWelcome;
			         break;	   
					 
		  }
	 }
	 break;
	 
     case stateGame:
	 {
       if (( (x>=squares[0].x) && (x<=squares[0].x+squares[0].width) && 
	         (y>=squares[0].y) && (y<=squares[0].y+squares[0].height) )
	         || selected )
       {
   	      squares[0].x=x-40;
          squares[0].y=y-40;	
          selected=true;	  		   	   
       }
	 }
	 break;
	 
	 case stateGameOver:
     {
	    // Reset to welcome screen when A is pressed
		saveHighScoreFile(HIGHSCORE_FILENAME);
        loadHighScoreFile(HIGHSCORE_FILENAME);	
        stateMachine=stateWelcome;
	    initGame(); 
     }
	 break;
	 
	 case stateHighScore1:
     {
        switch (buttonSelected(x,y))
	    {
          case 0: // Next button	
				  stateMachine=stateHighScore2;
			      break;	     
	    }
     }
	 break;
	 
	 case stateHighScore2:
     {
        switch (buttonSelected(x,y))
	    {
          case 0: // Back button	
				  stateMachine=stateWelcome;
			      break;	     
	    }
     }
	 break;
	 
	 
	 case stateHelp:
     {
        switch (buttonSelected(x,y))
	    {
          case 0: // Next button	
				  stateMachine=stateWelcome;
			      break;	     
	    }
     }
	 break;
	 
	 case stateCredits:
     {
        switch (buttonSelected(x,y))
	    {
          case 0: // Next button	
				  stateMachine=stateWelcome;
			      break;	     
	    }
     }
	 break;
  }  
}
	
void buttonExit(int index)
{    
   // Stop network thread
   tcp_stop_thread();
	
   WPAD_Rumble(0,0);
	
   MODPlay_Stop(&snd1);
	
   if (index==0)
   {   
	   exit(0);
   }
   else
   {
	   SYS_ResetSystem(SYS_RESTART,0,0);	
   }
}
 
// -----------------------------------------------------------
// SCREEN LOGIC
// -----------------------------------------------------------

void drawButtons(int x, int y)
{
   int i;
   bool button=false;
   
   for (i=0; i<maxButtons; i++)
   {
      if ((x>=buttons[i].x) && (x<=buttons[i].x+buttons[i].width) && 
	     (y>=buttons[i].y) && (y<=buttons[i].y+buttons[i].height) )			
	     {
		    // Draw selected button
	        GRRLIB_DrawImg(buttons[i].x, buttons[i].y, buttons[i].width, buttons[i].height, buttons[i].imageSelect, 0, 1, 1, 255 );		  
		    if (--rumbleCounter>0)  WPAD_Rumble(0,1); else WPAD_Rumble(0,0);
			button=true;
	     }
	     else
	     {
		    // Draw not selected button
	        GRRLIB_DrawImg(buttons[i].x, buttons[i].y, buttons[i].width, buttons[i].height, buttons[i].image, 0, 1, 1, 255 );
	     }
		 drawText(buttons[i].x,buttons[i].y, fontButton, buttons[i].name);
    }

	
	// If no button selected, stop rumble.
	if (!button) 
	{
	   WPAD_Rumble(0,0);
	   rumbleCounter=MAX_RUMBLE;
	}
}

void drawText(int x, int y, int type, char *text,...)
{
   char tmp[MAX_LEN];
   memset(tmp,0x00,sizeof(tmp));
   
   if (text!=NULL)
   {    		
     va_list argp;
     va_start(argp, text);
     vsprintf(tmp, text, argp);
     va_end(argp);
	 
     switch (type)
     {
       case fontTitle1: 
	   {
	      if (x==0) x=320-((strlen(tmp)*34)/2);  
		  GRRLIB_Printf2(x, y, tmp, 72, COLOR_DARKBLACK); 
	   }
	   break;
	   	   	 
       case fontTitle2: 
	   {
	      if (x==0) x=320-((strlen(tmp)*34)/2);  
		  GRRLIB_Printf2(x, y, tmp, 72, COLOR_WHITESMOKE); 
	   }
	   break;
  
       case fontWelcome: 
	   {
		  GRRLIB_Printf2(x, y, tmp, 40, COLOR_WHITESMOKE); 
	   }
	   break;
	   
	   case fontSubTitle:
	   {
	      if (x==0) x=320-((strlen(tmp)*12)/2);
		  GRRLIB_Printf2(x, y, tmp, 24, COLOR_DARKBLACK);          
	   }
	   break;
	   
	   case fontParagraph:
	   {
	       if (x==0) x=320-((strlen(tmp)*10)/2);	   
		   GRRLIB_Printf2(x, y, tmp, 24, COLOR_WHITESMOKE);            
	   }
	   break;
	   	   
	   case fontNormal:
	   {
	       if (x==0) x=320-((strlen(tmp)*7)/2);
		   GRRLIB_Printf2(x, y, tmp, 18, COLOR_WHITESMOKE);            
	   }
	   break;
	         
	   case fontNew:
	   {
	       if (x==0) x=320-((strlen(tmp)*8)/2);	   
		   GRRLIB_Printf2(x, y, tmp, 22, COLOR_WHITESMOKE);            
	   }
	   break;
	   
	   case fontSpecial:
	   {
	       if (x==0) x=320-((strlen(tmp)*10)/2);
		   GRRLIB_Printf2(x, y, tmp, 10, COLOR_WHITESMOKE);            
	   }
	   break;
	   
	   case fontButton:
	   {
		   GRRLIB_Printf2(x+50, y, tmp, 24, COLOR_WHITESMOKE);            
	   }
	   break;
	 }
   }
}


void drawGameboard(int x, int y)
{ 	   
    int i=0;
		  
    switch( stateMachine )
	{	
	   case stateWelcome:
	   {
	      char tmp[MAX_LEN];
	      int  ypos=yOffset;
		  char *version;
		  
   	      // Init text layer	  
          GRRLIB_initTexture();
		   
	      // Draw gameboard
		  GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1, 1, 255 );
		  
		   // Draw squares
	      for (i=1; i<MAX_SQUARES; i++)
          {   
              GRRLIB_DrawImg(squares[i].x,squares[i].y,squares[i].width, squares[i].height, squares[i].image, 0, 1, 1, 255 );   
          }	 
		  
		  sprintf(tmp,"%s v%s",PROGRAM_NAME, PROGRAM_VERSION );
		  drawText(20, ypos, fontWelcome, tmp);
          ypos+=50;
          
     	  sprintf(tmp,"%s",RELEASE_DATE );
		  drawText(20, ypos, fontParagraph, tmp);
		  ypos+=225;
		   
          version=tcp_get_version();
          if ( (version!=NULL) && (strlen(version)>0) && (strcmp(version,PROGRAM_VERSION)!=0) )
          {    
	         sprintf(tmp,"New version [v%s] is available.",version);
	         drawText(20, ypos, fontNew, tmp);
			 
			 ypos+=20;			 			 			 
			 sprintf(tmp,"Check the release notes.");
	         drawText(20, ypos, fontNew, tmp);			 
          }  

          sprintf(tmp,"NETWORK THREAD: %s",tcp_get_state());
		  drawText(20, 440, fontSpecial, tmp);
 
		  // Draw buttons
	      drawButtons(x,y); 
		  
		  // Draw text layer on top of gameboard 
          GRRLIB_DrawImg(0, 0, 640, 480, GRRLIB_GetTexture(), 0, 1.0, 1.0, 255);	
	   }
	   break;
	   
	   case stateGame:
       {   
	      // Draw gameboard
		  GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard2, 0, 1, 1, 255 );
      	
          // Draw squares
	      for (i=0; i<MAX_SQUARES; i++)
          {   
              GRRLIB_DrawImg(squares[i].x,squares[i].y,squares[i].width, squares[i].height, squares[i].image, 0, 1, 1, 255 );   
          }	 
	   }
	   break;
	   	   
	   case stateGameOver:
	   {
	       int ypos=yOffset+140;		   
		   struct tm *level;
		   char tmp[MAX_LEN];
				  
           // Init text layer	  
           GRRLIB_initTexture();
	
		   // Draw gameboard
		   GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard2, 0, 1, 1, 255 );
		   
		   // Draw squares
	       for (i=0; i<MAX_SQUARES; i++)
           {   
              GRRLIB_DrawImg(squares[i].x,squares[i].y,squares[i].width, squares[i].height, squares[i].image, 0, 1, 1, 255 );   
           }	
      		          
		   // Draw title 
	       sprintf(tmp,"GAME OVER   ");
		   drawText(0, ypos, fontTitle1, tmp);
		   ypos+=70;
		   
		   // Draw content
	       level = localtime(&playTime);
	       sprintf(tmp,"%02d:%02d:%02d", level->tm_hour, level->tm_min, level->tm_sec);
		   drawText(0, ypos, fontSubTitle, tmp);
		   		  
           // Draw text layer on top of gameboard 
           GRRLIB_DrawImg(0, 0, 640, 480, GRRLIB_GetTexture(), 0, 1.0, 1.0, 255);	
	   }
	   break;
	   	   
	   case stateHighScore1:
	   {
	      int  ypos=yOffset;
	      struct tm *local;
	      struct tm *level;
		  char tmp[MAX_LEN];
		  
          // Init text layer	  
          GRRLIB_initTexture();
		   
          // Draw gameboard
          GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1, 1, 255 );
      	     	
	      // Draw title
	      drawText(0, ypos, fontTitle2, "      Local High Score");	

          // Show Content
          ypos+=90;
	      drawText(180, ypos, fontParagraph, "DATE" );
	      drawText(400, ypos, fontParagraph, "PLAY TIME" );
		  ypos+=10;
		  
          for (i=0; i<MAX_HIGHSCORE; i++)
	      {
  	          ypos+=20;  
	    
	          local = localtime(&highscores[i].localTime);
	          sprintf(tmp,"%02d-%02d-%04d %02d:%02d:%02d", 
			     local->tm_mday, local->tm_mon+1, local->tm_year+1900, 
			     local->tm_hour, local->tm_min, local->tm_sec);
		      drawText(180, ypos, fontNormal, tmp);
	   
	          level = localtime(&highscores[i].playTime);
	          sprintf(tmp,"%02d:%02d:%02d", level->tm_hour, level->tm_min, level->tm_sec);
		      drawText(400, ypos, fontNormal, tmp);
		  }	 
		
          // Draw buttons
	      drawButtons(x,y); 
		  
          // Draw text layer on top of gameboard 
          GRRLIB_DrawImg(0, 0, 640, 480, GRRLIB_GetTexture(), 0, 1.0, 1.0, 255);	 	   
	   }
	   break;
	   
	   case stateHighScore2:
	   {
	      
		  int  ypos=yOffset;
	      struct tm *local;
	      struct tm *level;
		  char tmp[MAX_LEN];
		  
          // Init text layer	  
          GRRLIB_initTexture();
		   
          // Draw gameboard
          GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1, 1, 255 );
      	     	
	      // Draw title
	      drawText(0, ypos, fontTitle2, "   Global High Score");	

          // Show Content
          ypos+=90;

	      drawText(100, ypos, fontParagraph, "DATE" );
	      drawText(300, ypos, fontParagraph, "PLAY TIME" );
		  drawText(440, ypos, fontParagraph, "WHO" );
		  ypos+=10;
		  
		  if (bests[0].dt!=0)
		  {
            for (i=0; i<MAX_BEST; i++)
            {
  	          ypos+=20;  
	    
	          local = localtime(&bests[i].dt);
	          sprintf(tmp,"%02d-%02d-%04d %02d:%02d:%02d", 
			     local->tm_mday, local->tm_mon+1, local->tm_year+1900, 
			     local->tm_hour, local->tm_min, local->tm_sec);
		      drawText(100, ypos, fontNormal, tmp);
	   
	          level = localtime(&bests[i].score);
	          sprintf(tmp,"%02d:%02d:%02d", level->tm_hour, level->tm_min, level->tm_sec);
		      drawText(300, ypos, fontNormal, tmp);
			  
			  drawText(440, ypos, fontNormal, bests[i].name);
		    }			
		  }
		  else
		  {
		      ypos+=120;
		      drawText(0, ypos, fontParagraph, "No information available!");
			  ypos+=20;
			  drawText(0, ypos, fontParagraph, "Information could not be fetch from internet.");
		  }
			 
          // Draw buttons
	      drawButtons(x,y); 
		  
          // Draw text layer on top of gameboard 
          GRRLIB_DrawImg(0, 0, 640, 480, GRRLIB_GetTexture(), 0, 1.0, 1.0, 255);	 	   
	   }
	   break;
	   
	   
	   case stateHelp:
	   {
	      int  ypos=yOffset;
		  
		  // Init text layer	  
          GRRLIB_initTexture();
		  
	      // Draw gameboard
		  GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1, 1, 255 );
		  
          // Draw question icon
	      GRRLIB_DrawImg(ICON_X,ICON_Y,256, 256, images.question, angle, ICON_ZOOM, ICON_ZOOM, 255 );

		   // Show title
		  drawText(0, ypos, fontTitle2, "Help");
          ypos+=120;
		  
		  drawText(0, ypos, fontParagraph, "RedSquare is an classic 2D action game.");
		  ypos+=25;
     	  drawText(0, ypos, fontParagraph, "Click and hold the red square.");	
		  ypos+=25;
	      drawText(0, ypos, fontParagraph, "Now, move so that you neither touch the walls nor");
		  ypos+=25;
	      drawText(0, ypos, fontParagraph, "get hit by any of the blue blocks.");		
		  ypos+=25;
	      drawText(0, ypos, fontParagraph, "If you make it to 31 seconds, you are doing brilliantly!");
		
          ypos+=60;
	      drawText(0, ypos, fontParagraph, "Tip: You can control which music track is played during");
		  ypos+=25;
	      drawText(0, ypos, fontParagraph, "the game with the 1 and 2 button on your WiiMote!");
				  
		  // Draw buttons
	      drawButtons(x,y); 
		  
		  // Draw text layer on top of gameboard 
          GRRLIB_DrawImg(0, 0, 640, 480, GRRLIB_GetTexture(), 0, 1.0, 1.0, 255);
	   }
	   break;
	   
	   case stateCredits:
	   {
	      int  ypos=yOffset;
 
          // Init text layer	  
          GRRLIB_initTexture();
 
	      // Draw gameboard
		  GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1, 1, 255 );
		  
		  // Draw info icon
	      GRRLIB_DrawImg(ICON_X,ICON_Y,256, 256, images.info, angle, ICON_ZOOM, ICON_ZOOM, 255 );

		  // Show title
		  drawText(0, ypos, fontTitle2, "      Credits");
          ypos+=85;
		  
		  // Show Content
	      drawText(0, ypos, fontParagraph, "GAME LOGIC ");
          ypos+=20;
	      drawText(0, ypos, fontNormal, "wplaat");

	      ypos+=30;
	      drawText(0, ypos, fontParagraph, "GAME GRAPHICS  ");
  	      ypos+=20;
	      drawText(0, ypos, fontNormal, "wplaat");
	
	      ypos+=30;
	      drawText(0, ypos, fontParagraph, "MUSIC ");
	      ypos+=20;
	      drawText(0, ypos, fontNormal, "modarchive.org  ");

	      ypos+=30;
	      drawText(0, ypos, fontParagraph, "SOUND EFFECTS  ");
	      ypos+=20;
	      drawText(0, ypos, fontNormal, "wplaat");
		  ypos+=15;
	      drawText(0, ypos, fontNormal, "nowhereman");
  
  	      ypos+=30;
	      drawText(0, ypos, fontParagraph, "TESTERS");
	      ypos+=20;
	      drawText(0, ypos, fontNormal, "wplaat");	  
		  ypos+=15;
	      drawText(0, ypos, fontNormal, "Harima-kun");	    		  	  	
												
	      ypos+=30;
	      drawText(0, ypos, fontNormal,"Greetings to everybody in the Wii homebrew scene");

		  // Draw buttons
	      drawButtons(x,y); 
		  
		  // Draw text layer on top of gameboard 
          GRRLIB_DrawImg(0, 0, 640, 480, GRRLIB_GetTexture(), 0, 1.0, 1.0, 255);
	   }
	   break;
	   
	   case stateSound:
	   {
	      int  ypos=yOffset;
	
	      // Init text layer	  
          GRRLIB_initTexture();
 
	      // Draw gameboard
		  GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1, 1, 255 );
		  
	      // Draw Sound icon
	      GRRLIB_DrawImg((640/2)-128, ((480/2)-140)+yOffset,256, 256, images.sound, angle, 1.4, 1.4, 255 );
	
		  // Show title
		  drawText(0, ypos, fontTitle2, "   Sound Settings");
          ypos+=100;
		  
          // Draw content	
          drawText(0, ypos, fontParagraph, "Music Volume");	
	      ypos+=20;
          GRRLIB_DrawImg(104,ypos,432,60, images.bar, 0, 1, 1, 255 );
	      ypos+=10;
	      GRRLIB_DrawImg(115+(musicVolume*40),ypos,8,24, images.barCursor, 0, 1, 1, 255 );
  
          ypos+=80;
          drawText(0, ypos, fontParagraph, "Effects Volume" );
	      ypos+=20;	
	      GRRLIB_DrawImg(104,ypos,432,60, images.bar, 0, 1, 1, 255 );
	      ypos+=10;
	      GRRLIB_DrawImg(115+(effectVolume*40),ypos,8,24, images.barCursor, 0, 1, 1, 255 );
	
	      ypos+=70;
	      drawText(0, ypos, fontParagraph, "  Music track [%d]", selectedMusic);	
		  
		   // Draw buttons
	      drawButtons(x,y); 
		  
		  // Draw text layer on top of gameboard 
          GRRLIB_DrawImg(0, 0, 640, 480, GRRLIB_GetTexture(), 0, 1.0, 1.0, 255);
	   }
	   break;
	   
	   case stateReleaseNotes:
	   {
	      int  ypos=yOffset;
	      int  i=0;
		  int len=0;
		  int lineCount=0;
		  int maxLines=0;
		  char *buffer;
		  char text[MAX_BUFFER_SIZE];
		  char tmp[MAX_LEN];
		  
	      // Init text layer	  
          GRRLIB_initTexture();
 
	      // Draw gameboard
		  GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1, 1, 255 );
		 
          // Draw info icon
	      GRRLIB_DrawImg(ICON_X,ICON_Y,256, 256, images.info, angle, ICON_ZOOM, ICON_ZOOM, 255 );
 
	      // Draw Title	
          drawText(0, ypos, fontTitle2, "Release Notes");
          ypos+=80;
	      
		  // Draw content	
		  buffer=tcp_get_releasenote();
          if (buffer!=NULL) 
		  {
		     strncpy(text,buffer,MAX_BUFFER_SIZE);
			 len=strlen(text);
			 for (i=0;i<len;i++) if (text[i]=='\n') maxLines++;
		  }
				 
		  if (len!=0)
		  {		  
		    int startpos=0;
  		    for (i=0; i<len; i++)
		    {
			  if (text[i]=='\n') 
			  {			   
			     text[i]=0x00;
				 
				 // Show only 17 lines on screen
			     if ((lineCount++)>notesStartLine+17) break;
			     if (lineCount>notesStartLine) 
				 {				   
			        ypos+=15;
				    sprintf(tmp,"%s",text+startpos);
			        drawText(40, ypos, fontNormal, tmp);	
				 }
				 
				 startpos=i+1;
				 							
				 if (noteDelay++>800)
				 {
				    if (notesStartLine++>maxLines) notesStartLine=4;
				    noteDelay=0;
				 }
		      }
		    }
		  }
		  else
		  {
		     ypos+=120;
			 drawText(0, ypos, fontParagraph, "No information available!" );	
		     ypos+=20;
			 drawText(0, ypos, fontParagraph, "Information could not be fetch from internet.");
		  }
		  
		  // Draw buttons
	      drawButtons(x,y); 
		  
		  // Draw text layer on top of gameboard 
          GRRLIB_DrawImg(0, 0, 640, 480, GRRLIB_GetTexture(), 0, 1.0, 1.0, 255);
	   }
	   break;
	}
}

void drawJpegImage(char *pictureData, int pictureLength, int x, int y )
{
    JPEGIMG         jpeg;
    int             row,
                    col,
                    pix,
                    offset;
    unsigned int   *jpegout;

    memset(&jpeg, 0, sizeof(JPEGIMG));

    jpeg.inbuffer = pictureData;
    jpeg.inbufferlength = pictureLength;

    JPEG_Decompress(&jpeg);
	
    pix = 0;

    jpegout = (unsigned int *) jpeg.outbuffer;

    offset=(y+yjpegOffset)*320;
    for (row = 0; row < jpeg.height; row++)
    {
	    for (col = 0; col < (jpeg.width >> 1); col++)
		{
	       frameBuffer[0][offset + col + x] = jpegout[pix++];
		}
	    offset+=320;
    }

    free(jpeg.outbuffer);
}

void drawIntroScreen1(int channel)
{
   boolean flag = true;
   
   drawJpegImage(pic1data, pic1length, 0, 0 );
		   	
   // Handle buttons events
   while ( flag && !bPowerOff )
   {
		WPAD_ScanPads(); 
		if (WPAD_ButtonsDown(channel) & BUTTON_A) flag=false; 	
		VIDEO_SetNextFramebuffer(frameBuffer[0]); 
		VIDEO_Flush();
		VIDEO_WaitVSync();
   }
}

void drawIntroScreen2(int channel)
{
   boolean flag = true;
   
   drawJpegImage(pic2data, pic2length, 0, 0 );
		   	
   // Handle buttons events
   while ( flag && !bPowerOff )
   {
		WPAD_ScanPads(); 
		if (WPAD_ButtonsDown(channel) & BUTTON_A) flag=false; 	
		VIDEO_SetNextFramebuffer(frameBuffer[0]); 
		VIDEO_Flush();
		VIDEO_WaitVSync();
   }
}

void drawIntroScreen3(int channel)
{
   boolean flag = true;
  		  
   drawJpegImage(pic3data, pic3length, 0, 0 );
		   	
   // Handle buttons events
   while ( flag && !bPowerOff )
   {
		WPAD_ScanPads(); 
		if (WPAD_ButtonsDown(channel) & BUTTON_A) flag=false; 	
		VIDEO_SetNextFramebuffer(frameBuffer[0]); 
		VIDEO_Flush();
		VIDEO_WaitVSync();
   }
}

// -----------------------------------------------------------
// MAIN
// -----------------------------------------------------------

int main()
{
    // Init main variables
    int x=0, y=0;   
	stateMachine=stateWelcome;
	prevStateMachine=stateNone;
	selectedMusic=1;
    musicVolume=5;
    effectVolume=5;
   
    // Init video layer
    VIDEO_Init();
	
	// Init wiimote layer
    WPAD_Init();
    WPAD_SetIdleTimeout(60); // Wiimote is shutdown after 60 seconds of innactivity.
	WPAD_SetDataFormat(WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR);	// enable accelerometers and IR
  
  	// Set Shutdown Callbacks.
	SYS_SetPowerCallback( doPowerOff );
	WPAD_SetPowerButtonCallback( doPadPowerOff );
	
    // Obtain the preferred video mode from the system
	// This will correspond to the settings in the Wii menu
	rmode = VIDEO_GetPreferredMode(NULL);
	
	// allocate framebuffer (Needed for JPG welcome images)
	frameBuffer[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
		
	// Initialise the console, required for printf
	console_init(frameBuffer[0],20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
	
	// Set up the video registers with the chosen mode
	VIDEO_Configure(rmode);
	
	if (rmode->xfbHeight==528)
	{
	    // TV mode PAL 50Hz
	    yOffset = 25;  
		yjpegOffset = 25;
	}
	else
	{      
	    // TV mode PAL 60Hz
	    yOffset = 25;
		yjpegOffset = 0;
	}

	// Tell the video hardware where our display memory is
	VIDEO_SetNextFramebuffer(frameBuffer[0]);
	
	// Make the display visible
	VIDEO_SetBlack(FALSE);

	// Flush the video register changes to the hardware
	VIDEO_Flush();

    // Init Fat
    fatInitDefault();

	// Load highscore from SD card
    loadHighScoreFile(HIGHSCORE_FILENAME);	
	
    // Init Http thread
	initThreads();
	
  	// Init game variables    
	initGame();	
	initImages();
    initSquares();
	initSound();
   	
	// Init FreeType font engine
	GRRLIB_InitFreetype();
	
    // Show Intro screens
    drawIntroScreen1(0);	
	drawIntroScreen2(0);	
	drawIntroScreen3(0);	
	  			
    // Init GRRLib graphics library
    GRRLIB_InitVideo();
    GRRLIB_Start();
    
	// Make screen black
	GRRLIB_FillScreen(0xFFFFFF);
    GRRLIB_Render();
		
    initGame();
	
	// Repeat forever
    while(!bPowerOff )
	{		
        // icon angle 
	    if (++angle>=MAX_ANGLE) angle=0;

        // Init StateMachine			
	    initStateMachine();
			
        // Draw game board and other items
        drawGameboard(x+40, y+40);		

	    // Scan for button events
		WPAD_SetVRes(0, 640, 480);
        WPAD_ScanPads();
        u32 wpadheld = WPAD_ButtonsHeld(0);
		u32 wpaddown = WPAD_ButtonsDown(0);
		u32 wpadup = WPAD_ButtonsDown(0);

        // Scan for ir events
        ir_t ir1; 
		WPAD_IR(WPAD_CHAN_0, &ir1); 
		x=ir1.sx-WSP_POINTER_X;
		y=ir1.sy-WSP_POINTER_Y;

		// handle button events
		switch (stateMachine)
		{
		   case stateGame:
             if (wpadheld & BUTTON_A) buttonA(x+40,y+40); else selected=false;
			 break;
			 
		   default:
		     selected=false;
		     if (wpaddown & BUTTON_A) buttonA(x+40,y+40);	
	    }		
		if (wpaddown & BUTTON_HOME) buttonExit(0);	
		
		if (wpaddown & BUTTON_1 ) button1x();						
        if (wpadup & BUTTON_1) selected1=false;
  		
		if (wpaddown & BUTTON_2 ) button2y();		
		if (wpadup & BUTTON_2) selected2=false;
					
     	// Draw wiiremote ir pointer
		GRRLIB_DrawImg( x, y, 96, 96, images.pointer, ir1.angle, 1, 1, 255 );
		GRRLIB_Render();
	}
	
	// Shut the system down.
	if ( bPowerOff )
	{
		SYS_ResetSystem( SYS_POWEROFF, 0, 0 );
    }

    return 0;
}

// -----------------------------------------------------------
// THE END
// -----------------------------------------------------------
