/* 
**  SpaceBubble for Wii - Created by wplaat
**
**  SpaceBubble is a classic board puzzle game based bubble breaker. Select multiple 
**  bubbles with the same color and destroy them, the more you hit in one the higher 
**  score you will get.
**
**  Key Features:
**  - 2D Bubble Gameboard with special graphical effects
**  - Six game levels
**  - Game score is shared with all players
**  - Easy menu system
**  - WiiMote IR control
**  - 16 music tracks
**  - Several sound effects
**  
**  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:
**  ==============
**
**  Todo: 
**  - Add some graphical effects to welcome screen. 
**  - If player is 10 seconds idle give a random hint!
**  - Add super shift bubble option.
**  - Added bubble destroy graphical effect.
** 
**  23/01/2009 Version 0.20
**  - Added 16 background music tracks.
**  - Added several sound effects.
**  - Each level has it on score matrix and time parameter.
**  - First offical release for the Wii HomeBrew scene.
**  - Build game with libogc 1.7.1 and devkitPPC r16 compiler.
**
**  22/01/2009 Version 0.13
**  - Added player initials setting screen.
**  - Added load/save game setting to sdcard.
**  - Use only one unique cookie number during the game.
**  - Improve score calculation.
**  - If level is cleared (remaining time * factor) is added to score.
**  - Build game with libogc 1.7.1 and devkitPPC r16 compiler.
**
**  21/01/2009 Version 0.12
**  - Added Local / Global highscore functionality.
**  - Added game info panel to game screen.
**  - Added countdown clock.
**  - Build game with libogc 1.7.1 and devkitPPC r16 compiler.
**   
**  20/01/2009 Version 0.11
**  - Added ShiftRight bubble functionality.
**  - Added six game levels.
**  - Gamescore is send anonymouse to webservices on internet.
**  - Added help screen information.
**  - Cleanup not used source code.
**  - Build game with libogc 1.7.1 and devkitPPC r16 compiler.
**
**  18/01/2009 Version 0.10
**  - Started programming.
**  - Created raw gameboard layout
**  - Created intro screens.
**  - Basic game engine ready.
**  - Build game with libogc 1.7.1 and devkitPPC r16 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"
#include "track10_mod.h"
#include "track11_mod.h"
#include "track12_mod.h"
#include "track13_mod.h"
#include "track14_mod.h"
#include "track15_mod.h"
#include "track16_mod.h"

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

#define PROGRAM_NAME	    "SpaceBubble"
#define PROGRAM_VERSION     "0.20"
#define RELEASE_DATE        "23-01-2009" 

// Check latest available version 
#define URL1                "http://www.plaatsoft.nl/service/releasenotes4.html"
#define ID1			        "UA-6887062-1"

// Fetch Release notes
#define URL2                "http://www.plaatsoft.nl/service/releasenotes4.html"
#define ID2				    "UA-6887062-1"

// Set Score and get global Highscore
#define URL3                "http://www.plaatsoft.nl/service/score_set.php"
#define ID3				    "UA-6887062-1"

#define URL_TOKEN           " Version "
#define HIGHSCORE_FILENAME  "sd:/apps/SpaceBubble/highscore.xml"
#define SETTING_FILENAME    "sd:/apps/SpaceBubble/setting.xml"

#define WSP_POINTER_X       200
#define WSP_POINTER_Y       250

#define COLOR_WHITESMOKE    0xFFFFFF
#define COLOR_DARKBLACK     0x000000

#define MAX_BUTTONS         10
#define MAX_RUMBLE			4
#define MAX_BUBBLES   		144
#define MAX_BOARDSIZE 		12
#define MAX_HORZ_PIXELS		640
#define MAX_VERT_PIXELS		480
#define MAX_HIGHSCORES 		13
#define MAX_SETTINGS		10
#define MAX_BEST	 		13
#define MAX_LEVEL    	    6
#define MAX_LEN			    256
#define MAX_ANGLE			360
#define MAX_SIZE            8
#define MAX_SOUND_VOLUME	10
#define MAX_MUSIC_TRACK     16
#define MAX_LINES		    200
#define MAX_BUFFER_SIZE		4096

#define MUSIC_MULTIPLER		5
#define EFFECT_MULTIPLER	20

#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 loadLocalHighScoreFile(char* filename);
void loadGlobalHighScore(char *buffer);
void drawText(int x, int y, int type, char *text,...);
void buttonExit(int index);
void bubbleShiftDown(void);
void bubblePossibleOption(void);
void initBubbles(void);

// -----------------------------------------------------------
// TYPEDEFS 
// -----------------------------------------------------------
 
typedef struct 
{
  // Png Image index
  u8 *gameboard1;
  u8 *panel;
  u8 *button1;
  u8 *button1select;  
  u8 *button2;
  u8 *button2select;  
  u8 *bar;
  u8 *barCursor;
  u8 *sound;
  u8 *pointer;
  u8 *noBubble;
  u8 *blueBubble;
  u8 *blueSelectBubble;
  u8 *yellowBubble;
  u8 *yellowSelectBubble;
  u8 *greenBubble;
  u8 *greenSelectBubble;
  u8 *redBubble;
  u8 *redSelectBubble;
  u8 *pinkBubble;
  u8 *pinkSelectBubble;
  u8 *lightblueBubble;
  u8 *lightblueSelectBubble;
  u8 *blackBubble;
  u8 *blackSelectBubble;
} 
image;

image images;

typedef struct
{
   int  x;
   int  y;
   int  color;
   int  width;
   int  height;
   bool selected;
   bool enabled;
   bool check;
} 
bubble;

bubble bubbles[MAX_BUBBLES];

typedef struct
{
   time_t localTime;
   int    level;
   int    score; 
   char   name[MAX_LEN];
}
highscore;

highscore highscores[MAX_HIGHSCORES+1];

typedef struct
{
   char   key[MAX_LEN];
   char   value[MAX_LEN];
}
setting;

setting settings[MAX_SETTINGS+1];

typedef struct
{
   time_t dt;
   int    score;
   char   name[MAX_LEN];
   char   location[MAX_LEN];
}
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];

struct 
{
  time_t  localTime;
  time_t  playTime;
  int     score;
  int     level;
  int     possiblities;
  char    name[MAX_LEN];
  int     maxTime;
}
game;

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

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

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

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

int     bubbleCount=0;

int     yOffset;
int     yjpegOffset;
int     angle;

bool    selected1;
bool    selected2;
bool    selectedA;

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

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;

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

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

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

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

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

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

// bar Image
extern char     pic14data[];
extern int      pic14length;

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

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

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

// Black Bubble Image
extern char     pic18data[];
extern int      pic18length;

// Blue Bubble Image
extern char     pic19data[];
extern int      pic19length;

// Blue Select Bubble Image
extern char     pic20data[];
extern int      pic20length;

// Yellow Bubble Image
extern char     pic21data[];
extern int      pic21length;

// Yellow Select Bubble Image
extern char     pic22data[];
extern int      pic22length;

// Green Bubble Image
extern char     pic23data[];
extern int      pic23length;

// Green Select Bubble Image
extern char     pic24data[];
extern int      pic24length;

// Red Bubble Image
extern char     pic25data[];
extern int      pic25length;

// Red Select Bubble Image
extern char     pic26data[];
extern int      pic26length;

// Pink Bubble Image
extern char     pic27data[];
extern int      pic27length;

// PinkSelect Bubble Image
extern char     pic28data[];
extern int      pic28length;

// LightBlue Bubble Image
extern char     pic29data[];
extern int      pic29length;

// LightBlue Select Bubble Image
extern char     pic30data[];
extern int      pic30length;

// Black Bubble Image
extern char     pic31data[];
extern int      pic31length;

// Black Select Bubble Image
extern char     pic32data[];
extern int      pic32length;

// -----------------------------------------------------------
// 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;
			   
	  case 10 : MODPlay_SetMOD(&snd1, track10_mod);
			    MODPlay_Start(&snd1);	
		        break;
				
	  case 11 : MODPlay_SetMOD(&snd1, track11_mod);
			    MODPlay_Start(&snd1);	
			    break;
				
	  case 12 : MODPlay_SetMOD(&snd1, track12_mod);
			    MODPlay_Start(&snd1);	
			    break;
				
	  case 13 : MODPlay_SetMOD(&snd1, track13_mod);
			    MODPlay_Start(&snd1);	
			    break;
				
	  case 14 : MODPlay_SetMOD(&snd1, track14_mod);
			    MODPlay_Start(&snd1);	
			    break;
				
	  case 15 : MODPlay_SetMOD(&snd1, track15_mod);
			    MODPlay_Start(&snd1);	
			    break;
				
	  case 16 : MODPlay_SetMOD(&snd1, track16_mod);
			    MODPlay_Start(&snd1);					
			    break;
  } 
   MODPlay_SetVolume( &snd1, musicVolume*MUSIC_MULTIPLER,musicVolume*MUSIC_MULTIPLER); 
}

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",PROGRAM_NAME);
	   
   tcp_start_thread(PROGRAM_NAME, PROGRAM_VERSION, ID1, URL1, ID2, URL2, ID3, URL3, URL_TOKEN, userData1, userData2);
}

void initButtons(void)
{ 
   switch (stateMachine)
   {
      case stateWelcome:
      {
        maxButtons=9;

		// 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=50+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=90+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=130+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=170+yOffset;
		buttons[4].width=240;
		buttons[4].height=36;
		
		// Release Notes button 
		buttons[5].image=images.button1;
		buttons[5].imageSelect=images.button1select;
		strcpy(buttons[5].name,"Release Notes" );
		buttons[5].x=370;
		buttons[5].y=210+yOffset;
		buttons[5].width=240;
		buttons[5].height=36;
				
		// Initials Settings button 
		buttons[6].image=images.button1;
		buttons[6].imageSelect=images.button1select;
		strcpy(buttons[6].name,"General Settings" );
		buttons[6].x=370;
		buttons[6].y=250+yOffset;
		buttons[6].width=240;
		buttons[6].height=36;
		
		// Exit HBC button 
		buttons[7].image=images.button1;
		buttons[7].imageSelect=images.button1select;
		strcpy(buttons[7].name,"Exit HBC" );
		buttons[7].x=370;
		buttons[7].y=350+yOffset;
		buttons[7].width=240;
		buttons[7].height=36;
      
		// Reset Wii button 
		buttons[8].image=images.button1;
		buttons[8].imageSelect=images.button1select;
		strcpy(buttons[8].name,"Reset Wii" );
		buttons[8].x=370;
		buttons[8].y=390+yOffset;
		buttons[8].width=240;
		buttons[8].height=36;
      }
      break;
  
    case stateGame:
    {
		maxButtons=1;

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

		// Stop button 
		buttons[0].image=images.button1;
		buttons[0].imageSelect=images.button1select;
		strcpy(buttons[0].name,"Back");
		buttons[0].x=270;
		buttons[0].y=400+yOffset;
	    buttons[0].width=240;
		buttons[0].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;
	  }
	  break;
	  
	  case stateSettings:
	  {
	    maxButtons=7;
		
	    // First letter + button 
		buttons[0].image=images.button2;
		buttons[0].imageSelect=images.button2select;
		strcpy(buttons[0].name,"+");
		buttons[0].x=100;
		buttons[0].y=130+yOffset;
		buttons[0].width=80;
		buttons[0].height=36;
   
		// First letter - button 
		buttons[1].image=images.button2;
		buttons[1].imageSelect=images.button2select;
		strcpy(buttons[1].name,"-");   
		buttons[1].x=100;
		buttons[1].y=295+yOffset;
		buttons[1].width=80;
		buttons[1].height=36;
				
		// Second letter + button 
		buttons[2].image=images.button2;
		buttons[2].imageSelect=images.button2select;
		strcpy(buttons[2].name,"+");
		buttons[2].x=270;
		buttons[2].y=130+yOffset;
		buttons[2].width=80;
		buttons[2].height=36;
   
		// Second letter - button 
		buttons[3].image=images.button2;
		buttons[3].imageSelect=images.button2select;
		strcpy(buttons[3].name,"-");   
		buttons[3].x=270;
		buttons[3].y=295+yOffset;
		buttons[3].width=80;
		buttons[3].height=36;
				
		// Third letter + button 
		buttons[4].image=images.button2;
		buttons[4].imageSelect=images.button2select;
		strcpy(buttons[4].name,"+");
		buttons[4].x=460;
		buttons[4].y=130+yOffset;
		buttons[4].width=80;
		buttons[4].height=36;
   
		// Third letter - button 
		buttons[5].image=images.button2;
		buttons[5].imageSelect=images.button2select;
		strcpy(buttons[5].name,"-");   
		buttons[5].x=460;
		buttons[5].y=295+yOffset;
		buttons[5].width=80;
		buttons[5].height=36;
		
		// Back button 
		buttons[60].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;
   }
}

void initGame()
{
   // Init variables
   selectedA=false;   
   selected1=false;
   selected2=false;
      
   angle=0; 
   notesStartLine=4;
   
   initBubbles();
}

void initLevel()
{
   // Increes Level
   game.level++;	

   switch (game.level)
   {
      case 1:  game.maxTime=30;	           
			   break;

      case 2:  game.score+=(game.maxTime-game.playTime)*20;
			   game.maxTime=40;	           
			   break;		

      case 3:  game.score+=(game.maxTime-game.playTime)*30;
			   game.maxTime=50;	           
			   break;	   
			   
	  case 4:  game.score+=(game.maxTime-game.playTime)*40;
			   game.maxTime=60;	           
			   break;
			   
	  case 5:  game.score+=(game.maxTime-game.playTime)*50;
			   game.maxTime=70;	           
			   break;
			   
	  case 6:  game.score+=(game.maxTime-game.playTime)*60;
			   game.maxTime=80;	           
			   break;
   }
   
   // Reset time
   game.localTime=time(NULL);   
}
  			 
void initImages(void)
{
   // Buttons & Panels
   images.gameboard1=GRRLIB_LoadTexture((unsigned char*) pic4data);
   images.panel=GRRLIB_LoadTexture((unsigned char*) pic5data);
   images.button1=GRRLIB_LoadTexture((unsigned char*) pic10data);
   images.button1select=GRRLIB_LoadTexture((unsigned char*) pic11data);     
   images.button2=GRRLIB_LoadTexture((unsigned char*) pic12data);
   images.button2select=GRRLIB_LoadTexture((unsigned char*) pic13data);    
   images.bar=GRRLIB_LoadTexture((unsigned char*) pic14data); 
   images.barCursor=GRRLIB_LoadTexture((unsigned char*) pic15data);
   images.sound=GRRLIB_LoadTexture((unsigned char*) pic16data);         
   images.pointer=GRRLIB_LoadTexture((unsigned char*) pic17data); 
      
   images.noBubble=GRRLIB_LoadTexture((unsigned char*) pic18data); 
   images.blueBubble=GRRLIB_LoadTexture((unsigned char*) pic19data); 
   images.blueSelectBubble=GRRLIB_LoadTexture((unsigned char*) pic20data); 
   images.yellowBubble=GRRLIB_LoadTexture((unsigned char*) pic21data); 
   images.yellowSelectBubble=GRRLIB_LoadTexture((unsigned char*) pic22data); 
   images.greenBubble=GRRLIB_LoadTexture((unsigned char*) pic23data); 
   images.greenSelectBubble=GRRLIB_LoadTexture((unsigned char*) pic24data); 
   images.redBubble=GRRLIB_LoadTexture((unsigned char*) pic25data); 
   images.redSelectBubble=GRRLIB_LoadTexture((unsigned char*) pic26data); 
   images.pinkBubble=GRRLIB_LoadTexture((unsigned char*) pic27data); 
   images.pinkSelectBubble=GRRLIB_LoadTexture((unsigned char*) pic28data); 
   images.lightblueBubble=GRRLIB_LoadTexture((unsigned char*) pic29data); 
   images.lightblueSelectBubble=GRRLIB_LoadTexture((unsigned char*) pic30data); 
   images.blackBubble=GRRLIB_LoadTexture((unsigned char*) pic31data); 
   images.blackSelectBubble=GRRLIB_LoadTexture((unsigned char*) pic32data); 
}

void initBubbles(void)
{
   int x,y;
   int xoffset=210;
   int yoffset=25;
   maxBubbles=MAX_BUBBLES;
	
   for (y=0; y<MAX_BOARDSIZE; y++)
   {	
      for (x=0; x<MAX_BOARDSIZE; x++)
      {	
	       int nr=(y*MAX_BOARDSIZE)+x;
		   int color=rand() % (game.level+1);
		    
			bubbles[nr].color=color;
			bubbles[nr].enabled=true;
			bubbles[nr].selected=false;
			bubbles[nr].x=(32*x)+xoffset;
		    bubbles[nr].y=(32*y)+yoffset;
		    bubbles[nr].width=32;
		    bubbles[nr].height=32;
		}	
    }	
	bubblePossibleOption();
}

void initStateMachine(void)
{
    switch(stateMachine)
    {
		case stateWelcome:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		break;
		
        case stateGame:
		{  		 
		   if (stateMachine!=prevStateMachine) initButtons();
		
		   game.playTime = time(NULL)-game.localTime;   
		   		
		   if (game.possiblities==0)
		   {			  
			  			  
			  if (game.level<MAX_LEVEL)
			  {
    	         SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 8000, 0, (u8 *) effect2_pcm, effect2_pcm_size, effectVolume*EFFECT_MULTIPLER, effectVolume*EFFECT_MULTIPLER, NULL);				 
				 initLevel(); 	
                 initGame(); 				 			 
			  }			
			  else
			  {
     	         SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 8000, 0, (u8 *) effect1_pcm, effect1_pcm_size, effectVolume*EFFECT_MULTIPLER, effectVolume*EFFECT_MULTIPLER, NULL);

			     stateMachine=stateGameOver;
				 if (game.score>2000)
				 {
				    sprintf(appl_user3,"appl=%s&level=%d&score=%d&name=%s&dt=%d",PROGRAM_NAME,game.level,game.score,game.name,(int)game.localTime);
			        tcp_set_state(TCP_REQUEST3a, appl_user3);	
				 }
			  }
		   }

           if (	game.playTime>=game.maxTime )
		   {
  	          SND_SetVoice(SND_GetFirstUnusedVoice(), VOICE_MONO_8BIT, 8000, 0, (u8 *) effect1_pcm, effect1_pcm_size, effectVolume*EFFECT_MULTIPLER, effectVolume*EFFECT_MULTIPLER, NULL);

		      stateMachine=stateGameOver;
			  sprintf(appl_user3,"appl=%s&level=%d&score=%d&name=%s&dt=%d",PROGRAM_NAME,game.level,game.score,game.name,(int)game.localTime);
			  tcp_set_state(TCP_REQUEST3a, appl_user3);	
           }
		}
		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();
		      loadGlobalHighScore(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;
		
		case stateSettings:
		{
		   if (stateMachine!=prevStateMachine) initButtons();
		}
		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 saveLocalHighScoreFile(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_HIGHSCORES; i++)
   {
      if ((game.score>=highscores[i].score) && !store)
	  {
	     sprintf(tmp, "entry%d", i);
         data = mxmlNewElement(group, tmp);
  
	     sprintf(tmp, "%d", (int) game.localTime);
         mxmlElementSetAttr(data, "localTime", tmp);
	  
	     sprintf(tmp, "%d", game.score);
	     mxmlElementSetAttr(data, "score", tmp);

		 sprintf(tmp, "%d", game.level);
	     mxmlElementSetAttr(data, "level", tmp);
		 
	     mxmlElementSetAttr(data, "name", game.name);
		 
		 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", highscores[selectedHighScore].score);
	    mxmlElementSetAttr(data, "score", tmp);
		
		sprintf(tmp, "%d", highscores[selectedHighScore].level);
	    mxmlElementSetAttr(data, "level", tmp);
		
	    mxmlElementSetAttr(data, "name", highscores[selectedHighScore].name);
		
		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 saveSettingFile(char* filename)
{
   int i;
   mxml_node_t *xml;
   mxml_node_t *group;
   mxml_node_t *data;   
   char temp[MAX_LEN];
      
   xml = mxmlNewXML("1.0");
   
   group = mxmlNewElement(xml, "RedSquare");
   
   for(i=0; i<MAX_SETTINGS; i++)
   {
        sprintf(temp, "entry%d", i);
        data = mxmlNewElement(group, temp);
  
        mxmlElementSetAttr(data, "key", settings[i].key);	  
 	    mxmlElementSetAttr(data, "value", settings[i].value);			  
   }
  
   /* now lets save the xml file to a file! */
   FILE *fp;
   fp = fopen(filename, "w");

   mxmlSaveFile(xml, fp, MXML_NO_CALLBACK);
   
   fclose(fp);
   mxmlDelete(data);
   mxmlDelete(group);
   mxmlDelete(xml);
   
   // Update game.name value
   if ((settings[0].value[0]!=0x00) && (settings[1].value[0]!=0x00) && (settings[2].value[0]!=0x00))
   {
      sprintf(temp,"%c%c%c",settings[0].value[0],settings[1].value[0],settings[2].value[0]);
      strcpy(game.name,temp);
   }
}

