/*
 * Licensed under BSD license.  See LICENCE.TXT  
 *
 * Produced by:	Jeff Lait
 *
 *      	7DRL Development
 *
 * NAME:        rand.h ( Live Once Library, C++ )
 *
 * COMMENTS:
 *	This file is pre-7DRL.
 */

#ifndef __random__
#define __random__

typedef unsigned char u8;
typedef unsigned int u32;

// For our ISSPACE macro..
#include <ctype.h>

#include "dpdf.h"
#include "buf.h"

class DICE
{
public:
    int		myNumDie, mySides;
    int		myBonus;

    // Rolls dice with the random function.
    int		roll() const;

    // Builds a DPDF representing this die
    DPDF	buildDPDF() const;

    // OUtput the dice as a canonical format
    BUF		format(int extrabonus) const;
    BUF		format() const { return format(0); }

    // Read in the dice from a string.
    void	scanf(BUF buf) { scanf(buf.buffer()); }
    void	scanf(const char *buf);

    // Take me away in your spaceship!!!
    bool	operator==(const DICE &dice) const
    { return dice.myNumDie == myNumDie
	    && dice.mySides == mySides
	    && dice.myBonus == myBonus; }
    bool	operator!=(const DICE &dice) const
    { return dice != *this; }
    bool	operator<(const DICE &dice) const
    { return buildDPDF().expectedValue() < dice.buildDPDF().expectedValue(); }
    bool	operator<=(const DICE &dice) const
    { return dice == *this ||
	buildDPDF().expectedValue() < dice.buildDPDF().expectedValue(); }
    bool	operator>(const DICE &dice) const
    {
	return dice != *this &&
		!(*this < dice);
    }
    bool	operator>=(const DICE &dice) const
    { return !(*this < dice); }
};

/// Generates a random sequence.  Ideally would be something like
/// xorshift.  But isn't.
class RANDSEQ
{
public:
    RANDSEQ();
    RANDSEQ(int seed);

    // Random in inclusive range.
    int range(int min, int max);

    // Random from 0..num-1
    int choice(int num);
    bool chance(int per);
    // Depending where value is in range min..max, pick a 0..100% chance.
    bool chance_range(float value, float min, float max);

    // Giving a null terminated list of strings, pick one of the strings
    // at random
    const char *string(const char **stringlist);

protected:
    int		nextrandom();

    u32		mySeedLow, mySeedHigh;
};

// Random in inclusive range.
int rand_range(int min, int max);

// Random from 0..num-1
int rand_choice(int num);

// Giving a null terminated list of strings, pick one of the strings
// at random
const char *rand_string(const char **stringlist);

// Rolls the specific number sided die.  The reroll count
// is how many rerolls you get.  Largest is returned.
// Result is between 1..num, unless num is 0, in which case it is 0,
// or negative, in which case -1..-num.
// Negative reroll means you get rerolls but the LEAST is returned.
int rand_roll(int sides, int reroll = 0);

// Roll numdieDsides + bonus
int rand_dice(int numdie, int sides, int bonus);

// Returns the current seed.
long rand_getseed();

// Sets the seed...
void rand_setseed(long seed);
// Sets the seed from random device rather than time()
void rand_truerandomseed();

// Does a wang int hash.
unsigned int rand_wanginthash(unsigned int key);

// Returns true percentage % of the time.
bool rand_chance(int percentage);
// Returns true given a probability, 1.0 always, 0 never.
bool rand_prob(float prob);

// Returns -1 or 1.
int  rand_sign();

// Initializes dx & dy, only one of which is non-zero.
void	rand_direction(int &dx, int &dy);

// Given a direction from 0..3, returns the dx,dy pair for that direction.
void	rand_getdirection(int dir, int &dx, int &dy);
// Inverse of rand_getdirection
int	rand_dir4_toangle(int dx, int dy);

// Given an angle 0..7, returns dx, dy pair.
void	rand_angletodir(int angle, int &dx, int &dy);
// Given a dx & dy, returns the angle.
int	rand_dirtoangle(int dx, int dy);

// Initializes 8 way direction
inline void	rand_direction8(int &dx, int &dy) { rand_angletodir(rand_choice(8), dx, dy); }

// Return North, North-East, etc.
const char *rand_dirtoname(int dx, int dy, bool capital=true);
const char *rand_angletoname(int angle, bool capital=true);
// 3 north 5 east
BUF		rand_describeoffset(int dx, int dy);

// Shuffles the given set of numbers...
void	rand_shuffle(u8 *set, int n);
void	rand_shuffle(int *set, int n);

// Returns a double in range [0..1)
double	rand_double();
// Returns a random int.  Includes negative!
int	rand_int();

// Returns a random name
BUF	rand_name();

// Some standard methods...
#ifndef ABS
#define ABS(a) ((a) < 0 ? -(a) : (a))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef BOUND
#define BOUND(val, low, high) MAX(MIN(val, high), low)
#endif
#ifndef SIGN
#define SIGN(a) ((a) < 0 ? -1 : (a) > 0 ? 1 : 0)
#endif

// This is because MSVC is a broken compiler.
// Would it kill them to not crash on a negative input?
#define ISSPACE(c) ((c) < 0 ? 0 : isspace(c))

// Useful to iterate over all directions.
#define FORALL_4DIR(dx, dy) \
    for (int lcl_angle = 0; rand_getdirection(lcl_angle, dx, dy), lcl_angle < 4; lcl_angle++)

/// Random offset to slightly unbias things.
#define FORALL_4DIR_RAND(dx, dy) \
    for (int lcl_angle = 0, lcl_offset = rand_choice(4); rand_getdirection(lcl_angle+lcl_offset, dx, dy), lcl_angle < 4; lcl_angle++)

#define FORALL_8DIR(dx, dy) \
    for (int lcl_angle = 0; rand_angletodir(lcl_angle, dx, dy), lcl_angle < 8; lcl_angle++)

#define FORALL_8DIR_RAND(dx, dy) \
    for (int lcl_angle = 0, lcl_offset = rand_choice(8); rand_angletodir(lcl_angle+lcl_offset, dx, dy), lcl_angle < 8; lcl_angle++)

#define FORALL_XY(x, y) \
    for (y = 0; y < height(); y++) \
	for (x = 0; x < width(); x++)

#define FORALL_RCT_XY(rct, x, y) \
    for (y = rct.y; y < rct.y+rct.h; y++) \
	for (x = rct.x; x < rct.x+rct.w; x++)

#endif
