/*
 * ----------------------------------------------------------------
 *
 *		ITA core libs
 *		(c) Copyright Institute of Technical Acoustics (ITA)
 *		RWTH Aachen University, Germany, 2015-2024
 *
 * ----------------------------------------------------------------
 *				    ____  __________  _______
 *				   //  / //__   ___/ //  _   |
 *				  //  /    //  /    //  /_|  |
 *				 //  /    //  /    //  ___   |
 *				//__/    //__/    //__/   |__|
 *
 * ----------------------------------------------------------------
 *
 */
// $Id: ITAJACKInterface.h $

#pragma once

#include <functional>
#include <jack/jack.h>
#include <queue>
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>

// Forward declaration of ITADatasource
class ITADatasource;


// Forward declarations of callbacks
int ITAJackXRUN( void *arg );
void ITAJackMsg( const char *msg );
void ITAJackShutdown( void *arg );
void ITAJackPortConnected( jack_port_id_t a, jack_port_id_t b, int connect, void *arg );
void ITAJackPortRegistered( jack_port_id_t port_id, int reg, void *arg );


/*! ITAJACKInterface
 *
 *
 *
 * see also \ITADatasources
 *
 */
class ITAJACKInterface
{
public:
	//! ITAPortaudio error code table
	enum ITA_JACK_ERRORCODE
	{
		//! Portaudio/ITAPortaudio no error
		ITA_JACK_NO_ERROR = 0,
		ITA_JACK_ERROR    = 1,
		ITA_JACK_IS_OPEN,
		ITA_JACK_IS_STARTED,
		ITA_JACK_OPEN_FAILED,
		ITA_JACK_NOT_INITIALIZED,
		ITA_JACK_UNMATCHED_CHANNELS,
		ITA_JACK_NO_PLAYBACK_DATASOURCE,
		ITA_JACK_NO_RECORD_DATASOURCE,
		ITA_JACK_IS_NOT_OPEN,
		ITA_JACK_INTERNAL_ERROR,
		ITA_JACK_STREAM_IS_STOPPED,
		ITA_JACK_UNMATCHED_BUFFER_SIZE,
		ITA_JACK_UNMATCHED_SAMPLE_RATE
	};

	//! If no blockSize given the interface will use blockSize of JACK server
	ITAJACKInterface( int blockSize = -1 );
	~ITAJACKInterface( );


	//! Initialize JACK
	ITA_JACK_ERRORCODE Initialize( const std::string &clientName );


	//! Finalize JACK
	/**
	 * This also deletes the record datasource.
	 */
	ITA_JACK_ERRORCODE Finalize( );

	//! Opens a JACK client
	ITA_JACK_ERRORCODE Open( );

	//! Closes the JACK client
	ITA_JACK_ERRORCODE Close( );

	//! Start JACK client
	ITA_JACK_ERRORCODE Start( );

	//! Stop JACK client
	ITA_JACK_ERRORCODE Stop( );

	inline int GetNumInputChannels( ) const { return m_bInitialized ? m_iNumInputChannels : 0; }
	inline int GetNumOutputChannels( ) const { return m_bInitialized ? m_iNumOutputChannels : 0; }

	inline int GetSampleRate( ) const { return m_dSampleRate; }

	inline int GetBlockSize( ) const { return m_iBufferSize; }

	inline int SetBlockSize( int blockSize );


	//! Set the playback data source
	/**
	 * \note Enables playback, see IsPlaybackEnabled() and SetPlaybackEnabled()
	 */
	ITA_JACK_ERRORCODE SetPlaybackDatasource( ITADatasource *pidsDatasource );

	//! Get the recording data source
	/**
	  * This also creates the record datasource if not already present.

	  * \note Enables recording, see IsRecordingEnabled() and SetRecordingEnabled()
	  *
	  * \see Finalize()
	  */
	ITADatasource *GetRecordDatasource( );

	//! Returns a human readable error code string
	static std::string GetErrorCodeString( ITA_JACK_ERRORCODE err );

	inline static jack_client_t *GetJackClient( ) { return s_jackClient; }

	void printInfo( );

	struct ITAJackUserData
	{
		ITADatasource *pdsPlaybackDatasource; //!< ITADatasource playback datasource
		ITADatasource *pdsRecordDatasource;   //!< ITADatasource record datasource
		std::vector<jack_port_t *> input_ports, output_ports;
		uint64_t num_xruns;
		uint64_t num_samples;

		ITAJackUserData( ) : num_xruns( 0 ), num_samples( 0 ), pdsPlaybackDatasource( nullptr ), pdsRecordDatasource( nullptr ) {}
	};


private:
	static jack_client_t *s_jackClient;
	jack_client_t *m_jackClient;


	int m_iBufferSize;
	double m_dSampleRate;

	ITAJackUserData m_oUserData; //!< ITAPortaudioDatasource user data

	bool m_bInitialized; //!< Portaudio initialization status
	bool m_bOpen;        //!< Portaudio open status
	bool m_bStreaming;   //!< Portaudio streaming status


	int m_iNumInputChannels;  //!< Number of input channels
	int m_iNumOutputChannels; //!< Number of output channels

	ITA_JACK_ERRORCODE m_iError; //!< Last error

	bool connectJackPorts( bool connect );
};