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

#include "RandomGenerator.hpp"

#include <ctime>

constexpr double kOpenEndedHighThreshold = 95.0;
constexpr double kOpenEndedLowThreshold = 5.0;
constexpr int kMaxOpenEndedRolls = 5;

auto RandomGenerator::OpenEndedHighImpl(double acc, int maxRollsRemaining) -> double {
    double roll = 0.0;

    do {
        roll = Percentile();
        acc = acc + roll;
        maxRollsRemaining--;
    } while (maxRollsRemaining > 0 && roll > kOpenEndedHighThreshold);

    return acc;
}

auto RandomGenerator::Percentile() -> double { return DoubleZeroToOne() * 100.0; }

auto RandomGenerator::OpenEndedHighPercentile() -> double {
    return OpenEndedHighImpl(0.0, kMaxOpenEndedRolls);
}

auto RandomGenerator::OpenEndedLowPercentile() -> double {
    const double roll = Percentile();

    if (roll < kOpenEndedLowThreshold) {
        return roll - this->OpenEndedHighImpl(0.0, kMaxOpenEndedRolls - 1);
    }

    return roll;
}

auto RandomGenerator::OpenEndedPercentile() -> double {
    const double roll = Percentile();

    if (roll > kOpenEndedHighThreshold) {
        return OpenEndedHighImpl(roll, kMaxOpenEndedRolls - 1);
    } else if (roll < kOpenEndedLowThreshold) {
        return roll - OpenEndedHighImpl(0.0, kMaxOpenEndedRolls - 1);
    }

    return roll;
}

auto RandomGenerator::ReverseOpenEndedPercentile() -> double {
    const double roll = 100.0 - Percentile();

    if (roll > kOpenEndedHighThreshold) {
        return OpenEndedHighImpl(roll, kMaxOpenEndedRolls - 1);
    } else if (roll < kOpenEndedLowThreshold) {
        return roll - OpenEndedHighImpl(0.0, kMaxOpenEndedRolls - 1);
    }

    return roll;
}

auto RandomGenerator::IntBetween(const int min, const int max) -> int {
    return int(DoubleZeroToOne() * (max - min)) + min;
}

auto RandomGenerator::IntBelow(const int max) -> int { return IntBetween(0, max); }

auto RandomGenerator::ChanceOpenEndedPercentileAtOrAbove(const double value) -> double {
    if (value < 0) {
        return .95 + 0.05 * ChanceOpenEndedPercentileAtOrAbove(-value + 2.5);
    } else if (value < 100) {
        return 1.0 - 0.01 * value;
    } else {
        return .05 * ChanceOpenEndedPercentileAtOrAbove(value - 100.0);
    }
}

auto StdLibraryGenerator::DoubleZeroToOne() -> double { return unifDouble(engine); }

StdLibraryGenerator::StdLibraryGenerator() : RandomGenerator() { engine.seed(std::time(nullptr)); }

auto StdLibraryGenerator::IntBetween(const int min, const int max) -> int {
    std::uniform_int_distribution<int> unifInt(min, max - 1);
    return unifInt(engine);
}
