// Copyright 1999
// College of Computer Science
// Northeastern University Boston MA 02115

// This software may be used for educational purposes as long as this copyright
// notice is retained at the top of all files

// Should this software be modified, the words "Modified from Original" must be
// included as a comment below this notice

// All publication rights are retained.  This software or its documentation may
// not be published in any media either in whole or in part.

///////////////////////////////////////////////////////////////////////////////

// IOTools.h

///////////////////////////////////////////////////////////////////////////////

// IOTools has the following purposes:
//
// 1. To provide safe (error checked) interactive input that permits a user to
//    re-enter data in which errors are detected.
//
// 2. To automate the prompt-response cycle so that "io clutter" in the caller
//    program is minimized.
//
// 3. To enable the use of default responses if desired.
//
// 4. To integrate io into control flow smoothly.  For example to permit loops
//    of the form:
//
//		while (ReadingString(myprompt, mystring)) { ... }
//
//		while (ReadingDouble(myprompt, mydouble)) { ... }
//
// 5. To simplify conversions between strings and numbers.
//
// 6. Define a framework that will permit safe-io to be extended to user types.

///////////////////////////////////////////////////////////////////////////////

#ifndef IOTOOLS_H_
#define IOTOOLS_H_

#include "MathUtil.h"					// CHeaders and mathematical stuff

///////////////////////////////////////////////////////////////////////////////

// Preliminary definitions

// typedef for functions to test conditions on characters
// to comply with C library functions that use int-to-int

typedef int (*IntFcnPtr) (int);


// Useful character constants
	
const char	BackSlash	= '\\';
const char	Blank		= ' ';
const char	Comma		= ',';
const char	Tab			= '\t';
const char	NL			= '\n';
const char	CR			= '\r';
const char	FF			= '\f';
const char	VT			= '\v';
const char	Null		= '\0';


// Case conversion declarations

enum CaseAction {
	// The following case actions can be requested as the global default
	// case action in SetCaseAction:

	None,					// No case conversion
	LowerCase,				// Convert to lower case
	UpperCase,				// Convert to upper case
	MixedCase				// Capitalize the first letter in every word &
							// convert the rest of each word to lower case
};


EXPORT void SetCaseAction(CaseAction  action);		// Set global case action

EXPORT void GetCaseAction(CaseAction& action);		// Look up global case action

///////////////////////////////////////////////////////////////////////////////

// IOTools can read one or more lines of user input into a string subject
// to the following conventions:
//
//     1. If a line ends with  \  then the next line will be joined to it
//        and the backslash will be deleted from the input stream
//
//     2. Leading and trailing whitespace will be deleted from the buffer
//        once a line is read that does not end in a backslash
//
//     3. Default case conversion may be specified using SetCaseAction
//            None         No case conversion
//            LowerCase    Convert to lower case
//            UpperCase    Convert to upper case
//            MixedCase    Capitalize the first letter in every word &
//                         convert the rest of each word to lower case
//
//     4. If input begins with  \  then case conversion will be not be done
//        and the backslash will be deleted from the input stream
//
//     5. A prompt string may be specified
//
//     6. A default answer string may be specified

///////////////////////////////////////////////////////////////////////////////

// PromptAnswer handles the initial input phase in which a prompt and a default
// answer are provided to the user.  This tool is used throughout IOTools and
// is made public so that programmers can easily adapt IOTools to their own
// types.

EXPORT void PromptAnswer(
	const string&	prompt,				// prompt to user
	const string&	answer				// default answer
);

///////////////////////////////////////////////////////////////////////////////

// RequestString is the basic tool on which all other reading tools are based

///////////////////////////////////////////////////////////////////////////////

// RequestString using the C++ string class has three forms:
//
//		void RequestString(
//			const string&	prompt,				// prompt to user
//			const string&	answer,				// default answer
//			string& 		response			// user response
//		);
//
//		void RequestString(
//			const string&	prompt,				// prompt to user
//			string& 		response			// user response
//		);
//
//		void RequestString(
//			string& 		response			// user response
//		);

// The string version of RequestString returns void to emphasize that the data
// is returned in the reference parameter response.  This avoids an extra copy
// of the data that would be required if the string data was returned by value.

