#pragma once

#include <random>
#include <time.h>

#include "base.hpp"

namespace frozenwasteland {
namespace dsp {

class Seeds {
private:
	std::mt19937 _generator;
	Seeds();
	unsigned int _next();

public:
	Seeds(const Seeds&) = delete;
	void operator=(const Seeds&) = delete;
	static Seeds& getInstance();

	static unsigned int next();
};

struct NoiseGenerator : Generator {
	std::minstd_rand _generator; // one of the faster options.

	NoiseGenerator() : _generator(Seeds::next()) {}
};

struct WhiteNoiseGenerator : NoiseGenerator {
	std::uniform_real_distribution<float> _uniform;

	WhiteNoiseGenerator() : _uniform(-1.0, 1.0) {}

	virtual float _next() override {
		return _uniform(_generator);
	}
};

template<typename G>
struct BasePinkNoiseGenerator : NoiseGenerator {
	static const int _n = 7;
	G _g;
	G _gs[_n];
	uint32_t _count = _g.next();

	virtual float _next() override {
		// See: http://www.firstpr.com.au/dsp/pink-noise/
		float sum = _g.next();
		for (int i = 0, bit = 1; i < _n; ++i, bit <<= 1) {
			if (_count & bit) {
				sum += _gs[i].next();
			}
			else {
				sum += _gs[i].current();
			}
		}
		++_count;
		return sum / (float)(_n + 1);
	}
};

struct PinkNoiseGenerator : BasePinkNoiseGenerator<WhiteNoiseGenerator> {};

struct RedNoiseGenerator : BasePinkNoiseGenerator<PinkNoiseGenerator> {};

struct GaussianNoiseGenerator : NoiseGenerator {
	std::normal_distribution<float> _normal;

	GaussianNoiseGenerator() : _normal(0, 1.0) {}

	virtual float _next() override {
		return _normal(_generator);
	}
};

} // namespace dsp
} // namespace frozenwasteland
