/***************************************************************************
 *            system.c
 *
 *  Copyright  2009 - Steffen Pohle
 *  steffen@gulpe.de
 ****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "system.h"
#include "map.h"
#include "osmroute.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#if !defined(__MINGW32CE__) && !defined(_WIN32_WCE) && !defined(__MINGW32__)
  #include <sys/time.h>
  #include <sys/resource.h>
#endif

time_t starttime;

/*******************************************
 *
 * comparsion between doubles and floats 
 */
int cmpf (float f1, float f2) {
	return fabs(f1-f2) <= FLT_EPSILON;
};

int cmpd (double d1, double d2) {
	return fabs(d1-d2) < DBL_EPSILON;
};


#if !defined(__MINGW32CE__) && !defined(_WIN32_WCE) && !defined(__MINGW32__)
int execcall (struct s_execcall *p, char *prg, char **argv) {
	if (p->used) return 0;
	
	if ((p->pid = fork ()) < 0) {
		printf ("%s:%d syscall fork error: %s\n", __FILE__, __LINE__, strerror (errno));
		return 0;
	}
	else if (p->pid == 0) { // child //
		if (execvp(prg, argv) < 0) {
			printf ("%s:%d syscall execvp error:%s\n", __FILE__, __LINE__, strerror (errno));
			errorexit (-1);
		}
	}
	/* parent */
	
	p->used = 1;
	return 1;
};


int execwait (struct s_execcall *p) {
	pid_t ptmp;
	int status;
	
	if (p->used == 0) return -1;
	ptmp = waitpid (p->pid, &status, WNOHANG);
	if (ptmp == p->pid) {
		if (WIFEXITED (status)) {
			p->used = 0;
			return 1;
		}
	}
	else if (ptmp == -1) {
		p->used = 0;
		return 1;
	}

	return -2;
};
#endif


// #ifndef ANDROID
void d_printf (char *fmt,...) {
 	va_list args;
	char text1[1024];
	char text2[1024];
	char fn[LEN_FILENAME];
	int f;
#if defined(__MINGW32CE__) || defined(_WIN32_WCE)
//	return;
#endif
	va_start (args, fmt);
 	vsnprintf (text1, 1023, fmt, args);
 	va_end (args);
	text1[1023] = 0;
	text2[1023] = 0;

	snprintf (fn, LEN_FILENAME, "%slogfile.txt", cfg.logpath);

    snprintf (text2, 1023, "%-6d %s", (int)(time(NULL) - starttime), text1);
	
#ifndef COSMROUTE
	fprintf (stderr, "%s\n", text2);
#endif

	f = open (fn, O_RDWR | O_CREAT, 0644);
	if (f) {
		lseek (f, 0, SEEK_END);
		write (f, text2, strlen (text2));
#if !defined(__MINGW32CE__) && !defined(_WIN32_WCE) && !defined(__MINGW32__)  && !defined(ANDROID)
		write (f, "\n", 1);
#else
		write (f, "\n\r", 2);
#endif
		close (f);
	}
};
// #endif


void d_print_init() {
	int i;
	char **strings;
	char fn[LEN_FILENAME];
	
	snprintf (fn, LEN_FILENAME, "%slogfile.txt", cfg.logpath);
	starttime = time (NULL);
	unlink (fn);
	d_printf ("");
	d_printf ("************************************************************");
	d_printf ("*               Starting                                   *");
	d_printf ("************************************************************");
	d_printf ("size of datatypes in bytes - should be on all systems the same");
	d_printf ("     type       : size ");
	d_printf ("----------------+------");
	d_printf ("float           : %d", sizeof (float));
	d_printf ("char            : %d", sizeof (char));
	d_printf ("uint16_t        : %d", sizeof (uint16_t));
	d_printf ("uint32_t        : %d", sizeof (uint32_t));
	d_printf ("uint64_t        : %d", sizeof (uint64_t));
	d_printf ("size of datatypes in bytes - can be different");
	d_printf ("     type       : size ");
	d_printf ("----------------+------");
	d_printf ("long long int   : %d", sizeof (long long int));
	d_printf ("long int        : %d", sizeof (long int));
	d_printf ("int             : %d  INT_MIN:%d, INT_MAX:%d", sizeof (int), INT_MIN, INT_MAX);
	d_printf ("short int       : %d", sizeof (short int));
	d_printf ("\nTest for Backtraces:");

	strings = get_backtrace (&i);
	if (i < 1 || strings == NULL) d_printf ("seems not to work\n");
	else {
		d_printf ("works: %s\n", strings[2]);
		free (strings);
	}
};


