/**
* @file		component.h
* @brief	Components' related stuff (interfaces and adapters)
* @author	Cesar Mauri Loba (cesar at crea-si dot com)
*
* -------------------------------------------------------------------------
*
* Copyright:   (C) 2010-12 Cesar Mauri Loba - CREA Software Systems
*
*  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.
*
*  This program 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 General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SPCORE_COMPONENT_H
#define SPCORE_COMPONENT_H

#include "spcore/baseobj.h"
#include "spcore/coreruntime.h"
#include <vector>
#include <string>
#include "spcore/pin.h"
#include "spcore/iterator.h"
#include "spcore/configuration.h"
#include <algorithm>
#include <string.h>
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>

/**
	@todo typedef as a neutral window type
*/
class wxWindow;

namespace spcore {

// Forward declarations
template<class T> class IIterator; 
class IInputPin; 
class IOutputPin; 

/**
	@brief Interface for all components.
*/
class IComponent : public IBaseObject {
protected:
	virtual ~IComponent() {};

public:
	/**
		@brief Return the name of the component.
		@return Pointer to an internally managed string with the name of the component.
	*/
	virtual const char* GetName() const = 0;

	/**
		@brief Return the name of the type of the component.
		@return Pointer to an internally managed string with the name.
	*/
	virtual const char* GetTypeName() const = 0;

	/**
		@brief Opens a window for this component (if it has one).
		@param parent Pointer to the parent window.
		@return Pointer to the window or NULL.

		The lifetime of this instance belongs to the component and should never be deleted.
	*/
	virtual wxWindow* GetGUI(wxWindow * parent) = 0;

	/**
		@brief Add a child component.
		@param component Smart pointer to the child component.
		@return 0 when OK

		Note not all components support composition.
	*/
	virtual int AddChild(SmartPtr<IComponent> component) = 0;

	/**
		@brief Get the list of children compoents.
		@return Smart pointer to a component iterator. Can be NULL.

		Note not all components support composition.
	*/
	virtual SmartPtr<IIterator<IComponent*> > QueryComponents() = 0;

	/**
		@brief Get the input pins of a component (if any).
		@return Smart pointer to an iterator of input pins.
	*/
	virtual SmartPtr<IIterator<IInputPin*> > GetInputPins() = 0;

	/**
		@brief Get the output pins of a component (if any).
		@return Smart pointer to an iterator of output pins.
	*/
	virtual SmartPtr<IIterator<IOutputPin*> > GetOutputPins() = 0;

	/**
		@brief Returns whether this component provides a thread of execution.
		@return true or false.
		
		Use Start() and Stop() methods to control thread state.
	*/
	virtual bool ProvidesExecThread() const = 0;

	/**
		@brief Starts the component (and children if any). 
		@return 0 if successfully started, -1 otherwise.

		This call is used to start/stop the component which can perform specific
		actions to enable/disable it and, when ProvidesExecThread is true then
		also runs the thread.
	*/
	virtual int Start() = 0;

	/**
		@brief Stops the component. 
		
		See comment for Start() method.
	*/
	virtual void Stop() = 0;

	/**
		@brief	Initializes the component.
		@return 0 if successfully initialized, -1 otherwise.		
	*/
	virtual int Initialize() = 0;

	/**
		@brief Finishes the component.
	*/
	virtual void Finish() = 0;

	/**
		@brief Store internal settings.
		@param cfg Configuration object		
	*/
	virtual void SaveSettings(IConfiguration& cfg)= 0;

	/**
		@brief Store internal settings.
		@param cfg Configuration object.		
	*/
	virtual void LoadSettings(IConfiguration& cfg)= 0;

	/**
		@brief Static method which searches an input pin given its name.
		@param component Component
		@param name Name of the input pin to look for.
		@return Pointer to the pin or NULL if not found.
	*/
	static inline
	IInputPin* FindInputPin(IComponent & component, const char * name) {
		assert (name);
  		if (!name) return NULL;
  		SmartPtr<IIterator<IInputPin*> > ipit= component.GetInputPins();
  		for (; !ipit->IsDone(); ipit->Next()) {
  			if (strcmp(ipit->CurrentItem()->GetName(), name)== 0) return ipit->CurrentItem();
  		}
  		return NULL;
	}

