#include <psemek/audio/wave/white_noise.hpp>
#include <psemek/audio/constants.hpp>
#include <psemek/audio/detail/white_noise.hpp>
#include <psemek/random/generator.hpp>
#include <psemek/random/uniform.hpp>

#include <vector>
#include <atomic>

namespace psemek::audio
{

	namespace
	{

		struct white_noise_impl
			: stream
		{
			std::optional<std::size_t> length() const override
			{
				return std::nullopt;
			}

			std::size_t read(util::span<float> samples) override
			{
				for (std::size_t i = 0; i < samples.size(); i += 2)
				{
					float value = d_(rng_);
					samples[i + 0] = value;
					samples[i + 1] = value;
				}

				played_.fetch_add(samples.size());

				return samples.size();
			}

			std::size_t played() const override
			{
				return played_.load();
			}

		private:
			random::generator rng_{0x4b0a763ef6573bf2ull, 0};
			random::uniform_distribution<float> d_{-1.f, 1.f};
			std::atomic<std::size_t> played_;
		};

		struct fixed_white_noise_impl
			: stream
		{
			fixed_white_noise_impl(float frequency)
				: buffer_(detail::white_noise(std::round(audio::frequency / frequency)))
			{}

			std::optional<std::size_t> length() const override
			{
				return std::nullopt;
			}

			std::size_t read(util::span<float> samples) override
			{
				std::size_t const size = buffer_.size();

				for (std::size_t i = 0; i < samples.size(); i += 2)
				{
					samples[i + 0] = buffer_[buffer_pos_];
					samples[i + 1] = buffer_[buffer_pos_];

					buffer_pos_ += 1;
					buffer_pos_ %= size;
				}

				played_.fetch_add(samples.size());

				return samples.size();
			}

			std::size_t played() const override
			{
				return played_.load();
			}

		private:
			std::vector<float> buffer_;
			std::size_t buffer_pos_{0};

			std::atomic<std::size_t> played_;
		};

	}

	stream_ptr white_noise()
	{
		return std::make_shared<white_noise_impl>();
	}

	stream_ptr white_noise(float frequency)
	{
		return std::make_shared<fixed_white_noise_impl>(frequency);
	}

}
