/*
 * Copyright (c) 1997 - 2001 Hj. Malthaner
 *
 * This file is part of the Simutrans project under the artistic license.
 */

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

#ifdef SYSLOG
#include <syslog.h>
#endif

#define NO_LOG_EXTERNALS

#include "log.h"
#include "../simdebug.h"
#include "../simsys.h"


#ifdef MAKEOBJ
//debuglevel is global variable
#define dr_fopen fopen
#else
#ifdef NETTOOL
#define debuglevel (0)
#define dr_fopen fopen

#else
#define debuglevel (env_t::verbose_debug)

// for display ...
#include "../gui/messagebox.h"
#include "../display/simgraph.h"
#include "../gui/simwin.h"

#include "../dataobj/environment.h"
#endif
#endif

/**
 * writes a debug message into the log.
 * @author Hj. Malthaner
 */
void log_t::debug(const char *who, const char *format, ...)
{
	if(log_debug  &&  debuglevel>=4) {
		va_list argptr;
		va_start(argptr, format);

		if( log ) {                           /* only log when a log */
			fprintf(log ,"Debug: %s:\t",who); /* is already open */
			vfprintf(log, format, argptr);
			fprintf(log,"\n");

			if( force_flush ) {
				fflush(log);
			}
		}
		va_end(argptr);

		va_start(argptr, format);
		if( tee ) {                           /* only log when a log */
			fprintf(tee, "Debug: %s:\t",who); /* is already open */
			vfprintf(tee, format, argptr);
			fprintf(tee,"\n");
		}
		va_end(argptr);

#ifdef SYSLOG
		va_start( argptr, format );
		if (  syslog  ) {
			// Replace with dynamic memory allocation
			char buffer[4096];
			sprintf( buffer, "Debug: %s\t%s", who, format );
			vsyslog( LOG_DEBUG, buffer, argptr );
		}
		va_end( argptr );
#endif
	}
}


/**
 * writes a message into the log.
 * @author Hj. Malthaner
 */
void log_t::message(const char *who, const char *format, ...)
{
	if(debuglevel>=3) {
		va_list argptr;
		va_start(argptr, format);

		if( log ) {                             /* only log when a log */
			fprintf(log ,"Message: %s:\t",who); /* is already open */
			vfprintf(log, format, argptr);
			fprintf(log,"\n");

			if( force_flush ) {
				fflush(log);
			}
		}
		va_end(argptr);

		va_start(argptr, format);
		if( tee ) {                             /* only log when a log */
			fprintf(tee, "Message: %s:\t",who); /* is already open */
			vfprintf(tee, format, argptr);
			fprintf(tee,"\n");
		}
		va_end(argptr);

#ifdef SYSLOG
		va_start( argptr, format );
		if (  syslog  ) {
			// Replace with dynamic memory allocation
			char buffer[4096];
			sprintf( buffer, "Message: %s\t%s", who, format );
			vsyslog( LOG_INFO, buffer, argptr );
		}
		va_end( argptr );
#endif
	}
}


/**
 * writes a warning into the log.
 * @author Hj. Malthaner
 */
void log_t::warning(const char *who, const char *format, ...)
{
	if(debuglevel>=2) {
		va_list argptr;
		va_start(argptr, format);

		if( log ) {                             /* only log when a log */
			fprintf(log ,"Warning: %s:\t",who); /* is already open */
			vfprintf(log, format, argptr);
			fprintf(log,"\n");

			if( force_flush ) {
				fflush(log);
			}
		}
		va_end(argptr);

		va_start(argptr, format);
		if( tee ) {                             /* only log when a log */
			fprintf(tee, "Warning: %s:\t",who); /* is already open */
			vfprintf(tee, format, argptr);
			fprintf(tee,"\n");
		}
		va_end(argptr);

#ifdef SYSLOG
		va_start( argptr, format );
		if (  syslog  ) {
			// Replace with dynamic memory allocation
			char buffer[4096];
			sprintf( buffer, "Warning: %s\t%s", who, format );
			vsyslog( LOG_WARNING, buffer, argptr );
		}
		va_end( argptr );
#endif
	}
}


