import java.util.Arrays;
import java.util.Scanner;
import java.util.function.Function;
import java.util.stream.Collectors;

public class NQueens {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);

		int size;
		do {
			System.out.print("Size ==> ");
			size = sc.nextInt();
		} while (size < 0);

		String type = prompt(sc, "Type", "simple", "complex");
		Board b;
		if (type.equals("simple")) {
			b = new SimpleBoard(size);
		} else {
			b = new ComplexBoard(size);
		}

		String explore = prompt(sc, "Explore", "one", "all");
		if (explore.equals("one")) {
			explore(b);
			b.print();
		} else {
			exploreAll(b);
		}
	}

	/**
	 * variable to count total solutions
	 */
	private static int solutions = 0;

	/**
	 * Finds a solution (if any) to the NQueens problem
	 * @param b the board to explore
	 * @return true IFF a solution was found
	 */
	public static boolean explore(Board b) {
		return explore(b, 0);
	}

	/**
	 * Finds a solution (if any) to the NQueens problem
	 * @param b Board to explore
	 * @param row row number to insert the next queen
	 * @return true IFF a solution was found
	 */
	public static boolean explore(Board b, int row) {
		if (row == b.size()) {
			return true;
		}
		for (int col = 0; col < b.size(); ++col) {
			if (b.canPlace(row, col)) {
				b.place(row, col);
				if (explore(b, row + 1)) {
					return true;
				}
				b.unplace(row, col);
			}
		}
		return false;
	}
	
	/**
	 * Exhaustively explores the NQueens problem starting at row=0
	 * @param b board to explore
	 */
	public static void exploreAll(Board b) {
		exploreAll(b, 0);
	}
	
	/**
	 * Exhaustively explores the NQueens problem
	 * @param b Board to explore
	 * @param row row number to insert the next queen
	 */
	private static void exploreAll(Board b, int row) {
		if (row == b.size()) {
			++solutions;
			System.out.println("Solution " + solutions + ":");
			b.print();
			System.out.println();
			return;
		}
		for (int col = 0; col < b.size(); ++col) {
			if (b.canPlace(row, col)) {
				b.place(row, col);
				exploreAll(b, row + 1);
				b.unplace(row, col);
			}
		}
	}

	/**
	 * Helper method to read from a user until input is validated with one of the
	 * options provided
	 * 
	 * @param sc      Scanner to read from
	 * @param tag     Message to display to user
	 * @param options All valid options to check against
	 * @return a user-entered string which will be one of the options specified
	 */
	public static String prompt(Scanner sc, String tag, String... options) {
		final String msg = Arrays.stream(options).collect(Collectors.joining(", ", tag + " (", ") ==> "));
		final Function<String, Boolean> check = i -> Arrays.stream(options).anyMatch(x -> i.equals(x));
		String input;
		while (true) {
			System.out.println(msg);
			input = sc.next();
			if (check.apply(input)) {
				break;
			}
		}
		return input;
	}
}
