/*
* SACD Decoder plugin
* Copyright (c) 2011-2023 Maxim V.Anisiutkin <maxim.anisiutkin@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#pragma once

#include <foobar2000.h>
#include "dop_converter.h"
#include "dsd_processor_service.h"
#include "dsd_stream_service.h"
#include "output_backtrace.h"

using pfc::eventHandle_t;
using pfc::string_base;

constexpr int VME_instantiate = 3;

class NOVTABLE autoproxy_output_entry_t {
public:
	void instantiate(service_ptr_t<output>& p_out, const GUID& p_device, double p_buffer_length, bool p_dither, t_uint32 p_bitdepth);
};

typedef void (__thiscall autoproxy_output_entry_t::* autoproxy_instantiate_t)(service_ptr_t<output>& p_out, const GUID& p_device, double p_buffer_length, bool p_dither, t_uint32 p_bitdepth);

class autoproxy_output : public service_base {
	FB2K_MAKE_SERVICE_INTERFACE(autoproxy_output, service_base);
protected:
	static constexpr t_size DSD_CHUNKS_TRESHOLD{ 8 };
	dop_converter_t m_dop_converter;
	static_api_ptr_t<dsd_stream_service> m_dsd_stream;
	bool m_dsd_playback;
	double m_buffer_length;
	double m_latency;
	double m_transition;
	double m_volume_dB;
	bool m_flushed;
	bool m_paused;
	bool m_trace;
	audio_chunk::spec_t m_inp_spec;
	audio_chunk::spec_t m_out_spec;
	output_backtrace_t m_backtrace;
	audio_chunk::spec_t m_now_playing_spec;
	service_ptr_t<dsd_processor_service> m_dsddsp;
	audio_chunk::spec_t m_dsddsp_out_spec;
public:
	autoproxy_output(service_ptr_t<output>& p_output, double p_buffer_length, bool p_dither, t_uint32 p_bitdepth);
	virtual double get_latency();
	virtual void process_samples(const audio_chunk& p_chunk);
	virtual void update(bool& p_ready);
	virtual void pause(bool p_state);
	virtual void flush();
	virtual void force_play();
	virtual void volume_set(double p_val_dB);
protected:
	void process_output(bool p_dop, const audio_chunk& p_chunk);
	bool pick_dsd_processor(const audio_chunk::spec_t& p_dsd_spec, const audio_chunk::spec_t& p_pcm_spec);
	void volume_adjust();
	void check_dsd_stream(bool p_update);
	void shutdown();
	service_ptr_t<output> m_output;
};

class autoproxy_output_v2 : public autoproxy_output {
	FB2K_MAKE_SERVICE_INTERFACE(autoproxy_output_v2, autoproxy_output);
protected:
	bool m_track_marks;
public:
	autoproxy_output_v2(service_ptr_t<output>& p_output, double p_buffer_length, bool p_dither, t_uint32 p_bitdepth);
	virtual bool want_track_marks();
	virtual void on_track_mark();
	virtual void enable_fading(bool p_state);
	virtual void flush_changing_track();
protected:
	service_ptr_t<output_v2> m_output_v2;
};

class autoproxy_output_v3 : public autoproxy_output_v2 {
	FB2K_MAKE_SERVICE_INTERFACE(autoproxy_output_v3, autoproxy_output_v2);
public:
	autoproxy_output_v3(service_ptr_t<output>& p_output, double p_buffer_length, bool p_dither, t_uint32 p_bitdepth);
	virtual unsigned get_forced_sample_rate();
	virtual void get_injected_dsps(dsp_chain_config& p_dsps);
protected:
	service_ptr_t<output_v3> m_output_v3;
};

class autoproxy_output_v4 : public autoproxy_output_v3 {
	FB2K_MAKE_SERVICE_INTERFACE(autoproxy_output_v4, autoproxy_output_v3);
public:
	autoproxy_output_v4(service_ptr_t<output>& p_output, double p_buffer_length, bool p_dither, t_uint32 p_bitdepth);
	virtual eventHandle_t get_trigger_event();
	virtual bool is_progressing();
	virtual t_size update_v2();
protected:
	service_ptr_t<output_v4> m_output_v4;
};

class autoproxy_output_v5 : public autoproxy_output_v4 {
	FB2K_MAKE_SERVICE_INTERFACE(autoproxy_output_v5, autoproxy_output_v4);
public:
	autoproxy_output_v5(service_ptr_t<output>& p_output, double p_buffer_length, bool p_dither, t_uint32 p_bitdepth);
	virtual unsigned get_forced_channel_mask();
protected:
	service_ptr_t<output_v5> m_output_v5;
};

template<typename T>
class autoproxy_service_t : public T {
public:
	autoproxy_service_t(service_ptr_t<output>& p_output, double p_buffer_length, bool p_dither, t_uint32 p_bitdepth) : T(p_output, p_buffer_length, p_dither, p_bitdepth) {
	}
	virtual ~autoproxy_service_t() {
		T::shutdown();
	}
};
