/* $Id$ */

/**
 * \file 
 * File specific comments.
 *
 * Most recent change:
 *   $Date$
 * 
 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009 The University of Sydney, Australia
 *
 * This file is part of GPlates.
 *
 * GPlates is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, version 2, as published by
 * the Free Software Foundation.
 *
 * GPlates is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#ifndef GPLATES_GLOBAL_EXCEPTION_H
#define GPLATES_GLOBAL_EXCEPTION_H

#include <exception>
#include <iosfwd>
#include <string>

#include "utils/CallStackTracker.h"
#include "utils/QtStreamable.h"


// Note: we don't use BOOST_CURRENT_FUNCTION anymore since it can produce some pretty
// verbose output when a function has arguments that are template types.
// The filename and line number are all that are really needed.
#define GPLATES_EXCEPTION_SOURCE \
		GPlatesUtils::CallStack::Trace(__FILE__, __LINE__)


namespace GPlatesGlobal
{
	/**
	 * This is the base class of all exceptions in GPlates.
	 */
	class Exception :
			public std::exception,
			// Gives us "operator<<" for qDebug(), etc and QTextStream, if we provide for std::ostream...
			public GPlatesUtils::QtStreamable<Exception>
	{
		public:
			/**
			 * Constructor collects the current call stack trace as generated
			 * by @a CallStack.
			 * This is done in the constructor since that's where the exception
			 * is generated.
			 * Also adds the location at which exception the is thrown
			 * to the call stack trace.
			 * The location is given by the constructor arguments.
			 *
			 * You can conveniently call like:
			 *     throw Exception(GPLATES_EXCEPTION_SOURCE);
			 */
			Exception(
					const GPlatesUtils::CallStack::Trace &exception_source);

			virtual
			~Exception() throw() { }

			/**
			 * Write the message of an exception into the supplied output stream @a os.
			 *
			 * If @a include_exception_name is true then the exception name is prefixed to the message.
			 * If @a include_call_stack_trace is true then the call stack trace to the point at which
			 * the exception was thrown is appended to the message.
			 *
			 * It is not intended that these messages be internationalised for users --
			 * they are purely for debugging output when an exception is caught at the
			 * base-most frame of the function call stack.
			 */
			void
			write(
					std::ostream &os,
					bool include_exception_name = true,
					bool include_call_stack_trace = true) const;

			/**
			 * Returns a string containing the call stack trace to the location
			 * at which this exception was thrown.
			 */
			void
			get_call_stack_trace_string(
					std::string	&call_stack_trace_string) const
			{
				call_stack_trace_string = d_call_stack_trace_string;
			}

		protected:
			/**
			 * @return The name of this Exception.
			 */
			virtual
			const char *
			exception_name() const = 0;

			/**
			 * Derived classes can override this method and write their
			 * special message to the stream @a os.
			 * Default is to write nothing.
			 * This is ok for those exceptions where the exception class name
			 * provides enough description because @a write will output the
			 * exception class name.
			 */
			virtual
			void
			write_message(
					std::ostream &os) const
			{  }

			/**
			 * A convenience method so derived classes that contain only a string message
			 * can call without having to include <ostream> in the header and hence
			 * slow down compilation.
			 * This way it's defined in one place and in a ".cc" file rather than
			 * required each derived class to have a ".cc" file.
			 */
			void
			write_string_message(
					std::ostream &os,
					const std::string &message) const;

		private:
			/**
			 * Override std::exception.
			 */
			virtual
			const char *
			what() const throw();


			/**
			 * Stores the call stack trace at the point this exception was thrown.
			 * This has to be stored since the location at which we catch the exception
			 * and retrieve this string is different to the location at which the exception
			 * is thrown. And the call stack trace differs between those two locations.
			 */
			std::string d_call_stack_trace_string;

			/**
			 * The exception message generated by @a write_message is stored here
			 * when std::exception::what() is called.
			 *
			 * This is because is returns a 'const char *' and hence needs to point to something.
			 *
			 * This remains empty unless std::exception::what() is called.
			 */
			mutable std::string d_std_exception_what_message;

			/**
			 * Generates the call stack trace string.
			 * Must be called in the constructor since that's where the exception
			 * is thrown and that's where we want to record the call stack trace.
			 */
			void
			generate_call_stack_trace_string();
	};


	class NeedExitException : 
		public GPlatesGlobal::Exception
	{

	public:
		
		NeedExitException(
				const GPlatesUtils::CallStack::Trace &exception_source) :
			GPlatesGlobal::Exception(exception_source)
		{ }
		
	protected:
		const char *
			exception_name() const
		{
			return "Need Exit Exception";
		}

		void
			write_message(
			std::ostream &os) const
		{
			os << "GPlates needs to exit now.";
		}
	};


	/**
	 * Insert a string representation of the exception @a ex into the output stream @a os.
	 */
	std::ostream &
	operator <<(
			std::ostream &os,
			const Exception &ex);
}

#endif  // GPLATES_GLOBAL_EXCEPTION_H
