/*                                                                              *
 *   This file is part of the ESO UVES Pipeline                                 *
 *   Copyright (C) 2004,2005 European Southern Observatory                      *
 *                                                                              *
 *   This library 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA       *
 *                                                                              */

/*
 * $Author: amodigli $
 * $Date: 2013-07-01 15:36:29 $
 * $Revision: 1.31 $
 * $Name: not supported by cvs2svn $
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <uves_msg.h>

#include <cpl.h>

#include <stdarg.h>
#include <stdio.h>

/*----------------------------------------------------------------------------*/
/**
 * @defgroup uves_msg        Messaging
 *
 * CPL's info message level is expanded to a set of relative message level.
 * The functions uves_msg_louder() and uves_msg_softer() are used to turn up/down
 * the message volume level (instead of setting the verbosity to an absolute level
 * using @c cpl_msg_info() or @c cpl_msg_debug()).
 * These two functions should be used consistently, so that the volume level is
 * always the same on function exit as it was on function entry.
 *
 * These messaging functions never fail, but might print warnings if called
 * inconsistently.
 */
/*----------------------------------------------------------------------------*/

#undef DEBUG_CALLER              /* Define whether to check consistency 
                    of msg_louder/softer calls */
/* #define DEBUG_CALLER */

#define MAXLEVEL 256
#define MAXSTRINGLENGTH 1000


static int level = 0;                 /* Current message & indentation level  from 0 to MAXLEVEL-1.
                     0 is the most verbose level. */
static int outlevel = -1;             /* Only print message if level is in {0, 1, ..., outlevel}.
                     Always print if outlevel = - 1 */
#ifdef DEBUG_CALLER
static const char *callers[MAXLEVEL]; /* Check the consistency of calls to softer/louder  */
#endif

static char printbuffer[MAXSTRINGLENGTH]; /* Used to pass variable argument list 
                         to cpl_msg_info() */

static const char *domain = "Undefined domain";
                                     /* This is to support getting the current domain 
                      * which is currently not available in CPL
                      */
static bool initialized = false;

static int number_of_warnings = 0;     /* Coun't the number of warnings 
                      since initialization */

/**@{*/
/*-----------------------------------------------------------------------------
                            Implementation
 -----------------------------------------------------------------------------*/
//static void signal_handler(int signum)
//{
//    fprintf(stderr, "Panic! Signal %d caught, I'll just dump a trace and die\n", signum);
//
//    abort();
//}

/*----------------------------------------------------------------------------*/
/**
   @brief    Initialize messaging
   @param    olevel          The output level
   @param    dom             The message domain

   Only messages at levels 0 (most important) to @em outlevel are printed as 'info'. 
   Messages at levels above @em outlevel are printed as 'debug'.
   
   Therefore, set @em outlevel = 0 to print fewest messages. Increase @em outlevel
   to increase verbosity.

   To print all messages as 'info' set @em outlevel to the special value -1
   (which substitutes for infinity).
   
*/
/*----------------------------------------------------------------------------*/
void uves_msg_init(int olevel, const char *dom)
{
    /* Initialize per recipe: */
    number_of_warnings = 0;

//    signal(SIGSEGV, signal_handler);
//    raise(SIGSEGV);
        
    if (!initialized)
    {
        /* Initialize once: */
        outlevel = olevel;

        cpl_msg_set_indentation(2);
        
        /*  CPL message format is
         *  [Time][Verbosity][domain][component] message
         *
         *  Don't show the (variable length and wildly
         *  fluctuating) component. It interferes with
         *  indentation. The component is available anyway
         *  on CPL_MSG_DEBUG_MODE level.
         *
         *  Don't show the time. This is available on
         *  the DEBUG_MODE level. Use esorex --time to time
         *  a recipe.
         */
#if WANT_TIME_MEASURE
        cpl_msg_set_time_on();
#else
        cpl_msg_set_time_off();
#endif
        uves_msg_set_domain(dom);
        cpl_msg_set_domain_on();
        cpl_msg_set_component_off();

        initialized = true;
    }
}


/*----------------------------------------------------------------------------*/
/**
   @brief    Set output level
   @param    olevel          The output level

   See @c uves_msg_init() .
*/
/*----------------------------------------------------------------------------*/
void uves_msg_set_level(int olevel) 
{
    outlevel = olevel; 
} 

