#include <vector>
#include <Rcpp.h>

#include "SchachHoernchen/Move.h"
#include "SchachHoernchen/Board.h"

//' Samples a legal move from a given position
// [[Rcpp::export("sample.move", rng = true)]]
Move sample_move(const Board& pos) {
    // RNG for continuous uniform X ~ U[0, 1]
    auto runif = Rcpp::stats::UnifGenerator(0, 1);
    // RNG for discrete uniform X ~ U[0; max - 1]
    auto rindex = [&runif](const std::size_t max) {
        // sample random index untill in range [0; max - 1]
        unsigned index;
        do {
            index = static_cast<unsigned>(runif() * static_cast<double>(max));
        } while (index >= max);
        return index;
    };

    // generate all legal moves
    MoveList moves;
    pos.moves(moves);

    // check if there are any moves to sample from
    if (moves.empty()) {
        Rcpp::stop("Attempt to same legal move from terminal node");
    }

    return moves[rindex(moves.size())];
}

//' Samples a random FEN (position) by applying `ply` random moves to the start
//' position.
//'
//' @param nr number of positions to sample
//' @param min_depth minimum number of random ply's to generate random positions
//' @param max_depth maximum number of random ply's to generate random positions
// [[Rcpp::export("sample.fen", rng = true)]]
std::vector<Board> sample_fen(const unsigned nr,
    const unsigned min_depth = 4, const unsigned max_depth = 20
) {
    // Parameter validation
    if (min_depth > max_depth) {
        Rcpp::stop("max_depth must be bigger equal than min_depth");
    }
    if (128 < max_depth) {
        Rcpp::stop("max_depth exceeded maximum value 128");
    }

    // RNG for continuous uniform X ~ U[0, 1]
    auto runif = Rcpp::stats::UnifGenerator(0, 1);
    // RNG for discrete uniform X ~ U[0; max - 1]
    auto rindex = [&runif](const std::size_t max) {
        // Check if max is bigger than zero, cause we can not sample from the
        // empty set
        if (max == 0) {
            Rcpp::stop("Attempt to sample random index < 0.");
        }
        // sample random index untill in range [0; max - 1]
        unsigned index = max;
        while (index >= max) {
            index = static_cast<unsigned>(runif() * static_cast<double>(max));
        }
        return index;
    };

    // Setup response vector
    std::vector<Board> fens;
    fens.reserve(nr);

    // Sample FENs
    MoveList moves;
    for (unsigned i = 0; i < nr; ++i) {
        Board pos;  // start position
        unsigned depth = static_cast<unsigned>(runif() * (max_depth - min_depth));
        depth += min_depth;
        for (unsigned ply = 0; ply < depth; ++ply) {
            moves.clear();
            pos.moves(moves);
            if (moves.size()) {
                pos.make(moves[rindex(moves.size())]);
            } else {
                break;
            }
        }
        fens.push_back(pos);
    }

    return fens;
}