void loadGlobalHighScore(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,"dt");   
        if (tmp!=NULL) bests[i].dt=atoi(tmp); else bests[i].dt=0; 
		
		tmp=mxmlElementGetAttr(data,"score");   
        if (tmp!=NULL) bests[i].score=atoi(tmp); else bests[i].score=0;
		
        tmp=mxmlElementGetAttr(data,"name");   
        if (tmp!=NULL) strcpy(bests[i].name,tmp); else strcpy(bests[i].name,"");

		tmp=mxmlElementGetAttr(data,"location");   
        if (tmp!=NULL) strcpy(bests[i].location,tmp); else strcpy(bests[i].location,"");
      }   
      mxmlDelete(data);
      mxmlDelete(tree);
   }
}

void loadLocalHighScoreFile(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_HIGHSCORES; 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,"score"); 
		if (tmp!=NULL) highscores[i].score=atoi(tmp); else highscores[i].score=0;
		
		tmp=mxmlElementGetAttr(data,"level"); 
		if (tmp!=NULL) highscores[i].level=atoi(tmp); else highscores[i].level=0;
		
		tmp=mxmlElementGetAttr(data,"name"); 
		if (tmp!=NULL) strcpy(highscores[i].name,tmp); else strcpy(highscores[i].name,"");
	 } 
   }
   else
   {
     // If file not found, create empty highscore list.
     for(i=0; i<MAX_HIGHSCORES; i++)
     {
       	highscores[i].localTime=0;
		highscores[i].score=0;
		highscores[i].level=0;
		memset(highscores[i].name,0x00,MAX_LEN);
	 } 
   }
   
   mxmlDelete(data);
   mxmlDelete(tree);
}
 
 void loadSettingFile(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_SETTINGS; i++)
     {
	    sprintf(temp, "entry%d", i);
        data = mxmlFindElement(tree, tree, temp, NULL, NULL, MXML_DESCEND);
  
        tmp=mxmlElementGetAttr(data,"key");   
        if (tmp!=NULL) strcpy(settings[i].key,tmp); else strcpy(settings[i].key,"");
		
		tmp=mxmlElementGetAttr(data,"value"); 
		if (tmp!=NULL) strcpy(settings[i].value,tmp); else strcpy(settings[i].value,"");
	 } 
   }
   else
   {
     // If file not found, create empty highscore list.
     for(i=0; i<MAX_SETTINGS; i++)
     {
        memset(settings[i].key,0x00, MAX_LEN);
		memset(settings[i].value,0x00, MAX_LEN);
		
		settings[0].value[0]='A';
		strcpy(settings[0].key,"FIRST_CHAR");
		settings[1].value[0]='A';
		strcpy(settings[0].key,"SECOND_CHAR");
		settings[2].value[0]='A';
		strcpy(settings[0].key,"THIRD_CHAR");
	 } 
	 
	 
   }

   mxmlDelete(data);
   mxmlDelete(tree);
   
   // Update game.name value  
   if ((settings[0].value[0]!=0x00) && (settings[1].value[0]!=0x00) && (settings[2].value[0]!=0x00))
   {
      sprintf(temp,"%c%c%c",settings[0].value[0],settings[1].value[0],settings[2].value[0]);
      strcpy(game.name,temp);
   }
}

