/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation as in version 2 of the License.

 * 
 * The iWear Framework 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
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#ifndef __IWEAR_EXCEPTIONS_H
#define __IWEAR_EXCEPTIONS_H

#ifndef __IWEAR_IWEAR_H
#include <iwear/iwear.h>
#endif

#include <iwear/debug.h>

#include <iwear/ansicolor.h>

extern "C"
{
#include <errno.h>
}
#include "iwear/cstdint.h"

#include <iostream>
#include <stdexcept>
using std::string;
using std::runtime_error;
using std::logic_error;
using std::out_of_range;


namespace iwear
{

class IWAPIT CFFL/*{{{*/
{
private:
protected:
    string file;
    string function;
    uint32_t line;

    string backt;
public:
    CFFL( const string& fu, const string& fl, uint32_t ln ) 
	: file(fl), function(fu), line(ln) { }
    CFFL() : line(0) { }
    virtual ~CFFL() { }
    void set ( const CFFL& cf );

    const string& get_file( void ) const { return file; }
    const string& get_function( void ) const { return function; }
    const string& get_backtrace( void ) const { return backt; }
    uint32_t get_line( void ) const { return line; }
    virtual void output_additional( std::ostream& o ) const { (void)o; }
};/*}}}*/

#define __CFFL__ iwear::CFFL( __PRETTY_FUNCTION__, __FILE__, __LINE__ )
#define THROW( ex, args ) { ex __ex args; __ex.set(__CFFL__); throw __ex; }
#define UNIMPLEMENTED THROW(::iwear::unimplemented_error,(string("Sorry, the function \"") + __FFL__ + "\" is not yet implemented"))

#ifndef IWEAR_DEBUG
#define IWASSERT(x)
#else
#define IWASSERT(x) 		\
if( !(x) )			\
{				\
    try { 			\
	THROW(iwear::assertion,(#x));	\
    }catch( const iwear::assertion& a )		\
    {				\
	std::cerr << a << std::endl;	\
    }				\
    abort();			\
}
#endif


class IWAPIT iwruntime_error: public runtime_error, virtual public CFFL 
{ 
public:
    iwruntime_error( const string& __arg ) : runtime_error(__arg) { }
    ~iwruntime_error( ) throw() { }
};

class IWAPIT iwlogic_error: public logic_error, virtual public CFFL 
{ 
public:
    iwlogic_error( const string& __arg ) : logic_error(__arg) { }
    ~iwlogic_error( ) throw() { }
};

class IWAPIT iwout_of_range: public out_of_range, virtual public CFFL 
{ 
public:
    iwout_of_range( const string& __arg ) : out_of_range(__arg) { }
    ~iwout_of_range( ) throw() { }
};

class IWAPIT iwexception: public std::exception, virtual public CFFL 
{ 
public:
    iwexception( ) : ::std::exception() { }
    ~iwexception( ) throw() { }
    static string backtrace( void );
};

class IWAPIT iwdomain_error: public iwlogic_error
{
public:
    explicit iwdomain_error(const string& arg) : iwlogic_error(arg) { }
};

class IWAPIT assertion: public iwlogic_error
{
public:
    explicit assertion( const string& arg ) : iwlogic_error(arg) { }
    virtual void output_additional( std::ostream& o ) const
    {
	o << "Assertion failed:" << std::endl;
	o << what() << std::endl;
    }
};
/**
 * This class is not directly to be thrown as an exception, but more to inherit
 * within. It incorporates the ability to store an errno number as defined by
 * the POSIX standards. This makes it easier for the application to handle
 * certain error conditions, which are not worth to fit into single exceptions,
 * e.g. socket errors.
 */
class IWAPIT errno_error: public virtual CFFL/*{{{*/
{
private:
   /**
    * Since errno is also defined as int, we do it here too.
    */
    int error_number;
    string error_description;
public:
   /**
    * Takes the error number and saves it, as well as the error description.
    */
    explicit errno_error( int errn_ ) : error_number(errn_) 
    { 
#ifdef HAVE_STRERROR_R
	// We have two variants of the function here.. unfortunately we cannot
	// determine which is used. we could set some defines to control which
	// is used, but then other functions will behave in a way we dont want.
	// So we do the nasty stuff here...
	char serr[256];
	memset(&serr,0,256);
        char* errd = reinterpret_cast<char*>( strerror_r(error_number,serr,256));
//	int rete = (int)(long)(void*)(errd);
	if( errd == 0 || errd == (void*)-1 )
	{
	    error_description = serr;
	}
	else // it is a pointer...
	{
	    error_description = errd;
	}
#else
	error_description = ::strerror(error_number);
#endif
	/*
	cout << "Set error_description = " << error_description << endl;
	cout << "Set errno = " << errno << endl;
	*/
    }
    virtual ~errno_error() { }

   /**
    */
    int err_no( void ) const { return error_number; }
    const string& strerror( void ) const { return error_description; }
    virtual void output_additional( std::ostream& o ) const
    { 
	o << ANSI_BLUE << "Error Code " << ANSI_GREEN << err_no() << std::endl;
	o << ANSI_BLUE << "Error Description " << ANSI_DARKCYAN << strerror() << std::endl;
    }

};/*}}}*/

/**
 * This exception is thrown whenever an insonsistency in data is found, e.g.
 * missing/wrong database entries, or simply values of returned datasize that
 * simply can't be.
 * Since this is meant to be an error for setup conditions, it is an
 * iwlogic_error as it is assumed this is an error in the internal control/setup
 * logics.
 */
class IWAPIT consistency_error : public iwlogic_error/*{{{*/
{
public:
    explicit consistency_error( const string& wht ) : iwlogic_error(wht) { }
};/*}}}*/

/**
 * This is thrown if an operation was attempted while something else was in a
 * wrong internal state.
 * Usually this indicates a severe programming logic error, e.g. not checking
 * the status or wrongly assuming some status in the program flow. Therefore
 * this is an logic error.
 */
class IWAPIT invalidstate_error : public iwlogic_error/*{{{*/
{
    public:
	explicit invalidstate_error( const string& wht ) : iwlogic_error(wht) { }
};/*}}}*/

/**
 * This is to be thrown if a conversion from one unit to another (or one
 * encoding to another) is impossible. Although this can be affected by user
 * input data, it is more often an error in programming. E.g. assuming you can
 * convert seconds to celsius.
 * Therefore this is an logic error.
 */
class IWAPIT conversion_error : public iwlogic_error/*{{{*/
{
public:
    explicit conversion_error( const string& wht ) : iwlogic_error(wht) { }
    inline virtual ~conversion_error() throw() {}
};/*}}}*/

/**
 * This will be thrown if the error condition is about parsing a string.
 * Since this will highly depend on user input data, it is a iwruntime_error.
 */
class IWAPIT parse_error : public iwruntime_error/*{{{*/
{
    uint32_t line;
    string pstr;
    string expected;
public:
    explicit parse_error( const string& wht, uint32_t ln, const string& str, const string& exp = "<unknown>")
	: iwruntime_error(wht), line(ln), pstr(str),expected(exp) { }

    virtual ~parse_error() throw() { }

    /**
     * If applicable this returns the line in which the error occured. The
     * source of the error (file, stream, user input etc.) should be mentioned
     * in the std exception msg string.
     * @note According to the exact semantics of where the error occured, the
     * line information can mean something different, e.g. it can mean a user
     * input field, or an vector index.
     */
    uint32_t get_line( void ) const { return line; }

    /**
     * This gets the string which caused the parse error. For diagnostic
     * reasons this should always be set.
     */
    const string& get_string( void ) const { return pstr; }

    const string& get_expected( void ) const { return expected; }
    virtual void output_additional( std::ostream& o ) const
    {
	o << ANSI_BLUE << "At Input stream/file line" << ANSI_GREEN << get_line() << std::endl;
	o << ANSI_BLUE << "Line causing the error: \"" << ANSI_CYAN << get_string() << ANSI_BLUE << "\"" << std::endl;
	o << ANSI_BLUE << "Expected instead: " << ANSI_GREEN << get_expected() << ANSI_NORMAL << std::endl;
    }
};/*}}}*/

/**
 * This is a generic error that comes from the OS somewhere. It holds an errno,
 * as most system calls set one. It is mostly provided for C Compatibility and
 * mapping error values to one exception. 
 * @note Where applicable a more detailed exception should be used.
 */
class IWAPIT sys_error : public iwruntime_error, public errno_error/*{{{*/
{
public:
    explicit sys_error( const string& wht, int err ) 
	: iwruntime_error(wht), errno_error(err) { }
    virtual ~sys_error() throw () { }
};/*}}}*/

/**
 * This is thrown whenever something in thread specific code wents wrong. Since
 * most thread code errors basically come from the OS or threading library
 * which is presumably written in C this holds an errno value too. It is a
 * sys_error too.
 */
class IWAPIT thread_error : public sys_error/*{{{*/
{
public:
    explicit thread_error( const string& wht, int err ) 
	: sys_error(wht,err) { }
    virtual ~thread_error() throw () { }
};/*}}}*/

/**
 * This error is thrown if something was unimplemented. Since this is
 * preventable before program run (by implementing it) this is a iwlogic_error
 */
class IWAPIT unimplemented_error : public iwlogic_error/*{{{*/
{
public:
    explicit unimplemented_error( const string& wht ) : iwlogic_error(wht) { }
    virtual ~unimplemented_error() throw() {}
};/*}}}*/

/**
 * This error is thrown when a function beeing called is obsolete and shouldnt
 * be used any more
 */
class IWAPIT obsolete_error : public iwlogic_error/*{{{*/
{
public:
    explicit obsolete_error( const string& wht ) : iwlogic_error(wht) { }
    virtual ~obsolete_error() throw() {}
};/*}}}*/

/**
 * This error is to be thrown if e.g. an unexistant date was passed/set, or if
 * the date is invalid for some operation, like setting a past date for a alarm
 * clock.
 */
class IWAPIT invaliddate_error : public iwruntime_error/*{{{*/
{
public:
    explicit invaliddate_error( const string& wht ) : iwruntime_error(wht) { }
    inline virtual ~invaliddate_error() throw() {}
};/*}}}*/

/**
 * This is to be thrown if some argument passed is invalid, either in general
 * or for the current state.
 */
class IWAPIT invalidargument_error : public iwruntime_error
{
    string argtype;
    string argument;
public:
    /**
     * As obviously the argument needs to have an implemented (and in-scope)
     * operator<<. This argument is the argument that caused the invalid arg
     * error. If the operator<< is not implemented, you should at least pass
     * another useful value or the address of the failing object.
     */
    template<class T>  explicit invalidargument_error( const string& wht, const T& arg ) 
	: iwruntime_error(wht) 
	{ 
	    std::stringstream str;
	    str << arg;
	    argument = str.str();

	    argtype = typenameof(arg);
	}
    virtual ~invalidargument_error() throw() {}
    virtual void output_additional( std::ostream& o ) const
    {
	o << ANSI_BLUE << "Type of invalid argument : " << ANSI_GREEN << argtype << std::endl;
	o << ANSI_BLUE << "String representation (or pointer location) of argument : " << ANSI_CYAN << argument << std::endl;
    }
    const string& get_argtype( void ) const { return argtype; }
    const string& get_argument( void ) const { return argument; }
};

class IWAPIT memory_error : public iwruntime_error
{
public:
    explicit memory_error( const string& wht ) : iwruntime_error(wht) { }
    virtual ~memory_error() throw() {}
};

class IWAPIT abort_error : public iwruntime_error
{
public:
    explicit abort_error( const string& wht ) : iwruntime_error(wht) { }
    virtual ~abort_error() throw() {}
};

class IWAPIT rpc_error : public iwruntime_error
{
public:
    explicit rpc_error( const string& wht ) : iwruntime_error(wht) { }
    virtual ~rpc_error() throw() {}
};

/**
 * This error is thrown whenever a NULL Pointer dereference attempt was detected
 */
class IWAPIT nullptr_error : public iwruntime_error
{
public:
    explicit nullptr_error( const string& wht ) : iwruntime_error(wht) { }
    virtual ~nullptr_error() throw() {}
};

/**
 * This error is thrown whenever a Pointer isnt NULL, but obiously invalid.
 */
class IWAPIT invptr_error : public iwruntime_error
{
public:
    explicit invptr_error( const string& wht ) : iwruntime_error(wht) { }
    virtual ~invptr_error() throw() {}
};

/**
 * The system detected a high probable race condition and will therefore not go
 * on with the current functionality. Especially for a SPtr this means that the
 * object whose pointer should be assigned was deleted and does not exist any
 * more !!
 */
class IWAPIT race_error : public iwruntime_error
{
public:
    explicit race_error( const string& wht ) : iwruntime_error(wht) { }
    virtual ~race_error() throw() {}
};

class IWAPIT unsupported_error : public iwruntime_error
{
public:
    explicit unsupported_error( const string& wht ) : iwruntime_error(wht) { }
    virtual ~unsupported_error() throw() {}
};

/**
 * This error happens whenever some mapping fails.
 */
class IWAPIT mapping_error : public iwlogic_error
{
public:
    explicit mapping_error( const string& wht ) : iwlogic_error(wht) { }
    virtual ~mapping_error() throw() {}
};

class IWAPIT sequence_error : public iwlogic_error
{
public:
    explicit sequence_error( const string& wht ) : iwlogic_error(wht) { }
    virtual ~sequence_error() throw() {}
};


class IWAPIT bounds_error : public iwlogic_error
{
protected:
    int32_t value;
    int32_t bound;
public:
    explicit bounds_error( const string& wht, int32_t _val, int32_t _bnd ) : iwlogic_error(wht),value(_val),bound(_bnd) { }
    virtual ~bounds_error() throw() {}
    int32_t get_value( ) { return value; }
    int32_t get_bound( ) { return bound; }
    virtual void output_additional( std::ostream& o ) const
    {
	o << ANSI_BLUE << "Bound value : " << ANSI_GREEN << bound << std::endl;
	o << ANSI_BLUE << "Error causing value : " << ANSI_CYAN << value << std::endl;
    }
};

class IWAPIT timeout_error : public iwruntime_error
{
protected:
    float timeout;
public:
    explicit timeout_error( const string& wht, float to ) : iwruntime_error(wht),timeout(to) { }
    virtual ~timeout_error() throw() { }
    float get_timeout( void ) { return timeout; }

    virtual void output_additional( std::ostream& o ) const
    {
	o << ANSI_BLUE << "Timeout : " << ANSI_GREEN << timeout << std::endl;
    }
};

/**
 * Whenver sometihng with the database goes wrong, this is thrown.
 */
class IWAPIT database_error : public iwruntime_error
{
public:
    explicit database_error( const string& wht ) : iwruntime_error(wht) {}
    virtual ~database_error() throw() {}
	
};

/**
 * A Version mismatch is indicated by this error.
 */
class IWAPIT version_error : public iwruntime_error
{
protected:
    uint32_t exp;
    uint32_t have;
public:
    explicit version_error( const string& wht, uint32_t _exp, uint32_t _have ) : 
	iwruntime_error(wht), exp(_exp), have(_have) {}
    virtual ~version_error() throw() {}

    uint32_t get_expected( void ) { return exp; }
    uint32_t get_found( void ) { return have; }
    virtual void output_additional( std::ostream& o ) const
    {
	o << ANSI_BLUE << "Expected Version : " << ANSI_GREEN << exp << std::endl;
	o << ANSI_BLUE << "Found Version : " << ANSI_CYAN << have << std::endl;
    }
};


class IWAPIT chained_error : public iwruntime_error
{
    const chained_error* sub_error;
public:
    const chained_error* get_sub_error( void ) const { return sub_error; }
    explicit chained_error( const string& wht, const chained_error* ce ) : 
	    iwruntime_error(wht),sub_error(ce) { }
    virtual ~chained_error() throw () { }
};

class IWAPIT buffer_error : public chained_error
{
public:
    explicit buffer_error( const string& wht, const chained_error* ce ) 
	: chained_error(wht,ce) { }
    virtual ~buffer_error() throw() { }
    
};

namespace net 
{

class IWAPIT protocol_error: public chained_error
{
protected:
    uint32_t chan;
    uint32_t err;
public:
    uint32_t error( void ) const { return err; }
    uint32_t channel( void ) const { return chan; }
    explicit protocol_error( const string& wht, uint32_t ch, uint32_t er, const chained_error* ce ) 
	: chained_error(wht,ce), chan(ch),err(er) { }
    virtual ~protocol_error() throw () { }

    virtual void output_additional( std::ostream& o ) const
    {
	o << ANSI_BLUE << "Occured on Channel: " << ANSI_GREEN << chan << std::endl;
	o << ANSI_BLUE << "Error Code: " << ANSI_CYAN << err << std::endl;
    }
};
/**
 * This is an error, directly derived from the std::iwruntime_error, which is
 * part of the C++ standard.
 * It indicates a generic error when operating on a socket.
 */
class IWAPIT socket_error : public iwruntime_error, public errno_error
{
public:
    explicit socket_error( const string& wht, int err ) : iwruntime_error(wht), errno_error(err) { }
    virtual ~socket_error() throw () { }
};

/**
 * This is generic error if a connection goes wrong.
 */
class IWAPIT connection_error : public socket_error
{
    public:
	explicit connection_error( const string& wht, int err ) : socket_error(wht,err) {}
	virtual ~connection_error() throw ()  { }
};

/**
 * If a system refuses a  connection, this error is thrown.
 */
class IWAPIT connrefused_error : public connection_error
{
    public:
	explicit connrefused_error( const string& wht, int err ) : connection_error(wht,err) {}
	virtual ~connrefused_error() throw ()  { }
};

/**
 * If the connection is lost, we throw this error.
 */
class IWAPIT connlost_error : public connection_error
{
    public:
	explicit connlost_error( const string& wht, int err ) : connection_error(wht,err) {}
	virtual ~connlost_error() throw ()  { }
};

/**
 * If the connection cannot do something within time limit, its timed out.
 */
class IWAPIT conntimeout_error : public connection_error
{
    public:
	explicit conntimeout_error( const string& wht, int err ) : connection_error(wht,err) {}
	virtual ~conntimeout_error() throw ()  { }
};

class IWAPIT noconn_error : public connection_error
{
    public:
	explicit noconn_error( const string& wht, int err ) : connection_error(wht,err) {}
	virtual ~noconn_error() throw() { }
};

} // namespace net

/**
 * We intentionally do not derive from std::exception so people who usually
 * catch it will miss it. When this exception is thrown, something severly went
 * wrong which will affect the whole program and might have corrupted the stack
 * and/or heap.
 * @warning Never throw this exception if the current code flow could go
 * further, e.g. when confused due to user input etc. Its really for the case
 * when a severe bug has been detected in an important implementation.
 */
#define BUGFFL __PRETTY_FUNCTION__, __FILE__, __LINE__
#define BUG(x) throw bug(x,BUGFFL);
class IWAPIT bug
{
private:
    string what;
    string function;
    string file;
    uint32_t line;
public:
    explicit bug( const string& what_, const string& function_, const string& file_, uint32_t line_ )
	: what(what_),
	function(function_),
	file(file_),
	line(line_)
	{
	    std::cerr << "=================================================================" << std::endl;
	    std::cerr << "BUG detected at " << file << ":" << line << std::endl;
	    std::cerr << "in " << function << std::endl;
	    std::cerr << std::endl;
	    std::cerr << "Reason : " << what << std::endl;
	    std::cerr << "=================================================================" << std::endl;
	    abort(); // to get a stacktrace of what caused it
	}

    virtual ~bug()
    {
	abort();
    }
};

} // namespace iwear

/**
 * @note Since both arguments to this operator are in namespace std, this might
 * trigger some seemingly odd behaviour, often referred to as "Koenig Lookup".
 * When you bring std::ostream and exception into global scope by doing "using
 * namespace std;" or when you bring the operator<< into global scope by doing
 * "using namespace iwear;" the Koenig lookup does not find the operator<<
 * anymore, and thus you will most likely get an compile error. 
 * Since using namespaces to the global namespace isnt recommended anyways, and
 * you will probably not do it, this should not be a problem to you.
 *
 * The easiest way to use that operator when you are not in exactly namespace
 * iwear, is to do a "using iwear::operator<" in the namespace you want to use
 * it for.
 */
namespace iwear {
::std::ostream& operator<<( ::std::ostream& o, const ::std::exception& re );
}
#endif

