/***************************************************************************
                          logfile.c  -  description
                             -------------------
    begin                : Sat Feb 22 2003
    copyright            : (C) 2003 by Tim-Philipp Müller
    email                : t.i.m@orange.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "global.h"
#include "logfile.h"
#include "misc_strings.h"
#include "options.h"
#include "status_page.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* variables */

static FILE  *logfile = NULL;


/* functions */

static void   logfile_rotate (const gchar *logfilename);

static void   logfile_close (void);



/******************************************************************************
 *
 *   logfile_get_timestamp
 *
 ***/

const gchar *
logfile_get_timestamp (void)
{
	time_t        timenow = time(NULL);
	static gchar  stamp[128];
	struct tm    *tmnow = localtime (&timenow);
	GDate        *date;
	gsize         ret;

	date = g_date_new_dmy (tmnow->tm_mday, tmnow->tm_mon+1, tmnow->tm_year + 1900);

	ret = g_date_strftime (stamp, sizeof(stamp), "%d %b", date);

	g_return_val_if_fail (  ret != 0, NULL );

	g_snprintf (stamp+6, sizeof(stamp)-6, ", %02d.%02d.%02dh   ",
	              tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);

	g_date_free(date);

	return stamp;
}


/******************************************************************************
 *
 *   logfile_close
 *
 ***/

