import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

//This is the GUI for our 3D Tic Tac Toe board.  It will include the board and also
//a window where the playes can be set and, if a computer player is picked, which one
//will be playing.
public class gui
{
	private static boolean xPlaying = true;
	private static player player1 = new humanPlayer();
 	private static player player2 = new humanPlayer();
	private static Square[][][] buttons;
	private static JRadioButton[] player1Buttons;
	private static JRadioButton[] player2Buttons;
	private static ButtonGroup group1;
	private static ButtonGroup group2;
	private static JFrame frame;
	private static JFrame playerFrame;
	private static boolean playing = true;
	private static int[] humanMove;
	private static int numMoves = 0;

	public static void main(String[] args)
	{
		//Makes a new JFrame to display the board and set the objects
		frame = new JFrame("Tic Tac Toe God vs. You");
		buttons = new Square[5][5][5];
		gui.drawObjects(frame);
		gui.makeCloseable(frame);
		frame.pack();
		frame.setVisible(true);

		//Makes a new JFrame to choose between human & computer players
		playerFrame = new JFrame("Choose your opponent");
		gui.drawObjects2(playerFrame);
		playerFrame.pack();
		playerFrame.setVisible(true);

		setUpHumanMove();
	}

	//In this function the squares are drawn and the action listeners are set
	private static void drawObjects(JFrame frame)
	{
		//Setting up the board and and creating the buttons for the board.
		JPanel panel = new JPanel();

        panel.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
		panel.setLayout(new GridLayout(25, 25));

		//Creates the buttons in the 5 x 5 x 5 grid
		//3D grid is simulated using 5 grids of size 5 x 5 staggereda diagonally.
		//i denotes what "layer" we are in
		//j denotes the row
		//k denotes the column
		for(int i = 0; i < 5; i++)
			for(int j = 0; j<5; j++)
			{
				//Because we are simulating layers by staggering them
				//We need to create the "empty space" around the layers.
				//To do this we are creating JLables with no action listeners.

				//Creates empty space on the left.
				for(int blank = 0; blank < 5 * i; blank++)
				{
					JLabel filler = new JLabel();
					panel.add(filler);
				}

				//Put in the actual squares to click on.
				for (int k = 0; k < 5; k++)
				{
					buttons[j][k][i] = new Square(j, k, i);
					gui.setUpListener(buttons[j][k][i]);
					panel.add(buttons[j][k][i]);
				}

				//Creates empty space on the right.
				for(int blank = 5*(i + 1); blank < 25; blank++)
				{
					JLabel filler = new JLabel();
					panel.add(filler);
				}
			}
		
		frame.getContentPane().add(panel, BorderLayout.CENTER);
	}

	//In this function the player selection is drawn and the action listeners are set
	private static void drawObjects2(JFrame playerFrame)
	{
		//Setting up the board and and creating the buttons for the board.
		player1Buttons = new JRadioButton[4];
		player2Buttons = new JRadioButton[4];
		JPanel playerPanel = new JPanel();

        playerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
		playerPanel.setLayout(new GridLayout(3, 5));

		JLabel p1 = new JLabel("Player 1");
		playerPanel.add(p1);
		
		//Creates the buttons in the 3 x 5 grid
		player1Buttons[0] = new JRadioButton("Human", true);
		gui.setUpListener2(player1Buttons[0],1);
		playerPanel.add(player1Buttons[0]);

		player1Buttons[1] = new JRadioButton("Dumb Computer");
		gui.setUpListener2(player1Buttons[1],2);
		playerPanel.add(player1Buttons[1]);

		player1Buttons[2] = new JRadioButton("Two Layer");
		gui.setUpListener2(player1Buttons[2],3);
		playerPanel.add(player1Buttons[2]);
		
		player1Buttons[3] = new JRadioButton("Three Layer");
		gui.setUpListener2(player1Buttons[3],7);
		playerPanel.add(player1Buttons[3]);
		
		JLabel p2 = new JLabel("Player 2");
		playerPanel.add(p2);

		player2Buttons[0] = new JRadioButton("Human", true);
		gui.setUpListener2(player2Buttons[0],4);
		playerPanel.add(player2Buttons[0]);

		player2Buttons[1] = new JRadioButton("Dumb Computer");
		gui.setUpListener2(player2Buttons[1],5);
		playerPanel.add(player2Buttons[1]);	

		player2Buttons[2] = new JRadioButton("Two Layer");
		gui.setUpListener2(player2Buttons[2],6);
		playerPanel.add(player2Buttons[2]);
		
		player2Buttons[3] = new JRadioButton("Three Layer");
		gui.setUpListener2(player2Buttons[3],8);
		playerPanel.add(player2Buttons[3]);

		//Add buttons to either player1 or player2 group so only 1 can be selected
		group1 = new ButtonGroup();
		group1.add(player1Buttons[0]);
		group1.add(player1Buttons[1]);
		group1.add(player1Buttons[2]);
		group1.add(player1Buttons[3]);

		group2 = new ButtonGroup();
		group2.add(player2Buttons[0]);
		group2.add(player2Buttons[1]);
		group2.add(player2Buttons[2]);
		group2.add(player2Buttons[3]);

    	JButton resetBoard = new JButton("Reset");
    	gui.setUpListener3(resetBoard);
		playerPanel.add(resetBoard);
		
    	JLabel filler = new JLabel();
    	playerPanel.add(filler);
    	
		JLabel filler2 = new JLabel();
    	playerPanel.add(filler2);
    	
    	JLabel filler3 = new JLabel();
    	playerPanel.add(filler3);
		
		playerFrame.getContentPane().add(playerPanel, BorderLayout.CENTER);
	}


