/* 8 Aug 2000 Jesse fixes emsg_close() bug of uninitialized or NULL pointers*/

#ifdef __XCODE__
#include "xcode_quiet.h"
#endif

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>

#include "lprintf.h"

#ifdef WIN32
#include <windows.h>
#ifndef DO_THREADS
#include <process.h>
#endif
#endif
#include "emsg.h"
#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE (!FALSE)
#endif

#include "defines.h"
#ifdef DO_THREADS
int th_id(void);
#endif

#ifdef WIN32
#define vsnprintf _vsnprintf
#endif
static int emsg_printf=FALSE;
static int disable;

static int lf_terminate=FALSE;
static int log_lvl=D_DEBUG;
#define lib_file_osize emsg_lib_file_osize

#ifdef IOS_CONSOLE
void nslog_from_c(char*type,char *line);
#endif
#ifdef XCODE_CONSOLE
void nslog_from_c(char*type,char *line);
#endif

#ifdef __XCODE__
// Mutex safety for Online iOS and Mac clients (try for no impact on other consumers of sam)
#include "thread.h"
static Mutex *m;
static int save_id;
static int nheld;
void emsg_mutex_init(int enable)
{
    if (enable){
        if (m!=NULL) return;
        m = th_mutex_new("emsg.c");
    }else{
        if (m==NULL) return;
        //th_mutex_free(&m);
        m=NULL;
    }
}
int emsg_mutex_wait()
{
	if (m==NULL) return TRUE;
	if (save_id==th_id()) {
		nheld++;
		return TRUE;
	}
	if (!th_mutex_wait(m,10000)) { /* 30 seconds */
		if (!th_mutex_wait(m,5000)) { /* another 10 seconds, incase it came back to life*/
#ifdef YESIMONLINE
            // just ignore failed log lines (probably wake from sleep)
            return FALSE;
#endif
			emsg("Mutex failed (emsg.c)\n");
			abort();  // IT IS CRASHING HERE ???
		}
	}
	nheld++;
	save_id = th_id();
	return TRUE;
}
void emsg_mutex_release()
{
	if (m==NULL) return;
	nheld--;
	if (nheld>0) {
		//imsg("t_mutex_release: fake release nheld=%d\n",nheld);
		return; // not all free yet.
	}
	save_id = -1;
	th_mutex_release(m);
}

#else
int emsg_mutex_wait(){return TRUE;}
void emsg_mutex_release(){}
#endif

int emsg_setprint(int x)
{
	emsg_printf = x;
	return TRUE;
}
int emsg(const char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	char output[2000];
	if (disable) return 0;

        va_start(arg_ptr, arg_list);
        format = (char *) arg_list;
        len = vsnprintf(output,1000, format, arg_ptr); output[1000-1] = 0;
        if (len>=1000) emsg_do(D_ERROR,"FATAL, eINMSG ERROR TOO LONG\n");
        emsg_do(D_ERROR,output);
	return 0;
}
char *emsgncat(char *dst, char *src, int len)
{
        char *xdst = dst;
        int xlen = len;
	int r = strlen(dst);

	len -= r;
	dst += r;
        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
        xdst[xlen] = 0;
        return xdst;
}

int  imsg(const char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	char output[2000];

	if (disable) return 0;
        va_start(arg_ptr, arg_list);
        format = (char *)  arg_list;
        len = vsnprintf(output,1000, format, arg_ptr); output[1000-1] = 0;
        if (len>=1000) emsg_do(D_ERROR,"FATAL, iINMSG ERROR TOO LONG\n");
		if (strchr(output,'\n')==NULL) emsgncat(output,"\n",1900);
        emsg_do(D_INFO,output);
	return 0;
}
int  dmsg(const char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	char output[2000];

	if (disable) return 0;
	if (log_lvl<D_DEBUG) return TRUE;

        va_start(arg_ptr, arg_list);
        format = (char *)  arg_list;
        len = vsnprintf(output,1000, format, arg_ptr); output[1000-1] = 0;
        if (len>=1000) emsg_do(D_ERROR,"FATAL, dINMSG ERROR TOO LONG\n");
        emsg_do(D_DEBUG,output);
	return 0;
}
static char *slvl[]={"Err","Info","dbg"};
static FILE *f = NULL,*ef = NULL;
static int isdebug;
int emsg_isdebug(void)
{
	return isdebug;
}
int lib_file_osize(FILE *f)
{
	struct stat bf;
	if (fstat(fileno(f),&bf)!=0) {return -1;} /* Its nice to know when a file does not exist  */
	return bf.st_size;
}
void emsg_setdebug(int x)
{
	isdebug = x;
}
static int showthread;
void emsg_showthread(int x)
{
	showthread = x;
}
static int doflush;
void emsg_set_flush(int x)
{
	doflush = x;
}
int emsg_set_disable(int t)
{
	disable = t;
	return TRUE;
}
int emsg_set_lf_terminate(int t)
{
	lf_terminate = t;
	return TRUE;
}
int emsg_set_log_lvl(int t)
{
	log_lvl = t;
	return TRUE;
}
int emsg_truncate(void);

