/* $Id: irplib_error.h,v 1.4 2007-10-04 08:57:15 rhaigron Exp $
 *
 * This file is part of the irplib package
 * Copyright (C) 2002,2003,2004,2005,2006 European Southern Observatory
 *
 * This program 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */

/*
 * $Author: rhaigron $
 * $Date: 2007-10-04 08:57:15 $
 * $Revision: 1.4 $
 * $Name: not supported by cvs2svn $
 */

#ifndef IRPLIB_ERROR_H
#define IRPLIB_ERROR_H

/*-----------------------------------------------------------------------------
                                   Includes
 -----------------------------------------------------------------------------*/

#include <cpl.h>
#include <xsh_msg.h>
/*----------------------------------------------------------------------------*/
/**
 * @addtogroup irplib_error
 */
/*----------------------------------------------------------------------------*/
/**@{*/

/*-----------------------------------------------------------------------------
                                   Define
 -----------------------------------------------------------------------------*/
/* fixme:  This is to avoid including irplib_utils.h which includes this file */
#if defined HAVE_DECL___FUNC__ && !HAVE_DECL___FUNC__
#ifndef __func__
#define __func__ ""
#endif
#endif

/*----------------------------------------------------------------------------*/
/**
   @brief   Generic error handling macro
   @param   CONDITION    The condition to check
   @param   ERROR_CODE   The CPL error code to set if @em CONDTION evaluates
                         to false
   @param   MSG          A parentheses-enclosed, printf-style error message.
                         If this is an empty string or a string consisting
                         only of spaces, the default error message
                         associated with the provided error code is used.
   @param   ACTION       A statement that is executed iff the @em CONDITION
                         evaluates to false.

   This macro should not be used directly. It is defined only to allow the user
   to build his/her custom error handling macros.

   Useful definitions might include

   @code

   #define assure(BOOL, CODE)                                               \
     irplib_error_assure(BOOL, CODE, (" "), goto cleanup)

   #define  check(CMD)                                                      \
     irplib_error_assure((CMD, cpl_error_get_code() == CPL_ERROR_NONE),     \
                          cpl_error_get_code(), (" "), goto cleanup)

   #define assert(BOOL)                                                     \
     irplib_error_assure(BOOL, CPL_ERROR_UNSPECIFIED,                       \
                         ("Internal error, please report to "               \
                          PACKAGE_BUGREPORT), goto cleanup)

   @endcode

   or (same as above, but including printf-style error messages)

   @code

   #define assure(BOOL, CODE, ...)                                          \
     irplib_error_assure(BOOL, CODE, (__VA_ARGS__), goto cleanup)

   #define  check(CMD, ...)                                                 \
     irplib_error_assure((CMD, cpl_error_get_code() == CPL_ERROR_NONE),     \
                          cpl_error_get_code(), (__VA_ARGS__), goto cleanup)

   #define assert(BOOL, ...)                                                \
     irplib_error_assure(BOOL, CPL_ERROR_UNSPECIFIED,                       \
                         ("Internal error, please report to "               \
                          PACKAGE_BUGREPORT " " __VA_ARGS__), goto cleanup)
                         / *  Assumes that PACKAGE_BUGREPORT
                              contains no formatting special characters  * /
   
   @endcode

   or

   @code

   #define assure(BOOL, CODE, RETURN)                                         \
     irplib_error_assure(BOOL, CODE, (" "), return RETURN)

   #define assure_code(BOOL, CODE)                                            \
     irplib_error_assure(BOOL, CODE, (" "), return cpl_error_get_code())

   #define skip_if(BOOL)                                                      \
     irplib_error_assure(BOOL,                                                \
                         cpl_error_get_code() != CPL_ERROR_NONE ?             \
                         cpl_error_get_code() : CPL_ERROR_UNSPECIFIED,        \
                         (" "), goto cleanup)

   @endcode



   The check macros in the examples above can be used to check a command
   which set the cpl_error_code in case of failure (or, by use of a comma
   expression, a longer sequence of such commands):
   @code
   check(
       (x = cpl_table_get_int(table, "x", 0, NULL),
        y = cpl_table_get_int(table, "y", 0, NULL),
        z = cpl_table_get_int(table, "z", 0, NULL)),
       ("Error reading wavelength catalogue"));
   @endcode
 
   The provided @em ERROR_CODE, @em MSG and @em ACTION are 
   evaluated/executed only if the @em CONDITION evaluates to false.

   @note Some of the examples above use a goto statement to jump to a 
   cleanup label, assumed to be located immediately before the function's
   unified cleanup-code and exit point. This error-handling scheme
   is reminiscent of using exceptions in languages that support exceptions
   (C++, Java, ...). While the use of goto's "if the error-handling code is
   non-trivial, and if errors can occur in several places" is "sanctioned" 
   by Kernigan&Richie: "The C Programming Language", goto's should be
   avoided in all other cases.


*/
/*----------------------------------------------------------------------------*/