#define D_PRINT_COL 16
void d_print_data (char *data, int len) {
	int i, j;
	int val;
	char txt1 [256] = {0};
	char txt2 [256] = {0};
	char left [256] = {0};
	char right [256] = {0};

	d_printf ("memdump: len:%d", len);
	for (i = 0; i < len; i++) {
		val = data[i] & 0x00FF;
		sprintf (txt1, "%2x ", val);
		if (data[i] >= ' ' && data[i] <= '~') sprintf (txt2, "%c", data[i]);
		else sprintf (txt2, ".");
		strncat (left, txt1, 256);
		strncat (right, txt2, 256);
		if ((i%D_PRINT_COL)==D_PRINT_COL-1 || i == len-1) {
			sprintf (txt1, "   ");
			sprintf (txt2, " ");
			for (j = i%D_PRINT_COL; j != 0 && j < D_PRINT_COL; j++) {
				strncat (left, txt1, 256);
				strncat (right, txt2, 256);
			}
			d_printf ("  |%s | %s |", left, right);
			memset (left, 0x0, 256);
			memset (right, 0x0, 256);
		}
	}
};


unsigned long long int getticks () {
	static unsigned long long int ticks = 0;

	if (ticks > (unsigned long long int)(ticks+1)) {
		d_printf ("%s:%d ticks+1 < ticks", __FILE__, __LINE__);
		errorexit (-1);
	}

	return ++ticks;
};


#define SIZE 100
void errorexit (int nr) {
#if !defined(__MINGW32CE__) && !defined(_WIN32_WCE) && !defined(__MINGW32__)  && !defined(ANDROID)
	int j, nptrs;
	void *buffer[SIZE];
	char **strings;

	nptrs = backtrace(buffer, SIZE);
	d_printf("****************** backtrace:  (needs to be linked with -Wl,--export-dynamic)");

	/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
	 * would produce similar output to the following: */
	strings = (char **) backtrace_symbols(buffer, nptrs);
	if (strings == NULL) {
		d_printf ("backtrace_symbols: %s", strerror(errno));
		exit(EXIT_FAILURE);
	}

	for (j = 0; j < nptrs; j++)
		d_printf("%s", strings[j]);

	free(strings);

#endif
	
	exit (nr);
};


void d_print_backtrace () {
#if !defined(__MINGW32CE__) && !defined(_WIN32_WCE) && !defined(__MINGW32__) && !defined(ANDROID)
	int j, nptrs;
	void *buffer[SIZE];
	char **strings;

	nptrs = backtrace(buffer, SIZE);
	d_printf("****************** backtrace:  (needs to be linked with -Wl,--export-dynamic)");

	/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
	 * would produce similar output to the following: */
	strings = (char **) backtrace_symbols(buffer, nptrs);
	if (strings == NULL) {
		d_printf ("  backtrace_symbols: %s", strerror(errno));
		exit(EXIT_FAILURE);
	}

	for (j = 0; j < nptrs; j++)
		d_printf("  %s", strings[j]);

	free(strings);
#endif
};



char **get_backtrace (int *cnt) {
#if !defined(__MINGW32CE__) && !defined(_WIN32_WCE) && !defined(__MINGW32__) && !defined(ANDROID)
	void *buffer[SIZE];
	char **strings;

	*cnt = backtrace(buffer, SIZE);

	/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
	 * would produce similar output to the following: */
	strings = (char **) backtrace_symbols(buffer, *cnt);
	if (strings == NULL) {
		d_printf ("backtrace_symbols: %s", strerror(errno));
		exit(EXIT_FAILURE);
	}

	return strings;
#else
	return NULL;
#endif
};


/*
 * copy data to the givin address and return increased pointer
 */
