/*****
 NAME
 	cmessage.m - source code for CMessage class
 VERSION
 	$Id$
 CHANGELOG
 	$Log$
 */

#include <coconut/cmessage.h>
#include <coconut/cstring.h>
#include <coconut/csystem.h>
#include <coconut/dconststr.h>

static id <PStream>	error_stream ;

static void countMessage(message_t typ) ;

@implementation CMessage

+ setErrorStream: (id <PStream>) stream
{
	[error_stream release] ;
	[(error_stream = stream) retain] ;
	return nil ;
}

+ (u_int) makeFileMessage: (char *) buf file: (const char *) fname
    lineno: (u_int) lineno 
{
	u_int		len = 0 ;
	u_int		onelen ;
	size_t		msgsize = MSG_BUF_SIZE - 1 ;

	if(fname){
		len = snprintf(buf, msgsize, "%s", fname) ;
		msgsize -= len ;
		if(lineno > 0 && msgsize > 0){
			onelen = snprintf(&buf[len], msgsize,":%u", lineno) ;
			len += onelen ; /* msgsize -= onelen ; */
		}
	}
	buf[len] = '\0' ;
	return len ; /* the result does not count the final '\0' code */
}

+ (u_int) makeCategoryMessage: (char *) buf type: (message_t) type
    code: (int) code
{
	u_int	len = 0 ;
	switch(type){
	  case note_message:	
	  	/* nothing have to do */
	  break ;
	  case warning_message:	
	  	len = snprintf(buf, MSG_BUF_SIZE-1, "[%s] ", WARNING_STR) ;
	  break ;
	  case error_message:	
	  	len = snprintf(buf, MSG_BUF_SIZE-1, "[%s(%x)] ",ERROR_STR,code);
	  break ;
	}
	buf[len] = '\0' ;
	return len ;
}

+ message: (message_t) type code: (int) code format: (const char *) form, ... 
{
	va_list	args ;
	va_start(args, form) ;
	  [CMessage vmessage: type code:code format: form 
	    valist: args] ;
	va_end(args) ;
	return nil ;
}

+ vmessage: (message_t) type code: (int) code format: (const char *) 
    form valist: (va_list) args
{	
	gchar *		appname ;
	char 		category[MSG_BUF_SIZE] ;
	id <PString>	str ;

	countMessage(type) ;

	str = [[CString alloc] init] ;
	[str setFormat: form valist: args] ;

	[self makeCategoryMessage: category type: type code: code] ;
	appname = g_get_prgname() ;
	if(error_stream){
		if(appname){
			[error_stream putPtr: appname] ;
			[error_stream putPtr: ": "] ;
		}
		[error_stream putPtr: category] ; 
		[error_stream putStr: str] ;
		[error_stream putChar: '\n'] ;
		[error_stream flush] ;
	} else {
		if(appname){
			fprintf(stderr, "%s: ", appname) ;
		}
		fputs(category, stderr) ;
		fputs([str ptr], stderr) ;
		fputc('\n', stderr) ;
	}
	[str release] ;
	return nil ;
}

+ fmessage: (const char *) fname lineno: (u_int) lineno 
     type: (message_t) type code: (int) code format: (const char *) form, ...
{
	va_list	args ;
	va_start(args, form) ;
	  [CMessage vfmessage: fname lineno: lineno
	    type: type code: code format: form valist: args] ;
	va_end(args) ;
	return nil ;
}

+ vfmessage: (const char *) fname lineno: (u_int) lineno 
     type: (message_t) type code: (int) code format: (const char *) form 
     valist: (va_list) args
{
	id <PString>	str ;
	char		filemsg[MSG_BUF_SIZE] ;
	char 		category[MSG_BUF_SIZE] ;

	countMessage(type) ;

	str = [[CString alloc] init] ;
	[str setFormat: form valist: args] ;
	[self makeFileMessage: filemsg file: fname lineno: lineno] ;
	[self makeCategoryMessage: category type: type code: code] ;

	if(error_stream){
		[error_stream putPtr: filemsg] ;
		[error_stream putChar: ' '] ;
		[error_stream putPtr: category] ;
		[error_stream putChar: ' '] ;
		[error_stream putStr: str] ;
		[error_stream putChar: '\n'] ;
		[error_stream flush] ;
	} else {
		fprintf(stderr, "%s %s %s\n", filemsg, category, [str ptr]) ;
	}
	return nil ;
}

- init
{
	return [super init] ;
}

- (void) dealloc
{
	[super dealloc] ;
}

@end

static void countMessage(message_t typ)
{
	switch(typ){
	  case note_message: 
	  	[CSystem incNoteCount] ;
	  break ;
	  case warning_message: 
	  	[CSystem incWarningCount] ;
	  break ;
	  case error_message: 
	  	[CSystem incErrorCount] ;
	  break ;
	  default:
	  	g_error("unknown message type") ;
	  break ;
	}
}