/*----------------------------------------------------------------------------*/
/**
   @brief    Decrease message volume
   @param    fct           Identity of calling function

   Don't call this function directly, use @c uves_msg_softer().

*/
/*----------------------------------------------------------------------------*/
void uves_msg_softer_macro(const char *fct)
{
    if (level + 1 < MAXLEVEL)
    {
        level++;
        cpl_msg_indent_more();
#ifdef DEBUG_CALLER
        callers[level] = fct;
#else
        fct = fct; /* Satisfy compiler */
#endif
        }
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Increase message volume
   @param    fct           Identity of calling function

   Don't call this function directly, use @c uves_msg_louder().

*/
/*----------------------------------------------------------------------------*/
void uves_msg_louder_macro(const char *fct)
{
    if (level == 0)
    {
        /* 0 is the loudest, ignore request */
        return;
    }
    
    /* Only make louder, if called from the same function which called
       uves_msg_softer. (disable check if level is more than MAXLEVEL)
    */
#ifdef DEBUG_CALLER
    if (level >= MAXLEVEL || strcmp(callers[level], fct) == 0)
#else
    fct = fct;              /* Satisfy compiler */
#endif
    {
        level--;
        cpl_msg_indent_less();
    }
#ifdef DEBUG_CALLER
    else
    {
        uves_msg_warning("Message level decreased by '%s' but increased by '%s'",
                 callers[level], fct);
    }
#endif
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Print a message on 'info' or 'debug' level
   @param    fct             Identity of calling function
   @param    format          A printf()-like format string

   Don't call this function directly, use @c uves_msg().

   If the current level (which is often equal to the current depth
   of the function call-tree) is less than the output level, the message printed
   on the 'info' level, otherwise it is printed on the 'debug' level.
*/
/*----------------------------------------------------------------------------*/
void uves_msg_macro(const char *fct, const char *format, ...)
{
    va_list al;
    
    va_start(al, format);
    vsnprintf(printbuffer, MAXSTRINGLENGTH - 1, format, al);
    va_end(al);

    printbuffer[MAXSTRINGLENGTH - 1] = '\0';
    
    if (outlevel < 0 || level <= outlevel)
    {
//#undef cpl_msg_info
        cpl_msg_info(fct, "%s", printbuffer);
//#define cpl_msg_info(...)  use__uves_msg__instead__of__cpl_msg_info
    }
    else
    {
        cpl_msg_debug(fct, "%s", printbuffer);
    }
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Get number of warnings printed so far
   @return Number of warnings since initialization of messaging
*/
/*----------------------------------------------------------------------------*/
int uves_msg_get_warnings(void)
{
    return number_of_warnings;
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Accumulate warnings
   @param    n              Number of warnings to add

   The (internal) number of warnings (returned by @c uves_msg_get_warnings())
   is increased by @em n, but without actually printing any warnings.

*/
/*----------------------------------------------------------------------------*/
void uves_msg_add_warnings(int n)
{
    number_of_warnings += n;
}


/*----------------------------------------------------------------------------*/
/**
   @brief    Print a warning message
   @param    fct             Identity of calling function
   @param    format          A printf()-like format string

   Don't call this function directly, use @c uves_msg_warning().

   This function is used instead of @c cpl_msg_warning(), and saves
   the user from typing the calling function name.

   Additionally, record is kept on the total number of warnings printed
   (see @c uves_msg_get_warnings()).

   This function does not read or write the cpl_error_code
*/
/*----------------------------------------------------------------------------*/
void uves_msg_warning_macro(const char *fct, const char *format, ...)
{
    va_list al;
    
    va_start(al, format);
    vsnprintf(printbuffer, MAXSTRINGLENGTH - 1, format, al);
    va_end(al);

    printbuffer[MAXSTRINGLENGTH - 1] = '\0';
    
    cpl_msg_warning(fct, "%s", printbuffer);

    number_of_warnings += 1;
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Get current message domain
   @return    The current message domain set by @c uves_msg_init() or @c uves_msg_set_domain().
*/
/*----------------------------------------------------------------------------*/
const char *uves_msg_get_domain(void)
{
    return domain;
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Set message domain
   @param    d            The new message domain
*/
/*----------------------------------------------------------------------------*/
void uves_msg_set_domain(const char *d)
{
    /* Set domain and remember */
    cpl_msg_set_domain(d);
    domain = d;
}

/**@}*/
