#ifndef FORTUNA_FORTUNA_H
#define FORTUNA_FORTUNA_H

#include "generator.h"
#include "accumulator.h"

#include <fmt/core.h>

namespace fortuna {

class Fortuna {
public:
	// in microseconds
	static constexpr const unsigned int reseed_interval{10000};
	static constexpr const char num_of_pools{32};

	Fortuna();
	~Fortuna();

	auto random_data(unsigned int) -> void;

	auto set_reseed_ctr_to_null() -> void {
		Fortuna::R.null_da_ctr();
	}

	auto incr_reseed_ctr() -> void {
		++Fortuna::R.reseed_ctr;
	}

	auto initialize_prng() -> void {
		// TODO(me): handle the reseeds here as per Cryptography Engineering,
		// p. 153
		set_reseed_ctr_to_null();
		try {
			R.initialize_pools();
			fmt::print("pools initialized\n");
		} catch(std::exception& e) {
			fmt::print("{}\n", e.what());
		}
		fmt::print("PRNG initialized\n");
	};

	// PRNG state
	class R_state {
	friend fortuna::Fortuna;
	public:
		R_state(){};
		~R_state() = default;

	protected:
		auto null_da_ctr() -> void {
			reseed_ctr = 0x00;
			fmt::print("reseed_ctr set to 0x00\n");
		}

		auto initialize_pools() -> void {
			for (unsigned int i = accumulator::Accumulator::init_pool_num; i < num_of_pools; ++i) {
				pools[i].initialize_pool(i);
			}
		}

	private:
		generator::Generator Gen;
		#pragma GCC diagnostic push
		#pragma GCC diagnostic ignored "-Wpedantic"
		unsigned __int128 reseed_ctr;
		#pragma GCC diagnostic pop
		accumulator::Pool pools[num_of_pools];
	}; // class R_state

	fortuna::Fortuna::R_state R;

}; // class Fortuna

} // namespace fortuna

#endif//FORTUNA_FORTUNA_H