import javax.microedition.lcdui.*;

/**
 * <p>Title: FishCanvas</p>
 * <p>Description: Chapter 5 example of interactive animation</p>
 * <p>Copyright: Copyright (c) 2004</p>
 * @tpark
 * @version 1.0
 */
public class FishCanvas extends Canvas implements Runnable {

	//--------------------------------------------------------------------------
	// Capture the key states
	//--------------------------------------------------------------------------
	//Bitmask for directional keys
	private static final int KEYMASK_UP    = 1 << Canvas.UP;
	private static final int KEYMASK_DOWN  = 1 << Canvas.DOWN;
	private static final int KEYMASK_LEFT  = 1 << Canvas.LEFT;
	private static final int KEYMASK_RIGHT = 1 << Canvas.RIGHT;

	private int m_keyStates;

	protected final void keyPressed(int keycode) {
		switch(getGameAction(keycode)) {
			case UP:    m_keyStates |= KEYMASK_UP; break;
			case DOWN:  m_keyStates |= KEYMASK_DOWN; break;
			case LEFT:  m_keyStates |= KEYMASK_LEFT; break;
			case RIGHT: m_keyStates |= KEYMASK_RIGHT; break;
			default: break;
		}
	}
	protected final void keyReleased(int keycode) {
		switch(getGameAction(keycode)) {
			case UP:    m_keyStates &= ~KEYMASK_UP; break;
			case DOWN:  m_keyStates &= ~KEYMASK_DOWN; break;
			case LEFT:  m_keyStates &= ~KEYMASK_LEFT; break;
			case RIGHT: m_keyStates &= ~KEYMASK_RIGHT; break;
			default: break;
		}
	}

	private static final int MILLISECS_PER_FRAME = 70;
	private Thread m_thread;
	private long m_startTime;

	public boolean isStopped;
	public boolean isPaused;

	public int m_width, m_height;
	private Fish m_fish;

	private static final int WALL_THICKNESS = 6;
	private static final int WALL_TOP=0, WALL_BOTTOM=1, WALL_LEFT=2, WALL_RIGHT=3;
	private static final int WALL_COUNT=4;
	private static Obstacle[] m_walls = new Obstacle[WALL_COUNT];

	//Initialization
	public FishCanvas() {
		m_width = getWidth();
		m_height = getHeight();

		m_fish = new Fish(m_width/2, m_height/2);

		m_walls[WALL_TOP] = new Obstacle(0, 0, m_width, 0);
		m_walls[WALL_BOTTOM] = new Obstacle(0, m_height, m_width, m_height);
		m_walls[WALL_LEFT] = new Obstacle(0, 0, WALL_THICKNESS, m_height);
		m_walls[WALL_RIGHT] = new Obstacle(m_width-WALL_THICKNESS, 0, m_width, m_height);
	}

	public void start() {
		setCommandListener (FishMidlet.getMidlet());

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

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

	private void readInput() {
		if ((m_keyStates & KEYMASK_UP) != 0) {
			m_fish.swim(Fish.UP);
		}
		else if ((m_keyStates & KEYMASK_DOWN) != 0) {
			m_fish.swim(Fish.DOWN);
		}

		if ((m_keyStates & KEYMASK_LEFT) != 0) {
			m_fish.swim(Fish.LEFT);
		}
		else if ((m_keyStates & KEYMASK_RIGHT) != 0) {
			m_fish.swim(Fish.RIGHT);
		}
	}

	private static final boolean detectRectangleCollision(
										int rect0_lft, int rect0_top,
										int rect0_rgt, int rect0_bot,
										int rect1_lft, int rect1_top,
										int rect1_rgt, int rect1_bot) {
		if (rect0_lft > rect1_rgt ||
			rect0_top > rect1_bot ||
			rect0_rgt < rect1_lft ||
			rect0_bot < rect1_top) {
			return false;
		}
		return true;
	}

	private void simulateWorld() {
		//get the new coordinates of the fish
		int fish_left = m_fish.m_x + m_fish.m_dx;
		int fish_top  = m_fish.m_y + m_fish.m_dy;
		int fish_right  = fish_left + m_fish.m_width;
		int fish_bottom = fish_top + m_fish.m_height;

		//check for collision with walls
		for (int idx=WALL_COUNT-1; idx>=0; --idx) {
			//not overlapping?
			if (!detectRectangleCollision(
					fish_left, fish_top,
					fish_right, fish_bottom,
					m_walls[idx].m_x, m_walls[idx].m_y,
					m_walls[idx].m_x + m_walls[idx].m_width,
					m_walls[idx].m_y + m_walls[idx].m_height)) {
				continue;
			}
			//fish is moving out of bounds so block this move
			switch(idx) {
				case WALL_LEFT:
				case WALL_RIGHT:
					m_fish.m_dx = 0;
					break;
				case WALL_TOP:
				case WALL_BOTTOM:
					m_fish.m_dy = 0;
					break;
			}
		}
		m_fish.finishMove();
	}

	private void draw() {
		repaint();
		serviceRepaints();
	}

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

	private void paintBackground(Graphics g) {
		g.setColor(0x0000ff);
		g.fillRect(0,  0, getWidth(), getHeight());
	}
	protected void paint (Graphics g) {
		paintBackground(g);
		m_fish.paint(g);
	}
}