/**
 * writes an error into the log.
 * @author Hj. Malthaner
 */
void log_t::error(const char *who, const char *format, ...)
{
	if(debuglevel>=1) {
		va_list argptr;
		va_start(argptr, format);

		if( log ) {                           /* only log when a log */
			fprintf(log ,"ERROR: %s:\t",who); /* is already open */
			vfprintf(log, format, argptr);
			fprintf(log,"\n");

			if( force_flush ) {
				fflush(log);
			}

			fprintf(log ,"For help with this error or to file a bug report please see the Simutrans forum:\n");
			fprintf(log ,"http://forum.simutrans.com\n");
		}
		va_end(argptr);

		va_start(argptr, format);
		if( tee ) {                           /* only log when a log */
			fprintf(tee, "ERROR: %s:\t",who); /* is already open */
			vfprintf(tee, format, argptr);
			fprintf(tee,"\n");

			fprintf(tee ,"For help with this error or to file a bug report please see the Simutrans forum:\n");
			fprintf(tee ,"http://forum.simutrans.com\n");
		}
		va_end(argptr);

#ifdef SYSLOG
		va_start( argptr, format );
		if (  syslog  ) {

			// Replace with dynamic memory allocation
			char buffer[4096];
			sprintf( buffer, "ERROR: %s\t%s", who, format );
			vsyslog( LOG_ERR, buffer, argptr );
		}
		va_end( argptr );
#endif
	}
}



/**
 * writes a warning into the log.
 * @author Hj. Malthaner
 */
void log_t::doubled(const char *what, const char *name )
{
	if(debuglevel>=2) {

		if( log ) {                             /* only log when a log */
			fprintf(log ,"Warning: object %s::%s is overlaid!\n",what,name); /* is already open */
			if( force_flush ) {
				fflush(log);
			}
		}

		if( tee ) {
			fprintf(tee, "Warning: object %s::%s is overlaid!\n",what,name);
		}

#ifdef SYSLOG
		if(  syslog  ) {
			::syslog( LOG_WARNING, "Warning: object %s::%s is overlaid!", what, name );
		}
#endif
	}
	doublettes.append( (std::string)what+"::"+name+"<br/>" );
}



/**
 * writes an error into the log, aborts the program.
 * @author Hj. Malthaner
 */
void log_t::fatal(const char *who, const char *format, ...)
{
	va_list argptr;
	va_start(argptr, format);

	static char formatbuffer[512];
	sprintf( formatbuffer, "FATAL ERROR: %s - %s\nAborting program execution ...\n\nFor help with this error or to file a bug report please see the Simutrans forum at\nhttp://forum.simutrans.com\n", who, format );

	static char buffer[8192];
	int n = vsprintf( buffer, formatbuffer, argptr );

	if(  log  ) {
		fputs( buffer, log );
		if (  force_flush  ) {
			fflush( log );
		}
	}

	if(  tee  &&  log!=tee  ) {
		fputs( buffer, tee );
	}

#ifdef SYSLOG
	if (  syslog  ) {
		::syslog( LOG_ERR, buffer );
	}
#endif

	if (  tee == NULL  &&  log == NULL  &&  tag == NULL  ) {
		fputs( buffer, stderr );
	}

	va_end(argptr);

#if defined MAKEOBJ
	exit(1);
#elif defined NETTOOL
	// no display available
	puts( buffer );
#else
#  ifdef MSG_LEVEL
	int old_level = env_t::verbose_debug;
#  endif
	env_t::verbose_debug = 0;	// no more window concerning messages
	if(is_display_init()) {
		// show notification
		destroy_all_win( true );

		strcpy( buffer+n+1, "PRESS ANY KEY\n" );
		fatal_news* sel = new fatal_news(buffer);

		scr_coord xy( display_get_width()/2 - sel->get_windowsize().w/2, display_get_height()/2 - sel->get_windowsize().h/2 );
		event_t ev;

		create_win( xy.x, xy.y, sel, w_info, magic_none );

		while(win_is_top(sel)) {
			// do not move, do not close it!
			dr_sleep(50);
			dr_prepare_flush();
			sel->draw( xy, sel->get_windowsize() );
			dr_flush();
			display_poll_event(&ev);
			// main window resized
			check_pos_win(&ev);
			if(ev.ev_class==EVENT_KEYBOARD) {
				break;
			}
		}
	}
	else {
		// use OS means, if there
		dr_fatal_notify(buffer);
	}

#ifdef DEBUG
	if (old_level > 4) {
		// generate a division be zero error, if the user request it
		static int make_this_a_division_by_zero = 0;
		printf("%i", 15 / make_this_a_division_by_zero);
		make_this_a_division_by_zero &= 0xFF;
	}
#endif
#endif
	abort();
}