// Gameboard matrix (10x10)
// 00 01 02 03 04 05 06 07 08 09 
// 10 11 12 13 14 15 16 17 18 19 
// 20 21 22 23 24 25 26 27 28 29
// 30 31 32 33 34 35 36 37 38 39
// 40 41 42 43 44 45 46 47 48 49
// 50 51 52 53 54 55 56 57 58 59
// 60 61 62 63 64 65 66 67 68 69
// 70 71 72 73 74 75 76 77 78 79
// 80 81 82 83 84 85 86 87 88 89
// 90 91 92 93 94 95 96 97 98 99

void bubbleShiftRight(void)
{
   int x, y, i, nr;
   for (i=0; i<MAX_BOARDSIZE; i++) 
   {
     for (x=(MAX_BOARDSIZE-1);x>0;x--)
     {  
       if ( !bubbles[((MAX_BOARDSIZE-1)*MAX_BOARDSIZE)+x].enabled )
	   {
         for (y=0; y<MAX_BOARDSIZE; y++)
         {  
		   nr=(y*MAX_BOARDSIZE)+x;
	       bubbles[nr].enabled=bubbles[nr-1].enabled;
		   bubbles[nr].selected=false;
		   bubbles[nr].color=bubbles[nr-1].color;
			   
		   bubbles[nr-1].enabled=false;
		   bubbles[nr-1].selected=false;
		   bubbles[nr-1].color=MAX_BOARDSIZE;
		  }
        }
	 }
   }        
}

