package com.company.Minimax;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Board {

	private List<Cell> emptyCells;
    private Scanner scanner;
    private Player[][] board;
    private List<Cell> rootValues;

    public Board() {
    	initializeBoard();
    }

    private void initializeBoard() {
    	this.rootValues = new ArrayList<>();
		this.scanner = new Scanner(System.in);
		this.board = new Player[Constants.BOARD_SIZE][Constants.BOARD_SIZE];
	}

	public boolean isRunning() {
		
		if( isWinning(Player.COMPUTER) ) return false;
		if( isWinning(Player.USER)) return false;
		if( getEmptyCells().isEmpty() )return false;
		
		return true;
    }

    public boolean isWinning(Player player) {
    	
        if ( board[0][0] == player && board[1][1] == player && board[2][2] == player ) {
            return true;
        }
        
        if( board[0][2] == player && board[1][1] == player && board[2][0] == player ){
        	return true;
        }
        
        for (int i = 0; i < Constants.BOARD_SIZE; i++) {
           
        	// checking the rows
        	if ( board[i][0] == player && board[i][1] == player && board[i][2] == player ) {
                return true;
            }
        	
        	// checking the columns
        	if( board[0][i] == player && board[1][i] == player && board[2][i] == player ){
        		return true;
        	}
        }
        
        return false;
    }

    public List<Cell> getEmptyCells() {
        
    	emptyCells = new ArrayList<>();
        
        for (int i = 0; i < Constants.BOARD_SIZE; ++i) {
            for (int j = 0; j < Constants.BOARD_SIZE; ++j) {
                if (board[i][j] == Player.NONE) {
                    emptyCells.add(new Cell(i, j));
                }
            }
        }
        return emptyCells;
    }

    public void move(Cell point, Player player) {
        board[point.getX()][point.getY()] = player;   
    }

    public Cell getBestMove() {
    	
        int max = Integer.MIN_VALUE;
        int best = Integer.MIN_VALUE;

        for (int i = 0; i < rootValues.size(); ++i) { 
            if (max < rootValues.get(i).getMinimaxValue()) {
                max = rootValues.get(i).getMinimaxValue();
                best = i;
            }
        }

        return rootValues.get(best);
    }

    public void makeUserInput() {
        System.out.println("User's move: ");
        int x = scanner.nextInt();
        int y = scanner.nextInt();
        Cell point = new Cell(x, y);
        move(point, Player.USER); 
    }

    public void displayBoard() {

    	System.out.println();
    	
        for (int i = 0; i < Constants.BOARD_SIZE; ++i) {
            for (int j = 0; j < Constants.BOARD_SIZE; ++j) {
                System.out.print(board[i][j] + " ");
            }
            
            System.out.println();
        }
    }

    public int returnMin(List<Integer> list) {
    	
        int min = Integer.MAX_VALUE;
        int index = Integer.MIN_VALUE;
        
        for (int i = 0; i < list.size(); ++i) {
            if (list.get(i) < min) {
                min = list.get(i);
                index = i;
            }
        }
        return list.get(index);
    }

    public int returnMax(List<Integer> list) {
        int max = Integer.MIN_VALUE;
        int index = Integer.MIN_VALUE;
        for (int i = 0; i < list.size(); ++i) {
            if (list.get(i) > max) {
                max = list.get(i);
                index = i;
            }
        }
        
        return list.get(index);
    }
 
    public void callMinimax(int depth, Player player){
        rootValues.clear();
        minimax(depth, player);
    }
    
    public int minimax(int depth, Player player) {

        if (isWinning(Player.COMPUTER)) return +1;
        if (isWinning(Player.USER)) return -1;

        List<Cell> availableCells = getEmptyCells();
        
        if (availableCells.isEmpty()) return 0; 

        List<Integer> scores = new ArrayList<>(); 

        for (int i = 0; i < availableCells.size(); i++) {
            
        	Cell point = availableCells.get(i);  

            if (player == Player.COMPUTER) { //X's turn select the highest from below minimax() call
                move(point, Player.COMPUTER); 
                int currentScore = minimax(depth + 1, Player.USER);
                scores.add(currentScore);

                if (depth == 0) {
                	point.setMinimaxValue(currentScore);
                    rootValues.add(point);
                }    	
                
            } else if (player == Player.USER) {//O's turn select the lowest from below minimax() call
                move(point, Player.USER); 
                scores.add(minimax(depth + 1, Player.COMPUTER));
            }
            
            board[point.getX()][point.getY()] = Player.NONE; //Reset this point
        }
        
        if( player == Player.COMPUTER ){
        	return returnMax(scores);
        }
        
        return returnMin(scores);
    }

	public List<Cell> getAvailablePoints() {
		return emptyCells;
	}

	public void setAvailablePoints(List<Cell> availablePoints) {
		this.emptyCells = availablePoints;
	}
	
	public void setupBoard() {
		for(int i = 0; i< Constants.BOARD_SIZE; i++){
			for(int j = 0; j< Constants.BOARD_SIZE; j++){
				board[i][j] = Player.NONE;
			}
		}
	}
    
    //getters
	public Scanner getScanner() {
		return scanner;
	}

	public void setScanner(Scanner scanner) {
		this.scanner = scanner;
	}

	public Player[][] getBoard() {
		return board;
	}

	public void setBoard(Player[][] board) {
		this.board = board;
	}
	
	public List<Cell> getRootValues(){
		return this.rootValues;
	}
}