/**
* \class CExitPointManager
*
* \brief Declaration of CExitPointManager class
* \date	June 2023
*
* \authors 3DI-DIANA Research Group (University of Malaga), in alphabetical order: M. Cuevas-Rodriguez, D. Gonzalez-Toledo, L. Molina-Tanco, F. Morales-Benitez ||
* 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 _EXIT_POINT_MANAGER_
#define _EXIT_POINT_MANAGER_

#include <Common/ErrorHandler.hpp>
#include <Connectivity/ExitPoint.hpp>

namespace BRTConnectivity {
    class CExitPointManager {
    public:
		const std::string HRTF_EXIT_POINT_ID = "moduleHRTF";
		const std::string HRBRIR_EXIT_POINT_ID = "moduleHRBRIR";
        const std::string SOS_COEFFICIENTS_EXIT_POINT_ID = "moduleSOSCoefficients";
        const std::string ABIR_EXIT_POINT_ID = "moduleABIR";		
        

        /////////////////////
        // Transform
        /////////////////////
        
        /** \brief Initialises an exit point of type transform
        *	\param [in] Identifier to be given to the exit point
        *	\retval void
        *   \eh On error, NO error code is reported.
        */
        void CreateTransformExitPoint() {
            transformExitPoint = std::make_shared<CExitPointTransform>("moduleTransform");
        }

        /** \brief Returns a pointer to the exit point
        *	\param [in] Exit point identifier to be searched for
        *	\retval A pointer to this type of exit point or null if not found.
        *   \eh On error, an error code is reported to the error handler.
        */
		std::shared_ptr<BRTConnectivity::CExitPointTransform> GetTransformExitPoint() {
            if (transformExitPoint != nullptr)  { return transformExitPoint; }
            ASSERT(false, RESULT_ERROR_NOTINITIALIZED, "The exit point of type Transform has not been initialised.", "Call CExitPointManager::CreateTransformExitPoint() in your constructor.");
            return nullptr;
        }
        
        /////////////////////
        // Samples
        /////////////////////
        /** \brief Creates a new exit point of type samples and saves it
        *	\param [in] Identifier to be given to the exit point
        *	\retval void
        *   \eh On error, an error code is reported to the error handler.
        */
        void CreateSamplesExitPoint(std::string exitPointID) {
			std::shared_ptr<BRTConnectivity::CExitPointSamplesVector> _newExitPoint = std::make_shared<BRTConnectivity::CExitPointSamplesVector>(exitPointID);
            samplesExitPoints.push_back(_newExitPoint);
        }
        
        /** \brief Returns a pointer to the exit point
        *	\param [in] Exit point identifier to be searched for
        *	\retval A pointer to this type of exit point or null if not found.
        *   \eh On error, an error code is reported to the error handler.
        */
		std::shared_ptr<BRTConnectivity::CExitPointSamplesVector> GetSamplesExitPoint(std::string exitPointID) {
            for (auto& it : samplesExitPoints) {
                if (it->GetID() == exitPointID) { return it; }
            }
            ASSERT(false, RESULT_ERROR_INVALID_PARAM, "No exit point, of type Samples, has been found with this id." + exitPointID, "");
            return nullptr;
        }


        void CreateMultipleSamplesExitPoint(std::string exitPointID) {
			std::shared_ptr<BRTConnectivity::CExitPointMultipleSamplesVector> _newExitPoint = std::make_shared<BRTConnectivity::CExitPointMultipleSamplesVector>(exitPointID);
            multipleSamplesVectorExitPoints.push_back(_newExitPoint);
        }

        std::shared_ptr<BRTConnectivity::CExitPointMultipleSamplesVector> GetMultipleSamplesVectorExitPoint(std::string exitPointID) {
            for (auto& it : multipleSamplesVectorExitPoints) {
                if (it->GetID() == exitPointID) { return it; }
            }
            ASSERT(false, RESULT_ERROR_INVALID_PARAM, "No exit point, of type Samples, has been found with this id." + exitPointID, "");
            return nullptr;
        }
        
        /////////////////////
        // IDs 
        /////////////////////
        /** \brief Initialises an exit point of type ID. Used to send the identifier of this module to other modules.
        *	\param [in] Identifier to be given to the exit point
        *	\retval void
        *   \eh On error, an error code is reported to the error handler.
        */
        void CreateIDExitPoint() {
            moduleIDExitPoint = std::make_shared<CExitPointID>("moduleID");
        }
        
        /** \brief Returns a pointer to the exit point
        *	\param [in] Exit point identifier to be searched for
        *	\retval A pointer to this type of exit point or null if not found.
        *   \eh On error, an error code is reported to the error handler.
        */
		std::shared_ptr<BRTConnectivity::CExitPointID> GetIDExitPoint() {
            if (moduleIDExitPoint!=nullptr) { return moduleIDExitPoint; }
            ASSERT(false, RESULT_ERROR_NOTINITIALIZED, "The exit point of type ID has not been initialised.", "Call CExitPointManager::CreateIDExitPoint() in your constructor.");
            return nullptr;
        }

        /////////////////////
		// Service Modules
		/////////////////////
		void CreateServiceExitPoint(const std::string & _exitPointID) {
			std::shared_ptr<CExitPointServicePtr> newExitPoint = std::make_shared<CExitPointServicePtr>(_exitPointID);
			serviceExitPointList.push_back(std::move(newExitPoint));
		}

		std::shared_ptr<CExitPointServicePtr> GetServiceExitPoint(const std::string & _exitPointID) {			
			for (auto & it : serviceExitPointList) {
				if (it->GetID() == _exitPointID) {
					return it;
				}
			}
			ASSERT(false, RESULT_ERROR_INVALID_PARAM, "No service exit point has been found with this id." + _exitPointID, "");
			return nullptr;
		}

        /////////////////////
        // HRTFs 
        /////////////////////
        void CreateHRTFExitPoint() {			
			CreateServiceExitPoint(HRTF_EXIT_POINT_ID);
        }
		std::shared_ptr<CExitPointServicePtr> GetHRTFExitPoint() {
			return GetServiceExitPoint(HRTF_EXIT_POINT_ID);
		}
       

        /////////////////////
        // AmbisonicBIRs 
        /////////////////////
        void CreateABIRExitPoint() {     
            CreateServiceExitPoint(ABIR_EXIT_POINT_ID);           
        }

        std::shared_ptr<CExitPointServicePtr> GetABIRExitPoint() {
			return GetServiceExitPoint(ABIR_EXIT_POINT_ID);
        }

        /////////////////////
       // ILDs 
       /////////////////////
        void CreateSOSFilterExitPoint() {          
			CreateServiceExitPoint(SOS_COEFFICIENTS_EXIT_POINT_ID);
        }

        std::shared_ptr<CExitPointServicePtr> GetSOSFilterExitPoint() {            
			return GetServiceExitPoint(SOS_COEFFICIENTS_EXIT_POINT_ID);
        }

      /////////////////////
      // HRBRIRs 
      /////////////////////        
        void CreateHRBRIRExitPoint() {
			CreateServiceExitPoint(HRBRIR_EXIT_POINT_ID);
		}
		std::shared_ptr<CExitPointServicePtr> GetHRBRIRExitPoint() {
			return GetServiceExitPoint(HRBRIR_EXIT_POINT_ID);
		}
    
    private:
        std::shared_ptr<CExitPointTransform> transformExitPoint;
		std::vector<std::shared_ptr<BRTConnectivity::CExitPointSamplesVector>> samplesExitPoints;
		std::vector<std::shared_ptr<BRTConnectivity::CExitPointMultipleSamplesVector>> multipleSamplesVectorExitPoints;
        std::shared_ptr<CExitPointID> moduleIDExitPoint;
        
        std::vector<std::shared_ptr<CExitPointServicePtr>> serviceExitPointList;               
    };
}
#endif