void bubbleShiftDown(void)
{
   int x, y1, y2;
   for (x=0;x<MAX_BOARDSIZE;x++)
   {   
      for (y2=(MAX_BOARDSIZE-1);y2>0;y2--)
      {  
	     if (!bubbles[(y2*MAX_BOARDSIZE)+x].enabled )
		 {
           for (y1=(y2-1);y1>=0;y1--)
           {  
	         if ( bubbles[(y1*MAX_BOARDSIZE)+x].enabled )
		     {			  
		       bubbles[(y2*MAX_BOARDSIZE)+x].enabled=true;
			   bubbles[(y2*MAX_BOARDSIZE)+x].selected=false;
			   bubbles[(y2*MAX_BOARDSIZE)+x].color=bubbles[(y1*MAX_BOARDSIZE)+x].color;
			   
			   bubbles[(y1*MAX_BOARDSIZE)+x].enabled=false;
			   bubbles[(y1*MAX_BOARDSIZE)+x].selected=false;
			   bubbles[(y1*MAX_BOARDSIZE)+x].color=MAX_BOARDSIZE;
			   
			   break;
		     }
		   }
		 }
	  }
   }        
}

void bubbleMultiSelect2(int nr)
{
   int x=nr % MAX_BOARDSIZE;
   int y=nr / MAX_BOARDSIZE;
   		
   // Set select flag for bubble	   
   bubbles[nr].check=true;
   bubbleCount++;
   
   // Find all connected bubbles with the same color.
   if ((x>0) && (bubbles[nr].color==bubbles[nr-1].color) && !bubbles[nr-1].check) bubbleMultiSelect2(nr-1); 
   if ((x<(MAX_BOARDSIZE-1)) && (bubbles[nr].color==bubbles[nr+1].color) && !bubbles[nr+1].check) bubbleMultiSelect2(nr+1);
   if ((y>0) && (bubbles[nr].color==bubbles[nr-MAX_BOARDSIZE].color) && !bubbles[nr-MAX_BOARDSIZE].check) bubbleMultiSelect2(nr-MAX_BOARDSIZE); 
   if ((y<(MAX_BOARDSIZE-1)) && (bubbles[nr].color==bubbles[nr+MAX_BOARDSIZE].color) && !bubbles[nr+MAX_BOARDSIZE].check) bubbleMultiSelect2(nr+MAX_BOARDSIZE); 
}