void log_t::vmessage(const char *what, const char *who, const char *format, va_list args )
{
	if(debuglevel>=1) {
		va_list args2;

#if defined(va_copy)
		va_copy(args2, args);
#elif defined(__va_copy)
		// Deprecated macro possibly used by older compilers.
		__va_copy(args2, args);
#else
		// Undefined behaviour that might work.
		args2 = args; // If this throws an error then C++11 conformance may be required.
#endif

		if( log ) {                               /* only log when a log */
			fprintf(log ,"%s: %s:\t", what, who); /* is already open */
			vfprintf(log, format, args);
			fprintf(log,"\n");

			if( force_flush ) {
				fflush(log);
			}
		}
		if( tee ) {                              /* only log when a log */
			fprintf(tee,"%s: %s:\t", what, who); /* is already open */
			vfprintf(tee, format, args2);
			fprintf(tee,"\n");
		}
		va_end(args2);
	}
}


// create a logfile for log_debug=true
log_t::log_t( const char *logfilename, bool force_flush, bool log_debug, bool log_console, const char *greeting, const char* syslogtag )
{
	log = NULL;
	syslog = false;
	this->force_flush = force_flush; /* if true will always flush when */
	                                 /* an entry is written to the log */
	this->log_debug = log_debug;

	if(logfilename == NULL) {
		log = NULL;                       /* not a log */
		tee = NULL;
	}
	else if(strcmp(logfilename,"stdio") == 0) {
		log = stdout;
		tee = NULL;
	}
	else if(strcmp(logfilename,"stderr") == 0) {
		log = stderr;
		tee = NULL;
#ifdef SYSLOG
	}
	else if(  strcmp( logfilename, "syslog" ) == 0  ) {
		syslog = true;
		if (  syslogtag  ) {
			tag = syslogtag;
			// Set up syslog
			openlog( tag, LOG_PID, LOG_DAEMON );
		}
		log = NULL;
		tee = NULL;
#endif
	}
	else {
		log = dr_fopen(logfilename,"wb");

		if(log == NULL) {
			fprintf(stderr,"log_t::log_t: can't open file '%s' for writing\n", logfilename);
		}
		tee = stderr;
	}
	if (!log_console) {
	    tee = NULL;
	}

	if(  greeting  ) {
		if( log ) {
			fputs( greeting, log );
//			message("log_t::log_t","Starting logging to %s", logfilename);
		}
		if( tee ) {
			fputs( greeting, tee );
		}
#ifdef SYSLOG
		if (  syslog  ) {
			::syslog( LOG_NOTICE, greeting );
		}
#else
		(void)syslogtag;
#endif
	}
}


void log_t::close()
{
	message("log_t::~log_t","stop logging, closing log file");

	if( log ) {
		fclose(log);
		log = NULL;
	}
}


// close all logs during cleanup
log_t::~log_t()
{
	if( log ) {
		close();
	}
}
