#include <psemek/audio/combine/loop.hpp>
#include <psemek/audio/combine/duplicate.hpp>

namespace psemek::audio
{

	namespace
	{

		struct loop_impl
			: stream
		{
			loop_impl(track_ptr dup, std::optional<std::size_t> count)
				: dup_(std::move(dup))
				, stream_(dup_->stream())
				, count_(count)
			{}

			std::optional<std::size_t> length() const override
			{
				if (auto length = stream_->length(); length && count_)
					return (*length) * (*count_);
				return std::nullopt;
			}

			std::size_t read(util::span<float> samples) override
			{
				std::size_t count = 0;
				while (count < samples.size() && (!count_ || repeated_ < *count_))
				{
					auto need = samples.size() - count;
					auto scount = stream_->read({samples.begin() + count, need});
					count += scount;
					if (scount < need)
					{
						++repeated_;
						cached_length_ = stream_->played();
						stream_ = dup_->stream();
					}
				}
				return count;
			}

			std::size_t played() const override
			{
				return stream_->played() + cached_length_ * repeated_;
			}

		private:
			track_ptr dup_;
			stream_ptr stream_;
			std::optional<std::size_t> count_;
			std::size_t repeated_ = 0;
			std::size_t cached_length_ = 0;
		};

	}

	stream_ptr loop(track_ptr track, std::optional<std::size_t> count)
	{
		return std::make_shared<loop_impl>(track, count);
	}

	stream_ptr loop(stream_ptr stream, std::optional<std::size_t> count)
	{
		if (!stream->length())
			return stream;
		return loop(make_duplicator(std::move(stream)), count);
	}

}
