/***************************************************************************
 * logging.c
 *
 * Log handling routines for libmseed
 *
 * Chad Trabant
 * IRIS Data Management Center
 *
 * modified: 2014.197
 ***************************************************************************/

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

#include "libmseed.h"

void ms_loginit_main (MSLogParam *logp,
		      void (*log_print)(char*), const char *logprefix,
		      void (*diag_print)(char*), const char *errprefix);

int ms_log_main (MSLogParam *logp, int level, va_list *varlist);

/* Initialize the global logging parameters */
MSLogParam gMSLogParam = {NULL, NULL, NULL, NULL};


/***************************************************************************
 * ms_loginit:
 *
 * Initialize the global logging parameters.
 *
 * See ms_loginit_main() description for usage.
 ***************************************************************************/
void
ms_loginit (void (*log_print)(char*), const char *logprefix,
	    void (*diag_print)(char*), const char *errprefix)
{
  ms_loginit_main(&gMSLogParam, log_print, logprefix, diag_print, errprefix);
}  /* End of ms_loginit() */


/***************************************************************************
 * ms_loginit_l:
 *
 * Initialize MSLogParam specific logging parameters.  If the logging parameters
 * have not been initialized (log == NULL) new parameter space will
 * be allocated.
 *
 * See ms_loginit_main() description for usage.
 *
 * Returns a pointer to the created/re-initialized MSLogParam struct
 * on success and NULL on error.
 ***************************************************************************/
MSLogParam *
ms_loginit_l (MSLogParam *logp,
	      void (*log_print)(char*), const char *logprefix,
	      void (*diag_print)(char*), const char *errprefix)
{
  MSLogParam *llog;
  
  if ( logp == NULL )
    {
      llog = (MSLogParam *) malloc (sizeof(MSLogParam));
      
      if ( llog == NULL )
        {
          ms_log (2, "ms_loginit_l(): Cannot allocate memory\n");
          return NULL;
        }
      
      llog->log_print = NULL;
      llog->logprefix = NULL;
      llog->diag_print = NULL;
      llog->errprefix = NULL;
    }
  else
    {
      llog = logp;
    }
  
  ms_loginit_main (llog, log_print, logprefix, diag_print, errprefix);

  return llog;
}  /* End of ms_loginit_l() */


/***************************************************************************
 * ms_loginit_main:
 *
 * Initialize the logging subsystem.  Given values determine how ms_log()
 * and ms_log_l() emit messages.
 *
 * This function modifies the logging parameters in the passed MSLogParam.
 *
 * Any log/error printing functions indicated must except a single
 * argument, namely a string (char *).  The ms_log() and
 * ms_log_r() functions format each message and then pass the result
 * on to the log/error printing functions.
 *
 * If the log/error prefixes have been set they will be pre-pended to the
 * message.
 *
 * Use NULL for the function pointers or the prefixes if they should not
 * be changed from previously set or default values.  The default behavior
 * of the logging subsystem is given in the example below.
 *
 * Example: ms_loginit_main (0, (void*)&printf, NULL, (void*)&printf, "error: ");
 ***************************************************************************/
void
ms_loginit_main (MSLogParam *logp,
		 void (*log_print)(char*), const char *logprefix,
		 void (*diag_print)(char*), const char *errprefix)
{
  if ( ! logp )
    return;

  if ( log_print )
    logp->log_print = log_print;
  
  if ( logprefix )
    {
      if ( strlen(logprefix) >= MAX_LOG_MSG_LENGTH )
	{
	  ms_log_l (logp, 2, 0, "log message prefix is too large\n");
	}
      else
	{
	  logp->logprefix = logprefix;
	}
    }
  
  if ( diag_print )
    logp->diag_print = diag_print;
  
  if ( errprefix )
    {
      if ( strlen(errprefix) >= MAX_LOG_MSG_LENGTH )
	{
	  ms_log_l (logp, 2, 0, "error message prefix is too large\n");
	}
      else
	{
	  logp->errprefix = errprefix;
	}
    }
  
  return;
}  /* End of ms_loginit_main() */


/***************************************************************************
 * ms_log:
 *
 * A wrapper to ms_log_main() that uses the global logging parameters.
 *
 * See ms_log_main() description for return values.
 ***************************************************************************/
int
ms_log (int level, ...)
{
  int retval;
  va_list varlist;
  
  va_start (varlist, level);

  retval = ms_log_main (&gMSLogParam, level, &varlist);

  va_end (varlist);

  return retval;
}  /* End of ms_log() */


