/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss of reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#define  _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>

#include "lpc-internals.h"

/****************************************************************************/
/*					Local definitions										*/
/****************************************************************************/

#define	MAXLINE		1024
#define	MAXTIME		32

/****************************************************************************/
/*					Local data												*/
/****************************************************************************/

/*
*	The names of the various log types
*/

static const char *logtypes[ LPC_LOG_NUMLOG ] = {

	[ LPC_LOG_TIMESTAMP ] = NULL,
	[ LPC_LOG_ERROR  	] = "err",
	[ LPC_LOG_SERIAL 	] = "ser",
	[ LPC_LOG_TARGET 	] = "tgt",
	[ LPC_LOG_FILEIO 	] = "i/o"
};

/****************************************************************************/
/*					Library API functions									*/
/****************************************************************************/

/** @addtogroup lpc_prg_lib */
/** @{ */

/**
*	Open the logfile.
*
*	The function opens a log file. Events will be written into the file.
*
*	@param	lpc		The library state
*
*	@param	file	The name of the ligfile to open. The caller must have
*					write permission on the file. If the file specified is
*					"-", i.e. a single dash, then the all log info will be
*					sent to the stdout.
*
*	@param	append	If non-zero, the file will be opened for append, otherwise
*					it will be truncated to 0 length before logging commences.
*
*	@return		0 on success, a negative error code on failure.
*/

LPC_ERROR	LPC_LogOpen( LPC lpc, const char *file, int append )
{
	if ( lpc->log_file ) {

		_lpc_Error( lpc, "Logfile is already open" );
		return( -1 );
	}

	if ( ! strcmp( file, "-" ) ) {

		lpc->log_file = stdout;
		gettimeofday( &lpc->log_time, NULL );
		return( 0 );
	}

	if ( ! ( lpc->log_file = fopen( file, append ? "a" : "w" ) ) ) {

		_lpc_Error( lpc, "Couldn't open logfile: %s", strerror( errno ) );
		return( -1 );
	}

	return( 0 );
}

/**
*	Closes the logfile.
*
*	@param	lpc		The library state
*/

void	LPC_LogClose( LPC lpc )
{
	if ( lpc->log_file && lpc->log_file != stdout ) fclose( lpc->log_file );
	lpc->log_file = NULL;
}

/**
*	Enables the logging of certain events.
*
*	The function enables the logging of event classes, as defined by the
*	LPC_LOG_TYPE enumerator. A call to this function enables the logging
*	of the class defined by the argument.
*
*	@param	lpc		The library state
*
*	@param	type	The log class to enable.
*/

void	LPC_LogEnable( LPC lpc, LPC_LOG_TYPE type )
{
	lpc->log_mask |= 1 << type;
}

/**
*	Disables the logging of certain events.
*
*	The function enables the logging of event classes, as defined by the
*	LPC_LOG_TYPE enumerator. A call to this function disables the logging
*	of the class defined by the argument. Logging of errors can not be
*	disabled.
*
*	@param	lpc		The library state
*
*	@param	type	The log class to enable.
*/

void	LPC_LogDisable( LPC lpc, LPC_LOG_TYPE type )
{
	lpc->log_mask &= ~( 1 << type );
	lpc->log_mask |= 1 << LPC_LOG_ERROR;
}

/**
*	Returns the last error.
*
*	If there was an error, a textual error message is logged and the
*	API function returns to the caller with an error code.
*	This function returns a malloc() -ed string that contains the
*	error message (if there was no error since the library was opened or
*	since the last call to this function, NULL is returned).
*	It is the responsibility of the caller to release the string after use.
*
*	@param	lpc		The library state
*
*	@return			A malloc()-ed string containing the last error message
*					or NULL, if there was no error since the last call.
*/

char	*LPC_LogLastError( LPC lpc )
{
char	*p;

	p = lpc->lib_emsg;
	lpc->lib_emsg = NULL;
	return( p );
}

/**
*	Establishes a log callback.
*
*	Using this function the caller can set up a callback function, which
*	will be called whenever the library logs something. The callback
*	function will receive a user-specified argument and a pointer to
*	the log string. The callback MUST NOT modify the log string, nor
*	should it cache a pointer to it as it will go out of existence as
*	soon as the callback returns. if the user wants to keep the log
*	entry, then the callback must create a copy of the string.
*
*	The log callback is called regardless of whether logging to a file
*	is enabled or not, but the log types established by LPC_LogEnable()
*	and LPC_LogDisable() are respected.
*
*	Passing a NULL pointer as the callback function is legal and simply
*	turns the service off.
*
*	The line passed to the callback is a 0-terminated string. There is
*	no newline at the end of the string.
*
*	@param	lpc		The library state
*
*	@param	func	The callback function. The function prototype should be<br>
*					<tt>void callback( void *user, const char *logline );</tt>
*
*	@param	user	The first parameter of the callback function.
*/

void	LPC_LogCallback( LPC lpc, void (*func)(void*,const char*), void *user )
{
	lpc->log_func = func;
	lpc->log_parm = user;
}

/** @} */

/****************************************************************************/
/*					Library internal functions								*/
/****************************************************************************/

/*
*	Logs an error
*	~~~~~~~~~~~~~
*/

void	_lpc_Error( LPC lpc, const char *fmt, ... )
{
va_list	arg;

	if ( lpc->lib_emsg ) free( lpc->lib_emsg );
	va_start( arg, fmt );
	vasprintf( &lpc->lib_emsg, fmt, arg );
	va_end( arg );
	_lpc_Log( lpc, LPC_LOG_ERROR, "%s", lpc->lib_emsg );
}

/*
*	Logs an event
*	~~~~~~~~~~~~~
*/

void	_lpc_Log( LPC lpc, unsigned type, const char *fmt, ... )
{
va_list			arg;
struct timeval	now;
double			dif;
int				len;
char			tmp[ MAXLINE + 3 ];

	if ( ! lpc->log_file && ! lpc->log_func ) return;
	if ( type >= LPC_LOG_NUMLOG ) return;
	if ( ! ( lpc->log_mask & ( 1 << type ) ) ) return;

	gettimeofday( &now, NULL );
	dif  = ( now.tv_sec - lpc->log_time.tv_sec ) * 1e3;
	dif += ( now.tv_usec - lpc->log_time.tv_usec ) * 1e-3;

	if ( lpc->log_mask & ( 1 << LPC_LOG_TIMESTAMP ) )

		len = snprintf( tmp, MAXTIME, "%10.3f [%s] ", dif, logtypes[ type ] );
	else
		len = sprintf( tmp, "[%s] ", logtypes[ type ] );

	va_start( arg, fmt );
	len += vsnprintf( tmp + len, MAXLINE - len, fmt, arg );
	tmp[ len ] = 0;
	va_end( arg );

	if ( lpc->log_file ) fprintf( lpc->log_file, "%s\n", tmp );
	if ( lpc->log_func ) (*lpc->log_func)( lpc->log_parm, tmp );
	lpc->log_time = now;
}

