/**********************************************************************
 
	Copyright (C) 2004 Tomohito Nakajima <nakajima@zeta.co.jp>
	
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/


#include "machine/logger.h"

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <time.h>

/* system depend utility functions */

/* windows version */
#include <windows.h>
#include <tchar.h>
typedef HANDLE LockHandle;
LockHandle logger_open_lock(const char*name){
	return CreateMutex(NULL, FALSE, name);
}
void logger_lock(LockHandle handle){
	WaitForSingleObject(handle, INFINITE);
}
void logger_unlock(LockHandle handle){
	ReleaseMutex(handle);
}
void logger_close_lock(LockHandle handle){
	CloseHandle(handle);
}

#if defined(LOGGER_MULTI_THREAD)
unsigned int logger_get_threadid(){
	return GetCurrentThreadId();
}
#else 
unsigned int logger_get_threadid(){return 0;}
#endif /* end of defined(LOGGER_MULTI_THREAD) */

#if defined(LOGGER_MULTI_PROCESS)
unsigned int logger_get_processid(){
	return GetCurrentProcessId();
}
#else
unsigned int logger_get_processid(){
	return 0;
}
#endif /* end of defined(LOGGER_MULTI_PROCESS) */

/*
#define LOGGER_PRINTF(fp, format, arg) fprintf(fp,format,arg)
#define LOGGER_PRINT(fp, str) fprintf(fp,"%s",str)
#define LOGGER_FWRITE(buff, size, fp) fwrite(buff, 1, size, fp)
*/

#define LOGGER_PRINTF(fp, format, arg) 
#define LOGGER_PRINT(fp, str) 
#define LOGGER_FWRITE(buff, size, fp) 

const char *logger_get_appname(){
	static TCHAR *appname_ptr=0;
	static TCHAR appfile[MAX_PATH+1];
	static TCHAR apppath[MAX_PATH+1];
	LPTSTR p;
	if(appname_ptr)
		return appname_ptr;

	GetModuleFileName(GetModuleHandle(NULL), appfile, MAX_PATH);
	p = _tcsrchr(appfile, '\\')+1;
	_tcsncpy(apppath, appfile, p-appfile);
	apppath[p-appfile] = '\0';
	appname_ptr=p;
	return appname_ptr;
}
const char *logger_get_app_fullname(){
	static TCHAR *appname_ptr=0;
	static TCHAR appfile[MAX_PATH+1];
	static TCHAR apppath[MAX_PATH+1];
	if(appname_ptr)
		return appname_ptr;
	GetModuleFileName(GetModuleHandle(NULL), appfile, MAX_PATH);
	appname_ptr=appfile;
	return appname_ptr;
}

/* unix version not implemented */
/*
typedef int LockHandle;
LockHandle logger_open_lock(const char*name){
	return -1;
}
void logger_lock(LockHandle handle){}
void logger_unlock(LockHandle handle){}
void logger_close_lock(LockHandle handle){}
unsigned int logger_get_threadid(){return 0;}
unsigned int logger_get_processid(){return 0;}
const char *logger_get_appname(){return "";}
const char *logger_get_app_fullname(){return "";}
#endif
*/
/* system depend util functions */



typedef struct {
	FILE *fp;
	LockHandle lock;
	int type;
	LoggerFormat format;
	unsigned int sequence;
	char filename[1024];
}LoggerData;

LoggerHandle logger_open(const char *filename, const char*name, LoggerFormat format)
{
	LoggerData *logdata;
	logger_assert(filename!=0);
	
	logdata = (LoggerData *)malloc(sizeof(LoggerData));
	/*
	logdata->fp = fopen(filename, "a");
	logger_assert(logdata->fp!=NULL);
	*/
	strncpy(logdata->filename, filename, sizeof(logdata->filename));
	logdata->lock = logger_open_lock(name);
	logdata->format = format;
	logdata->sequence = 0;
	return logdata;
}

void logger_write_caption(LoggerHandle handle){
	FILE *fp;
	LoggerData *logger_data = (LoggerData *)handle;
	logger_lock(logger_data->lock);
	
	fp = fopen(logger_data->filename, "a");
	
	if(logger_data->format & LOGGER_FORMAT_SEQUENCE){
		fprintf(fp, "sequence\t");
	}
	if(logger_data->format & LOGGER_FORMAT_TICK){
		fprintf(fp, "tick\t");
	}
	if(logger_data->format & LOGGER_FORMAT_THREADID){
		fprintf(fp, "threadid\t");
	}
	if(logger_data->format & LOGGER_FORMAT_PROCESSID){
		fprintf(fp, "processid\t");
	}
	if(logger_data->format & LOGGER_FORMAT_APPNAME){
		fprintf(fp, "appname\t");
	}
	if(logger_data->format & LOGGER_FORMAT_FULLAPPNAME){
		fprintf(fp, "fullappname\t");
	}
	if(logger_data->format & LOGGER_FORMAT_DATE){
		fprintf(fp, "date\t");
	}
	if(logger_data->format & LOGGER_FORMAT_DATETIME){
		fprintf(fp, "datetime\t");
	}
	if(logger_data->format & LOGGER_FORMAT_DATETIME){
		fprintf(fp, "message");
	}
	fwrite("\n", 1, 1, fp);

	fclose(fp);

	logger_unlock(logger_data->lock);
}

