/* -----------------------------------------------------------------------------
 *
 * Giada - Your Hardcore Loopmachine
 *
 * -----------------------------------------------------------------------------
 *
 * Copyright (C) 2010-2025 Giovanni A. Zuliani | Monocasual Laboratories
 *
 * This file is part of Giada - Your Hardcore Loopmachine.
 *
 * Giada - Your Hardcore Loopmachine is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * Giada - Your Hardcore Loopmachine 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Giada - Your Hardcore Loopmachine. If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * -------------------------------------------------------------------------- */

#ifndef G_PLUGIN_H
#define G_PLUGIN_H

#if G_OS_WINDOWS
/* windows.h, included by JUCE, defines 'small' as a macro and it clashes with
some enum defined in the JUCE GUI module itself. Also NOMINMAX is required on
Windows. */
#undef small
#define NOMINMAX
#endif

#include "src/core/midiLearnParam.h"
#include "src/core/plugins/pluginHost.h"
#include "src/core/plugins/pluginState.h"
#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_gui_basics/juce_gui_basics.h>
#include <memory>
#include <vector>

namespace giada::m
{
class Plugin : private juce::ComponentListener
{
public:
	using Buffer = juce::AudioBuffer<float>;

	/* Plugin (1)
	Constructs an invalid plug-in. */

	Plugin(ID id, const std::string& juceId);

	/* Plugin (2)
	Constructs a valid and working plug-in. */

	Plugin(ID id, const std::string& juceId, std::unique_ptr<juce::AudioPluginInstance>, std::unique_ptr<PluginHost::Info>,
	    double samplerate, int buffersize);

	Plugin(const Plugin& o)          = delete;
	Plugin(Plugin&& o)               = delete;
	Plugin& operator=(const Plugin&) = delete;
	Plugin& operator=(Plugin&&)      = delete;

	~Plugin();

	/* getJuceId
	Returns a string ID, generated by JUCE. */

	std::string getJuceId() const;

	std::string                 getName() const;
	bool                        hasEditor() const;
	int                         getNumParameters() const;
	float                       getParameter(int index) const;
	std::string                 getParameterName(int index) const;
	std::string                 getParameterText(int index) const;
	std::string                 getParameterLabel(int index) const;
	bool                        isSuspended() const;
	bool                        isBypassed() const;
	bool                        isInstrument() const;
	int                         getNumPrograms() const;
	int                         getCurrentProgram() const;
	std::string                 getProgramName(int index) const;
	void                        setParameter(int index, float value) const;
	void                        setCurrentProgram(int index) const;
	PluginState                 getState() const;
	juce::AudioProcessorEditor* createEditor() const;

	/* countMainOutChannels
	Returns the current channel layout for the main output bus. */

	int countMainOutChannels() const;

	/* process
	Process the plug-in with audio and MIDI data. The audio buffer is a
	reference, while the MIDI buffer must be passed by copy: each plug-in must
	receive its own copy of the event set, so that any attempt to change/clear
	the MIDI buffer will only modify the local copy. Returns a reference of the
	local buffer filled with processed data. */

	const Buffer& process(const Buffer& b, juce::MidiBuffer m);

	void setState(PluginState p);
	void setBypass(bool b);

	/* id
	Unique identifier. */

	ID id;

	/* midiInParams
	A vector of MidiLearnParam's for controlling plug-in parameters with
	external hardware. */

	std::vector<MidiLearnParam> midiInParams;

	/* valid
	A missing plug-in is loaded anyway, yet marked as 'invalid'. */

	bool valid;

	std::function<void(int w, int h)> onEditorResize;

private:
#if G_OS_WINDOWS
/* Fuck... */
#undef IN
#undef OUT
#endif

	enum class BusType
	{
		IN  = true,
		OUT = false
	};

	/* JUCE overrides. */

	void componentMovedOrResized(juce::Component& c, bool moved, bool resized) override;

	juce::AudioProcessor::Bus* getMainBus(BusType b) const;

	std::unique_ptr<juce::AudioPluginInstance> m_plugin;
	std::unique_ptr<PluginHost::Info>          m_playHead;
	Buffer                                     m_buffer;

	std::atomic<bool> m_bypass;

	/* juceID
	The original JUCE id, used for missing plugins. */

	std::string m_juceId;

	/* m_hasEditor
	Cached boolean value that tells if the plug-in has editor. Some plug-ins
	take ages to query it, better fetch the property during construction. */

	bool m_hasEditor;
};
} // namespace giada::m

#endif