#include "set_error.h"

extern "C" {
	#include <sys/types.h>
	#include <stdio.h>
	#include <stdlib.h>
	#include <unistd.h>
}

class err_buffer {
public:
	char errmsg1[1024];
	char errmsg2[1024];
	char * errmsg;
	char * errmsgb;
	short errmsglen;
	
	pid_t pid;
	
	inline err_buffer(pid_t processid) :
	          errmsg(errmsg1), errmsgb(errmsg2), errmsglen(0), pid(processid) {};
	
	inline ~err_buffer(void) {};
	
};

// static Semaphore sema;
static char supress_autodisp = 0;
static char supress_error = 0;
static char autoinit = -1;

extern "C" void AutoDispOff(void) {
	supress_autodisp = -1;
}

#define max_erbu 10

static err_buffer * erbus[max_erbu];

static err_buffer * FindErBu(void) {
	
	pid_t me =  getpid();
	
	{
		err_buffer ** e = erbus;
		short x = max_erbu-1;
		do {
			if (*e) {
				if ( (*e)->pid == me) return *e;
			}
			e++;
		} while (x--);
	}
	
	// kein erbu gefunden, also neuen allokieren...
	
	err_buffer ** e = erbus;
	short x = max_erbu-1;
	do {
		if ( *e == 0 ) break;
		e++;
	} while (x--);
	
	if (x < 0) return 0;  // mehr prozesse geht nicht
	
	supress_error = -1;
	*e = new err_buffer(me);
	supress_error = 0;
	
	return *e; // kann null sein -> fehler
}

static void internal_disp_error(err_buffer * eb);
static void internal_add_error(err_buffer * eb, const char * msg1, const char * msg2);

static void autodisp(err_buffer * eb) {
	
	if (supress_autodisp) return;
	
//	sema.Obtain(); 
	
	if (eb->errmsglen != 0) {
		
		internal_add_error(eb, "The error-handler detected an untreated malfunction. This might\n",
		                       "be harmless, but if the program should crash, please remember\n"
		                       "the following error-message for your bug-report");
		          
		internal_disp_error(eb);
	}
	
//	sema.Release();
}

static void DelErBus(void) {
	
	err_buffer ** e = erbus;
	short x = max_erbu-1;
	do {
		if ( *e ) {
			autodisp(*e);
			delete *e;
			*e = 0;
		}
		e++;
	} while (x--);	
}

static void close_down(void) {
	
	DelErBus();
}

static void come_up(void) {
	autoinit = 0;
	err_buffer ** e = erbus;
	short x = max_erbu-1;
	do {
		*e++ = 0;
	} while (x--);
	
	atexit(close_down);
}

static void internal_add_error(err_buffer * eb, const char * msg1, const char * msg2) {
	
	char * m = eb->errmsgb;
	const char * o = msg1;
	short x;
	
	for (x = 0; x < 1024-4; x++) {
		if (0 == (*m++ = *o++)) break;
	}
	
	m--;
	o = msg2;
	
	for (; x < 1024-4; x++) {
		if (0 == (*m++ = *o++)) break;
	}
	
	if (eb->errmsglen != 0) {
		
		m--;
		o = ", reason:\n ";
		
		for (; x < 1024-4; x++) {
			if (0 == (*m++ = *o++)) break;
		}
		
		m--;
		o = eb->errmsg;
		for (; x < 1024-4; x++) {
			if (0 == (*m++ = *o++)) break;
		}
	}
	else {
		m--;
		if (x < 1024-4) {
			*m++ = 10;
			x++;
		}
	}
	
	eb->errmsglen = x;
	
	char * t = eb->errmsg;
	eb->errmsg = eb->errmsgb;
	eb->errmsgb = t;
	
	eb->errmsg[eb->errmsglen] = 0;
	
}

static char * internal_get_error(err_buffer * eb) {
	
	eb->errmsg[eb->errmsglen] = 0;
	eb->errmsglen = 0;
	
	return eb->errmsg;
}

static void internal_disp_error(err_buffer * eb) {
	fprintf(stderr, "MALFUNCTION:\n%s",internal_get_error(eb));
}

extern "C" void set_error(const char * msg1, const char * msg2) {
	
	if (supress_error) return;
	
//	sema.Obtain();
	
	if (autoinit) {
		come_up();
	}
	
	err_buffer * eb = FindErBu();
	if (!eb) {
//		sema.Release();
		return;
	}
	
	if (eb->errmsglen) {
		autodisp(eb);
	}
	
	char * m = eb->errmsg;
	const char * o = msg1;
	short x;
	
	for (x = 0; x < 1024-4; x++) {
		if (0 == (*m++ = *o++)) break;
	}
	
	m--;
	o = msg2;
	
	for (; x < 1024-4; x++) {
		if (0 == (*m++ = *o++)) break;
	}
	
	m--;
	if (x < 1024-4) {
		*m++ = 10;
		x++;
	}
	
	
	eb->errmsglen = x;
	eb->errmsg[eb->errmsglen] = 0;
	
//	sema.Release();
}

extern "C" void add_error(const char * msg1, const char * msg2) {
	
	if (supress_error) return;
	
//	sema.Obtain();
	
	if (autoinit) {
		come_up();
	}
	
	err_buffer * eb = FindErBu();
	if (!eb) {
//		sema.Release();
		return;
	}
	
	internal_add_error(eb, msg1, msg2);
	
//	sema.Release();
	
}


extern "C" char * get_error(void) {
	
//	sema.Obtain();
	
	if (autoinit) {
		come_up();
	}
	
	err_buffer * eb = FindErBu();
	if (!eb) {
//		sema.Release();
		return "**FATAL** error handler malfunction";
	}
	
	eb->errmsg[eb->errmsglen] = 0;
	eb->errmsglen = 0;
	
//	sema.Release();
	return eb->errmsg;
}

extern "C" int test_error(void) {
	
//	sema.Obtain();
	
	if (autoinit) {
		come_up();
	}
	
	err_buffer * eb = FindErBu();
	if (!eb) {
//		sema.Release();
		return 0;
	}
	
//	sema.Release();

	if (eb->errmsglen) return -1;
	return 0;
}

extern "C" void disp_error(void) {
	
//	sema.Obtain();
	
	if (autoinit) {
		come_up();
	}
	
	err_buffer * eb = FindErBu();
	if (!eb) {
//		sema.Release();
		return;
	}
	
	internal_disp_error(eb);
	
//	sema.Release();
	
}



syntax highlighted by Code2HTML, v. 0.9.1