/**
 @file PrecomputedRandom.h
 
 @maintainer Morgan McGuire, http://graphics.cs.williams.edu
 
 @created 2009-03-31
 @edited  2009-03-31

 Copyright 2000-2009, Morgan McGuire.
 All rights reserved.
 */
#ifndef G3D_PrecomputedRandom_h
#define G3D_PrecomputedRandom_h

#include "G3D/platform.h"
#include "G3D/Random.h"

namespace G3D {

/** Fast random numbers using a precomputed data table. 

    e.g., generates cosHemi about 13x faster than Random.
    This is useful for quickly generating seeded random 
    numbers for reproducibility.  G3D::Random takes a long
    time to seed; this is instantaneous (providing the 
    precomputed data is already available.)

    Not threadsafe.*/
class PrecomputedRandom : public Random {
public:
    /** Put the cosHemi and the uniform together so that when
        alternating between them we stay in cache. This is also packed
        into a good size for SIMD and GPU operations.*/
    class HemiUniformData {
    public:
        float   cosHemiX;
        float   cosHemiY;
        float   cosHemiZ;
        float   uniform;
    };

    class SphereBitsData {
    public:
        float   sphereX;
        float   sphereY;
        float   sphereZ;
        uint32  bits;
    };

protected:

    /** Array of 2^n elements. */
    const HemiUniformData*      m_hemiUniform;
    const SphereBitsData*       m_sphereBits;

    /** 2^n - 1; the AND mask for computing a fast modulo */
    int                         m_modMask;

    int                         m_index;

    /** If true, free m_hemiUniform and m_sphereBits in destructor */
    bool                        m_freeData;

public:

    /*
      \param dataSize Must be a power of 2
      \param data Will NOT be deleted by the destructor.
     */
    PrecomputedRandom(const HemiUniformData* data1, const SphereBitsData* data2, int dataSize, uint32 seed = 0xF018A4D2);

    /**
      \param dataSize Number of random numbers that can be requested before periodicity.  Must be a power of 2.
      */
    PrecomputedRandom(int dataSize, uint32 seed = 0xF018A4D2);

    ~PrecomputedRandom();

    /** Each bit is random.  Subclasses can choose to override just 
       this method and the other methods will all work automatically. */
    virtual uint32 bits();

    // integer is inherited

    /** Uniform random float on the range [min, max] */
    virtual float uniform(float low, float high);

    /** Uniform random float on the range [0, 1] */
    virtual float uniform();

    // gaussian is inherited

    /** Returns 3D unit vectors distributed according to 
        a cosine distribution about the z axis. */
    virtual void cosHemi(float& x, float& y, float& z);

    /** Returns 3D unit vectors distributed according to a cosine
        power distribution (\f$ \mbox{cos}^k \theta \f$) about
        the z-axis. */
    virtual void cosPowHemi(const float k, float& x, float& y, float& z);

    // hemi is inherited

    /** Returns 3D unit vectors uniformly distributed on the sphere */
    virtual void sphere(float& x, float& y, float& z);
};

}

#endif