/*
 * This software is licensed under the terms of the MIT License.
 * See COPYING for further information.
 * ---
 * Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
 * Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
 */

#pragma once
#include "taisei.h"

#include "util/crap.h"
#include "util/miscmath.h"

typedef struct RandomState {
	uint64_t state[4];
#ifdef DEBUG
	bool locked;
#endif
} RandomState;

typedef struct rng_val {
	uint64_t _value;
} rng_val_t;

uint64_t splitmix64(uint64_t *state) attr_nonnull(1);
uint32_t splitmix32(uint32_t *state) attr_nonnull(1);
uint64_t makeseed(void);

void rng_init(RandomState *rng, uint64_t seed) attr_nonnull(1);
void rng_seed(RandomState *rng, uint64_t seed) attr_nonnull(1);
void rng_make_active(RandomState *rng) attr_nonnull(1);
rng_val_t rng_next_p(RandomState *rng) attr_nonnull(1);

#ifdef DEBUG
INLINE void rng_lock(RandomState *rng) { rng->locked = true; }
INLINE void rng_unlock(RandomState *rng) { rng->locked = false; }
INLINE bool rng_is_locked(RandomState *rng) { return rng->locked; }
#else
#define rng_lock(rng) ((void)(rng), (void)0)
#define rng_unlock(rng) ((void)(rng), (void)0)
#define rng_is_locked(rng) (false)
#endif

// NOTE: if you rename this, also update scripts/upkeep/check-rng-usage.py!
rng_val_t rng_next(void);
void rng_nextn(size_t n, rng_val_t v[n]) attr_nonnull(2);

#define RNG_ARRAY(name, size) rng_val_t name[size]; RNG_NEXT(name)

#define RNG_NEXT(_val) \
	rng_nextn(sizeof(_val)/sizeof(rng_val_t), _Generic((_val), \
		rng_val_t: &(_val), \
		rng_val_t*: (_val) \
	))

uint64_t vrng_u64(rng_val_t v) attr_pure;
#define rng_u64() vrng_u64(rng_next())
int64_t vrng_i64(rng_val_t v) attr_pure;
#define rng_i64() vrng_i64(rng_next())

uint32_t vrng_u32(rng_val_t v) attr_pure;
#define rng_u32() vrng_u32(rng_next())
int32_t vrng_i32(rng_val_t v) attr_pure;
#define rng_i32() vrng_i32(rng_next())

double vrng_f64(rng_val_t v) attr_pure;
#define rng_f64() vrng_f64(rng_next())
double vrng_f64s(rng_val_t v) attr_pure;
#define rng_f64s() vrng_f64s(rng_next())

float vrng_f32(rng_val_t v) attr_pure;
#define rng_f32() vrng_f32(rng_next())
float vrng_f32s(rng_val_t v) attr_pure;
#define rng_f32s() vrng_f32s(rng_next())

#define vrng_real(v) vrng_f64(v)
#define rng_real() vrng_real(rng_next())
#define vrng_sreal(v) vrng_f64s(v)
#define rng_sreal() vrng_sreal(rng_next())

bool vrng_bool(rng_val_t v);
#define rng_bool() vrng_bool(rng_next())

double vrng_f64_sign(rng_val_t v) attr_pure;
#define rng_f64_sign() vrng_f64_sign(rng_next())
float vrng_f32_sign(rng_val_t v) attr_pure;
#define rng_f32_sign() vrng_f32_sign(rng_next())

#define vrng_sign(v) vrng_f64_sign(v)
#define rng_sign() vrng_sign(rng_next())

double vrng_f64_range(rng_val_t v, double rmin, double rmax) attr_pure;
#define rng_f64_range(rmin, rmax) vrng_f64_range(rng_next(), rmin, rmax)
float vrng_f32_range(rng_val_t v, float rmin, float rmax) attr_pure;
#define rng_f32_range(rmin, rmax) vrng_f32_range(rng_next(), rmin, rmax)

#define vrng_range(v, rmin, rmax) _Generic((rmin), \
	float: \
		_Generic((rmax), \
			float: vrng_f32_range, \
			default: vrng_f64_range \
		), \
	default: vrng_f64_range \
)(v, rmin, rmax)

#define rng_range(rmin, rmax) vrng_range(rng_next(), rmin, rmax)

int64_t vrng_i64_range(rng_val_t v, int64_t rmin, int64_t rmax) attr_pure;
#define rng_i64_range(rmin, rmax) vrng_i64_range(rng_next(), rmin, rmax)
int32_t vrng_i32_range(rng_val_t v, int32_t rmin, int32_t rmax) attr_pure;
#define rng_i32_range(rmin, rmax) vrng_i32_range(rng_next(), rmin, rmax)

#define vrng_irange(v, rmin, rmax) vrng_i32_range(v, rmin, rmax)
#define rng_irange(rmin, rmax) vrng_irange(rng_next(), rmin, rmax)

double vrng_f64_angle(rng_val_t v) attr_pure;
#define rng_f64_angle() vrng_f64_angle(rng_next())
float vrng_f32_angle(rng_val_t v) attr_pure;
#define rng_f32_angle() vrng_f32_angle(rng_next())

#define vrng_angle(v) vrng_f64_angle(v)
#define rng_angle() vrng_angle(rng_next())

cmplx vrng_dir(rng_val_t v) attr_pure;
#define rng_dir() vrng_dir(rng_next())

bool vrng_f64_chance(rng_val_t v, double chance) attr_pure;
#define rng_f64_chance(chance) vrng_f64_chance(rng_next(), chance)
bool vrng_f32_chance(rng_val_t v, float chance) attr_pure;
#define rng_f32_chance(chance) vrng_f32_chance(rng_next(), chance)

#define vrng_chance(v, chance) _Generic((chance), \
	float: vrng_f32_chance, \
	default: vrng_f64_chance \
)(v, chance)

#define rng_chance(chance) vrng_chance(rng_next(), chance)
