package net.woodyfolsom.msproj.gui;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.net.URL;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import javax.swing.ImageIcon;
import javax.swing.JPanel;

import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameBoard;
import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.sfx.SfxPlayer;

public class GridPanel extends JPanel implements MouseListener,
		MouseMotionListener {

	private static final long serialVersionUID = 1L;

	private final int boardPixelWidth;
	
	private static final int BORDER_WIDTH = 10;

	private static final int DOT_WIDTH = 5;
	private static final int STONE_WIDTH = DOT_WIDTH * 2;

	private Image backgroundImg;
	private int boardSize;

	private int mouseX = 0;
	private int mouseY = 0;
	
	private GameState gameState;
	private LinkedBlockingQueue<Action> actionQueue = new LinkedBlockingQueue<Action>();

	private Player guiPlayer;

	private SfxPlayer sfxPlayer;
	private String[] COLS = { "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T" };
	private String[] ROWS;

	public GridPanel(GameConfig gameConfig, Player guiPlayer, SfxPlayer sfxPlayer) {
		this.guiPlayer = guiPlayer;
		this.sfxPlayer = sfxPlayer;
		this.boardSize = gameConfig.getSize();

		initRows(this.boardSize);

		if (boardSize <= 9) {
			boardPixelWidth = 300;			
		} else if (boardSize <= 13) {
			boardPixelWidth = 434;
		} else {
			boardPixelWidth = 634;
		}
		this.setPreferredSize(new Dimension(boardPixelWidth + BORDER_WIDTH * 2,
				boardPixelWidth + BORDER_WIDTH * 2));
		URL urlBackgroundImg = getClass().getResource("wood3.jpg");
		this.backgroundImg = new ImageIcon(urlBackgroundImg).getImage();
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
	}

	public void addAction(Action action) {
		actionQueue.add(action);
	}
	
	public Action getAction() {
		int timeLimit = 30;
		TimeUnit timeUnit = TimeUnit.SECONDS;
		
		try {
			Action action = actionQueue.poll(timeLimit, timeUnit);
			if (action == null) {
				return Action.NONE;
			}
			return action;
		} catch (InterruptedException ie) {
			System.out.println("Interrupted while waiting " + timeLimit + " " + timeUnit + "s for player to move.");
			return Action.NONE;
		}
	}

	private void initRows(int boardSize) {
		ROWS = new String[boardSize];
		for (int i = 0; i < boardSize; i++) {
			ROWS[i] = Integer.valueOf(boardSize - i).toString();
		}
	}

	// Given a pair of (x,y) coordinates (pixels), returns a row index in the
	// range [0..(boardSize-1)].
	private int getColumn(int x, int y) {
		int intersectionSize = boardPixelWidth / boardSize;

		return (x - BORDER_WIDTH/*- centerOffset*/) / intersectionSize;
	}

	// Given a pair of (x,y) coordinates (pixels), returns a row index in the
	// range [0..(boardSize-1)].
	private int getRow(int x, int y) {
		int intersectionSize = boardPixelWidth / boardSize;

		return (y - BORDER_WIDTH/*- centerOffset*/) / intersectionSize;
	}

	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);

		// tile wood texture
		int iw = backgroundImg.getWidth(this);
		int ih = backgroundImg.getHeight(this);
		if (iw > 0 && ih > 0) {
			for (int x = 0; x < getWidth(); x += iw) {
				for (int y = 0; y < getHeight(); y += ih) {
					g.drawImage(backgroundImg, x, y, iw, ih, this);
				}
			}
		}

		int intersectionSize = boardPixelWidth / boardSize;
		int centerOffset = intersectionSize / 2;

		g.translate(BORDER_WIDTH, BORDER_WIDTH);

		g.setColor(Color.BLACK);
		
		for (int row = 0; row < boardSize; row++) {
			g.drawLine(centerOffset, centerOffset + row * intersectionSize,
					intersectionSize * boardSize - centerOffset, centerOffset
							+ row * intersectionSize);
		}

		for (int col = 0; col < boardSize; col++) {
			g.drawLine(centerOffset + col * intersectionSize, centerOffset,
					centerOffset + col * intersectionSize, intersectionSize
							* boardSize - centerOffset);
		}

		int mouseCol = getColumn(mouseX, mouseY);
		int mouseRow = getRow(mouseX, mouseY);

		for (int col = 0; col < boardSize; col++) {
			g.drawBytes(COLS[col].getBytes(), 0, 1, centerOffset + col
					* intersectionSize - 5, boardPixelWidth + 5);
		}

		for (int row = 0; row < boardSize; row++) {
			byte[] bytes = ROWS[row].getBytes();
			g.drawBytes(bytes, 0, bytes.length, boardPixelWidth - 5,
					centerOffset + row * intersectionSize + 5);
		}

		if (gameState != null) {
			GameBoard gameBoard = gameState.getGameBoard();
			
			for (int row = 0; row < boardSize; row++) {
				for (int col = 0; col < boardSize; col++) {
					
					char symbol = gameBoard.getSymbolAt(col,boardSize - row - 1);
					
					switch (symbol) {
						case GameBoard.BLACK_STONE :
							g.setColor(Color.BLACK);
							g.fillOval(intersectionSize * col + centerOffset - STONE_WIDTH,
									intersectionSize * row + centerOffset - STONE_WIDTH,
									STONE_WIDTH * 2, STONE_WIDTH * 2);
							break;
						case GameBoard.WHITE_STONE :
							g.setColor(Color.WHITE);
							g.fillOval(intersectionSize * col + centerOffset - STONE_WIDTH,
									intersectionSize * row + centerOffset - STONE_WIDTH,
									STONE_WIDTH * 2, STONE_WIDTH * 2);
							break;
						case GameBoard.EMPTY_INTERSECTION :
							if (row == mouseRow && col == mouseCol && gameState.getPlayerToMove() == guiPlayer) {
								if (guiPlayer == Player.BLACK) {
									g.setColor(Color.BLACK);
								} else if (guiPlayer == Player.WHITE) {
									g.setColor(Color.WHITE);
								} else {
									throw new RuntimeException("Invalid guiPlayer: " + guiPlayer);
								}
								g.fillOval(intersectionSize * col + centerOffset - STONE_WIDTH,
										intersectionSize * row + centerOffset - STONE_WIDTH,
										STONE_WIDTH * 2, STONE_WIDTH * 2);
								g.setColor(Color.RED);
								g.drawOval(intersectionSize * col + centerOffset - STONE_WIDTH,
										intersectionSize * row + centerOffset - STONE_WIDTH,
										STONE_WIDTH * 2, STONE_WIDTH * 2);								
							} else {
								g.setColor(Color.BLACK);
								g.fillOval(intersectionSize * col + centerOffset - DOT_WIDTH,
										intersectionSize * row + centerOffset - DOT_WIDTH,
										DOT_WIDTH * 2, DOT_WIDTH * 2);
							}
							break;
						default :
							continue;
					}
				}
			}
		}
	}

	public void setGameState(GameState gameState) {
		this.gameState = gameState;
		
		if (gameState.getPlayerToMove() == guiPlayer || gameState.isTerminal()) {
			setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		} else {
			setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		}
		
		this.repaint();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				sfxPlayer.play();
			}}).start();
	}

	@Override
	public void mouseDragged(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseMoved(MouseEvent arg0) {
		this.mouseX = arg0.getX();
		this.mouseY = arg0.getY();

		this.repaint();
	}

	@Override
	public void mouseClicked(MouseEvent arg0) {
		if (gameState == null) {
			System.out.println("Wait your turn!");
			return;
		}
		
		if (!actionQueue.isEmpty()) {
			System.out.println("You already moved.  Be patient!");
			return;
		}
		// ystem.out.println("Mouse clicked: " + arg0);
		int row = getRow(mouseX, mouseY);
		int column = getColumn(mouseX, mouseY);

		if (row < 0 || row >= boardSize) {
			return;
		}
		
		if (column < 0 || column >= boardSize) {
			return;
		}
		
		Player currentPlayer = gameState.getPlayerToMove();
		if (currentPlayer == guiPlayer) {
			// gameState.playStone(guiPlayer, Action.getInstance(COLS[row] +
			// ROWS[column]));
			Action action = Action.getInstance(COLS[column] + ROWS[row]);
			System.out.println("Made move: " + action);
			actionQueue.add(action);
		} else {
			System.out.println("Not your turn!");
		}
		
		this.repaint(); 
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

}