	/**
		@brief Static method which searches an output pin given its name.
		@param component Component
		@param name Name of the output pin to look for.
		@return Pointer to the pin or NULL if not found.
	*/
	static inline 
	IOutputPin* FindOutputPin(IComponent & component, const char * name) {
		assert (name);
  		if (!name) return NULL;
  		SmartPtr<IIterator<IOutputPin*> > opit= component.GetOutputPins();
  		for (; !opit->IsDone(); opit->Next()) {
  			if (strcmp(opit->CurrentItem()->GetName(), name)== 0) return opit->CurrentItem();
  		}
  		return NULL;
	}
};

/**
	@brief Adapter class to implement IComponent conforming classes.
	
	This class is intended to be derived to implement leaf type components
	(i.e. without child components). If you need to implement a component
	that is composed of children use class CCompositeComponentAdapter instead.
*/
class CComponentAdapter : public IComponent {
public:
	/**
		@brief Constructor 
		@param name Name given to the component.
		@param argc Number of arguments passed to the parameter argv.
		@param argv Array of parameters (can be NULL if argc==0).

		argc, argv are not used actually but these parameters are needed
		to provide an uniform construction mechanism.
	*/
	CComponentAdapter(const char * name, int argc, const char * argv[]) 
	: m_initialized(false)
	, m_name(name)
	{
		// Unused parameters
		(void) argc;
		(void) argv;
	}

	virtual ~CComponentAdapter() {
		std::vector<IInputPin*>::iterator iti;
		for (iti= m_inputPins.begin(); iti!= m_inputPins.end(); ++iti)
			(*iti)->Release();
		m_inputPins.clear();

		std::vector<IOutputPin*>::iterator ito;
		for (ito= m_outputPins.begin(); ito!= m_outputPins.end(); ++ito)
			(*ito)->Release();
		m_outputPins.clear();
	}

	virtual const char* GetName() const { return m_name.c_str(); }
	virtual wxWindow* GetGUI(wxWindow * parent) { 
		(void) parent;
		return NULL; 
	}

	virtual int AddChild(SmartPtr<IComponent> component) { return -1; }
	virtual SmartPtr<IIterator<IComponent*> > QueryComponents() {
		return SmartPtr<IIterator<IComponent*> >(NULL, false);
	}

	virtual SmartPtr<IIterator<IInputPin*> > GetInputPins() {
		return SmartPtr<IIterator<IInputPin*> >(new CIteratorVector<IInputPin *>(m_inputPins), false);
	}
	virtual SmartPtr<IIterator<IOutputPin*> > GetOutputPins() {
		return SmartPtr<IIterator<IOutputPin*> >(new CIteratorVector<IOutputPin *>(m_outputPins), false);
	}

	virtual bool ProvidesExecThread() const { return false; }

	virtual int Start() {
		int retval= Initialize();
		if (retval) return retval;
		return DoStart();
	}
	virtual void Stop() { DoStop(); }

	virtual int Initialize() {
		if (m_initialized) return 0;

		int retval= DoInitialize();
		if (retval) return retval;
		
		m_initialized= true;
			
		return 0; 
	}
	virtual void Finish() {
		if (m_initialized) {
			DoFinish();
			m_initialized= false;
		}
	}

	virtual void SaveSettings(IConfiguration&) {}
	virtual void LoadSettings(IConfiguration&) {}

protected:
	/**
		@brief	Perform the actual component initialization.
		@return 0 if successfully initialized, -1 otherwise.		
	*/
	virtual int DoInitialize() { return 0; }

	/**
		@brief	Perform the actual component finalization.
	*/
	virtual void DoFinish() { }

	/**
		@brief	Return whether the component is initialized.
	*/
	virtual bool IsInitialized() const { return m_initialized; }

	/**
		@brief	Actual component start.
	*/
	virtual int DoStart() { return 0; }

	/**
		@brief	Actual component stop.
	*/
	virtual void DoStop() {}

	/**
		@brief Registers a new input pin. 
		@return -1 when error (pin already registered).
	*/
	int RegisterInputPin(IInputPin & pin) {
		std::vector<IInputPin*>::iterator it= find (m_inputPins.begin(), m_inputPins.end(), &pin);
  		if (it!= m_inputPins.end())	return -1; // Already registered

  		pin.AddRef();
  		m_inputPins.push_back (&pin);

  		return 0;
	}

	/**
		@brief Registers a new output pin. 
		@return -1 when error (pin already registered).
	*/
	int RegisterOutputPin(IOutputPin & pin) {
		std::vector<IOutputPin*>::iterator it= find (m_outputPins.begin(), m_outputPins.end(), &pin);
  		if (it!= m_outputPins.end()) return -1; // Already registered

  		pin.AddRef();
  		m_outputPins.push_back (&pin);

  		return 0;
	}

private:
	bool m_initialized;
	std::vector<IInputPin *> m_inputPins;
	std::vector<IOutputPin *> m_outputPins;
	std::string m_name;
};

/**
	@brief Adapter class to implement IComponent conforming classes which,
	in turn, need composition support.
*/
class CCompositeComponentAdapter : public CComponentAdapter {
public:
	CCompositeComponentAdapter(const char * name, int argc, const char * argv[])
	: CComponentAdapter(name, argc, argv) { }
	
