
#include "utils.h"
#include "bomber.h"
#include "gfx.h"

#include <arpa/inet.h>

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int volatile hc = 0;
char volatile interrupted = 0;
static Uint32 cur_unique;
Uint32 network_unique;

Uint32 gtime(void) {
	return SDL_GetTicks();
}

Uint32 longtime(void) {
	return gtime() / 1000;
}

/* surf random generator: x (Daniel J. Bernstein) */
#define ROT(x, b) (((x) << (b)) | ((x) >> (32 - (b))))
#define MUSH(i, b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROT(x, b))
static void surf(Uint32 out[8], const Uint32 in[12], const Uint32 seed[32]) {
	Uint32 t[12], x, sum = 0;
	int r, i, loop;
	for (i = 0; i < 12; ++i) t[i] = in[i] ^ seed[12 + i];
	for (i = 0; i < 8; ++i) out[i] = seed[24 + i];
	x = t[11];
	for (loop = 0; loop < 2; ++loop) {
		for (r = 0; r < 16; ++r) {
			sum += 0x9e3779b9;
			MUSH(0, 5);
			MUSH(1, 7);
			MUSH(2, 9);
			MUSH(3, 13);
			MUSH(4, 5);
			MUSH(5, 7);
			MUSH(6, 9);
			MUSH(7, 13);
			MUSH(8, 5);
			MUSH(9, 7);
			MUSH(10, 9);
			MUSH(11, 13);
		}
		for (i = 0; i < 8; ++i) out[i] ^= t[i + 4];
	}
}
#undef ROT
#undef MUSH

static Uint32 surf_seed[32];
static Uint32 surf_in[12], surf_out[8];
static int surf_left;

static int surf_init_urandom(Uint32* unique) {
	int fd;
	int success = 1;

	fd = open("/dev/urandom", O_RDONLY);
	if (-1 == fd) return 0;

	if (read(fd, &unique, sizeof(unique)) != sizeof(unique)) success = 0;
	if (read(fd, &surf_seed, sizeof(surf_seed)) != sizeof(surf_seed)) success = 0;

	close(fd);

	return success;
}

static Uint32 surf_init_gtime_random() {
	Uint32 unique = 0;

	Uint32 genseed[4][8];
	surf_seed[0] = surf_seed[1] = gtime();
	surf_in[0]++;
	surf(genseed[0], surf_in, surf_seed);
	surf_in[0]++;
	surf(genseed[1], surf_in, surf_seed);
	surf_in[0]++;
	surf(genseed[2], surf_in, surf_seed);
	surf_in[0]++;
	surf(genseed[3], surf_in, surf_seed);
	memcpy(surf_seed, genseed[0], 32);
	memcpy(surf_seed, genseed[1], 32);
	memcpy(surf_seed, genseed[2], 32);
	memcpy(surf_seed, genseed[3], 32);
	surf_in[0] = gtime();
	surf(genseed[0], surf_in, surf_seed);
	surf_in[0] = 0;
	unique = genseed[0][0];

	return unique;
}

static Uint32 surf_init(void) {
	Uint32 unique = 0;

	memset(surf_in, 0, sizeof(surf_in));
	memset(surf_out, 0, sizeof(surf_out));
	memset(surf_seed, 0, sizeof(surf_seed));
	surf_left = 0;

	if (!surf_init_urandom(&unique)) { unique = surf_init_gtime_random(); }

	return unique;
}

static Uint32 surf_random(void) {
	if (surf_left == 0) {
		int i;
		for (i = 0; (i < 12) && !(++surf_in[i]); i++);
		surf_left = 8;
		surf(surf_out, surf_in, surf_seed);
	}
	return surf_out[--surf_left];
}

void read_seed_unique(unsigned char* buf) {
	int i;

	memset(surf_in, 0, sizeof(surf_in));
	memset(surf_out, 0, sizeof(surf_out));
	surf_left = 0;

	memcpy(&surf_seed, buf, sizeof(surf_seed));
	memcpy(&network_unique, buf + sizeof(surf_seed), sizeof(network_unique));
	cur_unique = ntohl(network_unique);
	for (i = 0; i < 32; i++) surf_seed[i] = ntohl(surf_seed[i]);
}

unsigned char* write_seed_unique(unsigned char* buf) {
	int i;

	for (i = 0; i < 32; i++) {
		Uint32 l = htonl(surf_seed[i]);
		memcpy(buf, &l, sizeof(l));
		buf += sizeof(l);
	}
	memcpy(buf, &network_unique, sizeof(network_unique));
	buf += sizeof(network_unique);
	return buf;
}

void create_seed_unique(void) {
	cur_unique = surf_init();
	network_unique = htonl(cur_unique);
}

int myrand(void) {
	return surf_random() & 0xffffu;
}

#if 0
/* random generator */

#define TAP1 250
#define TAP2 103
/*
#define TAP1 55
#define TAP2 31
*/

static unsigned char myrandblock[TAP1];
static int myrandtake;

static int myrand1(void) {
	int i;
	int val;

	i=myrandtake-TAP2;
	if(i<0) i+=TAP1;
	val=myrandblock[myrandtake++]^=myrandblock[i];
	if(myrandtake==TAP1) myrandtake=0;
	return val;
}

int myrand(void) {
	int v;
	v=myrand1();
	return (v<<8) | myrand1();
}

static void initmyrand(Uint32 unique) {
	int i,j;
	unsigned char *p;
	int msb,msk;

	myrandtake=0;
	p=myrandblock;
	j=12345 ^ unique;
	i=TAP1;
	while(i--) {
		j=(j*1277)&0xffff;
		*p++=j>>8;
	}
	p=myrandblock+14;
	msk=0xff;
	msb=0x80;
	do {
		*p&=msk;
		*p|=msb;
		p+=11;
		msk>>=1;
		msb>>=1;
	} while(msk);
	i=500;
	while(i--) myrand();
}

void create_unique(void) {
	set_unique(gtime());
}

void set_unique(Uint32 unique) {
	cur_unique = unique;
	network_unique = htonl(cur_unique);
	initmyrand(cur_unique);
}

Uint32 get_unique(void) {
	return cur_unique;
}
#endif

void nomem(char* str) {
	printf("No memory!!![%s]\n", str);
	exit(1);
}

int mypause(void) {
	while (!interrupted) {
		pollinput();
		SDL_Delay(1);
	}
	interrupted = 0;
	return 1;
}

static Uint32 sdlhandler(Uint32 time) {
#if defined(SDL_LATENCY)
	outmsgs();
#endif
	interrupted = 1;
	hc++;
	return time;
}

void pulseon(void) {
	SDL_SetTimer(40, sdlhandler);
}

void hexdump(unsigned char* p, int len) {
	int i;
	for (i = 0; i < len; i++) {
		if (15 == len % 16)
			fprintf(stderr, "0x%X\n", p[i]);
		else
			fprintf(stderr, "0x%X ", p[i]);
	}
	if (i % 16) fprintf(stderr, "\n");
}
