/*	XError.h
 *
 *		Standardized error handling mechanism.
 *
 *		There are three types of errors that can happen. The first are
 *	"fatal errors" which cause an exception to be thrown upwards. This
 *	should be done when something happens that prevents processing from
 *	continuing.
 *
 *		The second are "caution" alerts which give the user the option
 *	to interrupt processing. This is handled by a standard routine
 *	which posts the appropriate error and throws an exception if
 *	needed.
 *
 *		The third are "warnings" which throw up an alert but does not
 *	interrupt the flow of processing.
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

#ifndef PandaWave_XERROR
#define PandaWave_XERROR

#include <XConfig.h>
#include <XDataUtil.h>
#include <stdarg.h>					// XGPostError varargs

/************************************************************************/
/*																		*/
/*	Enumeration Objects													*/
/*																		*/
/************************************************************************/

/*$	XGAlertType
 *
 *		This controls the type of alert that XGPostError puts up.
 *	By default this is the 'fatal' error alert, which is used when
 *	processing was interrupted.
 */

enum XGAlertType {
	KXGAlertFatal = 1,
	KXGAlertCaution = 2,
	KXGAlertWarning = 3
};

/************************************************************************/
/*																		*/
/*	Error Codes															*/
/*																		*/
/************************************************************************/

/**@name Support--YAAF Internal Error Codes
 *
 *		These are the error codes used by YAAF to specify problems
 *	internally. Generally these are passed to the system by throwing
 *	an XGPostError exception upwards, and is normally caught by the
 *	default error handler in YAAF's default event loop handler.
 *
 *		The strings are provided in the default resources bundled
 *	with YAAF, and are stored in a string resource with identifier 128.
 */

//@{

// The resource identifier of the YAAF resource error codes
#define KXGYAAFErrorCodes		128

/// Insufficient memory to complete this operation
#define KXGOutMemory			MAKEDWORD(KXGYAAFErrorCodes,0)

/// An programmer error occured. This is used for assertion failures
#define KXGAssertError			MAKEDWORD(KXGYAAFErrorCodes,1)

/// The index provided to the XGStringList class was out of bounds.
#define KXGStringListError		MAKEDWORD(KXGYAAFErrorCodes,2)

/// A request to copy extra data into or out of an XGStringList failed
#define KXGStringListExtra		MAKEDWORD(KXGYAAFErrorCodes,3)

/// An internal error occured while trying to open the XGPreferences object
#define KXGPreferenceError		MAKEDWORD(KXGYAAFErrorCodes,4)

/// An internal error occured while trying to create a new thread.
#define KXGThreadCreateError	MAKEDWORD(KXGYAAFErrorCodes,5)

/// Threading is not currently supported by this build of YAAF.
#define KXGThreadNotSupported	MAKEDWORD(KXGYAAFErrorCodes,6)

///
#define KXGRandomException		MAKEDWORD(KXGYAAFErrorCodes,7)

///
#define KXGWindowFactoryError	MAKEDWORD(KXGYAAFErrorCodes,8)

///
#define KXGSingleAppError		MAKEDWORD(KXGYAAFErrorCodes,9)

///
#define KXGSingleAppDupError	MAKEDWORD(KXGYAAFErrorCodes,10)

///
#define KXGAppCoreError			MAKEDWORD(KXGYAAFErrorCodes,11)

///
#define KXGViewFactoryError		MAKEDWORD(KXGYAAFErrorCodes,12)

///
#define KXGPrintError			MAKEDWORD(KXGYAAFErrorCodes,13)

///
#define KXGPrintChooser			MAKEDWORD(KXGYAAFErrorCodes,14)

/// Specified file was not found (or other open error)
#define KXGFileOpenError		MAKEDWORD(KXGYAAFErrorCodes,15)

/// Specified file could not be created
#define KXGFileCreateError		MAKEDWORD(KXGYAAFErrorCodes,16)

/// Read error
#define KXGFileReadError		MAKEDWORD(KXGYAAFErrorCodes,17)

/// Write error
#define KXGFileWriteError		MAKEDWORD(KXGYAAFErrorCodes,18)

/// SetPos error
#define KXGFileSetPosError		MAKEDWORD(KXGYAAFErrorCodes,19)

/// SetEOF error
#define KXGFileSetEOFError		MAKEDWORD(KXGYAAFErrorCodes,20)

/// File does not exist
#define KXGFileNoExistError		MAKEDWORD(KXGYAAFErrorCodes,21)

/// File already exists
#define KXGFileExistError		MAKEDWORD(KXGYAAFErrorCodes,22)

//@}

/************************************************************************/
/*																		*/
/*	Standard Error Objects												*/
/*																		*/
/************************************************************************/

/**@name Support--Standard Error Processing Classes*/
//@{

/*$	XGError
 *
 *		This is a standard error handling class which is used to specify
 *	how errors are to be handled.
 *
 *		Okay, here's the deal. At the top of the event loop, if my
 *	library catches the event, I will attempt to call the 'DoAlert'
 *	method, and then continue on the loop. This allows me to present
 *	a meaningful error message when the error passes up to me. All
 *	other exceptions that are punted to me that are not a descendant
 *	of the XGError class will present a "unknown error" exception
 *
 *		This is an abstract class; you must override the three public
 *	methods below in order to instantiate an actual class.
 */

class XGError {
	public:
								XGError();
		virtual					~XGError();
		
		/*$	XGError::DisplayError
		 *
		 *		This abstract method is called by YAAF when
		 *	an exception of type XGError is caught.
		 */
		
		virtual void			DisplayError(void) = 0;