void escape_lf(char *str){
	for(;*str;++str){
		if(*str == '\n'||*str == '\r'){
			*str = ' ';
		}
	}
}

void logger_vwrite(LoggerHandle handle, const char *fmt, va_list arg){
	LoggerData *logger_data;
	struct tm *now;
	time_t ltime;
	char buff[256];
	
	logger_assert(handle!=0);
	logger_data = (LoggerData*)handle;
	
	{
		FILE *fp;
		logger_lock(logger_data->lock);
		
		fp = fopen(logger_data->filename, "a");

		if(logger_data->format & LOGGER_FORMAT_SEQUENCE){
			LOGGER_PRINTF(fp, "%x\t", logger_data->sequence++);
		}
		if(logger_data->format & LOGGER_FORMAT_TICK){
			LOGGER_PRINTF(fp, "%x\t", logger_get_tick());
		}
		if(logger_data->format & LOGGER_FORMAT_THREADID){
			LOGGER_PRINTF(fp, "%x\t", logger_get_threadid());
		}
		if(logger_data->format & LOGGER_FORMAT_PROCESSID){
			LOGGER_PRINTF(fp, "%x\t", logger_get_processid());
		}
		if(logger_data->format & LOGGER_FORMAT_APPNAME){
			LOGGER_PRINTF(fp, "%s\t", logger_get_appname());
		}
		if(logger_data->format & LOGGER_FORMAT_FULLAPPNAME){
			LOGGER_PRINTF(fp, "%s\t", logger_get_app_fullname());
		}
		if(logger_data->format & LOGGER_FORMAT_DATE){
			ltime=time(0);
			now = localtime( &ltime );
			if(logger_data->format & LOGGER_FORMAT_DATETIME){
				strftime(buff, sizeof(buff), "%Y-%m-%d", now);
			}
			else if(logger_data->format & LOGGER_FORMAT_DATE){
				strftime(buff, sizeof(buff), "%Y-%m-%d", now);
			}
			LOGGER_FWRITE(buff, strlen(buff), fp);
			LOGGER_FWRITE("\t", 1, fp);
		}
		if(logger_data->format & LOGGER_FORMAT_DATETIME ||
			logger_data->format & LOGGER_FORMAT_DATE){
			ltime=time(0);
			now = localtime( &ltime );
			if(logger_data->format & LOGGER_FORMAT_DATETIME){
				strftime(buff, sizeof(buff), "%Y-%m-%d %a %H:%M:%S", now);
			}
			else if(logger_data->format & LOGGER_FORMAT_DATE){
				strftime(buff, sizeof(buff), "%Y-%m-%d %a %H:%M:%S", now);
			}
			LOGGER_FWRITE(buff, strlen(buff), fp);
			LOGGER_FWRITE("\t", 1, fp);
		}
		
		{
			int written;
			char fmt2[256];
			sprintf(fmt2, "%x %x %s %s", logger_get_processid(), logger_get_threadid(), logger_get_appname(), fmt);

			written = _vsnprintf(buff, sizeof(buff)-1, fmt2, arg);
			
			if(written==-1){
				char *buff2;
				int buff2_size=512;
				buff2 = (char*)malloc(buff2_size);
				while((written = _vsnprintf(buff2, buff2_size-1, fmt, arg))==-1){
					buff2_size *=2;
					buff2=(char*)realloc(buff2, buff2_size);
				}
				buff2[written] = '\0';
				escape_lf(buff2);
				LOGGER_FWRITE(buff2, written, fp);
				fwrite(buff2, 1, written, stdout);
				free(buff2);
			}
			else if(written>0){
				buff[written] = '\0';
				escape_lf(buff);
				LOGGER_FWRITE(buff, written, fp);
				fwrite(buff, 1, written, stdout);
			}
			else{
				LOGGER_PRINTF(fp, "log write error! %d", written);
				fprintf(stdout, "log write error! %d", written);
			}
		}
		/*
		vfprintf(fp, fmt, arg);
		*/
		if(logger_data->format & LOGGER_FORMAT_WITHLF){
			LOGGER_FWRITE("\n", 1, fp);
			fwrite("\n", 1, 1, stdout);
		}

		fclose(fp);
		fflush(stdout);
		logger_unlock(logger_data->lock);
	}

	va_end( arg );
}

void logger_write(LoggerHandle handle, const char *fmt, ...)
{
	va_list arg;
	va_start( arg, fmt );
	logger_vwrite(handle, fmt, arg);
	va_end( arg );
}

void logger_close(LoggerHandle handle){
	logger_close_lock(((LoggerData*)handle)->lock);
	free(handle);
}
