/**
* \class CBilateralFilterBase
*
* \brief This class implements the bilateral filter.
* \date	Nov 2024
*
* \authors 3DI-DIANA Research Group (University of Malaga), in alphabetical order: M. Cuevas-Rodriguez, D. Gonzalez-Toledo ||
* Coordinated by , A. Reyes-Lecuona (University of Malaga)||
* \b Contact: areyes@uma.es
*
* \b Copyright: University of Malaga
* 
* \b Contributions: (additional authors/contributors can be added here)
*
* \b Project: SONICOM ||
* \b Website: https://www.sonicom.eu/
*
* \b Acknowledgement: This project has received funding from the European Union�s Horizon 2020 research and innovation programme under grant agreement no.101017743
* 
* \b Licence: This program 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.
*/

#ifndef _BILATERAL_FILTER_MODEL_BASE_HPP_
#define _BILATERAL_FILTER_MODEL_BASE_HPP_

#include <Base/ModelBase.hpp>
#include <Common/Buffer.hpp>
#include <Common/AudioMixer.hpp>
#include <ServiceModules/ServicesBase.hpp>
#include <ServiceModules/SphericalSOSTable.hpp>

namespace BRTBilateralFilter {
	enum T_BilateralFilterType {
		none,
		SOS_FILTER,
		FIR_FILTER
	};

	class CBilateralFilterModelBase : public BRTBase::CModelBase {
	public:		
		virtual bool SetSOSFilterCoefficients(std::shared_ptr<BRTServices::CSphericalSOSTable> _listenerILD) { return false; };
		virtual std::shared_ptr<BRTServices::CSphericalSOSTable> GetSOSFilter() const { return nullptr; }
		virtual void RemoveSOSFilter() {};
		
		virtual bool SetFIRTable(std::shared_ptr<BRTServices::CServicesBase> _firTable) { return false; };
		virtual std::shared_ptr<BRTServices::CServicesBase> GetFIRTable() const { return nullptr; }
		virtual void RemoveFIRTable() { };

		virtual bool ConnectListenerModel(const std::string & _listenerModelID, Common::T_ear _ear = Common::T_ear::BOTH) { return false; };
		virtual bool DisconnectListenerModel(const std::string & _listenerModelID, Common::T_ear _ear = Common::T_ear::BOTH) { return false; };


		CBilateralFilterModelBase(const std::string & _binauraFilterID)
			: CModelBase(_binauraFilterID)
			, filterType { T_BilateralFilterType::none }			
			{
			
			leftChannelMixer = Common::CAudioMixer(globalParameters.GetBufferSize());
			rightChannelMixer = Common::CAudioMixer(globalParameters.GetBufferSize());
			CreateSamplesEntryPoint("leftEar");
			CreateSamplesEntryPoint("rightEar");
			CreateIDEntryPoint("listenerID");			
			
			CreateIDExitPoint();
			CreateSamplesExitPoint("leftEar");
			CreateSamplesExitPoint("rightEar");
		}


		T_BilateralFilterType GeBilateralFilterType() const {
			return filterType;
		}
		/**
		 * @brief Check if this binaural filter is connected to a listener
		 * @return true if connected, false otherwise
		 */
		bool IsConnectedToListener() override {
			std::string _listenerID = GetIDEntryPoint("listenerID")->GetData();
			if (_listenerID != "") {
				return true;
			}
			return false;
		}				
		
		void ConnectionToListenerEstablished(const std::string & _listenerID) {
			AddOuputConnections(_listenerID);
		}

	private:
				
		/**
		 * @brief Implementation of CAdvancedEntryPointManager virtual method
		 * @param _entryPointId entryPoint ID
		*/
		void OneEntryPointOneDataReceived(std::string _entryPointId) override {

			if (_entryPointId == "leftEar") {				
				CMonoBuffer<float> newBuffer = GetSamplesEntryPoint("leftEar")->GetData();				
				leftChannelMixer.AddBuffer(newBuffer);

			} else if (_entryPointId == "rightEar") {				
				CMonoBuffer<float> newBuffer = GetSamplesEntryPoint("rightEar")->GetData();				
				rightChannelMixer.AddBuffer(newBuffer);
			} else {
				//nothing
			}
		}

		/**
		 * @brief Implementation of CAdvancedEntryPointManager virtual method
		*/
		void AllEntryPointsAllDataReady() override {

			CMonoBuffer<float> leftBuffer = leftChannelMixer.GetMixedBuffer();
			CMonoBuffer<float> rightBuffer = rightChannelMixer.GetMixedBuffer();

			GetSamplesExitPoint("leftEar")->sendData(leftBuffer);
			GetSamplesExitPoint("rightEar")->sendData(rightBuffer);			
		}

		void UpdateCommand() override {			
			// It is only executed if the listener model that derives from this one does not implement this method.
			BRTConnectivity::CCommand command = GetLastReceivedCommand();
			if (command.GetCommand() == BRTConnectivity::CCommandList::COMMAND_OVERALL_STOP) {
				ResetMixerBuffers();
			}
		}


		///**
		// * @brief Mix the new buffer received with the contents of the buffer.
		//*/
		bool MixEarBuffers(CMonoBuffer<float> & buffer, const CMonoBuffer<float> & newBuffer) {
			if (newBuffer.size() != 0) {
				buffer += newBuffer;
				return true;
			}
			return false;
		}

		void InitBuffer(CMonoBuffer<float> & buffer) {
			buffer = CMonoBuffer<float>(globalParameters.GetBufferSize());
		}

		// Attributes
		Common::CGlobalParameters globalParameters;	
	
	protected:	
		void ResetMixerBuffers() {
			leftChannelMixer.ResetBuffer();
			rightChannelMixer.ResetBuffer();
		}
		void SendMyID() { GetIDExitPoint()->sendData(modelID); }
		
		Common::CAudioMixer leftChannelMixer;
		Common::CAudioMixer rightChannelMixer;

		T_BilateralFilterType filterType;
	};
}
#endif