		/*$	XGError::GetShortError
		 *
		 *		This abstract method returns the "short"
		 *	error message. This is used to get the short
		 *	error description.
		 */
		
		virtual void			GetShortError(char *) = 0;

		/*$	XGError::GetLongError
		 *
		 *		This abstract method returns the "long"
		 *	error message. This is used to get a detailed
		 *	description of the error.
		 */
		
		virtual void			GetLongError(char *) = 0;
};

/************************************************************************/
/*																		*/
/*	Standard errors														*/
/*																		*/
/************************************************************************/

/*$	XGAbort
 *
 *		Throw this object to abort. This does nothing for the 'DoAlert'
 *	method--which means that this is a great way to quietly abort
 *	some processing or another
 */

class XGAbort : public XGError {
	public:
								XGAbort();
		
		/*
		 *	Standard punt
		 */
		
		virtual void			DisplayError(void);
		virtual void			GetShortError(char *);
		virtual void			GetLongError(char *);
};

/*$	XGPostError
 *
 *		Post an error message. This is the mechanism that YAAF
 *	uses to post errors. This is not re-entrant, meaning that
 *	you cannot throw multiple of these objects around. Part of
 *	the reason for this is that in order to accomodate the limited
 *	size of the exception buffer used on some platforms, I use
 *	global variables for storing some aspects of the error state.
 *
 *		NOTE: The XGAlertType parameter in the constructor is used
 *	with the Caution() and Warning() routines (below).
 */

class XGPostError : public XGError {
	public:
								XGPostError();
								XGPostError(unsigned long,...);
								XGPostError(unsigned long, va_list);

		/*$	XGPostError::SetAlertType
		 *
		 *		This is used internally by YAAF to set the type
		 *	of error alert that is thrown up when DisplayError
		 *	is called.
		 */
		 
		void					SetAlertType(XGAlertType a)
									{
										fType = a;
									}

		/*
		 *	Post error message
		 */
		
		virtual void			DisplayError(void);
		virtual void			GetShortError(char *);
		virtual void			GetLongError(char *);
		
		/*
		 *	'Caution' alert support
		 */
		
		/*$	XGPostError::GetAlertResult
		 *
		 *		This is used internally by YAAF to determine
		 *	if the cancel alert returns 'OK' or 'Cancel'
		 */
		
		static bool				GetAlertResult()
									{
										return gResult;
									}

	protected:
		/*
		 *	Setting the message (for my child classes)
		 */
		
		void					SetMessage(unsigned long, ...);
		void					SetMessage(unsigned long, va_list);

		static char				gSMessage[];
		static char				gLMessage[];
		static bool				gResult;
		
		// The type of alert that is being posted.
		XGAlertType				fType;
};

/************************************************************************/
/*																		*/
/*	Platform specific error reporting									*/
/*																		*/
/************************************************************************/

#if OPT_MACOS == 1

/*$	XGPostMacOSError
 *
 *		Post the Macintosh OS Error. This loads the error message from
 *	a resource file in my resource fork. If that error resource is not
 *	present, this punts the error message
 */

class XGPostMacOSError : public XGPostError {
	public:
								XGPostMacOSError(OSErr, unsigned long, ...);
		
		/*
		 *	Error management
		 */
		
		static void				GetErrorString(OSErr,char *);
		virtual void			GetLongError(char *);

		/*
		 *	Get MacOS Specific error code
		 */
		
		OSErr					GetMacOSError()
									{
										return fErrorID;
									}
	private:
		OSErr					fErrorID;
};

#endif

#if OPT_WINOS == 1

/*$	XGPostWinOSError
 *
 *		Post a Windows OS Error. This gets the error string from the
 *	Windows API function ::FormatMessage(), and assumes the error
 *	message is derived from ::GetLastError().
 */

class XGPostWinOSError : public XGPostError {
	public:
								XGPostWinOSError(DWORD, unsigned long,...);
	
		/*
		 *	Simplified interface to ::FormatMessage()
		 */
		
		static void				GetErrorString(DWORD,char *);
		virtual void			GetLongError(char *);
	
	private:
		DWORD					fErrorID;
};

#endif

#if OPT_XWIN == 1

/*$	XGPostUnixOSError
 *
 *		Post a Unix OS Error. This gets the error string from the
 *	Posix function ::strerror(), and assumes the error
 *	message is derived from errno.
 */

class XGPostUnixOSError : public XGPostError {
	public:
								XGPostUnixOSError(int, unsigned long,...);
	
		/*
		 *	Simplified interface to ::FormatMessage()
		 */
		
		static void				GetErrorString(int,char *);
		virtual void			GetLongError(char *);

	private:
		short					fErrorID;
};

#endif

//@}

/************************************************************************/
/*																		*/
/*	Routines															*/
/*																		*/
/************************************************************************/

extern void Error(unsigned long,...);
extern bool Caution(unsigned long,...);
extern void Warning(unsigned long,...);
extern void Message(const char *title, const char *smsg, const char *lmsg, ...);
extern void Message(const char *title, unsigned long, ...);

/************************************************************************/
/*																		*/
/*	Assertion Macros													*/
/*																		*/
/************************************************************************/

/*	_InlineError
 *
 *		To make the Assert macro a legal expression
 */

inline short _InlineError(char *a, char *f, long l)
{
	throw XGPostError(KXGAssertError,a,f,l);
	return 0;
}

/*$	Assert
 *
 *		This macro expands to a function which handles program
 *	asserts. If there is a problem, this throws an error and 
 *  unwinds, rather than halting the program
 */

#define Assert(x)	((x) ? 0 : _InlineError(#x,__FILE__,(long)__LINE__))
											   

#endif // PandaWave_XERROR