void bubblePossibleOption(void)
{
  int i;
  for (i=0; i<MAX_BUBBLES; i++) bubbles[i].check=false;  
  
  game.possiblities=0;  
  for (i=0; i<MAX_BUBBLES; i++)
  {     
     if (!bubbles[i].check)
	 {
	    bubbleCount=0;		
		bubbleMultiSelect2(i);
		if (bubbleCount>1) game.possiblities++;
	 }
  }
  game.possiblities--;
}

void bubbleRemove(void)
{
   int i;
   int count=0;
   
   for (i=0; i<maxBubbles; i++)
   {
      if (bubbles[i].selected) count++;
   }
   game.score+=count*(count-1);
   
   // If two or more bubbles are selected then remove them.
   if (count>1)
   {
      for (i=0; i<maxBubbles; i++)
      {
        if (bubbles[i].selected) 
	    {
	      bubbles[i].color=MAX_BOARDSIZE;
		  bubbles[i].selected=false;
		  bubbles[i].enabled=false;
	    }	  
      }
      bubbleShiftDown();
	  bubbleShiftRight();
   }
}

void bubbleMultiSelect(int nr)
{
   int x=nr % MAX_BOARDSIZE;
   int y=nr / MAX_BOARDSIZE;
   		
   // Set select flag for bubble	   
   bubbles[nr].selected=true;
   
   // Find all connected bubbles with the same color.
   if ((x>0) && (bubbles[nr].color==bubbles[nr-1].color) && !bubbles[nr-1].selected) bubbleMultiSelect(nr-1); 
   if ((x<(MAX_BOARDSIZE-1)) && (bubbles[nr].color==bubbles[nr+1].color) && !bubbles[nr+1].selected) bubbleMultiSelect(nr+1);
   if ((y>0) && (bubbles[nr].color==bubbles[nr-MAX_BOARDSIZE].color) && !bubbles[nr-MAX_BOARDSIZE].selected) bubbleMultiSelect(nr-MAX_BOARDSIZE); 
   if ((y<(MAX_BOARDSIZE-1)) && (bubbles[nr].color==bubbles[nr+MAX_BOARDSIZE].color) && !bubbles[nr+MAX_BOARDSIZE].selected) bubbleMultiSelect(nr+MAX_BOARDSIZE); 
}

