/* SDL output for foobar2000.
 * Somewhat scuffed since SDL does not provide latency */

#include <SDL3/SDL.h>

#if audio_sample_size == 32
# define OUTSDL_USE_NATIVE_F32 1
# define OUTSDL_FORMAT SDL_AUDIO_F32
#else
# define OUTSDL_FORMAT SDL_AUDIO_S32
#endif

class OutputSDL : public output {
public:
	OutputSDL();
	~OutputSDL();

	virtual double get_latency() override;
	virtual void process_samples(const audio_chunk &p_chunk) override;
	virtual void update(bool &p_ready) override;
	virtual void pause(bool p_state) override;
	virtual void flush() override;
	virtual void force_play() override;
	virtual void volume_set(double p_val) override;

private:
	void ReinitStream(unsigned int channels, unsigned int freq);

	SDL_AudioStream *stream_;
#ifndef OUTSDL_USE_NATIVE_F32
	std::vector<t_int32> buffer_;
#endif
	SDL_AudioSpec spec_;
	HANDLE sdl_;

#define FUNC(type, x, params, callparams) decltype(&SDL_##x) sdl3_##x;
#include "foo_out_sdl_funcs.h"
};

OutputSDL::OutputSDL()
	: sdl_(nullptr)
	, stream_(nullptr)
#ifndef OUTSDL_USE_NATIVE_F32
	/* sane default size */
	, buffer_(8096)
#endif
{
	sdl_ = LoadLibraryA("foo_out_sdl_wrapper.dll");
	if (!sdl3_)
		return;

#define FUNC(type, x, params, callparams) \
	sdl3_##x = (decltype(&SDL_##x))GetProcAddress(sdl_, "SDL_" #x); \
	if (!sdl3_##x) \
		return;
#include "foo_out_sdl_funcs.h"

	// increment subsystem counter
	// hope this succeeds!!!
	sdl3_InitSubSystem(SDL_INIT_AUDIO);

	/* make a guess */
	spec_.format = OUTSDL_FORMAT;
	spec_.channels = 2; /* Stereo is most likely. Who cares about surround? :) */
	spec_.freq = 44100; /* guess CD quality */

	// TODO supply output devices
	stream_ = sdl3_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec_, nullptr, nullptr);
}

OutputSDL::~OutputSDL()
{
	if (sdl_ && stream_ && sdl3_DestroyAudioStream)
		sdl3_DestroyAudioStream(stream_);
	if (sdl_ && sdl3_QuitSubSystem)
		sdl3_QuitSubSystem(SDL_INIT_AUDIO);
	if (sdl_)
		FreeLibrary(sdl_);
}

void OutputSDL::ReinitStream(unsigned int channels, unsigned int freq)
{
	// Fast path; no change
	// I'm assuming that the channel input config is the same for fb2k
	// and for SDL. This may not be the case. Whatever..
	if (spec_.channels == channels && spec_.freq == freq)
		return;

	spec_.channels = channels;
	spec_.freq = freq;

	// tell SDL about our change
	sdl3_SetAudioStreamFormat(stream_, &spec_, nullptr);
}

virtual double OutputSDL::get_latency()
{
	// TODO assume that one buffer's worth is queued
	return 0.016; // i guess
}

virtual void OutputSDL::process_samples(const audio_chunk &p_chunk)
{
	// Reinitialize stream with possibly new values for channels and frequency
	ReinitStream(p_chunk.get_channels(), p_chunk.get_srate());

	/* NOTE this is only actually tested with stereo */
#ifdef OUTSDL_USE_NATIVE_F32
	/* audio_sample is 32-bit floating point; SDL can use this directly */
	sdl3_PutAudioStreamData(stream_, p_chunk.get_data(), p_chunk.get_data_size());
#else
	/* Expand the buffer if necessary */
	t_size sz = p_chunk.get_data_size();

	if (sz > buffer_.size())
		buffer_.resize(sz);

	/* Convert to int32 */
	audio_math::convert_to_int32(p_chunk.get_data(), sz, buffer_.data(), buffer_.size());

	/* Add int32 audio to stream */
	sdl3_PutAudioStreamData(stream_, buffer_.data(), buffer_.size());
#endif
}

virtual void OutputSDL::update(bool &p_ready)
{
	/* seems legit */
	p_ready = (sdl3_GetAudioStreamQueued(stream_) == 0);
}

virtual void OutputSDL::pause(bool p_state)
{
	if (p_state) {
		sdl3_PauseAudioStreamDevice(stream_);
	} else {
		sdl3_ResumeAudioStreamDevice(stream_);
	}
}

// . these are easy
virtual void OutputSDL::flush()
{
	sdl3_ClearAudioStream(stream_);
}

virtual void OutputSDL::force_play()
{
	sdl3_FlushAudioStream(stream_);
}

virtual void OutputSDL::volume_set(double p_val)
{
	sdl3_SetAudioStreamGain(stream_, p_val);
}

static output_factory_t<OutputSDL> g_output_sdl_factory;

////////////////////////////////////////////////////////////////////////////////

const char *about = "Copyright (c) paper <paper@tflc.us>, 2026\n";

// very beta ;)
DECLARE_COMPONENT_VERSION("SDL output", "0.1", about);
VALIDATE_COMPONENT_FILENAME("foo_out_sdl.dll");