/***************************************************************************
 * ms_log_l:
 *
 * A wrapper to ms_log_main() that uses the logging parameters in a
 * supplied MSLogParam.  If the supplied pointer is NULL the global logging
 * parameters will be used.
 *
 * See ms_log_main() description for return values.
 ***************************************************************************/
int
ms_log_l (MSLogParam *logp, int level, ...)
{
  int retval;
  va_list varlist;
  MSLogParam *llog;

  if ( ! logp )
    llog = &gMSLogParam;
  else
    llog = logp;
  
  va_start (varlist, level);
  
  retval = ms_log_main (llog, level, &varlist);

  va_end (varlist);

  return retval;
}  /* End of ms_log_l() */


/***************************************************************************
 * ms_log_main:
 *
 * A standard logging/printing routine.
 *
 * The function uses logging parameters specified in the supplied
 * MSLogParam.
 * 
 * This function expects 2+ arguments: message level, fprintf format,
 * and fprintf arguments. 
 *
 * Three levels are recognized:
 * 0  : Normal log messages, printed using log_print with logprefix
 * 1  : Diagnostic messages, printed using diag_print with logprefix
 * 2+ : Error messagess, printed using diag_print with errprefix
 *
 * This function builds the log/error message and passes to it as a
 * string (char *) to the functions defined with ms_loginit() or
 * ms_loginit_l().  If the log/error printing functions have not been
 * defined messages will be printed with fprintf, log messages to
 * stdout and error messages to stderr.
 *
 * If the log/error prefix's have been set with ms_loginit() or
 * ms_loginit_l() they will be pre-pended to the message.
 *
 * All messages will be truncated to the MAX_LOG_MSG_LENGTH, this includes
 * any set prefix.
 *
 * Returns the number of characters formatted on success, and a
 * a negative value on error.
 ***************************************************************************/
int
ms_log_main (MSLogParam *logp, int level, va_list *varlist)
{
  static char message[MAX_LOG_MSG_LENGTH];
  int retvalue = 0;
  int presize;
  const char *format;
  
  if ( ! logp )
    {
      fprintf(stderr, "ms_log_main() called without specifying log parameters");
      return -1;
    }
  
  message[0] = '\0';

  format = va_arg (*varlist, const char *);

  if ( level >= 2 )  /* Error message */
    {
      if ( logp->errprefix != NULL )
        {
          strncpy (message, logp->errprefix, MAX_LOG_MSG_LENGTH);
          message[MAX_LOG_MSG_LENGTH - 1] = '\0';
        }
      else
        {
          strncpy (message, "Error: ", MAX_LOG_MSG_LENGTH);
        }
      
      presize = strlen(message);
      retvalue = vsnprintf (&message[presize],
   			    MAX_LOG_MSG_LENGTH - presize,
			    format, *varlist);
      
      message[MAX_LOG_MSG_LENGTH - 1] = '\0';

      if ( logp->diag_print != NULL )
        {
          logp->diag_print (message);
        }
      else
        {
          fprintf(stderr, "%s", message);
        }
    }
  else if ( level == 1 )  /* Diagnostic message */
    {
      if ( logp->logprefix != NULL )
        {
          strncpy (message, logp->logprefix, MAX_LOG_MSG_LENGTH);
          message[MAX_LOG_MSG_LENGTH - 1] = '\0';
        }
      
      presize = strlen(message);
      retvalue = vsnprintf (&message[presize],
		            MAX_LOG_MSG_LENGTH - presize,
			    format, *varlist);
      
      message[MAX_LOG_MSG_LENGTH - 1] = '\0';
      
      if ( logp->diag_print != NULL )
        {
          logp->diag_print (message);
        }
      else
        {
          fprintf(stderr, "%s", message);
        }
    }
  else if ( level == 0 )  /* Normal log message */
    {
      if ( logp->logprefix != NULL )
        {
          strncpy (message, logp->logprefix, MAX_LOG_MSG_LENGTH);
          message[MAX_LOG_MSG_LENGTH - 1] = '\0';
        }
      
      presize = strlen(message);
      retvalue = vsnprintf (&message[presize],
			    MAX_LOG_MSG_LENGTH - presize,
			    format, *varlist);
      
      message[MAX_LOG_MSG_LENGTH - 1] = '\0';
      
      if ( logp->log_print != NULL )
	{
           logp->log_print (message);
	}
      else
	{
	  fprintf(stdout, "%s", message);
	}
    }
  
  return retvalue;
}  /* End of ms_log_main() */
