#ifndef INCLUDED_RANDOM_
#define INCLUDED_RANDOM_

#include <cmath>
#include <random>               // using the stl's mercenne twister
#include <vector>
#include <memory>
#include <iosfwd>

#include "../enums/enums.h"
#include "../betadist/betadist.h"

class Random
{
    using LogNormalParams = std::lognormal_distribution<double>::param_type;
    using EngineVect = std::vector<std::mt19937>;

    size_t d_nEngines;
    size_t d_seed;
    size_t d_nCases;

    EngineVect d_engine;

        // all distribution types are default: double
        // and are allways available
    std::uniform_real_distribution<>  d_uniform;        // default: 0, max
    std::uniform_real_distribution<>  d_uniformCase;
                                                        // used by
    std::uniform_real_distribution<>  d_uniformVSD;     // Survival::cptVSDrow
    std::lognormal_distribution<>     d_logNormal;      // default: 0, 1

        // these distributions are used with
        // Scenario: spread
                                                            // default: 0, 1
    std::unique_ptr<std::normal_distribution<>> d_normalVary;
    std::unique_ptr<std::uniform_real_distribution<>> d_uniformVary;
    std::unique_ptr<std::uniform_real_distribution<>> d_uniformVSDvary;
    std::unique_ptr<std::lognormal_distribution<>> d_logNormalVary;

        // BetaDist is not used with SimulationType BREAST
    std::unique_ptr<BetaDist> d_betaVary;

    static std::unique_ptr<Random> s_random;

    public:
        Random(Random const &other) = delete;

        std::pair<size_t, double const *> betaParameters() const;

                                            // called by
        void prepareBeta(std::istream &in); // Distribution::extract

                                            // called at each iteration
        void reinit(size_t nCases,  GeneratorType type);

        static Random &initialize();
        static Random &instance();

        double uniform();
                                        // used for generating reproducible
        double uniformCase();           // case characteristics

                                        // used for generating reproducible
        double uniformVSD();            // case characteristics

        double logNormal(double mean, double stdDev);

                // only used when 'spread: true':
        double betaVary();
        double normalVary();
        double uniformVary();

        double uniformVSDvary();        // TO DO

        double logNormalVary(double mean, double stdDev);

        void setSeed(size_t seed);

    private:
        Random();

        void reset();
};

inline void Random::setSeed(size_t seed)
{
    d_seed = seed;
}

inline std::pair<size_t, double const *> Random::betaParameters() const
{
    return d_betaVary->parameters();
}
                                            // when parsing the config file
                                            // specify which vary-specific
                                            // distributions are used
// OBS        void use(DistType distribution);


#endif
