/*
 * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
#include "mas_logging.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>

#define LOG_LEVELS	32
#define INDENT_AMOUNT	3

static int	current_log_level = -1;	/* The current log level this is in */
static int	last_printed_level;	/* The last printed log level */
static char	*log_levels[LOG_LEVELS];/* Pointers to the names of each log level */
static int	verbosity_level;	/* The verbosity level of messages to print */
static FILE	*logfile;		/* The handle of the log file */
static int	entry_exit;		/* If true, all entries and exits from levels are printed */

/* Whitespace for quickly indenting messages. This is global so it can be initialized once. */
static char	spaces[LOG_LEVELS * INDENT_AMOUNT];	


/* Helper function that "indents" spaces[] */
static void indent(int level)
{
	static int	last_null = 0;


	/* Put a space on the last null character put in the string
	 * and increase or decrease the amount of spaces before the 
	 * first null. */
	spaces[last_null] = ' ';
	spaces[(level - 1) * INDENT_AMOUNT] = '\0';
	last_null = (level - 1) * INDENT_AMOUNT;
}


int masc_init_log_program(char *filename, char *first_log_title, int options)
{
	if(*filename == 0)
		logfile = stderr;
	else if((logfile = fopen(filename, "w")) == NULL)
		return -1;

	/* Turn off buffering so that all data is written to the disk immediately.
	 * This makes sure all the data is written to the drive before a crash happens. */
	if(options & LINE_BUFFER)
		setvbuf(logfile, NULL, _IOLBF, 0);
	else if(options & NO_BUFFER)
		setvbuf(logfile, NULL, _IONBF, 0);

	/* Set this to print all the entry and exit levels. */
	if(options & PRINT_ENTRY_EXIT)
		entry_exit = 1;

	/* Fill spaces with whitespace. */
	memset(spaces, ' ', LOG_LEVELS * INDENT_AMOUNT);

	/* Set the first log title */
	current_log_level = 1;
	log_levels[0] = first_log_title;

	/* Print the logger is entering this level if entry_exit is set */
	if(entry_exit)
		fprintf(logfile, "1:(Entering) %s\n", first_log_title);

	return 1;
}

void masc_exit_log_program(void)
{
	/* Print the logger is entering this level if entry_exit is set */
	if(entry_exit)
		fprintf(logfile, "1:(Exiting) %s\n", log_levels[0]);

	fclose(logfile);
}

void masc_log_message(int verbosity, char *format, ...)
{
	int	i;
	va_list ap;

        if(current_log_level == -1) masc_init_log_program("", "", 0);

        /* Only print messages that are less then the current verbosity level. */
	if(verbosity > verbosity_level) return;

	if(!entry_exit)
	{
		/* If the current log level hasn't been printed into the file yet
		 * print it. */
		if(current_log_level > last_printed_level)
                {
			for(i = last_printed_level + 1; i <= current_log_level; i++)
			{
				indent(i);
				fprintf(logfile, "%s%d: %s\n", spaces, i, log_levels[i - 1]);
			}
                }
		else if(current_log_level < last_printed_level)
		{
			indent(current_log_level);
			fprintf(logfile, "%s%d: %s\n", spaces, current_log_level, log_levels[current_log_level - 1]);
		}
	}
	/* Print the message into the file */
        if ( format[0] == ' ' || format[0] == 9 )
        {
            indent(current_log_level);
            if ( spaces[0] != 0 ) fprintf(logfile, spaces);
            fprintf(logfile, "%d+ ", current_log_level);
        }
        else
        {
            indent(current_log_level+1);
            if ( spaces[0] != 0 ) fprintf(logfile, spaces);
        }

	va_start(ap, format);
	vfprintf(logfile, format, ap);
	va_end(ap);
	fprintf(logfile, "\n");

	/* Set the last printed level so that it is not printed out again */
	last_printed_level = current_log_level;
}

void masc_log_verbosity(int verbosity)
{
	verbosity_level = verbosity;
}

void masc_entering_log_level(char *log_title)
{
	if (current_log_level == -1) masc_init_log_program("", "", 0);

	current_log_level++;

	/* Set the pointer to the log title for this level */
	log_levels[current_log_level - 1] = log_title;

	/* Print the logger is entering this level if entry_exit is set */
	if(entry_exit)
	{
		indent(current_log_level);
		fprintf(logfile, "%s%d:(Entering) %s\n", spaces, current_log_level, log_levels[current_log_level - 1]);
	}
}

void masc_exiting_log_level(void)
{
	if (current_log_level == -1) masc_init_log_program("", "", 0);
    
	/* Make sure the level doesn't reach 0 or below */
	if(current_log_level <= 1)
		current_log_level = 1;
	else
		current_log_level--;

	if(last_printed_level > current_log_level) last_printed_level = current_log_level;

	/* Print the logger is exiting this level if entry_exit is set */
	if(entry_exit)
	{
		indent(current_log_level + 1);
		fprintf(logfile, "%s%d:(Exiting) %s\n", spaces, current_log_level + 1, log_levels[current_log_level]);
	}
}


