 /**
  *  GameCanvas contains the Canvas for the Epsilon game.
  *  Epsilon is a simple space-shooter-vertical-scroller game.
  *
  *  Epsilon is available for non-commercial use only!
  *  You can learn from this sources and you can modify them
  *  for learning purposes.
  * 
  *  @author      Ziga Hajdukovic
  *  @version     1.0
  */  

import java.util.*;
import javax.microedition.lcdui.*;

import com.nokia.mid.ui.FullCanvas;

class GameCanvas extends FullCanvas implements Runnable
//class GameCanvas extends Canvas implements Runnable
{
	private final Game midlet;
	
	public static int canvasWidth;
	public static int canvasHeight;

	public static boolean gameRunning;

	public static final int SOFTKEY_1 = -6;
	public static final int SOFTKEY_2 = -7;
	
	public static boolean menu_command_1;
	public static boolean menu_command_2;
	
	public static boolean command_up;
	public static boolean command_down;
	public static boolean command_left;
	public static boolean command_right;
	public static boolean command_fire;
	
	public static byte mode;
	public static final byte MODE_INTRO = 0;
	public static final byte MODE_GAME = 1;
	public static final byte MODE_MENU = 2;
	
	public static int loadingCounter;
	
	public static Image imgIntro;
	public static Image imgBackground;
	public static Image imgPlayer;
	public static Image imgPlayerFire;
	public static Image imgEnemy;

	public static int menuScreen;
	public static final byte MENU_SCR_MAIN = 0; 
	public static final byte MENU_SCR_TEXT_ABOUT = 1;
	public static final byte MENU_SCR_TEXT_HELP = 2;
	public static final byte MENU_SCR_TEXT_GAME_OVER = 3;

	public static int menuSelectedOption;
	public static final byte MENU_OPTION_MAIN_NEW_GAME = 0;
	public static final byte MENU_OPTION_MAIN_ABOUT = 1;
	public static final byte MENU_OPTION_MAIN_HELP = 2;
	public static final byte MENU_OPTION_MAIN_MAX = 2;
	
	public static final String[] menuScreenMainOptions = { "New game", "About", "Help"};
	
	public static final String[][] menuScreensTextLines = {
		{ "" },
		{ "About", "", "Epsilon v0.1", "(c) 2005 zigah.", "GFX by Geci." },
		{ "Help", "", "Press LEFT, RIGHT, ", "UP or DOWN", "to move and", "FIRE to shoot." },
		{ "Game Over", "", "Well done!"},
	};
	
	public static GameSprite player;
	public static GameSprite player_fire;
	public static int player_score;

	public static final byte MAX_ENERGY_PLAYER = 100;
	public static final byte MAX_ENERGY_PLAYER_FIRE = 10;
	public static final byte MAX_ENERGY_ENEMY = 10;
	
	public static Vector enemySprites;
	
	public static byte max_enemy_sprites = 3;
	
	public static int space_scroll_y;
	public static int space_scroll_vy;
	
	public static Random random = new Random();
	
	GameCanvas(Game midlet)
	{
		this.midlet = midlet;

		canvasWidth = this.getWidth();
		canvasHeight = this.getHeight();

		loadingCounter = 0;
		menuScreen = MENU_SCR_MAIN;
		menuSelectedOption = MENU_OPTION_MAIN_NEW_GAME;
		mode = MODE_INTRO;
		gameRunning = true;
	}

	public void run()
	{
		while(gameRunning)
		{
			repaint();
			serviceRepaints();

			switch (mode) {
			case MODE_INTRO:

				try
				{
					if (loadingCounter == 0)
					{
						imgIntro = Image.createImage("/intro.png");
					}
					else if (loadingCounter == 1)
					{
						imgPlayer = Image.createImage("/p.png");
					}
					else if (loadingCounter == 2)
					{
						imgPlayerFire = Image.createImage("/pf.png");
					}
					else if (loadingCounter == 3)
					{
						imgEnemy = Image.createImage("/e.png");
					}
					else if (loadingCounter == 4)
					{
						imgBackground = Image.createImage("/bg.png");
					}
					else
					{
						// view the intro splash image for a while
						// it would be better to let user skip intro by pressing a key
						// but, in a full game project, loading would take forever, anyway, so...
						try	{
							Thread.sleep(1000);
						} catch (Exception e) {
							e.printStackTrace();
						}
						// switch mode
						mode = MODE_MENU;
					}
					loadingCounter++;
				}
				catch (Exception e)	{
					// ignore loading exception	
				};
				break;
				
			case MODE_MENU:

				if (command_up) {
					command_up = false;
					menuSelectedOption--;
					if (menuSelectedOption < 0)
						menuSelectedOption = 0;
				}
				if (command_down) {
					command_down = false;
					menuSelectedOption++;
					if ((menuScreen == MENU_SCR_MAIN) &&
							(menuSelectedOption > MENU_OPTION_MAIN_MAX))
						menuSelectedOption = MENU_OPTION_MAIN_MAX;
				}
				
				if (menu_command_1)
				{
					menu_command_1 = false;
					if (menuScreen == MENU_SCR_MAIN)
					{
						switch(menuSelectedOption)
						{
						case MENU_OPTION_MAIN_NEW_GAME:
							startGame();
							break;
							
						case MENU_OPTION_MAIN_ABOUT:
							menuScreen = MENU_SCR_TEXT_ABOUT;
							break;
							
						case MENU_OPTION_MAIN_HELP:
							menuScreen = MENU_SCR_TEXT_HELP;
							break;
						}
					} else {
						// all other menu screens go to main menu when OK is selected
						menuScreen = MENU_SCR_MAIN;
						menuSelectedOption = MENU_OPTION_MAIN_NEW_GAME;
					}
				}
				if (menu_command_2)
				{
					menu_command_2 = false;
					
					// exit when in main menu
					if (menuScreen == MENU_SCR_MAIN)
						midlet.exitRequested();
				}
				
				// delay, so the keypress events get their turn...
				try	{
					Thread.sleep(40);
				} catch (Exception e) {
					e.printStackTrace();
				}
				break;
				
			case MODE_GAME:

				// update space background
				space_scroll_y += space_scroll_vy;
				
				updateEnemySprites();

				updatePlayerSprite();
				
				updatePlayerFireSprite();
				
				// check player <-> enemy <-> player fire collision
				doCollision();
				
				// check for game end
				if (menu_command_2 || player.energy == 0)
				{
					menu_command_2 = false;
					// on softkey_2 end game and return to main menu..
					endGame();
				}

				// delay
				try	{
					Thread.sleep(40);
				} catch (Exception e) {
					e.printStackTrace();
				}
				break;
			}
		}
	}
	
	public void paint(Graphics g)
	{
		switch(mode)
		{
		case MODE_INTRO:

			g.setColor(0x000000);
			g.fillRect(0, 0, canvasWidth, canvasHeight);
			if (imgIntro != null) {
				g.drawImage(imgIntro, canvasWidth/2, canvasHeight/2, Graphics.VCENTER | Graphics.HCENTER);
			}

//			g.setColor(0xFFFFFF);
//			g.drawString(""+loadingCounter,0,0,Graphics.LEFT | Graphics.TOP);

			break;
			
		case MODE_MENU:

			paintSpaceBackground(g);

			switch(menuScreen)
			{
			case MENU_SCR_MAIN:
				paintMainMenu(g);
				break;
				
			case MENU_SCR_TEXT_ABOUT:
			case MENU_SCR_TEXT_HELP:
			case MENU_SCR_TEXT_GAME_OVER:
				paintTextMenu(g, menuScreen);
				break;
			}
			break;
			
		case MODE_GAME:
			
			paintSpaceBackground(g);
			
			paintSprite( g, player );
			
			if (player_fire != null)
				paintSprite( g, player_fire );
			
			for (int i=0; i < enemySprites.size(); i++)
			{
				paintSprite( g, (GameSprite) enemySprites.elementAt(i) );
			}
			
			paintStatusBars(g);
			
			break;		
		}
	}
	
	public void keyPressed(int key)
	{
		switch(key) {
		case SOFTKEY_1:
			menu_command_1 = true;
			break;
			
		case SOFTKEY_2:
			menu_command_2 = true;
			break;
		}
		
		int game_action = getGameAction(key);
		
		switch(game_action)
		{
		case UP:
			command_up = true;
			break;

		case DOWN:
			command_down = true;
			break;

		case LEFT:
			command_left = true;
			break;

		case RIGHT:
			command_right = true;
			break;

		case FIRE:
			command_fire = true;
			menu_command_1 = true;
			break;
		}
	}
	
	public void keyReleased(int key)
	{
		int game_action = getGameAction(key);

		switch(game_action)
		{
		case UP:
			command_up = false;
			break;

		case DOWN:
			command_down = false;
			break;

		case LEFT:
			command_left = false;
			break;

		case RIGHT:
			command_right = false;
			break;

		case FIRE:
			command_fire = false;
			break;
		}
	}

	public void paintSpaceBackground(Graphics g)
	{
		// fill the vast space
		g.setColor(0x000000);
		g.fillRect(0, 0, canvasWidth, canvasHeight);
		
		// init scrolling background image tiling
		int x = 0;
		int y = space_scroll_y % imgBackground.getHeight() - imgBackground.getHeight();

		// image is wide enough, so we only need to tile it in the vertical (y) direction
		while (y < canvasHeight)
		{
			g.drawImage(imgBackground, x, y, Graphics.TOP | Graphics.LEFT);
			g.setClip(0, 0, canvasWidth, canvasHeight);
		
			y += imgBackground.getHeight();
		}
	}

	public void paintMainMenu(Graphics g)
	{
		// init font
		g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE));
		int menu_y = canvasHeight/2 - 45;
		for(int i=0; i <= MENU_OPTION_MAIN_MAX; i++)
		{
			// paint the main menu options
			if (i == menuSelectedOption) {
				g.setColor(0xFFCC00);
			} else {
				g.setColor(0xFFFFFF);
			}
			
			g.drawString(menuScreenMainOptions[i], canvasWidth/2, menu_y, Graphics.HCENTER | Graphics.TOP);
			
			menu_y += 30;
		}
		g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
		g.setColor(0xFFFFFF);
		// paint softkey_1 label
		g.drawString("Select", 2, canvasHeight-2, Graphics.LEFT | Graphics.BOTTOM);
		// paint softkey_2 label
		g.drawString("Exit", canvasWidth-2, canvasHeight-2, Graphics.RIGHT | Graphics.BOTTOM);
	}

	public void paintTextMenu(Graphics g, int screen)
	{
		g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
		g.setColor(0xFFFFFF);
		int text_line_y = 10;
		for(int line=0; line < menuScreensTextLines[screen].length; line++)
		{
			// paint all the lines of text in this menu screen
			g.drawString(menuScreensTextLines[screen][line], canvasWidth/2, text_line_y, Graphics.HCENTER | Graphics.TOP);
			
			text_line_y += 15;
		}
		if (screen == MENU_SCR_TEXT_GAME_OVER)
		{
			// print score in the game over screen
			text_line_y += 15;
			g.drawString("Score: "+ player_score, canvasWidth/2, text_line_y, Graphics.HCENTER | Graphics.TOP);
		}

		g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
		// paint softkey_1 label
		g.drawString("Ok", 1, canvasHeight-1, Graphics.LEFT | Graphics.BOTTOM);
	}

	public static void paintSprite(Graphics g, GameSprite spr)
	{
		// paint the sprites image at its coordinates
		g.drawImage(spr.img, spr.x, spr.y, Graphics.TOP | Graphics.LEFT);
	}

	public static void paintStatusBars(Graphics g)
	{
		int max_energy_bar_width = canvasWidth/2;

		// paint the energy bar
		g.setColor(0xFFFFFF);
		g.drawRect(5, 10, max_energy_bar_width + 1, 4);
		g.setColor(0xFFCC00);
		g.fillRect(6, 11, player.energy * max_energy_bar_width / player.energyMax, 3);
		
		// paint the score count
		g.setColor(0xFFFFFF);
		g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL));
		g.drawString(""+player_score, canvasWidth-4, 8, Graphics.TOP | Graphics.RIGHT);
	}

	public void startGame()
	{
		// switch to game mode
		mode = MODE_GAME;

		// initialize player sprite
		player = new GameSprite(imgPlayer, MAX_ENERGY_PLAYER);
		player.x = canvasWidth / 2;
		player.y = canvasHeight - player.h - player.h/2;
		player_score = 0;
		
		// intialize the scrolling background
		space_scroll_y = 0;
		space_scroll_vy = 1;
		
		// initialize the enemy sprites list
		enemySprites = new Vector();
	}
	
	public void endGame()
	{
		// back to menu mode, set the game over text screen
		mode = MODE_MENU;
		menuScreen = MENU_SCR_TEXT_GAME_OVER;
		menuSelectedOption = MENU_OPTION_MAIN_NEW_GAME;

		// clean up
		player = null;
		enemySprites = null;
	}

	public void updateEnemySprites()
	{
		// check if a new enemy has to be generated
		if( enemySprites.size() < max_enemy_sprites )
		{
			GameSprite new_enemy;
			
			// create a new enemy
			new_enemy = new GameSprite(imgEnemy, MAX_ENERGY_ENEMY);

			new_enemy.x = Math.abs(random.nextInt()) % canvasWidth;
			new_enemy.y = -new_enemy.h;
			
			new_enemy.vx = random.nextInt() % 2;
			new_enemy.vy = 3 + random.nextInt() % 2;

			// add the new enemy to the enemy sprites list
			enemySprites.addElement(new_enemy);
		}

		// move enemy sprites
		for (int i=0; i < enemySprites.size(); i++)
		{
			GameSprite enemy = (GameSprite) enemySprites.elementAt(i);

			enemy.x += enemy.vx;
			enemy.y += enemy.vy;
			
			// if enemy is out of screen
			if ( (enemy.x > canvasWidth) || (enemy.x+enemy.w < 0) ||
				 (enemy.y > canvasHeight) || (enemy.y+enemy.h < 0) )
			{
				enemySprites.removeElementAt(i);
			}
		}
	}

	public void updatePlayerSprite()
	{
		// move player sprite on command, and check player <-> screen edge collision
		if (command_up)
		{
			player.y -= player.vy;
			if (player.y < 0)
				player.y = 0;
		}
		if (command_down)
		{	
			player.y += player.vy;
			if (player.y > canvasHeight - player.h)
				player.y = canvasHeight - player.h;
		}
		if (command_left)
		{	
			player.x -= player.vx;
			if (player.x < 0)
				player.x = 0;
		}
		if (command_right)
		{	
			player.x += player.vx;
			if (player.x > canvasWidth - player.w)
				player.x = canvasWidth - player.w;
		}
		if (command_fire)
		{
			if (player_fire == null)
			{
				player_fire = new GameSprite(imgPlayerFire, MAX_ENERGY_PLAYER_FIRE);
				player_fire.x = player.x + player.w/2 - player_fire.w/2;
				player_fire.y = player.y;
				player_fire.vy = -4;
			}
		}
	}

	public void doCollision()
	{
		for (int i=0; i < enemySprites.size(); i++)
		{
			GameSprite enemy = (GameSprite) enemySprites.elementAt(i);
			
			// check player <-> enemy collision
			if (player.collidesWith(enemy))
			{
				// player loses energy
				player.energy -= player.energyMax/4;

				// enemy sprite is destroyed
				enemySprites.removeElementAt(i);
			}

			if (player_fire != null && player_fire.collidesWith(enemy))
			{
				// destroy player fire
				player_fire = null;

				// enemy sprite is destroyed
				enemySprites.removeElementAt(i);
				
				// increase player score
				player_score += 10;
			}
		}
	}

	public void updatePlayerFireSprite()
	{
		if (player_fire != null)
		{
			player_fire.y += player_fire.vy;
			
			// destroy player fire when out of screen
			if (player_fire.y + player_fire.h < 0)
				player_fire = null;
		}
	}

}