	//Add the action listener that will put the correct mark on the square and also
	//set it to the other players turn.
	private static void setUpListener(Square button)
	{
		final Square holder = button;
		button.addActionListener(new ActionListener() {
		public void actionPerformed(ActionEvent e) {
				if (playing)
				{
					boolean markMade;
					if (xPlaying)
						markMade = holder.setMark('x');
					else
						markMade = holder.setMark('o');
					if(markMade)
					{
						numMoves++;
						checkForVictory(holder);
						xPlaying = !xPlaying;
					}

				if(xPlaying)
					implementMove(player1.getMove(getBoard('x'), getBoard('o')));
				else
					implementMove(player2.getMove(getBoard('o'), getBoard('x')));

				}
			}
		});
	}

	//Add the action listener that will put the correct mark on player choice.
	private static void setUpListener2(JRadioButton playerButton, int who)
	{	
		final int person  = who;
		playerButton.addActionListener(new ActionListener() {
		public void actionPerformed(ActionEvent e) {
				switch(person)
				{
					case 1:		player1 = new humanPlayer();
								break;
					case 2:		player1 = new randomPlayer();
								break;
					case 3:		player1 = new geneticPlayer();
								break;
					case 4:		player2 = new humanPlayer();
								break;
					case 5:		player2 = new randomPlayer();
								break;
					case 6:		player2 = new geneticPlayer();
								break;
					case 7:		player1 = new annPlayer();
								break;
					case 8:		player2 = new annPlayer();
								break;
				}
				if(playing)
				{
					if(xPlaying)
						implementMove(player1.getMove(getBoard('x'), getBoard('o')));
					else
						implementMove(player2.getMove(getBoard('o'), getBoard('x')));
				}
			}
		});
	}	
	
