/*



*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "lib.h"
#include "thread.h"
#define TRACE_MAIN 1
#include "trace.h"
#define TRACEBF 80
typedef struct TRACE {time_t when; int id; char *source; int line; char info[TRACEBF];} Trace;
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#define DMID 28 // was 21
#include "debug.h"

#define HASHSZ 901
#define NLINES 150

static int upto[HASHSZ];
static Trace *tlist[HASHSZ];



static int (*imsg)( const char *format ,...);

int global_line;

static int in_add;
static int isdisable;
int trace_disable(void)
{
	isdisable = TRUE;
	return TRUE;
}
// can I make this work without a mutex safely...
int trace_add(char *source, int line, char *info)
{
	Trace *t;
	Trace *tlines;
	int up;
	int i;
	int id = th_id();
	int hid = id % HASHSZ;
	if (isdisable) return TRUE;
	stack_ok("trace");
	if (hid<0) hid = -hid;
	tlines = tlist[hid];
	if (tlines==NULL) {
		for (i=0; i<HASHSZ; i++) {
			tlist[i] = calloc(NLINES,sizeof(Trace));  // Corruption found in this pointer... DMID changed so it cannot be anything else now. MAGIC NUMBER at end was lost...
		}
		tlines = tlist[hid];
	}
	d_check_pointer((char *)tlines,__FILE__,__LINE__);
	up = upto[hid]++;
	if (up>=NLINES) up = upto[hid] = 0;
	if (up<0) lib_abort();
	if (up>=NLINES) return TRUE; // lib_abort(); this can happen due to race condition, should be silently ignored.
	t = &tlines[up];
	d_check_pointer((char *)tlines,__FILE__,__LINE__);

	t->source = source;
	t->line = line;
	t->id = th_id();
	t->when = time(NULL);
	ncpy(t->info,info,TRACEBF-1);
	d_check_pointer((char *)tlines,__FILE__,__LINE__); // Checking to see if corruption occurs during trace_add...
	return TRUE;
}

int trace_add_safe_version(char *source, int line, char *info)
{
	Trace *t;
	Trace *tlines;
	int id = th_id();
	int hid = id % HASHSZ;
	if (isdisable) return TRUE;
	if (hid<0) hid = -hid;
	if (in_add) return TRUE; // avoid collisions... 
	trace_wait();
	if (!in_add) {
		in_add = TRUE;
		trace_release(); // safe due to in_add, but some lines will be lost, 
		tlines = tlist[hid];
		if (tlines==NULL) tlist[hid] = tlines = calloc(NLINES,sizeof(Trace));
		t = &tlines[upto[hid]];
		t->source = source;
		t->line = line;
		t->id = th_id();
		upto[hid]++;
		if (upto[hid]>=NLINES) upto[hid] = 0;
		ncpy(t->info,info,TRACEBF-1);
		in_add = FALSE;
	} else {
		trace_release();
	}
	return TRUE;
}
// these two made static so that trace_show would not use much stack up...
#undef fopen
#undef fclose
static char tmpstr[BFSZ];
static char tmp[BFSZ];
int trace_show(char *fname, int xsig, int thid)
{
	FILE *f;
	int hid = thid % HASHSZ;
	Trace *tlines;
	int i;
	int h;
	Trace *t;
	time_t tnow=time(NULL);
	time_t recent = tnow-2;  // last 2 seconds.
	if (hid<0) hid = -hid;
	isdisable = TRUE;

	if (imsg==NULL) imsg = printf;
	f = fopen(fname,"w");
	if (f==NULL) return TRUE;
//	trace_wait();
	
	fprintf(f,"-----------Trace of all active threads --- signal %d thread %d \n",xsig,thid);
	for (h=0;h<HASHSZ;h++) {
		if (h==hid) continue;
		tlines = tlist[h];
		if (tlines==NULL) continue;

	
		for (i=upto[hid]; i<NLINES; i++) {
			t = &tlines[i];
			if (t->source!=NULL) if (t->when>recent) fprintf(f,"[%d] %s:%d %s\n",t->id,t->source,t->line,t->info);
		}
		for (i=0; i<upto[hid]; i++) {
			t = &tlines[i];
			if (t->source!=NULL) if (t->when>recent) fprintf(f,"[%d] %s:%d %s\n",t->id,t->source,t->line,t->info);
		}

	}
	fprintf(f,"-----------Trace Starts of problem thread --- signal %d thread %d \n",xsig,thid);

	tlines = tlist[hid];
	if (tlines==NULL) return TRUE;
	
	for (i=upto[hid]; i<NLINES; i++) {
		t = &tlines[i];
		if (t->source!=NULL) if (thid==t->id) fprintf(f,"[%d] %s:%d %s\n",t->id,t->source,t->line,t->info);
	}
	for (i=0; i<upto[hid]; i++) {
		t = &tlines[i];
		if (t->source!=NULL) if (thid==t->id) fprintf(f,"[%d] %s:%d %s\n",t->id,t->source,t->line,t->info);
	}

	fprintf(f,"-----------Trace ENDS ----------- signal %d thread %d \n",xsig,thid);

	
	fprintf(f,"Date of trace dump %s, global_line=%d\n",safe_ctime(&tnow,tmpstr),global_line);

//	trace_release();
	fclose(f);
	return TRUE;
}
#ifdef DO_THREADS
static Mutex *m;
#endif
void trace_init(int (*vimsg)( const char *format ,...))
{
	imsg = vimsg;
#ifdef DO_THREADS
	m = th_mutex_new("trace");
#endif
}
void trace_wait(void)
{
#ifdef DO_THREADS
	if (m==NULL) return;
	if (!th_mutex_wait(m,600000)) { /* 10 minutes */
		imsg("Cannot trace_wait, fatal problem, dieing malloc\n");
//		fflush(NULL);
		lib_abort();
	}
#endif
}
void trace_release(void)
{
#ifdef DO_THREADS
	if (m==NULL) return;
	th_mutex_release(m);
#endif
}