int bubbleSelected(int x, int y)
{
   int i;
   
   for (i=0; i<maxBubbles; i++)
   {
       if ( (x>=bubbles[i].x+1) && (x<=bubbles[i].x+bubbles[i].width-1) && 
	        (y>=bubbles[i].y+1) && (y<=bubbles[i].y+bubbles[i].height-1) )
	   {	      
		  if (bubbles[i].enabled)
		  {
		     if (bubbles[i].selected) 
		     {
			 	// Double select is remove action
   		        bubbleRemove(); 				
				bubblePossibleOption();
			 }
             else
			 {	
			    // new selection clear old selections
                int j;
				for (j=0;j<maxBubbles;j++) bubbles[j].selected=false;
   
			    // Single select is select action
			    bubbleMultiSelect(i);
		     }          			  
             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;
}

// -----------------------------------------------------------
// 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 buttonMinus(int index)
{
   switch (index)   
   {
       case 0:
	      // Music volume
	      if (musicVolume>0) musicVolume--;   
	      MODPlay_SetVolume( &snd1, musicVolume*MUSIC_MULTIPLER,musicVolume*MUSIC_MULTIPLER);
		  break;
       
	   case 1:
          // Effect volume
          if (effectVolume>0) effectVolume--; 
		  break;
   
       case 2:
	      // Prev music track
          MODPlay_Stop(&snd1);
          if (selectedMusic>1) selectedMusic--; else selectedMusic=MAX_MUSIC_TRACK;
	      initMusicTrack();
		  break;
	
	    case 3:
		  // First Character
		  if (settings[0].value[0]==0x00) 
		  {
		     settings[0].value[0]='A';
			 strcpy(settings[0].key,"FIRST_CHAR");
		  }
		  else
		  {
		     if (settings[0].value[0]>65) settings[0].value[0]--; else settings[0].value[0]='Z';
		  }		
		  break;
		  
		case 4:
		  // Second Character
		  if (settings[1].value[0]==0x00) 
		  {
		     settings[1].value[0]='A';
			 strcpy(settings[1].key,"SECOND_CHAR");
		  }
		  else
		  {
		     if (settings[1].value[0]>65) settings[1].value[0]--; else settings[1].value[0]='Z';
		  }		
		  break;
		  
		case 5:
		  // Third Character
		  if (settings[2].value[0]==0x00) 
		  {
		     settings[2].value[0]='A';
			 strcpy(settings[2].key,"THIRD_CHAR");
		  }
		  else
		  {
		     if (settings[2].value[0]>65) settings[2].value[0]--; else settings[2].value[0]='Z';
		  }		
		  break;
   }
}

void buttonPlus(int index)
{
   switch (index)
   {
      case 0:
         // Music volume
	     if (musicVolume<MAX_SOUND_VOLUME) musicVolume++;   
	     MODPlay_SetVolume( &snd1, musicVolume*MUSIC_MULTIPLER,musicVolume*MUSIC_MULTIPLER); 
		 break;
		 
	  case 1:
         // Effect volume
         if (effectVolume<MAX_SOUND_VOLUME) effectVolume++; 
		 break;
		 
	  case 2:
	     // Next music Track
         MODPlay_Stop(&snd1);
         if (selectedMusic<MAX_MUSIC_TRACK) selectedMusic++; else selectedMusic=1;
	     initMusicTrack();
		 break;
		 
	  case 3:
		  // First Character
		  if (settings[0].value[0]==0x00) 
		  {
		     settings[0].value[0]='A';
			 strcpy(settings[0].key,"FIRST_CHAR");
		  }
		  else
		  {
		     if (settings[0].value[0]<90) settings[0].value[0]++; else settings[0].value[0]='A';
		  }		
		  break;
		  
	  case 4:
		  // Second Character
		  if (settings[1].value[0]==0x00) 
		  {
		     settings[1].value[0]='A';
			 strcpy(settings[1].key,"SECOND_CHAR");
		  }
		  else
		  {
		     if (settings[1].value[0]<90) settings[1].value[0]++; else settings[1].value[0]='A';
		  }		
		  break;
		  
	  case 5:
		  // Third Character
		  if (settings[2].value[0]==0x00) 
		  {
		     settings[2].value[0]='A';
			 strcpy(settings[2].key,"THIRD_CHAR");
		  }
		  else
		  {
		     if (settings[2].value[0]<90) settings[2].value[0]++; else settings[2].value[0]='A';
		  }		
		  break;
   }
}

void buttonA(int x, int y)
{
  if (selectedA) return;
  selectedA=true;
	  
  rumbleCounter=MAX_RUMBLE;
	 
  switch (stateMachine)
  {
     case stateWelcome:
     {
        switch (buttonSelected(x,y))
	    {
          case 0: // play button	      
           
		          // Reset score 
				  game.score=0;
				  
				  // Reset level
				  game.level=0;	
				  
				  // Init game
				  initLevel();
				  initGame(); 
				  
				  stateMachine=stateGame;
			      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: // Setting button 		 
				  stateMachine=stateSettings;
			      break;
				  
		  case 7: // Exit HBC button 
			      buttonExit(0);
			      break;	
				  
		  case 8: // Reset Wii button 
				  buttonExit(1);
			      break; 
	    }
     }
	 break;

     case stateGame:
	 {
	   bubbleSelected(x, y);
	 
       switch (buttonSelected(x,y))
	   {		
		  case 0: // Back button event          
			      stateMachine=stateWelcome;
				 
                  if (game.score>2000)
				  { 
   				     sprintf(appl_user3,"appl=%s&level=%d&score=%d&name=%s&dt=%d",PROGRAM_NAME,game.level,game.score,game.name,(int)game.localTime);
			         tcp_set_state(TCP_REQUEST3a, appl_user3);	
				  }				 
			      break;	   
					 
	   }	   
	 }
	 break;
	 
	 case stateGameOver:
     {
	   switch (buttonSelected(x,y))
	   {		
		  case 0: // Back button event          
		          saveLocalHighScoreFile(HIGHSCORE_FILENAME);
                  loadLocalHighScoreFile(HIGHSCORE_FILENAME);	
                  stateMachine=stateWelcome;
			      break;	   
	   }	   	   
     }
	 break;
	 
     case stateSound:
	 {     
  	      switch (buttonSelected(x,y))
	      {
			 case 0: // - music volume button event	           
			         buttonMinus(0);
			         break;	   
			 
			 case 1: // + music volume button event	           
			         buttonPlus(0);
			         break;	 
					 
			 case 2: // - effect volume button event	           
			         buttonMinus(1);
			         break;	   
			 
			 case 3: // + effect volume button event	           
			         buttonPlus(1);
			         break;	 
					 
			 case 4: // - music track button event	           
			         buttonMinus(2);
			         break;	   
			 
			 case 5: // + music track  button event	           
			         buttonPlus(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 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;
	 
	 case stateSettings:
	 {     
  	      switch (buttonSelected(x,y))
	      {
			 case 0: // + First Character button event	           
			         buttonPlus(3);
			         break;	   
			 
			 case 1: // - First Character button event	           
			         buttonMinus(3);
			         break;	 
					 
			 case 2: // + Second Character button event           
			         buttonPlus(4);
			         break;	   
			 
			 case 3: // - Second Character button event         
			         buttonMinus(4);
			         break;	 
					 
			 case 4: // + Third Character button event         
			         buttonPlus(5);
			         break;	   
			 
			 case 5: // - Third Character button event           
			         buttonMinus(5);
			         break;
					 
			 case 6: // Back button event      
					 saveSettingFile(SETTING_FILENAME);    
			         stateMachine=stateWelcome;
			         break;	   
					 
		  }
	 }
	 break;
  }  
}
	
void buttonExit(int index)
{    
   // Stop network thread
   tcp_stop_thread();
	
   // Stop rumble
   WPAD_Rumble(0,0);
		
   // Stop music
   MODPlay_Stop(&snd1);
	
   // Exit game
   if (index==0)
   {   
       // Exit to loader
	   exit(0);
   }
   else
   {
       // Reset Wii
	   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;
	}
}

u8 *imageBubble(boolean selected, int color)
{
    if (selected)
    {  
	  // Show selected bubble
      switch (color)
      {
        case 0: return images.blueSelectBubble;
  	 		    break;
			  
	    case 1: return images.yellowSelectBubble;
	 		    break;
			  
	    case 2: return images.greenSelectBubble;
	 		    break;
			  
	    case 3: return images.redSelectBubble;
	 		    break;
 			  
        case 4: return images.pinkSelectBubble;
	 	  	    break;

	    case 5: return images.lightblueSelectBubble;
	 		    break;

	    case 6: return images.blackSelectBubble;
	 		    break;
			  
	    default:return images.noBubble;
	 		    break;
      }
    }
    else
    {
	   // Show not selected bubble
       switch (color)
       {
        case 0: return images.blueBubble;
	 		    break;
			  
	    case 1: return images.yellowBubble;
	 		    break;
			  
	    case 2: return images.greenBubble;
	 		    break;

	    case 3: return images.redBubble;
	 		    break;
			  
	    case 4: return images.pinkBubble;
	 		    break;
			  
	    case 5: return images.lightblueBubble;
	 		    break;
			  			  
	    case 6: return images.blackBubble;
	 		    break;
			  
	    default:return images.noBubble;
	 		    break;
	  }
    }
}

void drawBubbles(int x, int y)
{
   int i;   
   for (i=0; i<maxBubbles; i++)
   {
       GRRLIB_DrawImg(bubbles[i].x, bubbles[i].y, bubbles[i].width, 
	      bubbles[i].height, imageBubble(bubbles[i].selected,bubbles[i].color), 0, 1, 1, 255 );
   }
}

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 fontTitle: 
	   {
	      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)*20)/2);
		  GRRLIB_Printf2(x, y, tmp, 30, COLOR_WHITESMOKE);          
	   }
	   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 );
		  		  
		  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+=230;
		   
          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:
       {   
		  char tmp[MAX_LEN];
		  struct tm *dt;
		  
		  // Init text layer	  
          GRRLIB_initTexture();
		   
	      // Draw gameboard
		  GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1.0, 1.0, 255 );
      	
          // Draw panels
		  GRRLIB_DrawImg(0,0, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
      	  GRRLIB_DrawImg(0,120, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
		  GRRLIB_DrawImg(0,240, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
		  GRRLIB_DrawImg(0,360, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
		  
		  // Draw content  
		  drawText(50, 20, fontSubTitle, "Score");
		  sprintf(tmp,"%05d", game.score);
		  drawText(40, 50, fontSubTitle, tmp);
		  
		  time_t timeLevel=game.maxTime-game.playTime;
	      dt = localtime(&timeLevel);
		  drawText(45, 140, fontSubTitle, "Time");
	      sprintf(tmp,"%02d:%02d", dt->tm_min, dt->tm_sec);
		  drawText(40, 170, fontSubTitle, tmp);
		   
		  drawText(40, 265, fontSubTitle, "Level");
	      sprintf(tmp,"%d", game.level);
		  drawText(70, 295, fontSubTitle, tmp);
		  
		  drawText(40, 390, fontSubTitle, "Moves");
		  sprintf(tmp," %02d", game.possiblities);
		  drawText(60, 420, fontSubTitle, tmp);
		  		 
		  // Draw buttons
	      drawButtons(x,y); 
		  
		  // Draw bubbles
		  drawBubbles(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 stateGameOver:
	   {
	      int ypos=yOffset+140;		   
		  char tmp[MAX_LEN];
		  struct tm *dt;
				  	         	
	      // Init text layer	  
          GRRLIB_initTexture();
		   
	      // Draw gameboard
		  GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1.0, 1.0, 255 );
      		
	      // Draw panels
		  GRRLIB_DrawImg(0,0, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
      	  GRRLIB_DrawImg(0,120, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
		  GRRLIB_DrawImg(0,240, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
		  GRRLIB_DrawImg(0,360, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
      		
		  // Draw content  
		  drawText(50, 20, fontSubTitle, "Score");
		  sprintf(tmp,"%05d", game.score);
		  drawText(40, 50, fontSubTitle, tmp);
		  
		  time_t timeLevel=game.maxTime-game.playTime;
	      dt = localtime(&timeLevel);
		  drawText(45, 140, fontSubTitle, "Time");
	      sprintf(tmp,"%02d:%02d", dt->tm_min, dt->tm_sec);
		  drawText(40, 170, fontSubTitle, tmp);
		   
		  drawText(40, 265, fontSubTitle, "Level");
	      sprintf(tmp,"%d", game.level);
		  drawText(70, 295, fontSubTitle, tmp);
		  
		  drawText(40, 390, fontSubTitle, "Moves");
		  sprintf(tmp," %02d", game.possiblities);
		  drawText(60, 420, fontSubTitle, tmp);

		  // Draw title 
	      sprintf(tmp,"GAME OVER");
		  drawText(210, ypos, fontTitle, tmp);
		  ypos+=70;
		  
		   // Draw buttons
	      drawButtons(x,y); 
		  		   		  
		  // Draw bubbles
		  drawBubbles(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 stateHighScore1:
	   {
	      int  ypos=yOffset;
	      struct tm *local;
		  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, fontTitle, "      Local High Score");	

          // Show Content
          ypos+=90;
	      drawText(60, ypos, fontParagraph, "DATE"  );
	      drawText(260, ypos, fontParagraph, "SCORE" );
		  drawText(360, ypos, fontParagraph, "NAME"  );
		  drawText(490, ypos, fontParagraph, "LEVEL" );
		  ypos+=10;
		  
          for (i=0; i<MAX_HIGHSCORES; 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(60, ypos, fontNormal, tmp);
	   
	   	      sprintf(tmp,"%05d", highscores[i].score);
		      drawText(260, ypos, fontNormal, tmp);
	
		      drawText(360, ypos, fontNormal, highscores[i].name);
	  
	          sprintf(tmp,"%02d", highscores[i].level);
		      drawText(490, 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;
		  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, fontTitle, "   Global High Score");	

          // Show Content
          ypos+=90;

	      drawText(40, ypos, fontParagraph,  "DATE" );
	      drawText(240, ypos, fontParagraph, "SCORE" );
		  drawText(330, ypos, fontParagraph, "NAME"  );
		  drawText(410, ypos, fontParagraph, "LOCATION" );
		  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(40, ypos, fontNormal, tmp);
	   
	          sprintf(tmp,"%05d", bests[i].score);
		      drawText(240, ypos, fontNormal, tmp);
			  
			  drawText(330, ypos, fontNormal, bests[i].name);
			  
			  drawText(410, ypos, fontNormal, bests[i].location);
		    }			
		  }
		  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 );
		  
		   // Show title
		  drawText(0, ypos, fontTitle, "Help");
          ypos+=120;
		  
		  drawText(0, ypos, fontParagraph, "SpaceBubble is a classic board puzzle game based");
		  ypos+=25;
     	  drawText(0, ypos, fontParagraph, "on bubble breaker. Select multiple bubbles");	
		  ypos+=25;
	      drawText(0, ypos, fontParagraph, "with the same color and destroy them, the more");
		  ypos+=25;
	      drawText(0, ypos, fontParagraph, "you hit in one the higher score you will get.");		
		
          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, fontTitle, "      Credits");
          ypos+=90;
		  
		  // 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+=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;
	   
	   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, fontTitle, "   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, fontTitle, "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;
	   
	   case stateSettings:
	   {
	      int ypos=yOffset;	   
			  	         	
	      // Init text layer	  
          GRRLIB_initTexture();
		   
	      // Draw gameboard
		  GRRLIB_DrawImg(0,0,MAX_HORZ_PIXELS, MAX_VERT_PIXELS, images.gameboard1, 0, 1.0, 1.0, 255 );
      		
	      // Draw Title	
          drawText(0, ypos, fontTitle, "    General Settings");
          ypos+=90;
		  
		  drawText(0, ypos, fontParagraph, "Player Initials");	
	      ypos+=80;
		  
	      // Draw panels		 
		  GRRLIB_DrawImg(60,ypos, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
      	  GRRLIB_DrawImg(240,ypos, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
		  GRRLIB_DrawImg(420,ypos, 160, 120, images.panel, 0, 1.0, 1.0, 255 );
      	  ypos+=10;
		  
		  // Draw text  
		  if (settings[0].value[0]!=0x00) drawText(110, ypos, fontTitle, settings[0].value);
		  if (settings[1].value[0]!=0x00) drawText(300, ypos, fontTitle, settings[1].value);
		  if (settings[2].value[0]!=0x00) drawText(480, ypos, fontTitle, settings[2].value);

		  ypos+=170;
	  	  drawText(0, ypos, fontParagraph, "This initials are used in the highscore area.");	
	      
		  
		   // 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 drawIntroScreen(int nr)
{
   boolean flag = true;
   
   switch (nr)
   {
      case 0: drawJpegImage(pic1data, pic1length, 0, 0 );
	          break;
			  
	  case 1: drawJpegImage(pic2data, pic2length, 0, 0 );
	          break;
			  
	  case 2: drawJpegImage(pic3data, pic3length, 0, 0 );
	          break;
   }
		   	
   // Handle buttons events
   while ( flag && !bPowerOff )
   {
		WPAD_ScanPads(); 
		if (WPAD_ButtonsDown(0) & 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
    loadLocalHighScoreFile(HIGHSCORE_FILENAME);	
	
	// Load setting from SD card
	loadSettingFile(SETTING_FILENAME);	
	
    // Init Http thread
	initThreads();
	
  	// Init game variables    
    initImages();
	initGame();	
	initSound();
   	
	// Init FreeType font engine
	GRRLIB_InitFreetype();
	
    // Show Intro screens
    drawIntroScreen(0);	
	drawIntroScreen(1);	
	drawIntroScreen(2);	
	  			
    // Init GRRLib graphics library
    GRRLIB_InitVideo();
    GRRLIB_Start();
    
	// Make screen black
	GRRLIB_FillScreen(0xFFFFFF);
    GRRLIB_Render();
		
	// Repeat forever
    while(!bPowerOff )
	{		
        // icon angle 
	    if (++angle>=MAX_ANGLE) angle=0;

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

	    // Scan for button events
		WPAD_SetVRes(0, 640, 480);
        WPAD_ScanPads();
		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;

        // Scan for button events
        if (wpaddown & BUTTON_A) buttonA(x+40,y+40); 
		if (wpadup & BUTTON_A) selectedA=false;

		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
// -----------------------------------------------------------

