/*
 * report.c
 * This file is part of LCDd, the lcdproc server.
 *
 * This file is released under the GNU General Public License. Refer to the
 * COPYING file distributed with this package.
 *
 * Copyright (c) 1999, William Ferrell, Scott Scriven
 *		 2001, Joris Robijn
 *		 2005, Peter Marschall
 *
 * Contains reporting functions
 *
 */

#include <stdlib.h>
#include "report.h"

#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifndef WIN32
# include <syslog.h>
#else
# include <windows.h>
#endif

static int report_level = RPT_INFO;
static int report_dest = RPT_DEST_STORE;

#ifdef WIN32
HANDLE event_log_handle = NULL;
#endif

#define MAX_STORED_MSGS 200

static char *stored_msgs[MAX_STORED_MSGS];
static int stored_levels[MAX_STORED_MSGS];
static int num_stored_msgs = 0;


/* local functions */
static void store_report_message( int level, const char *message );
static void flush_messages();

void report( const int level, const char *format, .../*args*/ )
{
	/* Check if we should report it */
	if( level <= report_level || report_dest == RPT_DEST_STORE ) {

	  	char buf[1024];

		/* Following functions appear to work on RedHat and Debian
		 * Linux, FreeBSD and Solaris
		 */

		va_list ap;
		va_start(ap, format); /* measure the required size (the number of elements of format) */

		switch( report_dest ) {
		  case RPT_DEST_STDERR:
			vfprintf( stderr, format, ap );
			fprintf( stderr, "\n" );
			break;
		  case RPT_DEST_SYSLOG:
                  {
#ifndef WIN32
			vsyslog( LOG_USER|(level+2), format, ap );
#else
                        LPTSTR tmpArray[1];
                        WORD eventType;
                        switch (level)
                        {
                        case RPT_CRIT:
                        case RPT_ERR:
                            eventType = EVENTLOG_ERROR_TYPE;
                            break;
                        case RPT_WARNING:
                            eventType = EVENTLOG_WARNING_TYPE;
                            break;
                        case RPT_NOTICE:
                        case RPT_INFO:
                        case RPT_DEBUG:
                        default:
                            eventType = EVENTLOG_INFORMATION_TYPE;
                            break;
                        }

                        vsnprintf( buf, sizeof(buf), format, ap );
                        tmpArray[0] = buf;
                        ReportEvent(event_log_handle,
                                    eventType,
                                    0,        /* category */
                                    0,        /* event ID */
                                    NULL,     /* user ID */
                                    1,        /* number of strings */
                                    0,         /* binary data size */
                                    (LPCTSTR*) tmpArray,   /* error string */
                                    NULL       /* binary data */
                            );
#endif
                  }
			break;
		  case RPT_DEST_STORE:
			vsnprintf( buf, sizeof(buf), format, ap );
			buf[sizeof(buf)-1] = 0; /* be sure to have a terminating 0 */
			store_report_message( level, buf );
			break;
		}
		va_end(ap);
	}
}


int set_reporting( char *application_name, int new_level, int new_dest )
{
	if( new_level < RPT_CRIT || new_level > RPT_DEBUG ) {
		report( RPT_ERR, "report level invalid: %d", new_level );
		return -1;
	}

	if( report_dest != RPT_DEST_SYSLOG && new_dest == RPT_DEST_SYSLOG ) {
#ifdef WIN32
                if (event_log_handle)
                {
                        CloseEventLog(event_log_handle);
                } else {
                        event_log_handle = RegisterEventSource(NULL, "LcdProc");
                }
#else
		openlog( application_name, 0, LOG_USER );
#endif
	}
	else if( report_dest == RPT_DEST_SYSLOG && new_dest != RPT_DEST_SYSLOG ) {
#ifdef WIN32
                if (event_log_handle)
                {
                        CloseEventLog(event_log_handle);
                        event_log_handle = NULL;
                }                
#else
		closelog();
#endif
	}

	report_level = new_level;
	report_dest = new_dest;

	if( report_dest != RPT_DEST_STORE )
		flush_messages();

	return 0;
}


static void store_report_message( int level, const char *message )
{
	if( num_stored_msgs < MAX_STORED_MSGS ) {
		stored_msgs[num_stored_msgs] = malloc(strlen( message )+1);
		strcpy( stored_msgs[num_stored_msgs], message );
		stored_levels[num_stored_msgs] = level;
		num_stored_msgs ++;
	}
}


static void flush_messages()
{
	int i;
	for( i=0; i<num_stored_msgs; i++ ) {
		report( stored_levels[i], "%s", stored_msgs[i] );
		free( stored_msgs[i] );
	}
	num_stored_msgs = 0;
}


syntax highlighted by Code2HTML, v. 0.9.1