/*
 * Copyright (C) 2025 Jakub Kruszona-Zawadzki, Saglabs SA
 * 
 * This file is part of MooseFS.
 * 
 * MooseFS 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, version 2 (only).
 * 
 * MooseFS 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 MooseFS; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
 * or visit http://www.gnu.org/licenses/gpl-2.0.html
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <inttypes.h>
#ifdef _USE_PTHREADS
#include <pthread.h>
#endif

#include "clocks.h"

static uint8_t i,j;
static uint8_t p[256];
#ifdef _USE_PTHREADS
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
#endif

int rnd_init(void) {
	uint8_t key[64],vkey[64];
	register uint8_t x;
	uint16_t l;

	srandom(time(NULL)+monotonic_useconds());
	for (l=0 ; l<64 ; l++) {
		key[l] = random();
		vkey[l] = random();
	}
#ifdef _USE_PTHREADS
	pthread_mutex_lock(&lock);
#endif
	for (l=0 ; l<256 ; l++) {
		p[l]=l;
	}
	for (l=0 ; l<768 ; l++) {
		i = l&0xFF;
		x = j+p[i]+key[l%64];
		j = p[x];
		x = p[i];
		p[i] = p[j];
		p[j] = x;
	}
	for (l=0 ; l<768 ; l++) {
		i = l&0xFF;
		x = j+p[i]+vkey[l%64];
		j = p[x];
		x = p[i];
		p[i] = p[j];
		p[j] = x;
	}
	i = 0;
#ifdef _USE_PTHREADS
	pthread_mutex_unlock(&lock);
#endif
	return 0;
}

#define RND_RC4_STEP(result) { \
	register uint8_t x; \
	x = j+p[i]; \
	j = p[x]; \
	x = p[j]; \
	x = p[x]+1; \
	result = p[x]; \
	x = p[i]; \
	p[i] = p[j]; \
	p[j] = x; \
	i++; \
}

uint8_t rndu8(void) {
	uint8_t r;
#ifdef _USE_PTHREADS
	pthread_mutex_lock(&lock);
#endif
	RND_RC4_STEP(r);
#ifdef _USE_PTHREADS
	pthread_mutex_unlock(&lock);
#endif
	return r;
}

uint32_t rndu32(void) {
	uint32_t res;
	uint8_t *r = (uint8_t*)&res;
#ifdef _USE_PTHREADS
	pthread_mutex_lock(&lock);
#endif
	RND_RC4_STEP(r[0]);
	RND_RC4_STEP(r[1]);
	RND_RC4_STEP(r[2]);
	RND_RC4_STEP(r[3]);
#ifdef _USE_PTHREADS
	pthread_mutex_unlock(&lock);
#endif
	return res;
}

uint64_t rndu64(void) {
	uint64_t res;
	uint8_t *r = (uint8_t*)&res;
#ifdef _USE_PTHREADS
	pthread_mutex_lock(&lock);
#endif
	RND_RC4_STEP(r[0]);
	RND_RC4_STEP(r[1]);
	RND_RC4_STEP(r[2]);
	RND_RC4_STEP(r[3]);
	RND_RC4_STEP(r[4]);
	RND_RC4_STEP(r[5]);
	RND_RC4_STEP(r[6]);
	RND_RC4_STEP(r[7]);
#ifdef _USE_PTHREADS
	pthread_mutex_unlock(&lock);
#endif
	return res;
}

void rndbuff(uint8_t *buff,uint32_t size) {
	uint32_t k;
#ifdef _USE_PTHREADS
	pthread_mutex_lock(&lock);
#endif
	for (k=0 ; k<size ; k++) {
		RND_RC4_STEP(buff[k]);
	}
#ifdef _USE_PTHREADS
	pthread_mutex_unlock(&lock);
#endif
}

uint64_t rndu64_ranged(uint64_t range) {
	uint64_t max,r;
	r = rndu64();
	if (range==0) {
		return r;
	}
	max = -(((uint64_t)-range) % range);
	if (max) {
		while (r>=max) {
			r = rndu64();
		}
	}
	return r%range;
}

uint32_t rndu32_ranged(uint32_t range) {
	uint32_t max,r;
	r = rndu32();
	if (range==0) {
		return r;
	}
	max = -(((uint32_t)-range) % range);
	if (max) {
		while (r>=max) {
			r = rndu32();
		}
	}
	return r%range;
}

/* test loop:
int main(int argc,char **argv) {
	uint32_t x,y,range;
	uint64_t i;
	double xmean=0.0,ymean=0.0;
	double delta;
	rnd_init();
	if (argc==2) {
		range = strtoul(argv[1],NULL,10);
	} else {
		range = 3000000000;
	}
	i = 1;
	while (i<100000000) {
		x = rndu32()%range;
		y = rndu32_ranged(range);
		delta = x-xmean;
		xmean += delta / i;
		delta = y-ymean;
		ymean += delta / i;
		i++;
		if ((i%1000000)==0) {
			printf("%.6lf : %.6lf\n",((range/2.0)-0.5-xmean)/range,((range/2.0)-0.5-ymean)/range);
		}
	}
	return 0;
}
*/
