/*
 * A simple implementation of a logging facility for Pan-STARRS
 */
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include "psLib.h"

/*****************************************************************************/

static int logDest = PS_LOG_TO_STDERR;	// where to log messages
static int logLevel = PS_LOG_INFO;	// log all messages at this or above

/*****************************************************************************/
/*
 * Set the current log level and return old level
 */
int psLogSetLevel(int level)		// new level
{
    int oldLevel = logLevel;

    if (level < 0 || level > 9) {
	psLogMsg("utils.logmsg", PS_LOG_WARN,
		 "Attempt to set invalid logMsg level: %d", level);
	level = (level < 0) ? 0 : 9;
    }

    logLevel = level;

    return oldLevel;
}

/*****************************************************************************/

int psLogSetDestination(int dest)	// new destination
{
    int old = logDest;

    switch (dest) {
      case PS_LOG_NONE:
      case PS_LOG_TO_STDOUT:
      case PS_LOG_TO_STDERR:
	logDest = dest;
	break;

      default:
	fprintf(stderr,"Unknown logDestination: %d (ignored)\n", dest);
	break;
    }

    return old;
}

/*****************************************************************************/
/*
 * Set the format of psLogMsg output.
 *
 * More precisely, provide a string consisting of the letters H (host), L (level), M (message),
 * N (name), and T (time).  The default is "HLMNT"
 */
static int log_time = 1, log_host = 1, log_level = 1, log_name = 1, log_msg = 1;

void psLogSetFormat(const char *fmt)
{
    int nlog_time, nlog_host, nlog_level, nlog_name, nlog_msg; // new values of log_time etc.

    nlog_host = nlog_level = nlog_msg = nlog_name = nlog_time = 0; // don\'t log anything
    for (const char *ptr = fmt; *ptr != '\0'; ptr++) {
	switch (*ptr) {
	  case 'H': case 'h':
	    nlog_host = 1;
	    break;
	  case 'L': case 'l':
	    nlog_level = 1;
	    break;
	  case 'M': case 'm':
	    nlog_msg = 1;
	    break;
	  case 'N': case 'n':
	    nlog_name = 1;
	    break;
	  case 'T': case 't':
	    nlog_time = 1;
	    break;
	  default:
	    psError(__func__, PS_ERR_UNKNOWN, 1, "Unknown logging keyword %c", *ptr);
	    break;
	}
    }

    if (!nlog_msg) {
	psTrace("utils.logMsg", 1, "You must at least log error messages (You chose \"%s\")", fmt);
    }

    log_host = nlog_host;
    log_level = nlog_level;
    log_msg = nlog_msg;
    log_name = nlog_name;
    log_time = nlog_time;
}


/*****************************************************************************/
#if !defined(HOST_NAME_MAX)		// should be in limits.h
#  define HOST_NAME_MAX 256
#endif

void psLogMsgV(const char *name, int level, const char *fmt, va_list ap)
{
    static int first = 1;
    static char hostname[HOST_NAME_MAX + 1];
     
    if (level > logLevel || logDest == PS_LOG_NONE) {
	return;
    }

    if (first) {
	first = 0;
	gethostname(hostname, HOST_NAME_MAX);
    }
    
    char clevel;			// letter-name for level
    switch (level) {
      case PS_LOG_ABORT:
	clevel = 'A';
	break;
	
      case PS_LOG_ERROR:
	clevel = 'E';
	break;
	
      case PS_LOG_WARN:
	clevel = 'W';
	break;
	
      case PS_LOG_INFO:
	clevel = 'I';
	break;

      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
      case 9:
	clevel = level + '0';
	break;

      default:
	psTrace("utils.logMsg", 2, "Invalid logMsg level: %d (%s)\n",
		level, fmt);
	level = (level < 0) ? 0 : 9;
	break;
    }

    char head[HOST_NAME_MAX + 40];	// yes, this is long enough
    char *head_ptr = head;		// where we\'ve got to in head
    time_t clock = time(NULL);
    struct tm *utc = gmtime(&clock);

    if (log_time) {
	sprintf(head_ptr, "%4d:%02d:%02d %02d:%02d:%02dZ",
		utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday,
		utc->tm_hour, utc->tm_min, utc->tm_sec);
	head_ptr += strlen(head_ptr);
    }
    if (log_host) {
	if (head_ptr > head) {
	    *head_ptr++ = '|';
	}
	sprintf(head_ptr, "%-20s", hostname);
	head_ptr += strlen(head_ptr);
    }
    if (log_level) {
	if (head_ptr > head) {
	    *head_ptr++ = '|';
	}
	sprintf(head_ptr, "%c", clevel);
	head_ptr += strlen(head_ptr);
    }
    if (log_name) {
	if (head_ptr > head) {
	    *head_ptr++ = '|';
	}
	sprintf(head_ptr, "%-15s", name);
	head_ptr += strlen(head_ptr);
    }
    if (head_ptr > head) {
	*head_ptr++ = '|';
    }
    *head_ptr = '\0';
    
    switch (logDest) {
      case PS_LOG_TO_STDOUT:
	puts(head);
	vprintf(fmt, ap);
	if (fmt[strlen(fmt) - 1] != '\n') {
	    putc('\n', stdout);
	}
	break;

      case PS_LOG_TO_STDERR:
	fputs(head, stderr);
	vfprintf(stderr, fmt, ap);
	if (fmt[strlen(fmt) - 1] != '\n') {
	    putc('\n', stderr);
	}
	break;

      default:
	fprintf(stderr, "psLogMsg: You cannot get here (%s:%d)\n",
		__FILE__, __LINE__);
	abort();
    }
}

void psLogMsg(const char *name, int level, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);

    psLogVMsg(name, level, fmt, ap);
    va_end(ap);
}