	private static void setUpListener3(JButton resetButton)
	{
		
		resetButton.addActionListener(new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			
			//Reset Players to Humans first so Computer doesnt start playing
			player1Buttons[0].doClick();
			player2Buttons[0].doClick();
			
			
			//Reset the board to empty
			for(int i = 0; i < 5; i++)
			{	for(int j = 0; j<5; j++)
				{
					for (int k = 0; k < 5; k++)
					{
						buttons[j][k][i].reset();
                    }
				}

			}
			
			numMoves = 0;
			playing = true;
			xPlaying = true;
			frame.setTitle("Tic Tac Toe God vs. You");
			
			}
		});
	}
		
	//Just makes the window so that the "x" in the upper right hand corner ends the program.
	public static void makeCloseable(JFrame frame)
	{
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
	}
	
	public static void setUpHumanMove()
	{
		humanMove = new int[3];
		for (int i = 0; i < 3; i++)
			humanMove[i] = -1;
	}

	public static void implementMove(int[] move)
	{
		if (!arrayEqual(move, humanMove))
		{
			buttons[move[0]][move[1]][move[2]].doClick();
		}
	}

	private static boolean arrayEqual(int[] arr1, int[] arr2)
	{
		boolean eq = true;
		for(int i = 0; i < 3; i++)
		{
			eq = eq && (arr1[i] == arr2[i]);
		}
		return eq;
	}

	private static boolean[][][] getBoard(char person)
	{
		boolean[][][] plBoard = new boolean[5][5][5];

		for (int i = 0; i < 5; i++)
			for (int j = 0; j < 5; j++)
				for (int k = 0; k < 5; k++)
					plBoard[i][j][k] = buttons[i][j][k].getMark() == person;

		return plBoard;
	}

	private static void checkForVictory(Square curButton)
	{
		boolean[][][] plBoard;
		char curMark;
		int x, y, z;
		boolean isWin = false;

		if (xPlaying)
			curMark = 'x';
		else
			curMark = 'o';

		plBoard = getBoard(curMark);

		x = curButton.gX();
		y = curButton.gY();
		z = curButton.gZ();

		isWin = isWin || checkXY(plBoard, x, y, z);
		isWin = isWin || checkXZ(plBoard, x, y, z);
		isWin = isWin || checkYZ(plBoard, x, y, z);
		isWin = isWin || checkDiag(plBoard);

		if (isWin)
		{
			String plWon;
			if(xPlaying)
				plWon = "X";
			else
				plWon = "O";			
			frame.setTitle("VICOTRY HAS BEEN ACHIEVED!!!  THE WINNER IS " + plWon + "!!!");
			for (int i = 0; i < 5; i++)
				for (int j = 0; j < 5; j++)
					for (int k = 0; k < 5; k++)
						playing = false;
		}
		else if (numMoves == 125)
		{
			frame.setTitle("TIE GAME");
			playing = false;
		}
	}

	//Check the XY Plane
	private static boolean checkXY(boolean[][][] board, int x, int y, int z)
	{
		boolean isWin = true;
		for (int i = 0; i < 5; i++)
		{
			isWin = isWin && board[x][i][z];
		}

		if (isWin)
			return isWin;

		isWin = true;
		for (int i = 0; i < 5; i++)
		{
			isWin = isWin && board[i][y][z];
		}
		if(isWin)
			return isWin;

		isWin = true;
		boolean isWinBkwd = true;
		for (int i = 0; i < 5; i++)
		{
			isWin = isWin && board[i][i][z];
			isWinBkwd = isWinBkwd && board[i][4-i][z];
		}

		return isWin || isWinBkwd;
	}

	//Check the XZ Plane
	private static boolean checkXZ(boolean[][][] board, int x, int y, int z)
	{
		boolean isWin = true;
		for (int i = 0; i < 5; i++)
		{
			isWin = isWin && board[x][y][i];
		}

		if (isWin)
			return isWin;

		isWin = true;
		for (int i = 0; i < 5; i++)
		{
			isWin = isWin && board[i][y][z];
		}
		if(isWin)
			return isWin;

		isWin = true;
		boolean isWinBkwd = true;
		for (int i = 0; i < 5; i++)
		{
			isWin = isWin && board[i][y][i];
			isWinBkwd = isWinBkwd && board[i][y][4-i];
		}

		return isWin || isWinBkwd;
	}

	//Check the YZ Plane
	private static boolean checkYZ(boolean[][][] board, int x, int y, int z)
	{
		boolean isWin = true;
		for (int i = 0; i < 5; i++)
		{
			isWin = isWin && board[x][y][i];
		}

		if (isWin)
			return isWin;

		isWin = true;
		for (int i = 0; i < 5; i++)
		{
			isWin = isWin && board[x][i][z];
		}
		if(isWin)
			return isWin;

		isWin = true;
		boolean isWinBkwd = true;
		for (int i = 0; i < 5; i++)
		{
			isWin = isWin && board[x][i][i];
			isWinBkwd = isWinBkwd && board[x][i][4-i];
		}

		return isWin || isWinBkwd;
	}


	//Checks the great diagonals of the cube
	public static boolean checkDiag(boolean[][][] plBoard)
	{
		boolean isWin1 = true;
		boolean isWin2 = true;
		boolean isWin3 = true;
		boolean isWin4 = true;
		for(int i = 0; i < 5; i++)
		{
			isWin1 = isWin1 && plBoard[i][i][i];
			isWin2 = isWin2 && plBoard[4-i][i][i];
			isWin3 = isWin3 && plBoard[i][4-i][i];
			isWin4 = isWin4 && plBoard[4-i][4-i][i];
		}

		return isWin1 || isWin2 || isWin3 || isWin4;
	}

	//This class represetns a "square" on the board.  In essence it is just a JButton
	//with a property called "mark" which denotes which mark ("x" or "o") is there
	//and methods to set and get the mark.
	public static class Square extends JButton
	{
		private char mark = '_';
		private int x, y, z;
	
		public Square(int x, int y, int z)
		{
			super();
			this.x = x;
			this.y = y;
			this.z = z;
			super.setText("  ");
		}

		public boolean setMark(char mark)
		{
			if (this.mark == '_')
			{
				this.mark = mark;
				super.setText((new Character(mark)).toString());
				return true;
			}
			return false;
		}

		public void reset()
		{
			this.mark = '_';
			super.setText((new Character(' ')).toString());
		}

		public char getMark()
		{
			return this.mark;
		}

		public int gX()
		{
			return this.x;
		}

		public int gY()
		{
			return this.y;
		}

		public int gZ()
		{
			return this.z;
		}
	}
}