/* 
 * Copyright (C) 2000-2001 Computer & Communications Research Laboratories,
 *			   Industrial Technology Research Institute
 */
/*
 * trace.c
 *
 * $Id: tracer.c,v 1.6 2001/06/21 02:56:09 hcc Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>

#include "msgSock.h"
#include "msgEvent.h"
#include "lowSync.h"
#include "thread.h"
#include "tracer.h"

#define LOG2FILE(f, lbuf)	if (f!=NULL) {\
					fwrite(lbuf, strlen(lbuf), 1, f);\
					fflush(f);\
				}

#define		TCR_LOG_NONE	0x00
#define		TCR_LOG_CONSOLE	0x01
#define		TCR_LOG_SOCK	0x02
#define		TCR_LOG_FILE	0x04

struct TCRObj {
	int		trace_level_;
	int		log_flag_;
	UINT32		sequence_no_;

	msgSock		sock_;		/* sock parameters */
	msgSockAddr	addr_;		/* sock parameters */
	FILE*		fd_;		/* file parameters */
	lowMutex	cs_;		/* critical section */

	msgSock		servSock_;	/* sock server parameters */
	msgSockAddr	servAddr_;	/* sock server parameters */
	thread		servThread_;	/* sock server parameters */
	int		servThreadON_;	/* sock server parameters */
	
	void		(*msgCB_)(const char*);	/* message callback function */
};

char* gettime(void)
{
	static char logtime[128];
	time_t ltime;
	struct tm *now;

	logtime[0]='\0';

	time( &ltime );
	now = localtime( &ltime );
	strftime(logtime, 128, "%m/%d/%H:%M:%S", now);

	return logtime;
}

void	TCRLogToAllLogger(TCR this_, char* logbuf)
{
	lowMutexLock(this_->cs_);

	/* log to console */
	if( (this_->log_flag_&TCR_LOG_CONSOLE) ) 
		printf("%s",logbuf);
	
	/* log to file */
	if( (this_->log_flag_&TCR_LOG_FILE)&&this_->fd_ ) 
		LOG2FILE(this_->fd_, logbuf);
	
	/* log to sock */
	if( (this_->log_flag_&TCR_LOG_SOCK)&&this_->sock_ ) 
		msgSockSendto(this_->sock_,logbuf,strlen(logbuf)+1,this_->addr_);

	if( this_->msgCB_ )
		(this_->msgCB_)(logbuf);

	lowMutexUnlock(this_->cs_);
}

TCR		TCRBegin(void)
{
	TCR	this_ = (TCR)malloc(sizeof(struct TCRObj));

	this_->cs_ = lowMutexNew(MUTEX_INTERTHREAD,0);
	lowMutexLock(this_->cs_);

	this_->trace_level_ = 0;
	this_->log_flag_ = TCR_LOG_NONE;
	this_->sock_ = NULL;
	this_->addr_ = NULL;
	this_->fd_ = NULL;
	this_->sequence_no_ = 0;
	this_->servSock_ = NULL;
	this_->servAddr_ = NULL;
	this_->servThread_ = NULL;
	this_->servThreadON_ = 0;
	this_->msgCB_ = NULL;
		
	lowMutexUnlock(this_->cs_);
	return this_;
}

void		TCREnd(TCR this_)
{
	if(!this_)
		return ;
	
	lowMutexLock(this_->cs_);
	TCRSockLoggerOFF(this_);
	TCRFileLoggerOFF(this_);
	TCRSockServerOFF(this_);
	lowMutexUnlock(this_->cs_);
	lowMutexFree(this_->cs_);
	free(this_);
}

RETCODE		TCRSetMsgCB(TCR this_, void(*msgCB)(const char*))
{
	if(!this_)
		return -1;
	
	this_->msgCB_ = msgCB;

	return 0;
}


RETCODE		TCRSockLoggerON(TCR this_, const char* raddr, UINT16 rport)
{
	int ret = 0;

	if(!this_)
		return -1;
	if(this_->sock_)
		return -1;

	lowMutexLock(this_->cs_);
	msgLibInit();

	this_->sock_ = msgSockNew(msgSockDgram,NULL);
	if( !this_->sock_ ) {
		ret = -1;
		goto end;
	}
	this_->addr_ = msgSockAddrNew(raddr,rport);
	if( !this_->addr_ ) {
		ret = -1;
		msgSockFree(this_->sock_);
		this_->sock_ = NULL;
		goto end;
	}
	this_->log_flag_ |= TCR_LOG_SOCK;

end:
	lowMutexUnlock(this_->cs_);
	
	return ret;
}

RETCODE		TCRSockLoggerOFF(TCR this_)
{
	if(!this_)
		return -1;
	if(!this_->sock_)
		return 0;

	lowMutexLock(this_->cs_);
	msgSockFree(this_->sock_);	this_->sock_ = NULL;
	msgSockAddrFree(this_->addr_);	this_->addr_ = NULL;
	this_->log_flag_ &= ~(TCR_LOG_SOCK);
	lowMutexUnlock(this_->cs_);

	return 0;
}