// Description of RequestString:
//
// Parameter  prompt  supplies a user prompt string.  If this parameter is
// omitted, then there is no prompt printed
//
// Parameter  answer  is the default data returned if the user presses RETURN
// without giving input.  If this parameter is omitted, then the empty string
// is the default.
//
// The data read is copied into the parameter  response.
//
// Before the string data is returned:
//
//     Leading and trailing whitespace is deleted
//
//     The current global case action is performed unless overridden by
//     a backslash at the start of the input string

EXPORT void RequestString(
	const string&	prompt,				// prompt to user
	const string&	answer,				// default answer
	string& 		response			// user response
);

inline void RequestString(
	const string&	prompt,				// prompt to user
	string& 		response			// user response
) {
	RequestString(prompt, "", response);
}

inline void RequestString(
	string& 		response			// user response
) {
	RequestString("", "", response);
}

///////////////////////////////////////////////////////////////////////////////

// ReadingString takes a prompt and a string object reference parameter
// ReadingString returns the number of valid characters input

///////////////////////////////////////////////////////////////////////////////

EXPORT int ReadingString(
	const string&	prompt,				// prompt to user
	string& 		response			// user response
);

///////////////////////////////////////////////////////////////////////////////

// Check for input errors

EXPORT bool InputError(istream& is = cin);			// check only

EXPORT bool InputErrorVerbose(istream& is = cin);	// check with error message to cout

///////////////////////////////////////////////////////////////////////////////

// Low level routines used to build RequestString and ReadingString
// These routines:
//     1. Use InputErrorVerbose to check for input errors on is
//     2. Fix "lookahead" bugs in library versions of getline


// Replacement for string getline
// Action:
//     Store entire input line up to newline into string s

EXPORT void GetOneLine(istream& is, string& s);		// general version

EXPORT void GetOneLine(string& s);					// cin version


// Replacement for istream getline
// Action:
//     1. Store at most (room - 1) characters from input line into buffer
//     2. Null terminate buffer
//     3. Maintain IO synchronization by calling SkipPastNewline if input
//        line (up to newline) has not been completely utilized

EXPORT void GetOneLine(istream& is, char* buffer, int room);	// general

EXPORT void GetOneLine(char* buffer, int room);					// cin


// Routine to skip "leftover characters" on an input line and thereby to
// restore IO synchronization on a line-by-line basis

// This routine is useful to restore synchronization after calls of form:
//     is >> ...

EXPORT void SkipPastNewline(istream& is = cin);

///////////////////////////////////////////////////////////////////////////////

// Useful string manipulations

///////////////////////////////////////////////////////////////////////////////

// Versions for classic C strings

// Define tool to automatically change case

EXPORT void ChangeCase(char* s, CaseAction action = None);

// Define squash function to delete leading and trailing whitespace in s

EXPORT void SquashString(char* s);

// Skip white space in s by updating s to next non-white-space character

EXPORT void SkipSpace(const char*& s);

// Skip characters in s while Test function is non-zero
// Stop if null character is reached

EXPORT void SkipWhile(const char*& s, IntFcnPtr Test);

///////////////////////////////////////////////////////////////////////////////

// Versions for C++ strings

// Define tool to automatically change case

EXPORT void ChangeCase(string& s, CaseAction action = None);

// Define squash function to delete leading and trailing whitespace in s

EXPORT void SquashString(string& s);


// Since C++ strings are self-contained we can have both forward and reverse
// variants of the Skip functions.


// Skip white space in s
//
// Begin at position start and end at position stop
//
// Require:
//		0 <= start
// If this condition fails then set start = 0.
//
// Require:
//		start < s.length()
// If this condition fails then set stop = s.length() and return immediately.
//
// Otherwise return stop such that:
//		start <= stop <= s.length()
//		if stop < s.length() then:
//				s[stop] is first non-space equal to or after s[start]

EXPORT void SkipSpace(const string& s, int start, int& stop);


// Skip white space in s in reverse direction
//
// Begin at position start and end at position stop
//
// Require:
//		start < s.length()
// If this condition fails then set start = s.length() - 1.
//
// Require:
//		0 <= start
// If this condition fails then set stop = -1 and return immediately.
//
// Otherwise return stop such that:
//		-1 <= stop <= start
//		if 0 <= stop then:
//				s[stop] is first non-space equal to or before s[start]

EXPORT void ReverseSkipSpace(const string& s, int start, int& stop);


