package sudoku.parse;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
import sudoku.Puzzle;

/**
 * <p>Parses the initial puzzle out of a Sadman Sudoku puzzle file. This format is defined at
 * http://www.sadmansoftware.com/sudoku/faq19.php (retrieved 25 May 2016).</p> <p>All elements of a
 * Sadman format file other than the initial state of the puzzle are ignored, including the number
 * of solutions.</p>
 * @author fiveham
 * @author fiveham
 *
 */
public class SadmanParser implements Parser{
	
    /**
     * <p>Content of the line prior to the statement of the initial state of the puzzle.</p>
     */
	public static final String INITIAL_PUZZLE_MARKER = "[Puzzle]";
	
    /**
     * <p>Indicates an empty cell in the initial puzzle state.</p>
     */
	public static final char EMPTY_CELL = '.';
	
	private final int mag;
	private final List<Integer> values;
	
    /**
     * <p>Constructs a Parser that interprets the contents of the file {@code f} as a Sadman-format
     * sudoku puzzle, using the specified {@code charset} to read the file.</p>
     * @param f the file to be interpreted
     * @param charset the name of the charset to be used in reading the file. If the
     * @throws FileNotFoundException
     */
	public SadmanParser(File f, String charset) throws FileNotFoundException{
		this(() -> new Scanner(f, charset));
	}
	
	public SadmanParser(File f) throws FileNotFoundException{
		this(() -> new Scanner(f));
	}
	
	/**
	 * <p>This is a knockoff of {@literal java.util.function.Supplier<Scanner>} that declares that it 
	 * throws a FileNotFoundException as part of its method header. This allows the two public 
	 * constructors to send Scanner-suppliers to the relevant constructor without needing to include 
	 * try-catch blocks inside the lambda bodies they send.</p>
	 * @author fiveham
	 */
	@FunctionalInterface
	private interface FNFSupplier{
	  public Scanner get() throws FileNotFoundException;
	}
	
	private SadmanParser(FNFSupplier supplier) throws FileNotFoundException{
		Scanner s = supplier.get();
		while(s.hasNext() && !INITIAL_PUZZLE_MARKER.equals(s.nextLine()));
		
		if(!s.hasNext()){
			s.close();
			s = supplier.get();
		}
		
		StringBuilder initCells;
		try{
			initCells = new StringBuilder(s.nextLine());
			this.mag = (int)Math.sqrt(initCells.length());
			for(int i=1; i<mag*mag; ++i){
				initCells.append(s.nextLine());
			}
		} catch(NoSuchElementException e){
			throw new IllegalArgumentException("Could not parse the file as Sadman format", e);
		} finally{
			s.close();
		}
		this.values = new ArrayList<>(initCells.length());
		for(int i=0; i<initCells.length(); ++i){
			char c = initCells.charAt(i);
			values.add(c == EMPTY_CELL 
					? Puzzle.BLANK_CELL 
					: Integer.parseInt(Character.toString(c), mag*mag+1));
		}
	}
	
	@Override
	public int mag() {
		return mag;
	}
	
	@Override
	public List<Integer> values() {
		return values;
	}
}

@@ -2063,16 +2063,943 @@
 n;%0A%09%7D%0A%09%0A
+%09/**%0A%09 * %3Cp%3EConstructs a SadmanParser based on the content output by the Scanner supplied by %0A%09 * %7B@code supplier%7D.%3C/p%3E%0A%09 * %3Cp%3EScans the file until %7B@literal %22%5BPuzzle%5D%22%7D is found or the end-of-file is reached. %0A%09 * Thereafter, the content of the puzzle is read in or a new Scanner replaces the previous one, %0A%09 * respectively. In the latter case, the new Scanner reads the first lines of the file to read the %0A%09 * puzzle just as a Scanner that found a %7B@literal %22%5BPuzzle%5D%22%7D marker would do with the lines %0A%09 * after the marker. %0A%09 * @param supplier supplies a Scanner for a file specified in a public constructor%0A%09 * @throws IllegalArgumentException if the file could not be parsed according to the Sadman format%0A%09 * @throws FileNotFoundException if the file backing %7B@code supplier%7D could not be read%0A%09 * @throws NumberFormatException if a character describing the initial value of a cell cannot be %0A%09 * parsed as an int%0A%09 */%0A
 %09private
@@ -3064,16 +3064,16 @@
 eption%7B%0A
-
 %09%09Scanne
@@ -3331,16 +3331,17 @@
  = (int)
+ 
 Math.sqr
@@ -3379,18 +3379,24 @@
 nt i
-=
+ = 
 1; i
-%3Cmag*
+ %3C mag * 
 mag;
@@ -3594,20 +3594,23 @@
 lose();%0A
-
 %09%09%7D%0A
+%09%09%0A
 %09%09this.v

add javadoc for private constructor