#include <psemek/audio/detail/resampler.hpp>
#include <psemek/audio/detail/smooth.hpp>
#include <psemek/math/math.hpp>

#include <psemek/log/log.hpp>

#include <vector>

namespace psemek::audio
{

	resampler::resampler(float ratio, float smoothness)
		: ratio_(ratio)
		, smoothness_multiplier_(smoothness_to_multiplier(smoothness))
		, real_ratio_(ratio)
	{}

	float resampler::ratio() const
	{
		return ratio_.load();
	}

	float resampler::ratio(float value)
	{
		return ratio_.exchange(value);
	}

	float resampler::smoothness() const
	{
		return multiplier_to_smoothness(smoothness_multiplier_.load());
	}

	float resampler::smoothness(float value)
	{
		return multiplier_to_smoothness(smoothness_multiplier_.exchange(smoothness_to_multiplier(value)));
	}

	void resampler::feed(util::span<float const> samples)
	{
		result_.clear();

		if (samples.empty())
			return;

		float const ratio = ratio_.load();
		float const smoothness_multiplier = smoothness_multiplier_.load();

		while (position_ < 0)
		{
			result_.push_back(math::lerp(last_sample_[0], samples[0], position_frac_));
			result_.push_back(math::lerp(last_sample_[1], samples[1], position_frac_));
			real_ratio_ += (ratio - real_ratio_) * smoothness_multiplier;
			advance(1.f / real_ratio_);
		}

		while (2 * position_ + 3 < samples.size())
		{
			result_.push_back(math::lerp(samples[2 * position_ + 0], samples[2 * position_ + 2], position_frac_));
			result_.push_back(math::lerp(samples[2 * position_ + 1], samples[2 * position_ + 3], position_frac_));
			real_ratio_ += (ratio - real_ratio_) * smoothness_multiplier;
			advance(1.f / real_ratio_);
		}

		position_ -= static_cast<int>(samples.size()) / 2;

		last_sample_[0] = samples[samples.size() - 2];
		last_sample_[1] = samples[samples.size() - 1];
	}

	std::vector<float> resampler::grab_result()
	{
		position_ = 0;
		position_frac_ = 0.f;
		last_sample_[0] = 0.f;
		last_sample_[1] = 0.f;
		return std::move(result_);
	}

	void resampler::advance(float delta)
	{
		position_frac_ += delta;
		auto floor = static_cast<int>(position_frac_);
		position_ += floor;
		position_frac_ -= floor;
	}

}
