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

#include "lpctcl.h"

/****************************************************************************/
/*							Local prototypes								*/
/****************************************************************************/

static	void	CallBack( void *user, const char *log );
static	int		LogChange( LPCTCL *lib, void (*func)(LPC,LPC_LOG_TYPE),
					  	   int objc, Tcl_Obj * const objv[] );

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

/*
*	Logtype names and type codes
*/

static const struct {

	const char 		*name;
	LPC_LOG_TYPE	code;

} logtypes[] = {

	{ "serial",		LPC_LOG_SERIAL		},
	{ "target",		LPC_LOG_TARGET		},
	{ "fileio",		LPC_LOG_FILEIO		},
	{ "deltas",		LPC_LOG_TIMESTAMP	},
	{ NULL,			0 					}
};

/****************************************************************************/
/*							Logging commands								*/
/****************************************************************************/

/*
*	Set up a log function
*	~~~~~~~~~~~~~~~~~~~~~
*	After this call whenever the underlaying LPC library would log
*	something, the given TCL procedure will be called. The procedure
*	will receive two arguments, the first is whatever argument was
*	passed to this procedure and the second is a string object containing
*	the single line of log message string.
*/

CMDFUNC( LogProc )
{
Tcl_Obj	*obj;

	if ( objc != 1 && objc != 3 ) {

		Tcl_AppendResult( tcl, "The ",
						  Tcl_GetStringFromObj( objv[ 0 ], NULL ),
						  " command needs two arguments", NULL );
		return( TCL_ERROR );
	}

	if ( lib->log_proc ) {

		Tcl_DecrRefCount( lib->log_proc );
		Tcl_DecrRefCount( lib->log_user );
		lib->log_user = NULL;
		lib->log_proc = NULL;
		LPC_LogCallback( lib->lpc, NULL, NULL );
	}

	if ( objc == 1 ) return( TCL_OK );

	obj = Tcl_NewStringObj( Tcl_GetStringFromObj( objv[ 1 ], NULL ), -1 );

	Tcl_IncrRefCount( obj );
	Tcl_IncrRefCount( objv[ 2 ] );
	lib->log_proc = obj;
	lib->log_user = objv[ 2 ];
	LPC_LogCallback( lib->lpc, CallBack, lib );
	return( TCL_OK );
}

/*
*	Enable logging certain events
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

CMDFUNC( LogOn )
{
	(void) tcl;
	return( LogChange( lib, LPC_LogEnable, objc, objv ) );
}

/*
*	Disable logging certain events
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

CMDFUNC( LogOff )
{
	(void) tcl;
	return( LogChange( lib, LPC_LogDisable, objc, objv ) );
}

/*
*	Returns the last low-level C error message
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	After the call the C library will delete the message, so
*	subsequent calls of this function will return the empty string.
*/

CMDFUNC( LogError )
{
	(void) objc;
	(void) objv;
	Tcl_AppendResult( tcl, LPC_LogLastError( lib->lpc ), NULL );
	return( TCL_OK );
}

/****************************************************************************/
/*							Local functions									*/
/****************************************************************************/

/*
*	The LPC library callback on log events
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	void	CallBack( void *user, const char *log )
{
LPCTCL		*lib = user;
Tcl_CmdInfo	info;
Tcl_Obj		*args[ 4 ];

	if ( Tcl_GetCommandInfo( lib->tcl,
							 Tcl_GetStringFromObj( lib->log_proc, NULL ),
							 &info ) == 0 ) {

		// Can't find the nominated callback - we can't do anything.

		return;
	}

	// We use the object API and leave it to TCL to do internal
	// conversions if need be. So, we have to create an object out
	// of the log string. Everything else is already an object.

	args[ 0 ] = lib->log_proc;
	args[ 1 ] = lib->log_user;
	args[ 2 ] = Tcl_NewStringObj( log, -1 );
	args[ 3 ] = NULL;

	Tcl_IncrRefCount( args[ 2 ] );
	(*info.objProc)( info.objClientData, lib->tcl, 3, args );
	Tcl_DecrRefCount( args[ 2 ] );
}

/*
*	Processes a logon or logoff command
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		LogChange( LPCTCL *lib, void (*func)(LPC,LPC_LOG_TYPE),
					  	   int objc, Tcl_Obj * const objv[] )
{
int			i, j;
const char	*s, *p;

	for ( i = 1 ; i < objc ; i++ ) {

		s = Tcl_GetStringFromObj( objv[ i ], NULL );

		for ( j = 0 ; ( p = logtypes[ j ].name ) ; j++ )

			if ( ! strcmp( p, s ) )

				break;

		if ( ! p ) {

			Tcl_AppendResult( lib->tcl, "Illegal logtype ", s, NULL );
			return( TCL_ERROR );
		}
		else {

			(*func)( lib->lpc, logtypes[ j ].code );
		}
	}

	return( TCL_OK );
}