// Skip characters in s while Test function is non-zero
//
// Begin at position start and end at position stop
//
// Require:
//		0 <= start
// If this condition fails then set start = 0.
//
// Require:
//		start < s.length()
// If this condition fails then set stop = s.length() and return immediately.
//
// Otherwise return stop such that:
//		start <= stop <= s.length()
//		for all i such that start <= i < stop:   Test(s[i])    != 0
//		if stop < s.length() then:               Test(s[stop]) == 0

EXPORT void SkipWhile(const string& s, IntFcnPtr Test, int start, int& stop);


// Skip characters in s in reverse direction while Test function is non-zero
//
// Begin at position start and end at position stop
//
// Require:
//		start < s.length()
// If this condition fails then set start = s.length() - 1.
//
// Require:
//		0 <= start
// If this condition fails then set stop = -1 and return immediately.
//
// Otherwise return stop such that:
//		-1 <= stop <= start
//		for all i such that stop < i <= start:   Test(s[i])    != 0
//		if 0 <= stop then:                       Test(s[stop]) == 0

EXPORT void ReverseSkipWhile(const string& s, IntFcnPtr Test, int start, int& stop);

///////////////////////////////////////////////////////////////////////////////

// PressReturn tool

// Print a newline
// If message is not empty then print the message and a newline
// Print "Press Return To Continue "
// Pause until user presses return and eat any miscellaneous input
// Print a newline

EXPORT void PressReturn(const string& message = "");

///////////////////////////////////////////////////////////////////////////////

// Confirm tool

// Generic tool to ask Yes-No questions
// Y or Yes corresponds to true
// N or No  corresponds to false

EXPORT bool Confirm(
	const string&	prompt,			// prompt to user
	bool			answer			// default answer
);

///////////////////////////////////////////////////////////////////////////////

// Single character input

// Version 1: Supply default

EXPORT char RequestChar(
	const string&	prompt,			// prompt to user
	char			answer			// default answer
);

// Version 2: Demand answer - no default

EXPORT char RequestChar(
	const string&	prompt			// prompt to user
);

// Version 3:
//		Return data read in a parameter
//		Return true as function value if data entered
//		by user - otherwise return false

EXPORT bool ReadingChar(
	const string&	prompt,			// prompt to user
	char&			answer			// user answer
);

///////////////////////////////////////////////////////////////////////////////

// Numeric data input
//
// For each numeric type, there are three input functions and two functions
// to convert from a string to the type 
//
// We will use type double as an illustration
// The other types supported are float, short, int, long, unsigned long
//
//
// Input functions
//
//
// 1. Prompt the user and provide a default answer.
//    If user responds with a valid double then return it as function value
//    If user simply presses return then return the default answer
//    If user responds with erroneous data then give feedback and ask again
//
//
//		double RequestDouble(			// request with default
//			const string&	prompt,		// prompt to user
//			double			answer		// default answer
//		);
//
//
// 2. Prompt the user
//    If user responds with a valid double then return it as function value
//    If user simply presses return then ask again
//    If user responds with erroneous data then give feedback and ask again
//
//
//		double RequestDouble(			// request without default
//			const string&	prompt		// prompt to user
//		);
//
//
// 3. Prompt the user
//    If user responds with a valid double then
//        store data in parameter answer
//        return true  as function value
//    If user simply presses return then
//        return false as function value
//    If user responds with erroneous data then give feedback and ask again
//
//		bool ReadingDouble(
//			const string&	prompt,		// prompt to user
//			double&			answer		// user answer
//		);
//
//
// Conversion functions
// These functions do the processing in the input functions
//
//
//		double StringToDouble(
//			const string&	source,		// string to read
//			int				start,		// start position to read
//			int&			next,		// next  position to read
//			int&			error		// error code
//		);
//
//
// For backward compatibility, we also retain the variation:
//
//		double StringToDouble(
//			const char*		start,		// start position to read
//			const char*&	next,		// next  position to read
//			int&			error		// error code
//		);
//
//
// The function  StringToDouble  parses the portion of the string
// beginning at start and converts it to type  double
//
// The next parameter indicates where to continue further parsing
//
// The error parameter returns a positive error value if there is
// a parsing failure
//
//
// The function  StringToDouble  is the basis for all other numeric
// conversions.  StringToDouble parses its input with an arithmetic
// expression evaluator which we describe below:
//
//
// Parsing Details:
//
// The string reader that converts to double parses fairly general
// arithmetic expressions using the operators: + - * / % #.  The
// rationale for the extra operator # is to support two forms of
// division:
// 
//		/	usual floating point division
//		#	floating division followed by truncate to an integer
//
// For example:
//
//		8 / 5   is 1.6
//		8 # 5   is 1.0
//		9.7 # 1 is 9.0
//
// When parsing integer types (int, long, short), the calculation is
// done using the double precision routine and is then rounded NOT
// truncated.  This is the only convention that makes sense for
// arbitrary input expressions. This implies that for integer types:
//
//		8 / 5 becomes 2
//		8 # 5 becomes 1
//
// This means that / does not work as in C and C++.  The problem is
// really in C and C++ since / is overloaded in an inconsistent way.
// To achieve the classic integer division on input use # not /.
//
// The parsing routine can also recognize certain function calls:
//
// Trigonometric functions in radians:
//
//		sin(x),  cos(x),  tan(x)
//		asin(x), acos(x), atan(x), atan2(y, x)
//
// Trigonometric functions in degrees:
//
//		sindeg(x),  cosdeg(x),  tandeg(x)
//		asindeg(x), acosdeg(x), atandeg(x), atan2deg(y, x)
//
// Hyperbolic functions:
//
//		sinh(x), cosh(x), tanh(x)
//
// Exponentals and logarithms:
//
//		exp(x), ln(x), log(x), log10(x), sqrt(x), pow(x, y)
//
// The functions ln(x) and log(x) are identical.
//
// The parsing routine also recognizes one constant: pi = 3.14159...
//
//
// Note that for all types  T  except  double  there is a conversion
// function:
//
//		bool DoubleToT(double d, T& x);
//
// The converted value is stored in x.  If the conversion is correct
// then DoubleToT returns true.  If not, it returns false.