	virtual ~CCompositeComponentAdapter() {
		Stop();
		Finish();
		// Delete components
		for (std::vector<IComponent*>::iterator it= m_components.begin(); it!= m_components.end(); ++it)
			(*it)->Release();
	}
	

	virtual int AddChild(SmartPtr<IComponent> component) {
		std::vector<IComponent*>::iterator it= m_components.begin();

		for(; it!= m_components.end(); ++it) {
			// Check if the component (by pointer) has been already registered.
			// TODO: check that component is not registered (comparing pointers)
			// anywhere in this component composite
			if ((*it)== component.get()) break;

			// Check also by name
			if (strcmp((*it)->GetName(), component->GetName())== 0) break;
		}

		if (it!= m_components.end()) {
			// Component already registered
			return -1;
		}

		// Finaly add component to vector
		component->AddRef();
		m_components.push_back(component.get());

		return 0;
	}

	virtual SmartPtr<IIterator<IComponent*> > QueryComponents() {
		return SmartPtr<IIterator<IComponent*> >(new CIteratorVector<IComponent*>(m_components), false);
	}

	virtual int Start() {
		int retval= Initialize();
		if (retval!= 0) return retval;

		std::vector<IComponent*>::iterator it;
		for (it= m_components.begin(); retval== 0 && it!= m_components.end(); ++it)
			retval= (*it)->Start();

		if (retval!= 0) Stop();

		return retval;
	}

	virtual void Stop() {
		std::vector<IComponent*>::iterator it;
		for (it= m_components.begin(); it!= m_components.end(); ++it)
			(*it)->Stop();
	}

	virtual int Initialize() {
		int retval= 0;

		retval= DoInitialize();
		if (retval) return retval;

		std::vector<IComponent*>::iterator it;
		for (it= m_components.begin(); retval== 0 && it!= m_components.end(); ++it)
			retval= (*it)->Initialize();

		if (retval!= 0) Finish();

		return retval;
	}

	virtual void Finish() {
		Stop();
		DoFinish();
		std::vector<IComponent*>::iterator it;
		for (it= m_components.begin(); it!= m_components.end(); ++it)
			(*it)->Finish();
	}

private:
	std::vector<IComponent *> m_components;
};

/**
	@brief Interface class for factories of components.
*/
class IComponentFactory : public IBaseObject {
protected:
	virtual ~IComponentFactory() {}

public:
	/**
		@brief Return the name of the type of components this factory creates.
		@return Pointer to an internally managed string.
	*/
	virtual const char* GetName() const = 0;

