// Copyright 2000
// 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.

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

// Basic2D.cpp
// Basic 2-dimensional types with double precision coordinates

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

#include "CoreTools.h"				// needed for core tools compile in Win32

#include "Basic2D.h"
#include "IOTools.h"				// tools for safe I/O


double AngleInRadians (const Point2D& P, const Point2D& Q) {
	// easy case
	if (IsZero(P) || IsZero(Q))
		return 0;
	
	// compute cosine of angle between vectors
	// arrange to compute only one sqrt
	
	double pp = DotProduct(P, P);
	double qq = DotProduct(Q, Q);
	double pq = DotProduct(P, Q);
	
	double cosine = pq / sqrt(pp * qq);
	
	// check for round off on parallel vectors
	if (cosine >  1)
		cosine =  1;
	
	if (cosine < -1)
		cosine = -1;
	
	// compute angle using arc cosine function
	return acos( cosine );
}

///////////////////////////////////////////////////////////////////////////////

// Point2D IO and string functions

Point2D& Point2D::Request(			// respond-or-default
	const string&		prompt,		// prompt to user
	const Point2D&		answer		// default answer
) {
	string stringanswer(answer.ToString());
	string stringresponse;
	Point2D response;
	
	while (true) {		
		RequestString(prompt, stringanswer, stringresponse);
		
		if (InputError() || stringresponse.length() == 0)
			return Set(answer);
		
		int start = 0;
		int next  =	0;
		int error = NoError;
		
		response.FromString(stringresponse, start, next, error);
		
		if (!error)
			return Set(response);
		
		ErrorPrint(stringresponse, next, error);
	}
}
	
	
Point2D& Point2D::Request(			// mandatory response
	const string&		prompt		// prompt to user
) {
	string stringresponse;
	Point2D response;

	while (true) {		
		RequestString(prompt, stringresponse);
		
		if (InputError())
			return Set();
		
		int start = 0;
		int next  =	0;
		int error = NoError;
		
		if (stringresponse.length()) {
			response.FromString(stringresponse, start, next, error);
			
			if (!error)
				return Set(response);
		}
		else
			error = NoDataError;
		
		ErrorPrint(stringresponse, next, error);
	}
}
	
	
bool Point2D::Reading(				// respond-or-decline
	const string&		prompt		// prompt to user
) {
	string stringresponse;
	Point2D response;
	
	while (true) {		
		RequestString(prompt, stringresponse);
		
		if (InputError() || stringresponse.length() == 0)
			return false;
		
		int start = 0;
		int next  =	0;
		int error = NoError;
		
		response.FromString(stringresponse, start, next, error);
		
		if (!error) {
			Set(response);
			return true;
		};
		
		ErrorPrint(stringresponse, next, error);
	}
}
	
	
Point2D& Point2D::FromString(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// string IO error code
) {
	// find size
	int size = source.length();
	
	// check start
	if (start < 0)
		start = 0;
	
	if (start >= size) {
		next  = size;
		error = NoDataError;
		return *this;
	}
	
	// initialize error
	error = NoError;
	
	// process string via const char* pointer
	const char* temp = source.c_str() + start;
	const char* stop = temp;
	
	// use the C string version of FromString
	FromString(temp, stop, error);
	
	// compute next index
	next = start + (stop - temp);
	
	return *this;
}
	
	
Point2D& Point2D::FromString(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// string IO error code
) {
	int count = 0;					// component number
	const int components = 2;		// number of components

	double number;					// temporary number variable
	Point2D result;					// temporary result variable

	// prepare for initial scan
	SkipSpace(start);
	next  = start;
	error = NoError;
	
	while (count < components) {
		// update component count
		count++;

		// update start to read next number
		start = next;

		// read next number
		number = StringToDouble(start, next, error);

		// check for error on read
		if (error)
			break;
		
		// if no error then assign number to proper field
		switch (count) {
			case 1:
				result.x = number;
				break;
			
			case 2:
				result.y = number;
				break;
			
			default:
				break;
		}
			
		// Remove comma separator if present
		
		if (count < components) {
			SkipSpace(next);
		
			if (*next == Comma) {
				next++;
				SkipSpace(next);
			}
		}
	}
	
	// if no error then use result to modify object
	// otherwise just return original object

	if (!error)
		return Set(result);
	else
		return *this;
}
	
		
string Point2D::ToString() const {
	string s;
	string t;

	Format(s, x);
	Format(t, y);

	string result = s + ", " + t;

	return result;
}