#define irplib_error_assure(CONDITION, ERROR_CODE, MSG, ACTION)                \
  do {\
      if (cpl_error_get_code() != CPL_ERROR_NONE){                             \
          irplib_error_push(cpl_error_get_code(),                              \
                           ("An error occurred that was not caught: %s",       \
                           cpl_error_get_where()) );                           \
          ACTION;                                                              \
      }                                                                        \
      else if (!(CONDITION))                                                   \
      {\
          irplib_error_push(ERROR_CODE, MSG);                                  \
          ACTION;                                                              \
      }                                                                        \
  } while (0)

/*----------------------------------------------------------------------------*/
/**
   @brief   Set or propagate an error
   @param   ec           The CPL error code
   @param   msg          A parentheses-enclosed, printf-style error message.
                         If this is an empty string or a string consisting
                         only of spaces, the default error message
                         associated with the provided error code is used.
   @see irplib_error_push_macro().
*/
/*----------------------------------------------------------------------------*/
#define irplib_error_push(ec, msg)                                             \
      do {                                                                     \
        xsh_irplib_error_set_msg msg;                                          \
        xsh_irplib_error_push_macro(__func__, ec, __FILE__, __LINE__);         \
      } while (0)



/*----------------------------------------------------------------------------*/
/**
   @brief   Print the error queue 
   @param   severity         The severity of the error message (usually
                             CPL_MSG_ERROR or CPL_MSG_WARNING)
   @param   trace_severity   The severity of the error tracing information
                             (e.g. CPL_MSG_ERROR, CPL_MSG_DEBUG or 
			     CPL_MSG_OFF)

   This macro prints the error queue in a format best described by example
   (for @em severity = CPL_MSG_ERROR and
        @em trace_severity = CPL_MSG_DEBUG)

   @code

   [ ERROR ]   Identification loop did not converge. After 13 iterations
   [ ERROR ]   the RMS was 20.3 pixels. (The iterative process did not converge)
   [ DEBUG ]     in [3]uves_wavecal_identify() at uves_wavecal_identify.c :101
   [ DEBUG ]    
   [ ERROR ]   Could not calibrate orders
   [ DEBUG ]     in [2]uves_wavecal_process_chip() at uves_wavecal.c  :426
   [ DEBUG ]     
   [ ERROR ]   Wavelength calibration failed
   [ DEBUG ]     in [1]uves_wavecal() at uves_wavecal.c  :679
   [ DEBUG ]

   @endcode

   Note that
   - the error that occured first (deepest in the call tree)
     is printed first,
   - the error queue is not reset, this is left to the caller
*/
/*----------------------------------------------------------------------------*/
#define irplib_error_dump(severity, trace_severity)                            \
        xsh_irplib_error_dump_macro(__func__, __FILE__, __LINE__,              \
				severity, trace_severity) 

/*-----------------------------------------------------------------------------
                               Functions prototypes
 -----------------------------------------------------------------------------*/

void xsh_irplib_error_reset(void);

/*
 * The following functions should not be called directly ; but they must
 * be exported in order to support the macro nature of the interface
 */
cpl_error_code xsh_irplib_error_push_macro(const char *func,
				       cpl_error_code ec,
				       const char *file, 
				       unsigned int line);
void xsh_irplib_error_dump_macro(const char *func,
			     const char *file,
			     unsigned int line,
			     cpl_msg_severity severity,
			     cpl_msg_severity trace_severity);

void xsh_irplib_error_set_msg(const char *format, ...)
#ifdef __GNUC__
    __attribute__((format (printf, 1, 2)))
#endif
    ;

#endif  /* IRPLIB_ERROR_H */

/**@}*/