// Double precision floats

EXPORT double RequestDouble(		// with default
	const string&	prompt,			// prompt to user
	double			answer			// default answer
);

EXPORT double RequestDouble(		// without default
	const string&	prompt			// prompt to user
);

EXPORT bool ReadingDouble(
	const string&	prompt,			// prompt to user
	double&			answer			// user answer if provided
);

EXPORT double StringToDouble(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
);

EXPORT double StringToDouble(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
);


// Single precision floats

EXPORT float RequestFloat(			// with default
	const string&	prompt,			// prompt to user
	float			answer			// default answer
);

EXPORT float RequestFloat(			// without default
	const string&	prompt			// prompt to user
);

EXPORT bool ReadingFloat(
	const string&	prompt,			// prompt to user
	float&			answer			// user answer if provided
);

EXPORT float StringToFloat(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
);

EXPORT float StringToFloat(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
);

EXPORT bool DoubleToFloat(double d, float& x);


// Integer: int

EXPORT int RequestInt(				// with default
	const string&	prompt,			// prompt to user
	int				answer			// default answer
);

EXPORT int RequestInt(				// without default
	const string&	prompt			// prompt to user
);

EXPORT bool ReadingInt(
	const string&	prompt,			// prompt to user
	int&			answer			// user answer if provided
);

EXPORT int StringToInt(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
);

EXPORT int StringToInt(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
);

EXPORT bool DoubleToInt(double d, int& x);


// Integer: long

EXPORT long RequestLong(			// with default
	const string&	prompt,			// prompt to user
	long			answer			// default answer
);

EXPORT long RequestLong(			// without default
	const string&	prompt			// prompt to user
);

EXPORT bool ReadingLong(
	const string&	prompt,			// prompt to user
	long&			answer			// user answer if provided
);

EXPORT long StringToLong(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
);

EXPORT long StringToLong(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
);

EXPORT bool DoubleToLong(double d, long& x);


// Integer: short

EXPORT short RequestShort(			// with default
	const string&	prompt,			// prompt to user
	short			answer			// default answer
);

EXPORT short RequestShort(			// without default
	const string&	prompt			// prompt to user
);

EXPORT bool ReadingShort(
	const string&	prompt,			// prompt to user
	short&			answer			// user answer if provided
);

EXPORT short StringToShort(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
);

EXPORT short StringToShort(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
);

EXPORT bool DoubleToShort(double d, short& x);


// For unsigned types will only support unsigned long

EXPORT unsigned long RequestUnsignedLong(	// with default
	const string&	prompt,			// prompt to user
	unsigned long	answer			// default answer
);

EXPORT unsigned long RequestUnsignedLong(	// without default
	const string&	prompt			// prompt to user
);

