/***
 * 
 * Copyright (C) 2008 Alessandro La Rosa
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Contact: alessandro.larosa@gmail.com
 *
 * Author: Alessandro La Rosa
 */

package com.jappit.japdarts.display;

import java.util.Random;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

import com.jappit.japdarts.app.JapDarts;
import com.jappit.japdarts.utils.ScoreManager;

public class DartScreen extends Canvas implements Runnable
{
	boolean changed = true;
	
	final static int MILLIS_PER_TICK = 50; 
	
	Font font = null;
	
	int doneDarts = 0;
	int totalDarts = 5;
	int[][] darts = null;
	
	int circles = 8;
	int[] circlePoints = new int[]{10, 8, 6, 5, 4, 3, 2, 1};
	public int points = 0;
	boolean scoreChanged = true;
	
	int[] circleColors = new int[]{0xffffff, 0x000000};
	
	int maxPointerShift = 0;
	int pointerMove = 0;
	
	int dartColor = 0xff0000;
	
	int bgColor = 0x009900;
	
	int circleWidth = 0;
	int padding = 10;
	
	int centerX = 0;
	int centerY = 0;
	
	int pointerX = 0;
	int pointerY = 0;
	
	int gameWidth = 0;
	int gameHeight = 0;
	int boardWidth = 0;
	int boardHeight = 0;
	int footerHeight = 0;
	
	int slidePad = 4;
	int slideWidth = 16;
	int slideX = 0;
	int slideY = 0;
	int slideMove = 20;
	int slideDir = 1;
	
	int STATE_MOVING = 0;
	int STATE_XSLIDE = 1;
	int STATE_YSLIDE = 2;
	int STATE_LAUNCHING = 3;
	int STATE_WAITING = 4;
	
	int targetX = 0;
	int targetY = 0;
	
	int dartX = 0;
	int dartY = 0;
	int dartMove = 0;
	int dartStartX = 0;
	int dartStartY = 0;
	int dartSteps = 10;
	int dartCurrentStep = 0;
	int[] dartYDeltas = new int[]{2, 4, 6, 8, 10, 8, 6, 4, 2, 0};
	
	int state = STATE_MOVING;
	
	Random rand = null;
	
	int currentKey = Integer.MIN_VALUE;
	
	public DartScreen()
	{	
		font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
		
		darts = new int[][]{
			new int[]{0, 0},new int[]{0, 0},new int[]{0, 0},new int[]{0, 0},new int[]{0, 0}
		};
		
		rand = new Random();
		
		footerHeight = font.getHeight() + 2;
		
		initializeSizes();
		
		new Thread(this).start();
	}
	public void newGame()
	{
		initializePositions();
		
		doneDarts = 0;
		points = 0;
		
		scoreChanged = true;
		
		state = STATE_MOVING;
	}
	
	void initializeSizes()
	{
		gameWidth = getWidth();
		gameHeight = getHeight() - footerHeight;
		
		boardWidth = gameWidth - slideWidth;
		boardHeight = gameHeight - slideWidth;
		
		circleWidth = (Math.min(boardWidth, boardHeight) - 2 * padding) / circles;
		
		centerX = boardWidth / 2;
		centerY = boardHeight / 2;
		
		pointerX = centerX;
		pointerY = centerY;
		
		maxPointerShift =  circleWidth / 3;
		pointerMove = circleWidth / 4;
		
		dartStartX = 0;
		dartStartY = boardHeight;
		initializePositions();
	}
	protected void sizeChanged(int w, int h)
	{
		initializeSizes();
	}
	void initializePositions()
	{
		dartX = dartStartX;
		dartY = dartStartY;
		
		pointerX = nextInt(boardWidth);
		pointerY = nextInt(boardHeight);
		
		slideX = 0;
		slideY = 0;
		
		currentKey = Integer.MIN_VALUE;
	}
	

