/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            pluginlv2.h
 *
 *  Sun Feb  7 15:15:23 CET 2016
 *  Copyright 2016 Bent Bisballe Nyeng
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *  This file is part of PluginGizmo.
 *
 *  PluginGizmo 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  PluginGizmo 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 PluginGizmo; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#pragma once

#include <plugin.h>

#include <array>
#include <atomic>

#include <lv2.h>
#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
#include <lv2/lv2plug.in/ns/ext/state/state.h>
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
#include <lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h>
#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>

#define DISPLAY_INTERFACE
#define MIDNAM_INTERFACE

#ifdef DISPLAY_INTERFACE
#include "inline-display.h"
#endif

#ifdef MIDNAM_INTERFACE
#include "midnam_lv2.h"
#endif

enum class LV2Ports
{
	FreeWheel = 0,
	Latency = 1,
	PortOffset = 2,
};

class PluginLV2
	: public Plugin
{
public:
	virtual ~PluginLV2() = default;

	//! Not used in LV2
	void init() override;

	//! Get current free-wheel mode.
	bool getFreeWheel() const override;


	//! Call this to get current samplerate.
	float getSamplerate() override;

	//! This method is called by the host when the samplerate changes.
	virtual void onSamplerateChange(float samplerate) override = 0;

	//! Call this to get current frame-size.
	std::size_t getFramesize() override;

	//! This method is called by the host when the frame-size changes.
	virtual void onFramesizeChange(std::size_t framesize) override = 0;


	//! Call this to get current active state
	bool getActive() override;

	//! This method is called by the host when the active state changes.
	virtual void onActiveChange(bool active) override = 0;


	//! This method is called by the host to get the current state for storing.
	virtual std::string onStateSave() override = 0;

	//! This method is called by the host when a new state has been loaded.
	virtual void onStateRestore(const std::string& config) override = 0;


	//! This is method is called by the host to get the current latency.
	//! \param The latency in samples.
	float getLatency() override;

	//! Call this method to signal a latency change to the host.
	//! \param latency The latency in samples.
	void setLatency(float latency) override;


	//! Called by the the host to get the number of audio input channels.
	//! This must remain constant during the lifespan of the plugin instance.
	virtual std::size_t getNumberOfAudioInputs() override = 0;

	//! Called by the the host to get the number of audio output channels.
	//! This must remain constant during the lifespan of the plugin instance.
	virtual std::size_t getNumberOfAudioOutputs() override = 0;


	//! Called by the the host to get the number of midi input channels.
	//! This must remain constant during the lifespan of the plugin instance.
	virtual std::size_t getNumberOfMidiInputs() override = 0;

	//! Called by the the host to get the number of midi output channels.
	//! This must remain constant during the lifespan of the plugin instance.
	virtual std::size_t getNumberOfMidiOutputs() override = 0;

	//! Call this method to set midnam data for midi input
	virtual void setMidnamData(const std::vector<std::pair<int, std::string>>& midnam) override;

	//! Get unique plugin id.
	std::string getId() override = 0;

	// Functions used to set plugin information.
	std::string getEffectName() override = 0;
	std::string getVendorString() override = 0;
	std::string getProductString() override = 0;
	PluginCategory getPluginCategory() override = 0;

	virtual void process(std::size_t pos,
	                     const std::vector<MidiEvent>& input_events,
	                     std::vector<MidiEvent>& output_events,
	                     const std::vector<const float*>& input_samples,
	                     const std::vector<float*>& output_samples,
	                     std::size_t count) override = 0;

	//
	// Inline GUI (optional)
	//

	//! Return true if a GUI implementation is to be used.
	virtual bool hasInlineGUI() override
	{
		return false;
	}

	//! Render call back.
	//! \param width The client area width as specified by the host.
	//! \param max_height The maximum allowed clieant area height as specified
	//!  by the host.
	//! \param context The render context filled an maintained by the plugin.
	virtual void onInlineRedraw(std::size_t width,
	                            std::size_t max_height,
	                            InlineDrawContext& context) override {}

	//
	// GUI (optional)
	//

	//! Return true if a GUI implementation is to be used.
	virtual bool hasGUI() override
	{
		return false;
	}

	//! Create new window.
	virtual void* createWindow(void *parent) override { return nullptr; }

	//! Destroy window.
	virtual void onDestroyWindow() override {}

	//! Show window.
	virtual void onShowWindow() override {}

	//! Hide window.
	virtual void onHideWindow() override {}

	//! Called regularly by host; process ui events.
	virtual void onIdle() override {}

	//! Signal new window size to host.
	void resizeWindow(std::size_t width, std::size_t height) override;

	//! Signal close window event to the host.
	void closeWindow() override;

public:
	static LV2_Handle instantiate(const LV2_Descriptor* descriptor,
	                              double sample_rate,
	                              const char* bundle_path,
	                              const LV2_Feature* const * features);

	static void connectPort(LV2_Handle instance, uint32_t port,
	                        void *data_location);

	static void run(LV2_Handle instance, uint32_t sample_count);

	static void activate(LV2_Handle instance);

	static void deactivate(LV2_Handle instance);

	static void cleanup(LV2_Handle instance);

	static const void* extensionData(const char *uri);

	static LV2_State_Status save(LV2_Handle instance,
	                             LV2_State_Store_Function store,
	                             LV2_State_Handle handle,
	                             uint32_t flags,
	                             const LV2_Feature *const * features);

	static LV2_State_Status restore(LV2_Handle instance,
	                                LV2_State_Retrieve_Function retrieve,
	                                LV2_State_Handle handle,
	                                uint32_t flags,
	                                const LV2_Feature *const * features);

	static LV2UI_Handle uiInstantiate(const LV2UI_Descriptor * descriptor,
	                                  const char * plugin_uri,
	                                  const char * bundle_path,
	                                  LV2UI_Write_Function write_function,
	                                  LV2UI_Controller controller,
	                                  LV2UI_Widget * widget,
	                                  const LV2_Feature * const * features);

	static void uiCleanup(LV2UI_Handle handle);

	static int uiIdle(LV2UI_Handle handle);

	static const void* uiExtensionData(const char* uri);

private:
	float* free_wheel_port{nullptr};
	bool free_wheel{false};

	float sample_rate{0};

	float* latency_port{nullptr};

	std::size_t frame_size{0};

	std::size_t pos{0};

	std::vector<LV2_Atom_Sequence*> input_event_ports;
	std::vector<LV2_Atom_Sequence*> output_event_ports;
	std::vector<const float*> input_audio_ports;
	std::vector<float*> output_audio_ports;

	LV2_URID_Map* map{nullptr};

#ifdef DISPLAY_INTERFACE
	LV2_Inline_Display_Image_Surface surf;
	LV2_Inline_Display* queue_draw{nullptr};

	InlineDrawContext drawContext;

	static LV2_Inline_Display_Image_Surface *inlineRender(LV2_Handle instance,
	                                                      uint32_t w,
	                                                      uint32_t max_h);
#endif

#ifdef MIDNAM_INTERFACE
	LV2_Midnam* midnam{nullptr};
	static char* MidnamFile  (LV2_Handle instance);
	static char* MidnamModel (LV2_Handle instance);
	static void  MidnamFree  (char*);
#endif
	std::atomic<bool> midnam_changed{false};
	std::array<std::pair<int, std::string>, 127> midnamData; // At most 127 different midinotes.

	bool active{false};

	//
	// GUI
	//
	LV2UI_Resize* resize{nullptr};
};

PG_EXPORT PluginLV2* createEffectInstance();
