#pragma once

#include <psemek/audio/stream.hpp>
#include <psemek/audio/duration.hpp>

#include <vector>

namespace psemek::audio
{

	struct segment
	{
		enum segment_type
		{
			linear,
			exponential,
		};

		segment_type type;
		audio::duration duration;
	};

	constexpr struct keep_playing_tag {} keep_playing;

	// values.size() must equal smoothing.size() + 1
	// truncates the stream in the end if keep_playing isn't specified
	stream_ptr envelope(stream_ptr stream, std::vector<float> values, std::vector<segment> segments);
	stream_ptr envelope(stream_ptr stream, keep_playing_tag, std::vector<float> values, std::vector<segment> segments);

	namespace detail
	{

		inline void envelope_helper(std::vector<float> &, std::vector<segment> &)
		{}

		template <typename ... Args>
		void envelope_helper(std::vector<float> & values, std::vector<segment> & segments, segment::segment_type type, duration duration, float value, Args ... args)
		{
			values.push_back(value);
			segments.push_back({type, duration});
			envelope_helper(values, segments, args...);
		}

		template <typename ... Args>
		stream_ptr envelope_impl(stream_ptr stream, bool keep_playing, float first_value, Args ... args)
		{
			std::vector<float> values;
			std::vector<segment> segments;
			values.push_back(first_value);
			envelope_helper(values, segments, args...);
			if (keep_playing)
				return audio::envelope(std::move(stream), audio::keep_playing, std::move(values), std::move(segments));
			return audio::envelope(std::move(stream), std::move(values), std::move(segments));
		}

	}

	// Must be called alternating volume levels, segment types, and segment durations,
	// starting and ending with a volume level, e.g.
	//     envelope(stream, 0.4, segment::linear, 100_ms, 1.2, segment::exponential, 400_ms, 5.3)

	template <typename ... Args>
	stream_ptr envelope(stream_ptr stream, float first_value, Args ... args)
	{
		return detail::envelope_impl(std::move(stream), false, first_value, args...);
	}

	template <typename ... Args>
	stream_ptr envelope(stream_ptr stream, keep_playing_tag, float first_value, Args ... args)
	{
		return detail::envelope_impl(std::move(stream), true, first_value, args...);
	}

}