char *mempush (void *mem, void *source, int size) {
	memcpy (mem, source, size);
	return mem+size;
};

char *mempop (void *mem, void *dest, int size) {
	memcpy (dest, mem, size);
	return mem+size;
};

char *memstrpop (void *mem, void *dest, int *size) {
	uint8_t *m = mem, *d = dest;
	void *mout;
	
	while (*m != 0) {
		*d = *m;
		d++;
		m++;
	} 
	*d = *m;
	m++;

	mout = m;
	*size = mout - mem;

	return (char*)m;
};

#if !defined(ANDROID)
void memswap (void *mem1, void *mem2, int size) {
	char data[32];

	if (size > 32) {
		d_printf ("memswap maximum memory size of 32 bytes overrun size:%d", size);
		errorexit (-1);
	}
	memcpy (data, mem2, size);
	memcpy (mem2, mem1, size);
	memcpy (mem1, data, size);
};
#endif

int file_exist (char *fn) {
	struct stat fs;

	if (stat(fn, &fs) != 0)
		return 0;
	
	return 1;
};


int dir_exist (char *fn) {
	struct stat fs;
	
	if (stat(fn, &fs) == 0) {
		if (S_ISDIR(fs.st_mode))
			return 1;
	}
	return 0;
}


void d_print_stat () {
#if !defined(__MINGW32CE__) && !defined(_WIN32_WCE) && !defined(__MINGW32__) && !defined(ANDROID)
	struct rusage usage;
	
	d_printf ("getrusage:");
	if (getrusage(RUSAGE_SELF, &usage) == 0) {
//		d_printf ("ru_utime   : %6ld user CPU time used", usage.ru_utime);
//		d_printf ("ru_stime   : %6ld system CPU time used", usage.ru_stime);
		d_printf ("ru_maxrss  : %6ld maximum resident set size", usage.ru_maxrss);
//		d_printf ("ru_ixrss   : %6ld integral shared memory size", usage.ru_ixrss);
//		d_printf ("ru_idrss   : %6ld integral unshared data size", usage.ru_idrss);
//		d_printf ("ru_isrss   : %6ld integral unshared stack size", usage.ru_isrss);
		d_printf ("ru_minflt  : %6ld page reclaims (soft page faults)", usage.ru_minflt);
		d_printf ("ru_majflt  : %6ld page faults (hard page faults)", usage.ru_majflt);
		d_printf ("ru_nswap   : %6ld swaps", usage.ru_nswap);
		d_printf ("ru_inblock : %6ld block input operations", usage.ru_inblock);
		d_printf ("ru_oublock : %6ld block output operations", usage.ru_oublock);
//		d_printf ("ru_msgsnd  : %6ld IPC messages sent", usage.ru_msgsnd);
//		d_printf ("ru_msgrcv  : %6ld IPC messages received", usage.ru_msgrcv);
//		d_printf ("ru_nsignals: %6ld signals received", usage.ru_nsignals);
		d_printf ("ru_nvcsw   : %6ld voluntary context switches", usage.ru_nvcsw);
		d_printf ("ru_nivcsw  : %6ld involuntary context switches", usage.ru_nivcsw);
	}
	else d_printf ("getrusage error.");
#else
	d_printf ("getrusage not implemented in windows..");
#endif
}



char _flags2text_[255] = "";
char* flags2text (int f, char *text) {
	int i;
	int c;
	char *fn1;
	char *fn2;

	fn1 = text;
	memset (_flags2text_, 0x0, 255);
	snprintf (_flags2text_, 250, "%d:", f);

	for (c = 1; (c < 0xFFFF && fn1 != NULL); c *= 2) {
		fn2 = strchr (fn1, '\n');
		if (fn2 == NULL) i = strlen (fn1);
		else i = fn2-fn1;
		if (f & c) {
			if (c > 1) {
				_flags2text_[strlen(_flags2text_)+1] = 0;
				_flags2text_[strlen(_flags2text_)] = ',';
			}
			strncpy (_flags2text_+strlen(_flags2text_), fn1, i);
		}
		if (fn2 != NULL) fn2 += 1;
		fn1 = fn2;
	}
	return _flags2text_;
}

