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);
		}

		explore(b);
		b.print();
	}

	/**
	 * 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;
	}

	/**
	 * 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;
	}
}