	/**
		@brief Create a new component instance.
		@param name Name the component will be given.
		@param argc Number of arguments passed to the parameter argv.
		@param argv Array of parameters (can be NULL if argc==0).
		@return Smart pointer to the newly created component which can contain NULL if an error ocurred.
	*/
	virtual SmartPtr<IComponent> CreateInstance(const char * name, int argc, const char * argv[]) = 0;
};

/**
	@brief Connects an output pin of a component with an input of another component.
	@param srcComponent Source component.
	@param srcPin Number of order of the output of the source component.
	@param dstComponent Destination component.
	@param dstPin Number of order of the input of the destination component.
	@return 0 when success
	@return -1 invalid component
	@return -2 trying to connect the component with itself
	@return -3 invalid pin number
	@return -4 pin type mismatch

	Uses pin number to refer to a specific pin
*/
inline int Connect(IComponent * srcComponent, unsigned int srcPin, IComponent * dstComponent, unsigned int dstPin) {
  	if (srcComponent== NULL || dstComponent== NULL) return -1;	// Invalid component
  	if (srcComponent== dstComponent) return -2;	// Forbids connecting pins of the same component

  	unsigned int i;
	SmartPtr<IIterator<IOutputPin*> > itop= srcComponent->GetOutputPins();
  	itop->First();
  	i= 0;
  	while (i!= srcPin && !itop->IsDone()) {
  		itop->Next();
  		++i;
  	}
  	if (itop->IsDone()) return -3;	// Invalid pin number

  	SmartPtr<IIterator<IInputPin*> > itip= dstComponent->GetInputPins();
  	itip->First();
  	i= 0;
  	while (i!=dstPin && !itip->IsDone()) {
  		itip->Next();
  		++i;
  	}
  	if (itip->IsDone()) return -3; // Invalid pin number

  	if (itop->CurrentItem()->Connect (*(itip->CurrentItem()))!= 0) return -4; // Pin type mismatch

  	return 0;
}

/**
	@brief Connects an output pin of a component with an input of another component.
	@param srcComponent Source component.
	@param srcPin Namne of the output of the source component.
	@param dstComponent Destination component.
	@param dstPin Name of the input of the destination component.
	@return 0 when success
	@return -1 invalid component
	@return -2 trying to connect the component with itself
	@return -3 invalid pin number
	@return -4 pin type mismatch
*/
inline int Connect(IComponent * srcComponent, const char * srcPin, IComponent * dstComponent, const char * dstPin) {
  	if (srcComponent== NULL || dstComponent== NULL) return -1;	// Invalid component
  	if (srcComponent== dstComponent) return -2;	// Forbids connecting pins of the same component
	if (srcPin== NULL || dstPin== NULL) return -3;	// Invalid pin name

  	SmartPtr<IIterator<IOutputPin*> > itop= srcComponent->GetOutputPins();
	for (itop->First(); !itop->IsDone(); itop->Next()) {
		if (strcmp(srcPin, itop->CurrentItem()->GetName())== 0) break;
	}
	if (itop->IsDone()) return -3;	// Invalid pin name

  	SmartPtr<IIterator<IInputPin*> > itip= dstComponent->GetInputPins();
  	for (itip->First(); !itip->IsDone(); itip->Next()) {
		if (strcmp(dstPin, itip->CurrentItem()->GetName())== 0) break;
	}
  	if (itip->IsDone()) return -3; // Invalid pin name

  	if (itop->CurrentItem()->Connect (*(itip->CurrentItem()))!= 0) return -4; // Pin type mismatch

  	return 0;
}

/**
	@brief Helper template class to create simple component factories.

	@tparam COTYPE Type of the component.
*/
template <typename COTYPE>
class ComponentFactory : public spcore::IComponentFactory {
	BOOST_STATIC_ASSERT( (boost::is_base_of<IComponent,COTYPE>::value) );

public:
	virtual const char* GetName() const { return COTYPE::getTypeName(); }

	virtual SmartPtr<IComponent> CreateInstance(const char * name, int argc, const char * argv[]) {
		std::string exceptionMessage;
		try {
			return SmartPtr<IComponent>(new COTYPE(name, argc, argv), false);
		}
		catch(std::exception& e) {
			exceptionMessage= e.what();
		}
		catch(...) {
			exceptionMessage= "unexpected error creating component: " + std::string(name);
		}
		// If code reaches this point means that an exception has been raised
		// signal this error condition by adding a log entry and retuning a
		// null instance
		std::string msg("error creating instance:");
		msg.append(name);
		if (exceptionMessage.size()> 0) {
			msg.append (":");
			msg.append (exceptionMessage);
		}
		getSpCoreRuntime()->LogMessage(ICoreRuntime::LOG_ERROR, msg.c_str(), "spcore");
		return SmartPtr<IComponent>(NULL);
	}
};

/**
	@brief Helper template to create simple singleton component factories.

	@tparam COTYPE Type of the component.
*/
template <typename COTYPE>
class SingletonComponentFactory : public spcore::IComponentFactory {
	BOOST_STATIC_ASSERT( (boost::is_base_of<IComponent,COTYPE>::value) );

public:
	virtual const char* GetName() const { return COTYPE::getTypeName(); }

	virtual SmartPtr<IComponent> CreateInstance(const char * name, int argc, const char * argv[]) {
		if (m_instance.get()) return m_instance;

		// Crete instance for the first time
		std::string exceptionMessage;
		try {
			m_instance= SmartPtr<COTYPE>(new COTYPE(name, argc, argv), false);
			return m_instance;
		}
		catch(std::exception& e) {
			exceptionMessage= e.what();
		}
		catch(...) {
			exceptionMessage= "unexpected error creating component: " + std::string(name);
		}
		// If code reaches this point means that an exception has been raised
		// signal this error condition by adding a log entry and retuning a
		// null instance
		assert (m_instance.get()== NULL);
		std::string msg("error creating instance:");
		msg.append(name);
		if (exceptionMessage.size()> 0) {
			msg.append (":");
			msg.append (exceptionMessage);
		}
		getSpCoreRuntime()->LogMessage(ICoreRuntime::LOG_ERROR, msg.c_str(), "spcore");
		return SmartPtr<IComponent>(NULL);
	}
private:
	SmartPtr<COTYPE> m_instance;
};

} // namespace spcore
#endif