static void
logfile_close (void)
{
	if (logfile)
	{
		fprintf (logfile, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
		fclose(logfile);
		logfile = NULL;
	}
}



/******************************************************************************
 *
 *   write_to_logfile
 *
 ***/

void
logfile_write (const gchar *format, ...)
{
	/* lock, so the status log output that the log rotate function creates will not
	 * recursively lead to doom if max logsize is set smaller than the output created
	 * by the status log rotate function...
	 */
	static gboolean  lock = FALSE;

	static time_t    last_checked_logfilesize = (time_t) 0;
	struct stat      logstat;
	gchar           *logmsg = NULL;
	va_list          ap;

	if (lock)
		return;

	g_return_if_fail (format!=NULL);

	va_start(ap, format);
	logmsg = g_strdup_vprintf (format, ap);
	va_end(ap);

	if ( logmsg == NULL || *logmsg == 0x00 )
	{
		G_FREE(logmsg);
		return;
	}

	if ( *logmsg==' ' && *(logmsg+1) == 0x00)
	{
		G_FREE(logmsg);
		return;
	}

	if ( opt_get_bool(OPT_GUI_NO_LINKS_TO_LOGFILE) && strncmp(logmsg, "ed2k://", 6) == 0 )
	{
		G_FREE(logmsg);
		return;
	}

	if ( strncmp(logmsg, "No more servers to extend to",10) == 0 )
	{
		G_FREE(logmsg);
		return;
	}

	if (!logfile)
	{
		static gboolean setup_atexit; /* FALSE */
		const gchar *fn;

		fn = opt_get_opt_filename("gui_statuslog");

		g_return_if_fail ( fn != NULL );

		logfile = fopen (fn, "a");
		g_return_if_fail ( logfile != NULL );

		fprintf (logfile, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");

		if (!setup_atexit)
		{
			g_atexit(logfile_close);
			setup_atexit = TRUE;
		}
	}

	/* string might have markup escapes */
	if ( strchr(logmsg,'&') != NULL )
	{
		gchar *logmsg_unescaped;
		logmsg_unescaped = misc_strings_unescape_text(logmsg);
		if (logmsg_unescaped)
		{
			fprintf(logfile,"%s%s", logfile_get_timestamp(), logmsg_unescaped);
			g_free(logmsg_unescaped);
		}
	}
	else
		fprintf (logfile, "%s%s", logfile_get_timestamp(), logmsg);

	fflush (logfile);

	G_FREE(logmsg);
	logmsg = NULL;

	/* make sure logfile doesn't get too big (check every minute) */
	if (time(NULL) - last_checked_logfilesize < 60)
		return;

	last_checked_logfilesize = time(NULL);

	if (fstat (fileno(logfile), &logstat) != 0)
	{
		status_system_error_msg (_("Couldn't fstat() logfile gui_statuslog\n"));
		return;
	}

	if (logstat.st_size > (opt_get_int(OPT_GUI_MAX_LOGFILE_SIZE)*1024))
	{
		fclose(logfile);	/* close file before we move/compress it */
		logfile = NULL;

		lock = TRUE;	/* see above why */
		logfile_rotate(opt_get_opt_filename("gui_statuslog"));
		lock = FALSE;
	}
}



/******************************************************************************
 *
 *   logfile_rotate
 *
 ***/

static void
logfile_rotate (const gchar *logfilename)
{
	static gchar	*compress_program = NULL;
	static gchar	*compressed_extension = NULL;
	const gchar		*ret = NULL;
	gint			 number;

	g_return_if_fail (logfilename!=NULL);

	status_message_blue (_("GUI: Logfile 'gui_statuslog' is getting big. Rotating log files...\n"));

	/* if we haven't done so before, check for a compress program on this system
	 * (note: if we have found one, compress_program and compressed_extension are set,
	 *  if we checked and haven't found one, compress_program will be NULL and
	 *  compressed_extension will be set (to "")
	 */
	if (!compressed_extension)
	{
		/* check whether we have bzip2 on this system (and in our PATH) */
		ret = g_find_program_in_path("bzip2");
		/* found bzip2 */
		if (ret)
		{
			compress_program = g_strdup (ret);	/* this leaks once, but who cares? */
			g_return_if_fail (compress_program!=NULL);
			compressed_extension = ".bz2";
		}

		/* if not, check whether we have gzip on this system (and in our PATH) */
		if (!ret)
		{
			ret = g_find_program_in_path("gzip");
			/* found gzip */
			if (ret)
			{
				compress_program = g_strdup (ret);	/* this leaks once, but who cares? */
				g_return_if_fail (compress_program!=NULL);
				compressed_extension = ".gz";
			}
		}
	}

	/* neither bzip2 nor gzip found? => don't compress */
	if (!compress_program)
	{
		compressed_extension = "";
	} else status_message_blue (_("GUI: Using '%s' for compression\n"), compress_program);

	g_return_if_fail (compressed_extension!=NULL);

	/* rotate old (possibly compressed) statuslogs (max. 999) */
	for (number=999; number>=0; number--)
	{
		gchar *oldlogfn = g_strdup_printf ("%s-old.%d%s", logfilename, number, compressed_extension);
		g_return_if_fail (oldlogfn!=NULL);
		/* check whether old log file exists in the first place */
		if (access(oldlogfn,F_OK)==0)
		{
			gchar *oldlogfn2 = g_strdup_printf ("%s-old.%d%s", logfilename, number+1, compressed_extension);
			g_return_if_fail (oldlogfn2!=NULL);
			/* shift old log file up one number */
			status_message_blue (_("GUI: Renaming old logfile '%s' -> '%s'\n"), g_path_get_basename(oldlogfn), g_path_get_basename(oldlogfn2));
			if (rename(oldlogfn, oldlogfn2)<0)
				status_system_error_msg (_("Couldn't rename() old status logfile.\n"));
			g_free(oldlogfn2);
		}
		g_free(oldlogfn);
	}

	/* now rename current logfile into gui_statuslog<.instance>.0 and compress it */
	if (1)
	{
		gchar *newlogfn = g_strdup_printf ("%s-old.0", logfilename);
		g_return_if_fail (newlogfn!=NULL);
		status_message_blue (_("GUI: Renaming current logfile '%s' -> '%s'\n"), g_path_get_basename(logfilename), g_path_get_basename(newlogfn));
		if (rename(logfilename,newlogfn)==0)
		{
			/* now compress it if we found a compress program */
			if (compress_program)
			{
				gchar *sysmsg = g_strdup_printf ("%s \"%s\"", compress_program, newlogfn);
				g_return_if_fail (sysmsg!=NULL);
				status_message_blue (_("GUI: Compressing logfile '%s' -> '%s%s'\n"), g_path_get_basename(newlogfn), g_path_get_basename(newlogfn), compressed_extension);
				/* XXX - would there be any point in checking the return value here? */
				system (sysmsg);
				g_free(sysmsg);
			}
		} else status_system_error_msg (_("Couldn't rename() current status logfile.\n"));
		g_free(newlogfn);
	}

	status_message_blue (_("GUI: Log rotation complete.\n"));
}


