/*                                                                          *
 *   This file is part of the ESO SINFO 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: 2008-02-12 14:57:39 $
 * $Revision: 1.7 $
 * $Name: not supported by cvs2svn $
 */

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

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

/**@{*/
/*--------------------------------------------------------------------------*/
/**
 * @defgroup sinfo_msg        Messaging
 *
 * CPL's info message level is expanded to a set of relative message level.
 * The functions sinfo_msg_louder() and sinfo_msg_softer() are used to turn 
 up/down the message level (instead of setting the verbosity to an absolute
 level)
 * These two functions should be used consistently, so that the level is always
 * the same on function exit as on function entry.
 *
 * These functions never fail, but print warnings if called inconsistently.
 */
/*---------------------------------------------------------------------------*/

#define DEBUG_CALLER 0  /* 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
const char *sinfo_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 int initialized = FALSE;

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

/*--------------------------------------------------------------------------*/
/**
 @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 has the effect of infinity).

 */
/*--------------------------------------------------------------------------*/
void
sinfo_msg_init(int olevel, const char *dom)
{
    /* Initialize per recipe: */
    number_of_warnings = 0;

    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 level.
         */
        cpl_msg_set_time_on();
        sinfo_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 sinfo_msg_init() .
 */
/*---------------------------------------------------------------------------*/
void
sinfo_msg_set_level(int olevel)
{
    outlevel = olevel;
}

/*---------------------------------------------------------------------------*/
/**
 @brief    Decrease message level
 @param    fctid Identity of calling function

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

 */
/*---------------------------------------------------------------------------*/
void
sinfo_msg_softer_macro(const char *fctid)
{
    if (level + 1 < MAXLEVEL) {
        level++;
        cpl_msg_indent_more();
#if DEBUG_CALLER
        sinfo_callers[level] = fctid;
#else
        fctid = fctid; /* Satisfy compiler */
#endif
    }
}

/*---------------------------------------------------------------------------*/
/**
 @brief    Increase message level
 @param    fctid           Identity of calling function

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

 */
/*---------------------------------------------------------------------------*/
void
sinfo_msg_louder_macro(const char *fctid)
{
    if (level == 0) {
        /* 0 is the loudest, ignore request */
        return;
    }

    /* Only make louder, if called from the same function which called
     sinfo_msg_softer. (disable check if level is more than MAXLEVEL)
     */
#if DEBUG_CALLER
    if (level >= MAXLEVEL || strcmp(sinfo_callers[level], fctid) == 0)
#else
    fctid = fctid; /* Satisfy compiler */
#endif
    {
        level--;
        cpl_msg_indent_less();
    }
#if DEBUG_CALLER
    else
    {
        sinfo_msg_warning("Message level decreased by '%s' but increased by '%s'",
                        sinfo_callers[level], fctid);
    }
#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 sinfo_msg().

 If the current level (which might be 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
sinfo_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__sinfo_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
sinfo_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 sinfo_msg_get_warnings())
 is increased by @em n, but without actually printing any warnings.

 */
/*---------------------------------------------------------------------------*/
void
sinfo_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 sinfo_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 sinfo_msg_get_warnings()).
 */
/*---------------------------------------------------------------------------*/
void
sinfo_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 sinfo_msg_init() or
 @c sinfo_msg_set_domain().
 */
/*---------------------------------------------------------------------------*/
const char *
sinfo_msg_get_domain(void)
{
    return domain;
}

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

/**@}*/