void		servEvnCB(msgSock sock, msgEventType event, int err, void* context)
{
	TCR		this_ = (TCR)context;
	char		buff[TCRMAXBUFLEN];

	int len = msgSockRecvfrom(sock, buff, TCRMAXBUFLEN);
	if( len<0 ) {
		printf("msgSockRecvfrom error.\n");
	}
	else {
		buff[len] = 0;
		TCRLogToAllLogger(this_,buff);
	}
}

void*		servThread(void* data)
{
	TCR		this_ = (TCR)data;
	msgEvent	evn = msgEventNew();

	msgEventRegister(evn,this_->servSock_,msgEventRd,servEvnCB,this_);

	while( this_->servThreadON_ ) {
		msgEventDispatch(evn,100);
	}
	msgEventUnregister(evn,this_->servSock_);
	msgEventFree(evn);

	return NULL;
}

RETCODE		TCRSockServerON(TCR this_, UINT16 port)
{
	int	ret = 0;
	
	if(!this_)
		return -1;
	if( this_->servSock_ ) 	/* already on */
		return -1;

	lowMutexLock(this_->cs_);
	msgLibInit();
	
	this_->servAddr_ = msgSockAddrNew(NULL,port); /* auto fill my ip */
	if( !this_->servAddr_ ) {
		ret = -1;
		goto end;
	}	
	this_->servSock_ = msgSockNew(msgSockDgram,this_->servAddr_);
	if( !this_->servSock_ ) {
		ret = -1;
		msgSockAddrFree(this_->servAddr_);
		this_->servAddr_ = NULL;
		goto end;
	}
	this_->servThreadON_ = 1;
	this_->servThread_ = threadCreate(servThread,(void*)this_);
	if( !this_->servThread_ ) {
		ret = -1;
		msgSockAddrFree(this_->servAddr_);
		this_->servAddr_ = NULL;
		msgSockFree(this_->servSock_);
		this_->servSock_ = NULL;
		this_->servThreadON_ = 0;
	}
	
end:
	lowMutexUnlock(this_->cs_);
	return ret;
}

RETCODE		TCRSockServerOFF(TCR this_)
{
	if(!this_)
		return -1;
	if(!this_->servSock_)
		return 0;

	lowMutexLock(this_->cs_);

	this_->servThreadON_ = 0;
	threadJoin(this_->servThread_);
	this_->servThread_ = NULL;
	msgSockFree(this_->servSock_);
	this_->servSock_ = NULL;
	msgSockAddrFree(this_->servAddr_);
	this_->servAddr_ = NULL;
	msgLibClean();

	lowMutexUnlock(this_->cs_);

	return 0;
}

RETCODE		TCRConsoleLoggerON(TCR this_)
{
	if(!this_)
		return -1;

	lowMutexLock(this_->cs_);
	this_->log_flag_ |= TCR_LOG_CONSOLE;
	lowMutexUnlock(this_->cs_);
	return 0;
}

RETCODE		TCRConsoleLoggerOFF(TCR this_)
{
	if(!this_)
		return  -1;

	lowMutexLock(this_->cs_);
	this_->log_flag_ &= ~(TCR_LOG_CONSOLE);
	lowMutexUnlock(this_->cs_);
	return 0;
}

RETCODE		TCRFileLoggerON(TCR this_, const char* fname)
{
	char	logbuf[128];
	if(!this_||!fname)
		return -1;
	if(this_->fd_)
		return -1;

	lowMutexLock(this_->cs_);

	this_->fd_ = fopen(fname,"w"); 
	if (this_->fd_ == NULL) { 
		printf("Log file %s open error.\n", fname); 
		fclose(this_->fd_); 
	} 
	sprintf(logbuf, "*** start logging %s***\n", gettime()); 
	LOG2FILE(this_->fd_, logbuf);
	this_->log_flag_ |= TCR_LOG_FILE;

	lowMutexUnlock(this_->cs_);

	return 0;
}

RETCODE		TCRFileLoggerOFF(TCR this_)
{
	char	logbuf[128];
	if(!this_)
		return -1;

	lowMutexLock(this_->cs_);

	if (this_->fd_ != NULL) { 
		sprintf(logbuf, "*** stop logging %s***\n", gettime()); 
		LOG2FILE(this_->fd_, logbuf); 
		fclose(this_->fd_);
		this_->fd_ = NULL;
		this_->log_flag_ &= ~(TCR_LOG_FILE);
	} 

	lowMutexUnlock(this_->cs_);

	return 0;
}

int		TCRPrint(TCR this_, int level, const char* format, ...)
{
	char	logbuf[TCRMAXBUFLEN];
	va_list v;

	if(!this_)
		return -1;
	if( this_->trace_level_<level )
		return 0;

	lowMutexLock(this_->cs_);

	sprintf(logbuf,"[%u %s] ",++this_->sequence_no_,gettime());
	va_start(v,format);
	vsprintf(logbuf+strlen(logbuf),format,v);

	lowMutexUnlock(this_->cs_);

	TCRLogToAllLogger(this_,logbuf);

	return strlen(logbuf);
}

RETCODE		TCRSetTraceLevel(TCR this_, int trace_level)
{
	if(!this_)
		return -1;

	this_->trace_level_ = trace_level;
	return 0;
}

int		TCRGetTraceLevel(TCR this_)
{
	return this_->trace_level_;
}

