#include <psemek/audio/effect/all_pass.hpp>

#include <vector>

namespace psemek::audio
{

	namespace
	{

		struct all_pass_impl
			: stream
		{
			all_pass_impl(stream_ptr stream, duration delay, float gain)
				: stream_(std::move(stream))
				, buffer_(std::max<std::size_t>(2, delay.samples()), 0.f)
				, gain_(gain)
			{}

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

			// y[n] = (−g·x[n]) + x[n−M] + (g·y[n−M])
			std::size_t read(util::span<float> samples) override
			{
				auto count = stream_->read(samples);

				for (std::size_t i = 0; i < count; ++i)
				{
					float input = samples[i];
					samples[i] = - gain_ * input + buffer_[buffer_pos_];
					buffer_[buffer_pos_] = input + gain_ * samples[i];
					++buffer_pos_;
					buffer_pos_ %= buffer_.size();
				}

				for (std::size_t i = count; i < samples.size(); ++i)
				{
					samples[i] = buffer_[buffer_pos_];
					buffer_[buffer_pos_] = gain_ * buffer_[buffer_pos_];
					++buffer_pos_;
					buffer_pos_ %= buffer_.size();
				}

				return samples.size();
			}

			std::size_t played() const override
			{
				return stream_->played();
			}

		private:
			stream_ptr stream_;
			std::vector<float> buffer_;
			std::size_t buffer_pos_ = 0;
			float gain_;
		};

	}

	stream_ptr all_pass(stream_ptr stream, duration delay, float gain)
	{
		return std::make_shared<all_pass_impl>(std::move(stream), delay, gain);
	}
}