	protected void paint(Graphics g)
	{
		if(state != STATE_XSLIDE && state != STATE_YSLIDE)
		{
			g.setColor(bgColor);
			
			g.fillRect(0, 0, gameWidth, gameHeight);
			
			for(int i = 0; i < circles; i++)
			{
				g.setColor(circleColors[i % 2]);
				
				int radius = (circles - i) * circleWidth/ 2;
				
				g.fillArc(centerX - radius, centerY - radius, radius * 2, radius * 2, 0, 360);
			}
			paintDoneDarts(g);
		}
		
		paintSlides(g);
		
		if(state == STATE_LAUNCHING)
		{
			paintDart(g);
		}
		else if(state != STATE_WAITING)
		{
			paintPointer(g);
		}
		if(scoreChanged)
		{
			g.translate(0, gameHeight);
			
			paintFooter(g);
			
			g.translate(0, - gameHeight);
		}
	}
	void paintSlides(Graphics g)
	{
		g.setColor(0x000000);
		
		int pointRadius = slideWidth / 2 - 1;
		
		g.fillRoundRect(gameWidth - slideWidth - slidePad / 2, slidePad, 
			slideWidth, gameHeight - 2 * slidePad - slideWidth, 
			slideWidth / 2, slideWidth / 2);
		
		g.fillRoundRect(slidePad, gameHeight - slideWidth - slidePad / 2,  
			gameWidth - 2 * slidePad - slideWidth, slideWidth,  
			slideWidth / 2, slideWidth / 2);
		
		g.setColor(0xff0000);
		
		int y = pointRadius + slidePad + (100 + slideY) * (gameHeight - 2 * slidePad - slideWidth - 2 * pointRadius) / (2 * 100);
		int x = pointRadius + slidePad + (100 + slideX) * (gameWidth - 2 * slidePad - slideWidth - 2 * pointRadius) / (2 * 100);
		
		g.fillArc(gameWidth - slideWidth / 2 - pointRadius - slidePad / 2, y - pointRadius, 
			pointRadius * 2, pointRadius * 2, 
			0, 360);
		g.fillArc(x - pointRadius, gameHeight - slideWidth / 2 - pointRadius - slidePad / 2, 
			pointRadius * 2, pointRadius * 2, 
			0, 360);
	}
	void paintFooter(Graphics g)
	{
		scoreChanged = false;
		
		g.setColor(bgColor);
		
		g.fillRect(0, 0, gameWidth, footerHeight);
		
		g.setColor(0xffffff);
		
		g.setFont(font);
		
		g.drawString("pts " + points, 1, footerHeight - 1, Graphics.LEFT | Graphics.BOTTOM);
		
		g.drawString((totalDarts - doneDarts) + " darts", gameWidth - 1, footerHeight - 1, Graphics.RIGHT | Graphics.BOTTOM);
	}
	void paintPointer(Graphics g)
	{
		g.setColor(dartColor);
		
		int dartSize = 4;
		
		g.drawLine(pointerX - dartSize, pointerY, pointerX + dartSize, pointerY);
		
		g.drawLine(pointerX, pointerY - dartSize, pointerX, pointerY + dartSize);
		
		//g.fillArc(pointerX - dartRadius, pointerY - dartRadius, dartRadius * 2, dartRadius * 2, 0, 360);
	}
	void paintDart(Graphics g)
	{
		g.setColor(dartColor);
		
		int diff = targetX - dartX;
		int size = diff > (targetX - dartX) / 2 ? 10 : 20;
		
		g.drawLine(dartX - size, dartY - size, dartX + size, dartY + size);
		g.drawLine(dartX - size, dartY + size, dartX + size, dartY - size);
		
		g.drawArc(dartX - size / 2, dartY - size / 2, size, size, 0, 360);		
	}
	void paintDoneDarts(Graphics g)
	{
		int dartSize = 5;
		
		g.setColor(dartColor);
		
		for(int i = 0; i < doneDarts; i++)
		{
			g.drawLine(darts[i][0] - dartSize, darts[i][1] - dartSize, darts[i][0] + dartSize, darts[i][1] + dartSize);
			
			g.drawLine(darts[i][0] + dartSize, darts[i][1] - dartSize, darts[i][0] - dartSize, darts[i][1] + dartSize);
		}
	}
	void movePointer()
	{
		pointerX += nextInt(2 * maxPointerShift) - maxPointerShift;
		pointerY += nextInt(2 * maxPointerShift) - maxPointerShift;
		
		pointerX = Math.max(0, Math.min(boardWidth, pointerX));
		pointerY = Math.max(0, Math.min(boardHeight, pointerY));
	}
	
