// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project

#include "ChannelsConverter.hxx"
#include "PcmChannels.hxx"
#include "pcm/Features.h" // for ENABLE_DSD
#include "lib/fmt/AudioFormatFormatter.hxx"
#include "lib/fmt/RuntimeError.hxx"
#include "util/SpanCast.hxx"

#include <cassert>
#include <utility> // for std::unreachable()

void
PcmChannelsConverter::Open(SampleFormat _format,
			   unsigned _src_channels, unsigned _dest_channels)
{
	assert(_format != SampleFormat::UNDEFINED);

	switch (_format) {
	case SampleFormat::DSD:
	case SampleFormat::S16:
	case SampleFormat::S24_P32:
	case SampleFormat::S32:
	case SampleFormat::FLOAT:
		break;

	default:
		throw FmtRuntimeError("PCM channel conversion for {} is not implemented",
				      _format);
	}

	format = _format;
	src_channels = _src_channels;
	dest_channels = _dest_channels;
}

void
PcmChannelsConverter::Close() noexcept
{
#ifndef NDEBUG
	format = SampleFormat::UNDEFINED;
#endif
}

std::span<const std::byte>
PcmChannelsConverter::Convert(std::span<const std::byte> src) noexcept
{
	switch (format) {
	case SampleFormat::UNDEFINED:
	case SampleFormat::S8:
		std::unreachable();

	case SampleFormat::DSD:
#ifdef ENABLE_DSD
		return pcm_convert_channels_dsd(buffer, dest_channels, src_channels, src);
#else
		std::unreachable();
#endif

	case SampleFormat::S16:
		return std::as_bytes(pcm_convert_channels_16(buffer, dest_channels,
							     src_channels,
							     FromBytesStrict<const int16_t>(src)));

	case SampleFormat::S24_P32:
		return std::as_bytes(pcm_convert_channels_24(buffer, dest_channels,
							     src_channels,
							     FromBytesStrict<const int32_t>(src)));

	case SampleFormat::S32:
		return std::as_bytes(pcm_convert_channels_32(buffer, dest_channels,
							     src_channels,
							     FromBytesStrict<const int32_t>(src)));

	case SampleFormat::FLOAT:
		return std::as_bytes(pcm_convert_channels_float(buffer, dest_channels,
								src_channels,
								FromBytesStrict<const float>(src)));
	}

	std::unreachable();
}
