// © Copyright 1997, Joseph Bergin. All rights reserved.

package cs1;
import java.io.InputStream;
import java.io.Reader;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.NoSuchElementException;

/** Provides line buffered input that can be used either interactively
* or from a file.  In interactive mode (using a non-null prompt file), 
* one item is read per line.  In "batch" mode (prompt = null), lines 
* may contain several elements.  Note that getLine
* retrieves the rest of the current line.  In non-interactive mode,
* tokens will be skipped to try to find one of the desired kind. For 
* example if you getInt() when the next input token is not a legal int,
* tokens will be skipped looking for an int.  Failure will come at the
* end of the file, of course.  <p>
* Sources: <a href= SafeInput.java> SafeInput.java </a>
*/

public class SafeInput
{	
/** Create a new inputter. The PrintStream is used to prompt the user.  Data is read
*  from the InputFlie.  If the input stream is a file, then the print stream 
* used for prompting should be null.  
* @param prompt A print stream used for prompting interactively. If null, no
* 		prompting will be done.
* @param in The input stream to be read.  
*/
	public SafeInput(PrintStream prompt, InputStream in)	
	// if prompt is null, no prompting will be done.	
	{	input = new BufferedReader(new InputStreamReader(in));
		output = new PrintWriter(prompt);
	}

/** Create a new inputter. The PrintWriter is used to prompt the user.  Data is read
*  from the Reader.  If the input stream is a file, then the print stream 
* used for prompting should be null.  
* @param prompt A PrintWriter used for prompting interactively. If null, no
* 		prompting will be done.
* @param in The input stream to be read.  
*/
	public SafeInput(PrintWriter prompt, Reader in)	
	// if prompt is null, no prompting will be done.	
	{	input = new BufferedReader(in);
		output = prompt;
	}

/** Guarantees that we have a non-empty line buffer.
*/		
	private void guaranteeBuffer()	
	{	try		
		{	if( inBuffer == null || ! fetcher.hasMoreElements() )			
			{	while(fetcher == null || ! fetcher.hasMoreElements())
				{	inBuffer = input.readLine();
					fetcher = new StringTokenizer(inBuffer, delimiters);
				}
			}
		}
		catch(NoSuchElementException e)
		{ 	System.err.println(e);
 		}
		catch(IOException e)
		{ 	System.err.println(e);
 		}
	}

/** Get an int from the next location in the input.  When used interactively
* the user will be continuously prompted until a valid int is entered.  
*/	
	public int getInt()	
	{	int result = 0;
		boolean inputOk = false;
		while(!inputOk)
		{	if(output != null){inBuffer = null; fetcher = null; output.print("Enter an integer: "); output.flush();}
			guaranteeBuffer();
			inputOk = true;
			try
			{	result = Integer.parseInt(fetcher.nextToken(delimiters));			
			}
			catch(NumberFormatException e)
			{	inputOk = false;
				if(output != null){output.println("Not an integer.");}
			}
		}
		return result;
	}
	
/** Get a float from the next location in the input.  When used interactively
* the user will be continuously prompted until a valid float is entered.  
*/	
	public float getFloat()	
	{	float result = 0;
		boolean inputOk = false;
		while(!inputOk)
		{	if(output != null){inBuffer = null; fetcher = null; output.print("Enter a float: "); output.flush();}
			guaranteeBuffer();
			inputOk = true;
			try
			{	result = Float.valueOf(fetcher.nextToken(delimiters)).floatValue();			
			}
			catch(NumberFormatException e)
			{	inputOk = false;
				if(output != null){output.println("Not a float.");}
			}
		}
		return result;
	}
		
/** Get a double from the next location in the input.  When used interactively
* the user will be continuously prompted until a valid double is entered.  
*/	
	public double getDouble()	
	{	double result = 0;
		boolean inputOk = false;
		while(!inputOk)
		{	if(output != null){inBuffer = null; fetcher = null; output.print("Enter a double: "); output.flush();}
			guaranteeBuffer();
			inputOk = true;
			try
			{	result = Double.valueOf(fetcher.nextToken(delimiters)).doubleValue();			
			}
			catch(NumberFormatException e)
			{	inputOk = false;
				if(output != null){output.println("Not a double.");}
			}
		}
		return result;
	}
		
/** Get a long from the next location in the input.  When used interactively
* the user will be continuously prompted until a valid long is entered.  
*/	
	public long getLong()	
	{	long result = 0;
		boolean inputOk = false;
		while(!inputOk)
		{	if(output != null){inBuffer = null; fetcher = null; output.print("Enter a long: "); output.flush();}
			guaranteeBuffer();
			inputOk = true;
			try
			{	result = Long.parseLong(fetcher.nextToken(delimiters));		
			}
			catch(NumberFormatException e)
			{	inputOk = false;
				if(output != null){output.println("Not a long.");}
			}
		}
		return result;
	}
		
/** Get a word from the next location in the input.  In interactive mode, 
* a new line will be read first.   
*/	
	public String getWord()	
	{	if(output != null){inBuffer = null; fetcher = null; output.print("Enter a word: "); output.flush();}
		guaranteeBuffer();
		return fetcher.nextToken(delimiters);
	}
		
/** Get the remainder of the current line buffer as input.  In interactive
* mode, a fresh line will be read entirely. 
*/	
	public String getLine()	
	{	if(output != null){inBuffer = null; fetcher = null; output.print("Enter a line of text: "); output.flush();}
		guaranteeBuffer();
		String result = fetcher.nextToken("");
		return result;
	}

/** Set the delimiters that are used to separate tokens.  The default set
* is space and tab only.  
* @param newDelimiters the new set of delimiting chars in a String.
* @return the original set of delimiters.  
*/	
	public String setDelimiters(String newDelimiters)
	// returns old delimiters
	// The default delimiters are just spacs and tab.
	{	String result = delimiters;
		delimiters = newDelimiters;
		return result;
	}
		
	private String inBuffer = null;
	private PrintWriter output;
	private StringTokenizer fetcher = null;
	private BufferedReader input;
	private String delimiters = " \t";
}
