//
//  RandomGenerator.hpp
//  eagle0
//
//  Created by Dan Crosby on 1/18/15.
//  Copyright (c) 2015 none. All rights reserved.
//

#ifndef EAGLE0_COMMON_RANDOM_GENERATOR_HPP
#define EAGLE0_COMMON_RANDOM_GENERATOR_HPP

#include <cstddef>
#include <memory>
#include <random>
#include <stdexcept>
#include <vector>

class RandomGenerator;

class RandomGenerator {
private:
    virtual auto DoubleZeroToOne() -> double = 0;

    auto OpenEndedHighImpl(double acc, int maxRollsRemaining) -> double;

public:
    RandomGenerator() = default;
    RandomGenerator(const RandomGenerator&) = default;
    auto operator=(const RandomGenerator&) -> RandomGenerator& = default;
    RandomGenerator(RandomGenerator&&) = default;
    auto operator=(RandomGenerator&&) -> RandomGenerator& = default;

    virtual ~RandomGenerator() = default;

    auto Percentile() -> double;
    auto OpenEndedHighPercentile() -> double;
    auto OpenEndedLowPercentile() -> double;
    auto OpenEndedPercentile() -> double;
    auto ReverseOpenEndedPercentile() -> double;
    virtual auto IntBetween(int min, int max) -> int;  // inclusive, exclusive (min<=return<max)
    virtual auto IntBelow(int max) -> int;

    template<class T>
    auto RandomElement(const std::vector<T>& vec) -> T {
        if (vec.empty()) { throw std::invalid_argument("cannot choose from an empty vector"); }
        return vec.at(static_cast<std::size_t>(IntBelow(static_cast<int>(vec.size()))));
    }

    static auto ChanceOpenEndedPercentileAtOrAbove(double value) -> double;
};

class StdLibraryGenerator : public RandomGenerator {
private:
    std::mt19937_64 engine;
    std::uniform_real_distribution<double> unifDouble =
            std::uniform_real_distribution<double>(0, 1);

    auto DoubleZeroToOne() -> double override;

public:
    StdLibraryGenerator();
    explicit StdLibraryGenerator(std::mt19937_64::result_type seed);

    auto IntBetween(int min, int max) -> int override;  // inclusive, exclusive (min<=return<max)
};

#endif  // EAGLE0_COMMON_RANDOM_GENERATOR_HPP