EXPORT bool ReadingUnsignedLong(
	const string&	prompt,			// prompt to user
	unsigned long&	answer			// user answer if provided
);

EXPORT unsigned long StringToUnsignedLong(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
);

EXPORT unsigned long StringToUnsignedLong(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
);

EXPORT bool DoubleToUnsignedLong(double d, unsigned long& x);


///////////////////////////////////////////////////////////////////////////////

// Number to string format routines

// Foundation routines will format to a string stream
// These routines will be applied to format to a string or to a CharBuffer (for
// backward compatibility)

// Case 1: Arguments: ... double or float x
//
// if x is negative then output a minus sign and make x positive
// if x equals an unsigned long integer then format x as integer
// otherwise format x as double using:
//		precision = 6
//		fixed format if 0.1 <= x <= 1000000 otherwise scientific

// Case 2: Arguments: ... 
//      double or float x, int width, int precision, bool fixed = true
//
// format double x using specified field width and decimal precision
// if fixed is true  use fixed format and right justify
// if fixed is false use scientific format and left justify
// if width > 0
//      add blank if needed to make sure that decimal points align

// Case 3: Arguments: ... integer types x, int width = 0
//
// if width > 0 then
//		format x using field width and right justify
// otherwise
//		format x using minimal space

// Case 4: Arguments: ... unsigned integer types x, int width = 0
//
// if width > 0 then
//		format x using field width and right justify
// otherwise
//		format x using minimal space

///////////////////////////////////////////////////////////////////////////////

// Format using stringstream

EXPORT void Format(stringstream& stream, double x);

EXPORT void Format(stringstream& stream, float  x);

EXPORT void Format(
	stringstream& stream,
	double x,
	int width,
	int precision,
	bool fixed = true);

EXPORT void Format(
	stringstream& stream,
	float  x,
	int width,
	int precision,
	bool fixed = true);

EXPORT void Format(stringstream& stream, int   x, int width = 0);

EXPORT void Format(stringstream& stream, long  x, int width = 0);

EXPORT void Format(stringstream& stream, short x, int width = 0);

EXPORT void Format(stringstream& stream, unsigned int   x, int width = 0);

EXPORT void Format(stringstream& stream, unsigned long  x, int width = 0);

EXPORT void Format(stringstream& stream, unsigned short x, int width = 0);

///////////////////////////////////////////////////////////////////////////////

// Format using string

EXPORT void Format(string& target, double x);

EXPORT void Format(string& target, float  x);

EXPORT void Format(
	string& target,
	double x,
	int width,
	int precision,
	bool fixed = true);

EXPORT void Format(
	string& target,
	float  x,
	int width,
	int precision,
	bool fixed = true);

EXPORT void Format(string& target, int   x, int width = 0);

EXPORT void Format(string& target, long  x, int width = 0);

EXPORT void Format(string& target, short x, int width = 0);

EXPORT void Format(string& target, unsigned int   x, int width = 0);

EXPORT void Format(string& target, unsigned long  x, int width = 0);

EXPORT void Format(string& target, unsigned short x, int width = 0);

///////////////////////////////////////////////////////////////////////////////

// Routine to print double to cout using the rules of Format Case 1 above
// Used to print default in numeric input routines

EXPORT void PrintDouble(double x);

///////////////////////////////////////////////////////////////////////////////

// Error codes for numeric data input

enum {
	NoError,				// No error = 0
	NumberError,			// Error: Reading numeric data
	ColorError,				// Error: Reading color data
	SymbolError,			// Error: Undefined identifier
	ConvertError,			// Error: Out of bounds
	AddError,				// Error: Addition
	SubError,				// Error: Subtraction
	MulError,				// Error: Multiplication
	DivError,				// Error: Division
	ModError,				// Error: Modulus
	NoLPError,				// Error: No starting (
	NoRPError,				// Error: No matching )
	NoCommaError,			// Error: Comma required
	NoBARError,				// Error: No matching |
	NoDataError,			// Error: Data required
	BadDataError,			// Error: Unexpected data
	EndDataError,			// Error: Data ends prematurely
	FunctionEvalError		// Error: Function evaluation error
};

// Obtain error message from error code

EXPORT const char* IOErrorMessage(int error);

// Print error message and signal error position in string
// Request repeat of input data

EXPORT void ErrorPrint(const string& source, int next, int error);

EXPORT void ErrorPrint(const char* start, const char* next, int error);

#endif // IOTOOLS_H_