	protected void keyPressed(int key)
	{
		currentKey = getGameAction(key);
		
		if(currentKey == Canvas.FIRE)
		{
			if(state == STATE_MOVING)
			{
				state = STATE_XSLIDE;
			}
			else if(state == STATE_XSLIDE)
			{
				state = STATE_YSLIDE;
			}
			else if(state == STATE_YSLIDE)
			{
				setTarget();
				
				state = STATE_LAUNCHING;				
			}
		}
		else if(isRightSoftKey(key))
		{
			JapDarts.instance.setScreen(new MainMenu());
		}
	}
	protected static boolean isRightSoftKey(int key)
	{
		return key == -22 || key == -7 || key == Canvas.KEY_POUND || key == 57346;
	}
	void setTarget()
	{
		targetX = pointerX + boardWidth * slideX / (2 * 100);
		targetY = pointerY + boardHeight * slideY / (2 * 100);
		
		dartMove = Math.max(1, targetX / 10);
		dartCurrentStep = 0;
		
		darts[doneDarts][0] = targetX;
		darts[doneDarts][1] = targetY;
	}
	protected void keyReleased(int key)
	{
		currentKey = Integer.MIN_VALUE;
	}
	void evalKey()
	{
		switch(currentKey)
		{
		case Canvas.RIGHT: pointerX += pointerMove; break;
		case Canvas.LEFT: pointerX -= pointerMove; break;
		case Canvas.UP: pointerY -= pointerMove; break;
		case Canvas.DOWN: pointerY += pointerMove; break;
		}
	}
	void moveXSlide()
	{
		slideX += slideDir * slideMove;
		
		if(slideX >= 100 || slideX <= -100)
		{
			slideDir *= -1;
		}
	}
	void moveYSlide()
	{
		slideY += slideDir * slideMove;
		
		if(slideY >= 100 || slideY <= -100)
		{
			slideDir *= -1;
		}
	}
	void moveDart()
	{
		dartX += dartMove;
		
		dartCurrentStep++;
		
		if(dartCurrentStep == dartSteps)
		{
			dartX = targetX;
			dartY = targetY;
		}
		else
		{
			dartY = dartStartY + dartX * (targetY - dartStartY) / targetX;
		}
		
	}
	void addPoints()
	{
		int diff = Math.max(Math.abs(targetX - centerX), Math.abs(targetY - centerY));
		
		int scoreIndex = 2 * diff / circleWidth;
		
		if(scoreIndex < circles)
		{
			points += circlePoints[scoreIndex];
		}
		scoreChanged = true;
	}
	void checkDartEnd()
	{
		if(dartX == targetX && dartY == targetY)
		{
			addPoints();
			
			state = STATE_MOVING;
			
			initializePositions();
			
			doneDarts++;
			
			if(doneDarts >= totalDarts)
			{
				state = STATE_WAITING;

				if(ScoreManager.getInstance().isRecord(points))
				{
					JapDarts.instance.setScreen(new NewRecordScreen(points));
				}
				else
				{
					JapDarts.instance.setScreen(new BestScoresScreen());
				}
			}
		}
	}
	int nextInt(int max)
	{
		int n = rand.nextInt();
		
		return n < 0 ? (- n) % max : n % max;
	}
	public void beforePaint()
	{
		if(state == STATE_MOVING)
		{
			evalKey();
			
			movePointer();
		}
		else if(state == STATE_XSLIDE)
		{
			moveXSlide();
		}
		else if(state == STATE_YSLIDE)
		{
			moveYSlide();
		}
		else if(state == STATE_LAUNCHING)
		{
			moveDart();
		}
		if(state != STATE_WAITING)
		{
			notifyChange();
		}
	}
	
	public void afterPaint()
	{
		if(state == STATE_LAUNCHING)
		{
			checkDartEnd();
		}
	}
	protected void onScreenShow()
	{
		newGame();
	}
	void notifyChange()
	{
		changed = true;
	}
	public void run()
	{
		while(true)
		{
			try
			{
				long startTime = System.currentTimeMillis();
				
				beforePaint();
				
				if(changed)
				{
					repaint();
				}
				afterPaint();
				
				long timeTaken = System.currentTimeMillis() - startTime;
				
				if( timeTaken < MILLIS_PER_TICK )
				{
					synchronized ( this )
					{
						wait(MILLIS_PER_TICK - timeTaken);
					}
				}
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}
	}
}
