#include <stdio.h>
#include <string.h>
#include <vector>
#include <iostream>
#include "htra_api.h" 
#include "portaudio.h"
#include "example.h"
using namespace std;
#define IS_USB 1 // By default, a USB-type device is used. If using an Ethernet-type device, define IS_USB as 0.

int DSP_FMDemod()
{
	int Status = 0;      // Return value of the function.
	void* Device = NULL; // Memory address of the current device.
	int DevNum = 0;      // Specifies the device number.

	BootProfile_TypeDef BootProfile; // Boot profile structure, includes physical interface, power supply type, etc.
	BootInfo_TypeDef BootInfo;       // Boot information structure, includes device info, USB speed, etc.

	BootProfile.DevicePowerSupply = USBPortAndPowerPort; // Use both USB data port and external power port for power supply.

#if IS_USB==1
	// Configure USB interface.
	BootProfile.PhysicalInterface = USB;
#else 
	// Configure Ethernet interface.
	BootProfile.PhysicalInterface = ETH;
	BootProfile.ETH_IPVersion = IPv4;
	BootProfile.ETH_RemotePort = 5000;
	BootProfile.ETH_ReadTimeOut = 5000;
	BootProfile.ETH_IPAddress[0] = 192;
	BootProfile.ETH_IPAddress[1] = 168;
	BootProfile.ETH_IPAddress[2] = 1;
	BootProfile.ETH_IPAddress[3] = 100;
#endif

	Status = Device_Open(&Device, DevNum, &BootProfile, &BootInfo); // Open the device.

	Device_Open_ErrorHandling(Status, &Device, DevNum, &BootProfile, &BootInfo); // If Status is not 0, perform corresponding error handling based on the return value.

	IQStream_TypeDef IQStream;							   // This structure is used to store parameters related to IQ data, including IQ data, ScaleToV, I_MaxValue, and others.
	IQS_Profile_TypeDef IQS_ProfileIn;                     //Configure parameters for IQS mode including start/stop frequency, decimate factor, R.L. etc.
	IQS_Profile_TypeDef IQS_ProfileOut;                    //Feedback information for IQS mode including start/stop frequency, decimate factor, R.L. etc.
	IQS_StreamInfo_TypeDef StreamInfo;                     //feedback information for the configuration including IQ data points, time domain data etc.

	Status = IQS_ProfileDeInit(&Device, &IQS_ProfileIn); // Initialize related parameters for IQS mode configuration.

	IQS_ProfileIn.CenterFreq_Hz = 101.1e6;   // Set the center frequency.
	IQS_ProfileIn.RefLevel_dBm = -40;        // Set the reference level.
	IQS_ProfileIn.DataFormat = Complex16bit; // Set the IQ data format.
	IQS_ProfileIn.TriggerMode = Adaptive;    // Set the trigger mode. 
	IQS_ProfileIn.TriggerSource = Bus;       // Set the trigger source as internal bus trigger. 
	IQS_ProfileIn.DecimateFactor = 512;      // Set the decimation factor.
	IQS_ProfileIn.BusTimeout_ms = 5000;      // Set the bus timeout

	Status = IQS_Configuration(&Device, &IQS_ProfileIn, &IQS_ProfileOut, &StreamInfo); // Configure IQS mode settings by calling this function.

	IQS_Configuration_ErrorHandling(Status, &Device, DevNum, &BootProfile, &BootInfo, &IQS_ProfileIn, &IQS_ProfileOut, &StreamInfo); // Perform corresponding error handling based on the Status return value when it is not 0.

	//Demod
	void* ADM = nullptr;
	ADM_Open(&ADM);

	/* Audio playback configuration */
	PaStreamParameters outputParameters; // Structure to store audio configuration parameters such as device ID, channel count, and data format.
	PaStream* stream;                    // Pointer used by PortAudio to store the audio input/output stream information.
	PaError err;                         // Error code returned by PortAudio functions.

	err = Pa_Initialize();                             // Initialize PortAudio.
	vector<float> audio(StreamInfo.PacketSamples);     // Allocate memory to store demodulated audio data.
	vector<float> LPF_audio(StreamInfo.PacketSamples); // Allocate memory to store demodulated and low-pass filtered audio data.

	outputParameters.device = Pa_GetDefaultOutputDevice();									                // Configure the default output audio device.
	outputParameters.channelCount = 1;									                                    // Configure the number of audio channels.
	outputParameters.sampleFormat = paFloat32;									                            // Configure the audio data type. Each audio sample is stored as a 32-bit signed floating-point value, with a range from -1.0 to 1.0.
	outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; // Configure the desired latency.
	outputParameters.hostApiSpecificStreamInfo = NULL;                                                      // Optional pointer to a host API-specific data structure for device or stream-specific settings. Set to NULL if not used.

	err = Pa_OpenStream(&stream, NULL, &outputParameters, StreamInfo.IQSampleRate, StreamInfo.PacketSamples, paClipOff, NULL, NULL); // Open an audio stream.
	err = Pa_StartStream(stream);																									 // Start audio processing.

	/* Start DSP functionality */
	void* DSP = NULL;        // Allocate memory for digital signal processing.
	DSP_Open(&DSP); // Enable signal processing functionality.

	// Low-pass filter configuration
	Filter_TypeDef filter; // Configure the low-pass filter for processing audio data.
	filter.As = 90;        // Set the stopband attenuation of the filter.
	filter.fc = 0.025;     // Set the cutoff frequency of the filter.
	filter.n = 90;         // Set the filter order.
	filter.mu = 0;         // Set the fractional sample offset.

	DSP_LPF_DeInit(&filter);
	DSP_LPF_Configuration(&DSP, &filter, &filter);

	Status = IQS_BusTriggerStart(&Device); // Call IQS_BusTriggerStart to trigger the device. If the trigger source is external, this function does not need to be called.

	// Continuously acquire and play FM broadcast
	while (1)
	{
		Status = IQS_GetIQStream_PM1(&Device, &IQStream); // Acquire IQ data.

		if (Status == APIRETVAL_NoError)
		{
			ADM_FMDemod(&ADM, IQStream.AlternIQStream, IQStream.IQS_Profile.DataFormat, StreamInfo.PacketSamples, IQStream.IQS_StreamInfo.IQSampleRate, true, audio.data());

			for (int i = 0; i < StreamInfo.PacketSamples; i++) // Scale the demodulated audio data to the range -1~1; the divisor can be adjusted as needed.
			{
				audio[i] /= 655360;
			}

			DSP_LPF_Execute_Real(&DSP, audio.data(), LPF_audio.data());               // Apply low-pass filtering to the audio data.
			err = Pa_WriteStream(stream, LPF_audio.data(), StreamInfo.PacketSamples); // Play the audio data.

		}
		else // When Status is not 0, handle the error according to the returned value.
		{
			IQS_ErrorHandlingExceptOpenAndConfiguration(Status, &Device, DevNum, &BootProfile, &BootInfo, &IQS_ProfileIn, &IQS_ProfileOut, &StreamInfo);
		}

	}

	Status = IQS_BusTriggerStop(&Device); // Call IQS_BusTriggerStop to stop triggering the device. If the trigger source is external, this function does not need to be called.

	// Close all functional modules.
	err = Pa_StopStream(stream);  // Stop audio processing.
	err = Pa_CloseStream(stream); // Close the audio stream.
	Pa_Terminate();               // Release all resources allocated by PortAudio.

	DSP_Close(&DSP);                // Release all resources allocated for DDC.
	ADM_Close(&ADM);                // Release all resources allocated for analog demodulation.
	Status = Device_Close(&Device); // Release all resources allocated by htra_api.

	return 0;

}
