#include <psemek/audio/recorder.hpp>
#include <psemek/util/exception.hpp>

#include <vector>

namespace psemek::audio
{

	namespace
	{

		struct recorder_impl
			: recorder
		{
			recorder_impl()
				: channel_(std::make_shared<audio::channel>())
			{}

			channel_ptr channel() override
			{
				return channel_;
			}

			std::size_t request(std::size_t sample_count) override
			{
				std::size_t count = 0;

				if (storage_.size() < samples_.size() + sample_count)
				{
					storage_.resize(std::max<std::size_t>(samples_.size() + sample_count, storage_.size() * 2));
					samples_ = {storage_.data(), storage_.data() + samples_.size()};
				}

				if (auto stream = channel_->stream())
				{
					count = stream->read({storage_.data() + samples_.size(), sample_count});
					if (count < sample_count)
						channel_->stop();
				}

				std::fill(storage_.data() + samples_.size() + count, storage_.data() + samples_.size() + sample_count, 0.f);
				samples_ = {storage_.data(), storage_.data() + samples_.size() + sample_count};

				return sample_count;
			}

			util::span<float const> buffer() const override
			{
				return samples_;
			}

			std::vector<float> grab_buffer() override
			{
				storage_.resize(samples_.size());
				samples_ = {};
				return std::move(storage_);
			}

		private:
			channel_ptr channel_;
			std::vector<float> storage_;
			util::span<float const> samples_;
		};

	}

	std::shared_ptr<recorder> make_recorder()
	{
		return std::make_shared<recorder_impl>();
	}

	std::shared_ptr<recorder> make_recorder(stream_ptr stream)
	{
		auto recorder = make_recorder();
		recorder->channel()->stream(std::move(stream));
		return recorder;
	}

	std::shared_ptr<track> record(stream_ptr stream)
	{
		if (!stream->length())
			throw util::exception("Cannot record an infinite stream");
		return record(stream, *stream->length());
	}

	std::shared_ptr<track> record(stream_ptr stream, duration duration)
	{
		auto recorder = make_recorder(std::move(stream));
		recorder->request(duration.samples());
		return load_raw(recorder->grab_buffer());
	}

}
