/* 
**  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) 2008 
**  ==================
**
**  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:
**  ==============
**
**  05/12/2008 version 0.4
**  - Added Welcome screen
**  - Added Help screen
**  - Added Credits screen
**  - Added exit to HBC functionality
**  - Added reset Wii functionality
**
**  04/12/2008 version 0.3
**  - Added HighScore screen
**  - Store highscore (in xml format) on SD Card 
**  - Added google analytic http request
**
**  02/12/2008 version 0.2
**  - Added new version http request check
**  - Remove some minor reported bugs
**  - Complete redesign of gameboard graphics
**  - Add background music
**  - Add some sound effects
**
**  29/11/2008 Version 0.1
**  - First release for Wii homebrew scene
**  - Started programming
**  - Gameboard design
**  - Added WiiRemote IR control
**  - Added three intro screens
**  - Basic game engine ready
*/

#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_wav.h"
#include "effect2_wav.h"
#include "effect3_wav.h"
#include "track1_mod.h"

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

#define PROGRAM_NAME	    "RedSquare"
#define PROGRAM_VERSION     "0.4"
#define RELEASE_DATE        "05-12-2008" 
#define URL                 "http://www.wiibrew.org/wiki/RedSquare"
#define URL_TOKEN           " Version "
#define HIGHSCORE_FILENAME  "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_LEN			    100
#define MAX_ANGLE			360
#define MAX_ALFA			128

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

#define EFFECT_VOLUME	    80

#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)

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

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

// -----------------------------------------------------------
// 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 *background1;
  u8 *info;
  u8 *question;
} 
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
{
   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,
   stateHighScore=3,
   stateHelp=4,
   stateCredits=5,
   stateNone=6
};

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;
int     alfa;
boolean alfa_direction;

bool    selected;
int     stateMachine;
int     prevStateMachine;
static  MODPlay snd1;
int     speed;
int     maxHighScores;
char    tmp[MAX_LEN];
int     maxButtons;
int     rumbleCounter;
 
// 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;

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

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;	
			  SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 12000, 0, (u8 *) effect1_wav, effect1_wav_size, EFFECT_VOLUME-50, EFFECT_VOLUME-50, NULL);

	       }	
	   }
       else  	   
	   {
	       if (squares[i].x<=0)
	       {
	          squares[i].directionX=true;
		      squares[i].x=squares[i].x+speed;	
			  SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 12000, 0, (u8 *) effect1_wav, effect1_wav_size, EFFECT_VOLUME-50, EFFECT_VOLUME-50, 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;	
			  SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 12000, 0, (u8 *) effect1_wav, effect1_wav_size, EFFECT_VOLUME-50, EFFECT_VOLUME-50, NULL);
	       }	
	   }
       else  	   
	   {
	       if (squares[i].y<=0)
	       {
	          squares[i].directionY=true;
		      squares[i].y=squares[i].y+speed;	
			  SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 12000, 0, (u8 *) effect1_wav, effect1_wav_size, EFFECT_VOLUME-50, EFFECT_VOLUME-50, NULL);
		   }
		   else
		   {
		      squares[i].y=squares[i].y-speed;	
		   }
	   }
   }
}

bool collisionDetection(void)
{
     int i,x,y;
	  	 
	 // Wall collesion 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);
   
   MODPlay_SetVolume( &snd1, 32,32); 
   MODPlay_SetMOD(&snd1, track1_mod);
   MODPlay_Start(&snd1);		
}