string Point2D::ToString(int width, int precision, bool fixed) const {
	string s;
	string t;

	Format(s, x, width, precision, fixed);
	Format(t, y, width, precision, fixed);

	string result = s + ", " + t;

	return result;
}
	
///////////////////////////////////////////////////////////////////////////////

// Rect2D IO and string functions

Rect2D& Rect2D::Request(		// respond-or-default
	const string&		prompt,		// prompt to user
	const Rect2D&		answer		// default answer
) {
	string stringanswer = answer.ToString();

	string stringresponse;
	Rect2D response;
	
	while (true) {		
		RequestString(prompt, stringanswer, stringresponse);
		
		if (InputError() || stringresponse.length() == 0)
			return Set(answer);
		
		int start = 0;
		int next  =	0;
		int error = NoError;
		
		response.FromString(stringresponse, start, next, error);
		
		if (!error)
			return Set(response);
		
		ErrorPrint(stringresponse, next, error);
	}
}
	
	
Rect2D& Rect2D::Request(		// mandatory response
	const string&		prompt		// prompt to user
) {
	string stringresponse;
	Rect2D response;

	while (true) {		
		RequestString(prompt, stringresponse);
		
		if (InputError())
			return Set();
		
		int start = 0;
		int next  =	0;
		int error = NoError;
		
		if (stringresponse.length()) {
			response.FromString(stringresponse, start, next, error);
			
			if (!error)
				return Set(response);
		}
		else
			error = NoDataError;
		
		ErrorPrint(stringresponse, next, error);
	}
}
	
	
bool Rect2D::Reading(			// respond-or-decline
	const string&		prompt		// prompt to user
) {
	string stringresponse;
	Rect2D response;
	
	while (true) {		
		RequestString(prompt, stringresponse);
		
		if (InputError() || stringresponse.length() == 0)
			return false;
		
		int start = 0;
		int next  =	0;
		int error = NoError;
		
		response.FromString(stringresponse, start, next, error);
		
		if (!error) {
			Set(response);
			return true;
		};
		
		ErrorPrint(stringresponse, next, error);
	}
}
	
	
Rect2D& Rect2D::FromString(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// string IO error code
) {
	// find size
	int size = source.length();
	
	// check start
	if (start < 0)
		start = 0;
	
	if (start >= size) {
		next  = size;
		error = NoDataError;
		return *this;
	}
	
	// initialize error
	error = NoError;
	
	// process string via const char* pointer
	const char* temp = source.c_str() + start;
	const char* stop = temp;
	
	// use the C string version of FromString
	FromString(temp, stop, error);
	
	// compute next index
	next = start + (stop - temp);
	
	return *this;
}
	
	
Rect2D& Rect2D::FromString(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// string IO error code
) {
	int count = 0;					// component number
	const int components = 4;		// number of components

	double number;					// temporary number variable
	Rect2D result;					// temporary result variable

	// prepare for initial scan
	SkipSpace(start);
	next  = start;
	error = NoError;
	
	while (count < components) {
		// update component count
		count++;

		// update start to read next number
		start = next;

		// read next number
		number = StringToDouble(start, next, error);

		// check for error on read
		if (error)
			break;
		
		// if no error then assign number to proper field
		switch (count) {
			case 1:
				result.x1 = number;
				break;
			
			case 2:
				result.y1 = number;
				break;
			
			case 3:
				result.x2 = number;
				break;
			
			case 4:
				result.y2 = number;
				break;
			
			default:
				break;
		}
			
		// Remove comma separator if present
		
		if (count < components) {
			SkipSpace(next);
		
			if (*next == Comma) {
				next++;
				SkipSpace(next);
			}
		}
	}
	
	// if no error then use result to modify object
	// otherwise just return original object

	if (!error)
		return Set(result);
	else
		return *this;
}


string Rect2D::ToString() const {
	string s1;
	string t1;
	string s2;
	string t2;

	Format(s1, x1);
	Format(t1, y1);
	Format(s2, x2);
	Format(t2, y2);

	string result = s1 + ", " + t1 + ", " + s2 + ", " + t2;

	return result;
}


string Rect2D::ToString(int width, int precision, bool fixed) const {
	string s1;
	string t1;
	string s2;
	string t2;

	Format(s1, x1, width, precision, fixed);
	Format(t1, y1, width, precision, fixed);
	Format(s2, x2, width, precision, fixed);
	Format(t2, y2, width, precision, fixed);

	string result = s1 + ", " + t1 + ", " + s2 + ", " + t2;

	return result;
}
