package game.logic;

import game.gui.GamerGUI;
import game.training.Best;
import game.training.MiniMax;

import java.util.List;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JTabbedPane;

import player.gamer.statemachine.sadna.FourInARow;

/**
 * Our agent, Nemesh, that implements the FourInARow interface.
 *
 */
public class Nemesh implements FourInARow {
	public static JFrame MainFrame;
	public static int GameNumber = 1;
	public static JTabbedPane GameTabs;

	String myRole;
	String myName = "Nemesh";
	int myRoleInteger;
	GameBoard board;
	GamerGUI gui;
	Vector myWeights;

	Random rnd = new Random();

	/**
	 * The server has started the game, and we got a role as an argument.
	 * Open the main GUI window, initialize the game board representation and
	 * read the weight vector from the right file, according to our role.
	 * 
	 * @param role Our role as was assigned by the server.
	 */
	@Override
	public void fourStart(String role){
		this.myRole = role;
		if (this.myRole.equals("red")){
			this.myRoleInteger = GameBoard.RedPlayer;
		} else {
			this.myRoleInteger = GameBoard.BlackPlayer;
		}

		/* If first game, open main frame (name prompting is optional) */
		if (Nemesh.GameNumber == 1){

			Nemesh.GameTabs = new JTabbedPane();
			Nemesh.MainFrame = new JFrame("Four In a Row");
			Nemesh.MainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			Nemesh.MainFrame.setSize(600, 600);
			Nemesh.MainFrame.add(Nemesh.GameTabs);
			Nemesh.MainFrame.setVisible(true);

			/* uncomment this and the import to get the user name */
			//	myName = JOptionPane.showInputDialog("Please enter your name", JOptionPane.PLAIN_MESSAGE);
		}

		/* Build a new board with player1 as the starting player */
		this.board = new GameBoard(GameBoard.RedPlayer);
		this.gui = new GamerGUI(this.board, this.myRoleInteger);
		gui.setName(this.getName());
		gui.setColor(myRole);

		System.out.println(getClass().getCanonicalName());
		/* Read weights from file. Decide which file to read according to our role. */
		if (this.myRoleInteger == GameBoard.RedPlayer){
			myWeights = Vector.readfromFile(getClass().getResourceAsStream("/resources/weights/Player1Weights.txt"));
		} else {
			myWeights = Vector.readfromFile(getClass().getResourceAsStream("/resources/weights/Player2Weights.txt"));
		}
		
	}

	/**
	 * Game is over, so save the received score in the GUI panel and advance
	 * the number of games that was played so far (used to display tabs with game numbers).
	 * 
	 * @param score The score of the last game
	 */
	@Override
	public void fourEnd(int score){
		gui.setScore(score);
		Nemesh.GameNumber++;
	}

	/**
	 * This procedure return the name of our player.
	 * 
	 * @return A string representing the name of our agent.
	 */
	@Override
	public String getName(){
		return this.myName;
	}

	/**
	 * The most important procedure of our agent. It chooses the next move of our agent from
	 * a list of possible options, and sends it back to the server.
	 * The main problem is that the server does not call this procedure at the end of the game,
	 * so in case we lost, we cannot know what was the winning move.
	 * 
	 * @param moved a list containing the last move of our opponent.
	 * @param options a list of possible options for us to choose from.
	 * @param timelimit Indicates how much time we have for completing our move.
	 * 
	 * @return An integer ranges from 1 to # of columns which represents our move.
	 */
	@Override
	public int fourMove(List<Integer> moved, List<Integer> options, long timelimit){
		int ourMove;

		/* In case the agent will loose no matter what */
		int randomMove = rnd.nextInt(options.size());

		/* Update the move of our opponent and switch players */
		if (moved.size() != 0){
			gui.updateOpponentMove(moved.get(0) - 1);
			board.switchPlayer();
		}

		/*
		 * Choose a move. In case we have a coerced move (either we are winning or a move that should
		 * block our opponent from winning), just make this move.
		 * Else, choose the best possible move according to the MiniMax algorithm.
		 */
		if (this.myRoleInteger == GameBoard.RedPlayer && this.board.Player1ForcedWin != null
				&& !this.board.Player1ForcedWin.isEmpty()){
			ourMove = this.board.Player1ForcedWin.get(0);

		} else if (this.myRoleInteger == GameBoard.RedPlayer && this.board.Player2ForcedWin != null
				&& !this.board.Player2ForcedWin.isEmpty()){
			ourMove = this.board.Player2ForcedWin.get(0);

		} else if (this.myRoleInteger == GameBoard.BlackPlayer && this.board.Player2ForcedWin != null
				&& !this.board.Player2ForcedWin.isEmpty()){
			ourMove = this.board.Player2ForcedWin.get(0);
		} else if (this.myRoleInteger == GameBoard.BlackPlayer && this.board.Player1ForcedWin != null
				&& !this.board.Player1ForcedWin.isEmpty()){
			ourMove = this.board.Player1ForcedWin.get(0);
		} else {
			GameBoard b1 = GameBoard.CopyBoard(this.board);

			Best myBest = MiniMax.chooseMove(MiniMax.MAX, Double.NEGATIVE_INFINITY,
					Double.POSITIVE_INFINITY, b1, 5, this.myRoleInteger, myWeights);

			ourMove = myBest.getMove();

			/* Player is loosing anyway */
			if (ourMove == -1){
				ourMove = options.get(randomMove) - 1;
			}

		}

		/* Update our move in the GUI panel, switch players and return the move. */
		gui.updateOurMove(ourMove);
		board.switchPlayer();

		return options.indexOf(ourMove+1);
	}
}