void initThreads(void)
{ 
   tcp_start_thread(PROGRAM_NAME, PROGRAM_VERSION, URL, URL_TOKEN);
}

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

		// Play button 
		buttons[0].image=images.button1;
		buttons[0].imageSelect=images.button1select;
		strcpy(buttons[0].name,"Play");
		buttons[0].x=370;
		buttons[0].y=20+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=70+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=120+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=170+yOffset;
		buttons[3].width=240;
		buttons[3].height=36;
   
		// Exit HBC button 
		buttons[4].image=images.button1;
		buttons[4].imageSelect=images.button1select;
		strcpy(buttons[4].name,"Exit HBC" );
		buttons[4].x=370;
		buttons[4].y=330+yOffset;
		buttons[4].width=240;
		buttons[4].height=36;
      
		// Reset Wii button 
		buttons[5].image=images.button1;
		buttons[5].imageSelect=images.button1select;
		strcpy(buttons[5].name,"Reset Wii" );
		buttons[5].x=370;
		buttons[5].y=380+yOffset;
		buttons[5].width=240;
		buttons[5].height=36;
      }
      break;
	  
    case stateHighScore:
	  {
	    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 stateHelp:
	  {
	    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 stateCredits:
	  {
	    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;
   }
}

void initGame()
{
   // Init variables
   localTime=time(NULL);
   prevPlayTime=0;
   speed=1;
   selected=false;
   
   angle=0;
   alfa=0;
   alfa_direction=true;   
}

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);
}

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, 10000, 0, (u8 *) effect2_wav, effect2_wav_size, EFFECT_VOLUME, EFFECT_VOLUME, NULL);
			  stateMachine=stateGameOver;
			  if (stateMachine!=prevStateMachine) initButtons();
		   }	
	   
		   moveSquares();				   
		}
		break;
		
		case stateGameOver:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
		
		case stateHighScore:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
		
		case stateHelp:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
		
		case stateCredits:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
	} 
	prevStateMachine=stateMachine;
}

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[100];
      
   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 loadHighScoreFile(char* filename)
{
   int i;
   FILE *fp;
   mxml_node_t *tree=NULL;
   mxml_node_t *data=NULL;
   const char *tmp;
   char temp[100];
   
   /*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, 8000, 0, (char *) effect3_wav, effect3_wav_size, EFFECT_VOLUME, EFFECT_VOLUME, NULL);
	      return i;
	   }	   
   }
   return -1;
}

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=stateHighScore;
			      break;
				  
		  case 2: // Help button 
				  stateMachine=stateHelp;
			      break;
	  
	      case 3: // Credits button 		 
				  stateMachine=stateCredits;
			      break;
		  
		  case 4: // Exit HBC button 
			      buttonExit(0);
			      break;	
				  
		  case 5: // Reset Wii button 
				  buttonExit(1);
			      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 stateHighScore:
     {
        switch (buttonSelected(x,y))
	    {
          case 0: // Next 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, 14, 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:
	   {
	      int  ypos=yOffset+10;
		  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+=170;
		   
          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,"on www.wiibrew.org/wiki/RedSquare");
	         drawText(20, ypos, fontNew, 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;
				  
           // 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 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 stateHighScore:
	   {
	      int  ypos=yOffset;
	      struct tm *local;
	      struct tm *level;
		  
          // 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, "HIGH SCORE");	

          // Show Content
          ypos+=80;
	      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 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+=140;
		  
		  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!");
		  
		  // 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+=100;
		  
		  // 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, "wplaat");

	      ypos+=30;
	      drawText(0, ypos, fontParagraph, "SOUND EFFECTS  ");
	      ypos+=20;
	      drawText(0, ypos, fontNormal, "wplaat");
  
  	      ypos+=30;
	      drawText(0, ypos, fontParagraph, "TESTERS");
	      ypos+=20;
	      drawText(0, ypos, fontNormal, "wplaat");	  
		  		  	  	
	      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;
	}
}

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 )
   {
		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 )
   {
		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 )
   {
		WPAD_ScanPads(); 
		if (WPAD_ButtonsDown(channel) & BUTTON_A) flag=false; 	
		VIDEO_SetNextFramebuffer(frameBuffer[0]); 
		VIDEO_Flush();
		VIDEO_WaitVSync();
   }
}

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

int main()
{
    int x=0, y=0;   
	stateMachine=stateWelcome;
	prevStateMachine=stateNone;
		
    VIDEO_Init();
	
    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
 
    // 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();
	
  	// Init game variables    
	initGame();	
	initImages();
    initSquares();
	initThreads();
	initSound();
	
	// Load highscore from SD card
    loadHighScoreFile(HIGHSCORE_FILENAME);	
   	
	// 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(1)
	{		
        // icon angle 
	    if (++angle>=MAX_ANGLE) angle=0;
		
		// icon alfa 
		if (alfa_direction)
		{
		   ++alfa;
		   if (++alfa>=MAX_ALFA) alfa_direction=false;
		}
		else
		{
		   --alfa;
		   if (--alfa<=0) alfa_direction=true;
		}
		
        // 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);

        // 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);	
					
     	// Draw wiiremote ir pointer
		GRRLIB_DrawImg( x, y, 96, 96, images.pointer, (ir1.angle/2), 1, 1, 255 );
		GRRLIB_Render();
	}
    return 0;
}

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