char *elib_emsg_time(char *tstr)
{
	time_t t;
	struct tm *atm;
	int msec=0;
	time(&t);
	atm = localtime(&t);
	if (atm==NULL) strcpy(tstr,"notime");
	else sprintf(tstr,"%04d-%02d-%02d %02d:%02d:%02d",atm->tm_year+1900, atm->tm_mon+1, atm->tm_mday, atm->tm_hour, atm->tm_min, atm->tm_sec);
	return tstr;
}


void emsg_do(int lvl, char *line)
{
        time_t t;
	char *ccc;
	char tstr[BFSZ];
	char tid[BFSZ];

//return;
	strcpy(tid,"");

//	time(&t);
//	ccc = ctime(&t);
//	if (ccc==NULL) ccc = "badtimebadtimebadtime";
  //  strcpy(tstr,ccc+8);
  //  tstr[11] = 0;
	elib_emsg_time(tstr); // chrisp adding year/month. 

	if (f==NULL) return;

	if (!emsg_mutex_wait()) return;

#ifdef DO_THREADS
	if (showthread)
 #ifdef HEX_THREAD_ID
	sprintf(tid,"t=%x ",th_id());
 #else
	sprintf(tid,"t=%d ",th_id());
 #endif
#else
	sprintf(tid,"p=%d ",getpid());
#endif
	if(lf_terminate){
		int len=strlen(line);
		if (len>0 && line[len-1] != '\n') {
			line[len++]='\n';
			line[len]='\0';
		}
	}
#ifdef XCODE_CONSOLE
	nslog_from_c(slvl[lvl],line);
#endif
	fprintf(f,"%s:%s:%s %s",tstr,slvl[lvl],tid,line);
	if (ef!=NULL) if (lvl==D_ERROR) fprintf(ef,"%s:%s:%s %s",tstr,slvl[lvl],tid,line);
	if (lvl==D_ERROR) fflush(f);
	else if (doflush) fflush(f);
	if (isdebug) printf("%s",line);
	else if (emsg_printf) if (lvl==D_ERROR) printf("%s",line);

	if (f!=NULL) if (lib_file_osize(f)>2000000) {
		emsg_truncate();
	}

    emsg_mutex_release();
}
static char save_name[1000];
static char elogfile[1000];
int emsg_open_error(char * logfile);
static int maxsize;
char *lib_replace(char *in, char *out, int outbfsz, char *from, char *to);

int emsg_truncate(void)
{
	if (f!=NULL) fclose(f); f = NULL;
	if (strlen(save_name)==0) return FALSE;
	//remove(save_name);
	if (ef!=NULL) {
		if (lib_file_osize(ef)>2000000) {
            char oldfile[BFSZ];
			if (ef!=NULL) fclose(ef); ef = NULL;
            lib_replace(elogfile, oldfile, BFSZ, ".log", "2.log");
            remove(oldfile);
            rename(oldfile, elogfile);
			remove(elogfile);
		}
	}
	f = fopen(save_name,"a");
	if (f==NULL) {printf("Log file open failed (%s) %s\n",save_name,strerror(errno));  return FALSE; }

	return TRUE;
}
int emsg_set_max(int x)
{
	maxsize = x;
	return TRUE;
}
int lib_file_osize(FILE *f);
int emsg_open(char *logfile)
{
	emsg_open_error(logfile);
	strncpy(save_name,logfile,300); save_name[300] = 0;
	if (f!=NULL) fclose(f);
	f = fopen(logfile,"a");
	if (f==NULL) {perror(logfile); return FALSE; }
	if (maxsize>0) if (lib_file_osize(f)>maxsize) {
        char oldfile[BFSZ];
        lib_replace(logfile, oldfile, BFSZ, ".log", "2.log");
		fclose(f);
		remove(oldfile);
		rename(logfile, oldfile);
		f = fopen(logfile,"a");
	}
	return TRUE;
}
int emsg_open_error(char * logfile)
{
	char bf[BFSZ];
	char *s;
	strncpy(bf,logfile,300); bf[300] = 0;
	s = strstr(bf,".log");
	if (s==NULL) return FALSE;
	*s = 0;
	strcpy(s,".err");
	strcpy(elogfile,bf);

	if (ef!=NULL) fclose(ef);
	ef = fopen(elogfile,"a");
	return TRUE;
}

void emsg_close(void)
{
	if (f!= NULL){fclose(f); f= NULL;}
	if (ef!= NULL){fclose(ef); ef= NULL;}
}
void emsg_flush(void)
{
	if (f!=NULL) fflush(f);
	if (ef!=NULL) fflush(ef);
}
char *emsg_fname(void)
{
	return save_name;
}
char *emsg_fname_err(void)
{
	return elogfile;
}
char *emsg_time(char *tstr)
{
        time_t t;
	char *ccc;
	int msec=0;
#ifdef WIN32
	SYSTEMTIME systime;
	GetSystemTime(&systime);
	msec = systime.wMilliseconds;
#endif
	time(&t);
	ccc = ctime(&t);
	if (ccc==NULL) ccc = "badtimebadtimebadtime";
        strcpy(tstr,ccc+8);
        tstr[11] = 0;
	sprintf(tstr+11,".%.02d",msec/10);

	return tstr;
}
