import javax.microedition.lcdui.game.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class FishGameCanvas extends GameCanvas implements Runnable {

	public static final Random RANDOM = new Random();
	private static final int MILLISECS_PER_FRAME = 70;
	private Thread m_thread;
	private long m_startTime;

	public boolean m_isStopped;
	public boolean m_isPaused;

	public int m_width, m_height;
	private Graphics m_gOffscreen;

	private static final int MANTA_BUMP = 2;
	private static final int MANTA_MARGIN = 20;
	private static final int MAX_STARS = 6;
	private Vector m_stars = new Vector(MAX_STARS);
	private FishSprite m_fish;
	private Manta m_manta;
	private Background m_background;
	private LayerManager m_layerManager;

	//Initialization
	public FishGameCanvas() {
		//Suppress events for game action keys
		super(true);

		setFullScreenMode(true);

		m_width = getWidth();
		m_height = getHeight();
		m_gOffscreen = getGraphics();

		try {
			m_fish = new FishSprite(m_width, m_height);
			m_manta = new Manta(m_width);
			m_background = new Background();
			m_layerManager = new LayerManager();
		}
		catch (Exception e) {
			System.err.println("ERROR creating game object.");
		}
	}

	public boolean start() {
		if (m_fish == null ||
			m_manta == null ||
			m_background == null ||
			m_layerManager == null) {
			return false;
		}
		setCommandListener (FishMidlet.m_midlet);

		m_layerManager.append(m_fish);
		m_layerManager.append(m_manta);
		m_layerManager.append(m_background);

		//launch the game loop thread
		m_thread = new Thread(this);
		m_thread.start();
		m_startTime = System.currentTimeMillis();
		return true;
	}
	public void stop() {
		m_isStopped = true;
		m_thread = null;
	}
	public void exit() {
		stop();
	}

	//Runnable - the main game loop
	public void run () {
		while (!m_isStopped) {
			if (!m_isPaused) {
				readInput();
				simulateWorld();
				draw();
			}
			delayUntilNextFrame();
		}
	}

	private void readInput() {
		int keyStates = getKeyStates();
		if ((keyStates & UP_PRESSED) != 0) {
			m_fish.swim(FishSprite.UP);
		}
		else if ((keyStates & DOWN_PRESSED) != 0) {
			m_fish.swim(FishSprite.DOWN);
		}

		if ((keyStates & LEFT_PRESSED) != 0) {
			m_fish.swim(FishSprite.LEFT);
		}
		else if ((keyStates & RIGHT_PRESSED) != 0) {
			m_fish.swim(FishSprite.RIGHT);
		}
	}

	private void simulateWorld() {
		m_background.animate();
		m_fish.move();

		//add starfish
		Star star;
		if ((RANDOM.nextInt() % 20 == 0) &&
			(m_stars.size() < MAX_STARS)) {
			star = new Star(RANDOM, m_width, 0);
			m_stars.addElement(star);
			m_layerManager.insert(star,0);
		}

		//add manta ray
		if (!m_manta.isVisible() && RANDOM.nextInt() % 50 == 0) {
			m_manta.setVisible(true);

			int dir, x, y;
			if (RANDOM.nextInt() > 0 &&
				m_fish.getX() > m_width/8) {
				dir = Manta.RIGHT;
				x = -m_manta.getWidth()/2;
			}
			else {
				dir = Manta.LEFT;
				x = m_width - m_manta.getWidth()/2;
			}
			y = MANTA_MARGIN +
				(Math.abs(RANDOM.nextInt()) %
				 (m_height - m_manta.getHeight() - 2*MANTA_MARGIN));
			m_manta.init(x, y, dir);
		}

		//check for collision with manta ray
		if (m_manta.isVisible()) {
			m_manta.swim();
			if (m_fish.collidesWith(m_manta, false)) {
				int x = m_fish.getX();
				int y = m_fish.getY();
				//bump fish if it's near front of manta
				if (m_manta.m_direction == Manta.LEFT) {
					if (x < m_manta.getX() + m_fish.getWidth()) {
						x = m_manta.getX() - m_fish.getWidth();
					}
				}
				else {
					int mantaXR = m_manta.getX() + m_manta.getWidth();
					if (x > mantaXR - m_fish.getWidth()) {
						x = mantaXR;
					}
				}
				if (m_fish.getRefPixelY() < m_manta.getRefPixelY()) {
					y -= m_fish.getHeight();
				}
				else {
					y -= m_manta.getHeight();
				}
				m_fish.setPosition(x, y);
			}
		}

		//check for collision with falling stars
		for (int idx=m_stars.size()-1; idx>=0; --idx) {
			star = (Star) m_stars.elementAt(idx);
			star.fall();
			if (m_fish.collidesWith(star, false) ||
				m_manta.collidesWith(star, false) ||
				star.getY() > m_height) {

				m_stars.removeElement(star);
				m_layerManager.remove(star);
			}
		}
	}

	private void eraseBackground(Graphics g) {
		g.setColor(0x0000ff);
		g.fillRect(0,  0, getWidth(), getHeight());
	}

//	private void paintStars(Graphics g) {
//		Star star;
//		for (int idx=m_stars.size()-1; idx>=0; --idx) {
//			star = (Star) m_stars.elementAt(idx);
//			star.paint(g);
//		}
//	}

	private void draw() {
		eraseBackground(m_gOffscreen);
		m_layerManager.paint(m_gOffscreen,0,0);
//		m_background.paint(m_gOffscreen);
//		m_manta.paint(m_gOffscreen);
//		m_fish.paint(m_gOffscreen);
//		paintStars(m_gOffscreen);
		flushGraphics();
	}

	private void delayUntilNextFrame() {
		long elapsed = m_startTime;
		m_startTime = System.currentTimeMillis();
		elapsed = m_startTime - elapsed;
		if (elapsed < MILLISECS_PER_FRAME) {
			try {
				m_thread.sleep(MILLISECS_PER_FRAME - elapsed);
			}
			catch (Exception e) {
			}
		}
		else {
			//We consumed all our allotted time so don’t sleep, but before
			//exiting, yield to allow another thread a chance to process.
			m_thread.yield();
		}
	}
}
