/////////////////////////////////////////////////////////////////////////////
// File:        commoncomponents.cpp
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Copyright:   (C) 2010 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 COMMONCOMPONENTS_H
	#define COMMONCOMPONENTS_H
#else
	#error This file is intended to be included only once
#endif

#include "spcore/basictypes.h"
#include "spcore/pinimpl.h"
#include "spcore/component.h"
#include "spcore/conversion.h"
#include <vector>
#include <string>
#include <stdio.h>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>

using namespace std;

namespace spcore {

/**
	split component

	Send each child of the input type through each output pin.

	Input pins:
		input (CTypeAny)

	Output pins:
		one or more of type CTypeAny named with a number starting from 1

	Command line:
		[-o <num>] (= 1) number of outputs (from 1 to 100)
*/

class Split : public CComponentAdapter {
public:
	static const char* getTypeName() { return "split"; }
	virtual const char* GetTypeName() const { return Split::getTypeName(); }
    Split(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv) {
		// Input pins
  		if (RegisterInputPin(*SmartPtr<InputPinData>(new InputPinData("input", *this), false))!= 0)
			throw std::runtime_error("error creating input pin");

		// Parses command line
		int output_count= 1;
		if (argc) {
			int i= 0;
			for (; i< argc; ++i) if (strcmp("-o", argv[i])== 0) break;
			if (i< argc) {
				++i;
				if (i>= argc) throw std::runtime_error("Missing value for parameter -o");

				if (!StrToInt(argv[i], &output_count) || output_count< 1 || output_count> 100)
					throw std::runtime_error("childs component: invalid value for parameter -o");
			}
		}

		// Create output pins and permanent instances
		for (int i= 0; i< output_count; ++i) {
			// Output pins
			char buff[10];
			sprintf (buff, "%d", i+1);
			SmartPtr<IOutputPin> pin= CTypeAny::CreateOutputPinAny(buff);
			if (pin.get()== NULL)
				throw std::runtime_error("error creating output pin");

			if (RegisterOutputPin(*pin)!= 0)
				throw std::runtime_error("error registering output pin");

			// Permanent instances
			m_outputInstances.push_back(SmartPtr<CTypeAny>());
		}
	}

	void ForwardChildren (const CTypeAny & message) {

		SmartPtr<IIterator<CTypeAny*> > it_children= message.QueryChildren();
		if (it_children.get()== NULL) return;

		SmartPtr<IIterator<IOutputPin*> > it_opins= this->GetOutputPins();
		std::vector<SmartPtr<CTypeAny> >::iterator it_instances= m_outputInstances.begin();

		while (!it_children->IsDone() && !it_opins->IsDone() && it_instances!= m_outputInstances.end()) {
			// Copy instances
			(*it_instances)= it_children->CurrentItem()->Clone((*it_instances).get(), true);

			// Send
			it_opins->CurrentItem()->Send((*it_instances));

			// Advance iterators
			it_children->Next();
			it_opins->Next();
			++it_instances;
		}
	}

private:
	virtual ~Split() {}

	class InputPinData : public CInputPinWriteOnly<CTypeAny, Split > {
	public:
		InputPinData (const char * name, Split & component)
		: CInputPinWriteOnly<CTypeAny, Split >(name, component) {}

		virtual int DoSend(const CTypeAny & message) {
			this->m_component->ForwardChildren (message);
			return 0;
		}
	};

	std::vector<SmartPtr<CTypeAny> > m_outputInstances;
};

typedef ComponentFactory<Split> SplitFactory;

/**
fthreshold component - Threshold component

	Sends a value to the output pin taking into account if the input
	value is below or above the threshold.		

Input pins:
	value (CTypeFloat)
	thres (CTypeFloat)

Output pins:
	result (CTypeFloat)

Command line:
	[-a (orig|<value>)]	(default 1)
	
		Above value. Sets the value which will be sent to the output
		when the input value is above the threshold. 
		"orig" sends the input value as is.
		"orig_minus_thres" sends the input value minus the threshold
	
	[-b (orig|<value>)]	(default 0)

		Below value. Sets the value which will be sent to the output
		when the input value is below the threshold. 
		"orig" sends the input value as is.
		"orig_minus_thres" sends the input value minus the threshold

	[-t <value>] (default 0)

		Set threshold.
*/

class FThreshold : public CComponentAdapter {
public:
	enum EThresholdMode { FIXED= 0, ORIG, ORIG_MINUS_THRES };

	static const char* getTypeName() { return "fthreshold"; }
	virtual const char* GetTypeName() const { return FThreshold::getTypeName(); }
    FThreshold(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv)
	, m_threshold(0)
	, m_belowMode(FIXED)
	, m_aboveMode(FIXED)
	, m_belowValue(0)
	, m_aboveValue(1.0f)
	{
		// Pins
  		RegisterInputPin(*SmartPtr<IInputPin>(new InputPinValue("value", *this), false));
		RegisterInputPin(*SmartPtr<IInputPin>(new InputPinThreshold("thres", *this), false));
		m_oPin= CTypeFloat::CreateOutputPin("result");
		RegisterOutputPin(*m_oPin);

		// Result value
		m_result= CTypeFloat::CreateInstance();

		// Parse command line
		if (argc) {
			for (int i= 0; i< argc; ++i) {
				if (strcmp ("-t", argv[i])== 0) {
					++i;
					if (i== argc || !StrToFloat(argv[i], &m_threshold))
						throw std::runtime_error(string(FThreshold::getTypeName()) + ". Wrong value for option -t");
				}
				else if (strcmp ("-a", argv[i])== 0) {
					++i;
					bool err= false;
					if (i== argc) err= true;
					else {
						if (strcmp("orig", argv[i])== 0) 
							m_aboveMode= ORIG;
						else if (strcmp("orig_minus_thres", argv[i])== 0) 
							m_aboveMode= ORIG_MINUS_THRES;
						else if (!StrToFloat(argv[i], &m_aboveValue)) 
							err= true;
					}
					if (err) 
						throw std::runtime_error(string(FThreshold::getTypeName()) + ". Wrong value for option -a");
				}
				else if (strcmp ("-b", argv[i])== 0) {
					++i;
					bool err= false;
					if (i== argc) err= true;
					else {
						if (strcmp("orig", argv[i])== 0) 
							m_belowMode= ORIG;
						else if (strcmp("orig_minus_thres", argv[i])== 0) 
							m_belowMode= ORIG_MINUS_THRES;
						else if (!StrToFloat(argv[i], &m_belowValue)) 
							err= true;
					}
					if (err) 
						throw std::runtime_error(string(FThreshold::getTypeName()) + ". Wrong value for option -b");
				}
				else if (strlen(argv[i]))
					throw std::runtime_error(string(FThreshold::getTypeName()) + ". Unknown option.");
			}
		}
	}

	void OnValue (const CTypeFloat & msg) {
		float val= msg.getValue();

		if (val< m_threshold) {
			switch (m_belowMode) {
				case FIXED: 
					m_result->setValue(m_belowValue); 
					break;
				case ORIG:
					m_result->setValue(val);
					break;
				case ORIG_MINUS_THRES:
					m_result->setValue(val - m_threshold);
					break;
				default:
					assert (false);
			}
		}
		else {
			switch (m_aboveMode) {
				case FIXED: 
					m_result->setValue(m_aboveValue); 
					break;
				case ORIG:
					m_result->setValue(val);
					break;
				case ORIG_MINUS_THRES:
					m_result->setValue(val - m_threshold);
					break;
				default:
					assert (false);
			}
		}

		m_oPin->Send(m_result);
	}

	void OnThreshold (const CTypeFloat & msg) {
		m_threshold= msg.getValue();	
	}

private:
	virtual ~FThreshold() {}

	class InputPinValue : public CInputPinWriteOnly<CTypeFloat, FThreshold > {
	public:
		InputPinValue (const char * name, FThreshold & component)
		: CInputPinWriteOnly<CTypeFloat, FThreshold >(name, component) {}

		virtual int DoSend(const CTypeFloat & message) {
			this->m_component->OnValue (message);
			return 0;
		}
	};

	class InputPinThreshold : public CInputPinWriteOnly<CTypeFloat, FThreshold > {
	public:
		InputPinThreshold (const char * name, FThreshold & component)
		: CInputPinWriteOnly<CTypeFloat, FThreshold >(name, component) {}

		virtual int DoSend(const CTypeFloat & message) {
			this->m_component->OnThreshold (message);
			return 0;
		}
	};

	float m_threshold;
	EThresholdMode m_belowMode;
	EThresholdMode m_aboveMode;
	float m_belowValue;
	float m_aboveValue;

	SmartPtr<IOutputPin> m_oPin;
	SmartPtr<CTypeFloat> m_result;
};

typedef ComponentFactory<FThreshold> FThresholdFactory;

/**
	faccum component - Accumulator component

		Accumulates the value received in the pin 'val' and sends
		the result to the 'result' pin.

	Input pins:
		val (CTypeFloat)

	Output pins:
		result (CTypeFloat)

	Command line:
		[--min <num>]	(= 0) minimum limit
		[--max <num>]	(= 1) maximum limit
		[-w]			(= false) wrap value
*/
class FAccumulator : public CComponentAdapter {
public:
	static const char* getTypeName() { return "faccum"; }
	virtual const char* GetTypeName() const { return FAccumulator::getTypeName(); }
    FAccumulator(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv)
	, m_wrap(false)
	, m_offset(0)
	, m_max(1.0f)
	, m_current(0)	
	{
		// Input pins
  		if (RegisterInputPin(*SmartPtr<InputPinVal>(new InputPinVal("val", *this), false))!= 0)
			throw std::runtime_error("error creating input pin");		

		m_oPin= CTypeFloat::CreateOutputPin("result");
		if (RegisterOutputPin(*m_oPin)!= 0)
			throw std::runtime_error("error registering output pin");

		m_result= CTypeFloat::CreateInstance();

		float min= 0, max= 1.0f;

		// Parse command line
		if (argc) {
			for (int i= 0; i< argc; ++i) {
				if (strcmp ("--min", argv[i])== 0) {
					// Min
					++i;
					if (i== argc || !StrToFloat(argv[i], &min))
						throw std::runtime_error("flimit. Wrong value for option --min");
				}
				else if (strcmp ("--max", argv[i])== 0) {
					// Max
					++i;
					if (i== argc || !StrToFloat(argv[i], &max))
						throw std::runtime_error("flimit. Wrong value for option --max");
				}
				else if (strcmp ("-w", argv[i])== 0) {
					// Wrap value
					m_wrap= true;					
				}
				else if (strlen(argv[i]))
					throw std::runtime_error("flimit. Unknown option.");
			}
		}

		if (min>= max)
			throw std::runtime_error("flimit. min cannot be greater or equal than max");

		// Compute interval range values. All operations are 0 based and then an offset is applied
		m_offset= min;
		m_max= max - min;
	}

	void OnValue (const CTypeFloat & msg) {
		m_current+= msg.getValue();

		if (m_wrap) {
			if (m_current< 0) m_current= m_max + fmodf(m_current, m_max);
			else if (m_current> m_max) m_current= fmodf(m_current, m_max);
		}
		else {
			if (m_current< 0) m_current= 0;
			else if (m_current> m_max) m_current= m_max;
		}

		assert (m_current>= 0 && m_current<= m_max);

		m_result->setValue(m_current + m_offset);
		m_oPin->Send(m_result);
	}

private:
	virtual ~FAccumulator() {}

	class InputPinVal : public CInputPinWriteOnly<CTypeFloat, FAccumulator > {
	public:
		InputPinVal (const char * name, FAccumulator & component)
		: CInputPinWriteOnly<CTypeFloat, FAccumulator >(name, component) {}

		virtual int DoSend(const CTypeFloat & message) {
			this->m_component->OnValue (message);
			return 0;
		}
	};

	bool m_wrap;
	float m_offset;
	float m_max;
	float m_current;
	SmartPtr<IOutputPin> m_oPin;
	SmartPtr<CTypeFloat> m_result;
};

typedef ComponentFactory<FAccumulator> FAccumulatorFactory;




/**
	flimit component - Limit component (float)

		Limits the value within a specific range

	Input pins:
		in (CTypeFloat)

	Output pins:
		out (CTypeFloat)

	Command line:
		[--min <num>] (= 0) minimum limit
		[--max <num>] (= 1) maximum limit
*/
class FLimit : public CComponentAdapter {
public:
	static const char* getTypeName() { return "flimit"; }
	virtual const char* GetTypeName() const { return FLimit::getTypeName(); }
    FLimit(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv) 
	, m_min(0)
	, m_max(1.0f) 
	{
		m_opin= CTypeFloat::CreateOutputPinAny("out");
		if (RegisterOutputPin(*m_opin)!= 0)
			throw std::runtime_error("error registering output pin");

  		if (RegisterInputPin(*SmartPtr<IInputPin>(new InputPinVal("in", *this), false))!= 0)
			throw std::runtime_error("error creating input pin");
		
		m_result= CTypeFloat::CreateInstance();

		// Parse command line
		if (argc) {
			for (int i= 0; i< argc; ++i) {
				if (strcmp ("--min", argv[i])== 0) {
					// Min
					++i;
					if (i== argc || !StrToFloat(argv[i], &m_min))
						throw std::runtime_error("flimit. Wrong value for option --min");
				}
				else if (strcmp ("--max", argv[i])== 0) {
					// Max
					++i;
					if (i== argc || !StrToFloat(argv[i], &m_max))
						throw std::runtime_error("flimit. Wrong value for option --max");
				}
				else if (strlen(argv[i]))
					throw std::runtime_error("flimit. Unknown option.");
			}
		}

		if (m_min> m_max)
			throw std::runtime_error("flimit. min cannot be greater than max");
	}

	int OnValue(const CTypeFloat & message) {
		float v= message.getValue();
		if (v> m_max) v= m_max;
		else if (v< m_min) v= m_min;
		m_result->setValue (v);
		return m_opin->Send (m_result);
	}

private:
	virtual ~FLimit() {}

	class InputPinVal : public CInputPinWriteOnly<CTypeFloat, FLimit > {
	public:
		InputPinVal (const char * name, FLimit & component)
		: CInputPinWriteOnly<CTypeFloat, FLimit>(name, component) {}

		virtual int DoSend(const CTypeFloat & message) {
			return this->m_component->OnValue (message);			
		}
	};

	float m_min;
	float m_max;
	SmartPtr<CTypeFloat> m_result;
	SmartPtr<IOutputPin> m_opin;
};

typedef ComponentFactory<FLimit> FLimitFactory;

/*!
	forward component - Forward component

		Forwards a message comming from the input pin to the output pin		

	Input pins:
		in (any)
		gate (bool)

	Output pins:
		out (any)
*/
class ForwardComponent : public CComponentAdapter {
public:
	static const char* getTypeName() { return "forward"; }
	virtual const char* GetTypeName() const { return ForwardComponent::getTypeName(); }
    ForwardComponent(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv) {
		
		m_opin= CTypeAny::CreateOutputPinAny("out");
		if (RegisterOutputPin(*m_opin)!= 0)
			throw std::runtime_error("error registering output pin");

		SmartPtr<InputPinIn> pinIn= SmartPtr<InputPinIn>(new InputPinIn("in", *m_opin), false);

  		if (RegisterInputPin(*pinIn)!= 0)
			throw std::runtime_error("error creating input pin");

		if (RegisterInputPin(*SmartPtr<IInputPin>(new InputPinGate("gate", *pinIn), false))!= 0)
			throw std::runtime_error("error creating input pin");
	}

private:
	virtual ~ForwardComponent() {}

	class InputPinIn : public CInputPinWriteOnly<CTypeAny, IOutputPin> {
	public:
		InputPinIn (const char * name, IOutputPin & component)
		: CInputPinWriteOnly<CTypeAny, IOutputPin>(name, component)
		, m_gate(true)
		{
		}

		virtual int DoSend(const CTypeAny & message) {
			if (m_gate)
				return this->m_component->Send (SmartPtr<const CTypeAny>(&message));
			else
				return 0;
		}

		bool m_gate;
	};

	class InputPinGate : public CInputPinWriteOnly<CTypeBool, InputPinIn> {
	public:
		InputPinGate (const char * name, InputPinIn & component)
		: CInputPinWriteOnly<CTypeBool, InputPinIn>(name, component) 
		{

		}

		virtual int DoSend(const CTypeBool & message) {
			return this->m_component->m_gate= message.getValue();
		}
	};

	SmartPtr<IOutputPin> m_opin;
};

typedef ComponentFactory<ForwardComponent> ForwardComponentFactory;

/**
	freductor component - Reductor component (float)
		
		Allows to reduce the number of message of type float to a half, 
		a third, etc. according to the supplied parameter. 
		Intermediate values are  accumulated or averaged.

	Input pins:
		in (CTypeAny)

	Output pins:
		out (CTypeAny)

	Command line
		[- r <val> ] (= 1)	reduction (integer)
		[-a ] average values. if this flag is not present, values accumulated
*/
class FReductor : public CComponentAdapter {
public:
	static const char* getTypeName() { return "freductor"; }
	virtual const char* GetTypeName() const { return FReductor::getTypeName(); }
	FReductor(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv)
	, m_average(false)
	, m_reduction(1)
	, m_freduction(1.0f)
	, m_msgCount(0)
	{
		m_opin= CTypeFloat::CreateOutputPin("out");
		if (RegisterOutputPin(*m_opin)!= 0)
			throw std::runtime_error("error registering output pin");

  		if (RegisterInputPin(*SmartPtr<IInputPin>(new InputPinIn("in", *this), false))!= 0)
			throw std::runtime_error("error creating input pin");

		// Parse command line
		if (argc) {
			for (int i= 0; i< argc; ++i) {
				if (strcmp ("-r", argv[i])== 0) {
					++i;
					if (i== argc || !StrToUint(argv[i], &m_reduction) || !m_reduction)
						throw std::runtime_error("freductor. Wrong value for option -r");
					
					m_freduction= static_cast<float>(m_reduction);
				}
				else if (strcmp ("-a", argv[i])== 0) m_average= true;
				else if (strlen(argv[i]))
					throw std::runtime_error("flimit. Unknown option.");
			}
		}

		m_result= CTypeFloat::CreateInstance();
	}

	int OnMessage (const CTypeFloat & message) {
		if (!m_msgCount++) m_accum= message.getValue();
		else m_accum+= message.getValue();

		if (m_msgCount== m_reduction) {
			if (m_average) m_accum/= m_freduction;
			m_result->setValue(m_accum);
			m_msgCount= 0;
			return this->m_opin->Send (m_result);
		}
		return 0;
	}

private:
	virtual ~FReductor() {}

	class InputPinIn : public CInputPinWriteOnly<CTypeFloat, FReductor> {
	public:
		InputPinIn (const char * name, FReductor & component)
		: CInputPinWriteOnly<CTypeFloat, FReductor>(name, component) 
		{
		}

		virtual int DoSend(const CTypeFloat & message) {
			return this->m_component->OnMessage(message);
		}
	};
	bool m_average;
	unsigned int m_reduction;
	float m_freduction;
	float m_accum;
	unsigned int m_msgCount;
	SmartPtr<IOutputPin> m_opin;
	SmartPtr<CTypeFloat> m_result;

};

typedef ComponentFactory<FReductor> FReductorFactory;


/**
	send_main_sync

		Sends a message synchronously thorugh the main thread

	Input pins:
		in (CTypeAny)

	Output pins:
		out (CTypeAny)
*/
static void CallbackSync (IComponent* compo, const CTypeAny* msg);

class SendMainSync : public CComponentAdapter {
public:
	static const char* getTypeName() { return "send_main_sync"; }
	virtual const char* GetTypeName() const { return SendMainSync::getTypeName(); }

    SendMainSync(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv)
	, m_threadWaiting(false)
	, m_running(false)
	, m_result(-1)
	{
		// Output pin
		m_oPin= CTypeAny::CreateOutputPinAny("out");			
		if (RegisterOutputPin(*m_oPin)!= 0)
			throw std::runtime_error("error registering output pin");
		
		// Input pin
  		if (RegisterInputPin(*SmartPtr<IInputPin>(new InputPinAny("in", *this), false))!= 0)
			throw std::runtime_error("error creating input pin");

		m_cr= getSpCoreRuntime();
	}

	virtual bool ProvidesExecThread() const { return true; }

	virtual int DoStart() {
		assert (m_cr->IsMainThread());

		boost::mutex::scoped_lock (m_mutex);

		m_running= true;
		return 0;
	}
   
	virtual void DoStop() {
		assert (m_cr->IsMainThread());

		boost::mutex::scoped_lock lock(m_mutex);

		if (m_running) {
			m_running= false;
			if (m_threadWaiting) {
				// Secondary thread is waiting so receiver has not 
				// processed the message yet => awake it
				m_condition.notify_one();
			}
		}
	}

	int OnSendReceived (const CTypeAny& msg) {
		// We don't expect messages comming from the main thread, but if someone
		// has connected this component when not is needed, just forward the message
		assert (!m_cr->IsMainThread());
		if (m_cr->IsMainThread()) return m_oPin->Send (SmartPtr<const CTypeAny>(&msg));
	
		boost::mutex::scoped_lock lock(m_mutex);

		if (!m_running) return 0;

		// If there are pending messages means that someone
		// connected more than one ouput to our input pin
		assert (!m_threadWaiting);
		if (m_threadWaiting) return -1;

		m_threadWaiting= true;
		m_cr->SendMessageMainThreadAsync (msg, *this, CallbackSync);
		
		// Wait until request is completed		
		m_condition.wait(lock);

		m_threadWaiting= false;

		return m_result;		
	}

	void OnCallbackReceived(const CTypeAny& msg) {
		assert (m_cr->IsMainThread());
		
		boost::mutex::scoped_lock lock(m_mutex);

		if (m_threadWaiting) {
			if (m_running) 
				m_result= m_oPin->Send (SmartPtr<const CTypeAny>(&msg));
			else
				m_result= -1;
			m_condition.notify_one();
		}
		else {
			m_result= -1;
#ifndef NDEBUG		
			m_cr->LogMessage (ICoreRuntime::LOG_DEBUG, "message discarded", SendMainSync::getTypeName());
#endif
		}
	}

private:
	bool m_threadWaiting;
	bool m_running;
	int m_result;
	boost::mutex m_mutex;
	boost::condition m_condition;	
	SmartPtr<IOutputPin> m_oPin;
	ICoreRuntime* m_cr;
	
	virtual ~SendMainSync() {
		// Before destructing this object must have been stopped
		assert (!m_running);

		// Finished receiving input pin calls
		assert (!m_threadWaiting);
		
		//	Stop();
	}

	class InputPinAny : public CInputPinWriteOnly<CTypeAny, SendMainSync > {
	public:
		InputPinAny (const char * name, SendMainSync & component)
		: CInputPinWriteOnly<CTypeAny, SendMainSync >(name, component) {}

		virtual int DoSend(const CTypeAny & msg) {
			return  m_component->OnSendReceived (msg);			
		}	
	};
};

typedef ComponentFactory<SendMainSync> SendMainSyncFactory;

static void CallbackSync (IComponent* compo, const CTypeAny* msg)
{
	static_cast<SendMainSync*>(compo)->OnCallbackReceived(*msg);	
}


/**
	send_main_async

		Sends a message asynchronously thorugh the main thread

	Input pins:
		in (CTypeAny)

	Output pins:
		out (CTypeAny)
*/
static void CallbackAsync (IComponent* compo, const CTypeAny* msg);

class SendMainAsync : public CComponentAdapter {
public:
	static const char* getTypeName() { return "send_main_async"; }
	virtual const char* GetTypeName() const { return SendMainAsync::getTypeName(); }

    SendMainAsync(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv)
	, m_pendingMessage(false)
	, m_running(false)
	{
		// Output pin
		m_oPin= CTypeAny::CreateOutputPinAny("out");			
		if (RegisterOutputPin(*m_oPin)!= 0)
			throw std::runtime_error("error registering output pin");
		
		// Input pin
  		if (RegisterInputPin(*SmartPtr<IInputPin>(new InputPinAny("in", *this), false))!= 0)
			throw std::runtime_error("error creating input pin");

		m_cr= getSpCoreRuntime();
	}

	virtual bool ProvidesExecThread() const { return true; }

	virtual int DoStart() {
		m_running= true;
		return 0;
	}
   
	virtual void DoStop() { m_running= false; }

	int OnSendReceived (const CTypeAny& msg) {
		// If no listeners, do nothing
		if (m_oPin->GetNumComsumers()== 0) return 0;

		// We don't expect messages comming from the main thread, but if someone
		// has connected this component when not is needed, just forward the message
		assert (!m_cr->IsMainThread());
		if (m_cr->IsMainThread()) return m_oPin->Send (SmartPtr<const CTypeAny>(&msg));
		
		if (!m_running) return 0;

		{
			boost::mutex::scoped_lock (m_mutex);
			// Message pending, discard current one
			if (m_pendingMessage) {
#ifndef NDEBUG
				m_cr->LogMessage (ICoreRuntime::LOG_DEBUG, "message discarded", SendMainAsync::getTypeName());
#endif
				return 0;
			}
			m_pendingMessage= true;
		}

		//
		// TODO: embed the information about if the type follows the "fire-and-forget"
		// approach or not. Currently it checks the know types by name.
		//
		// Currently instances of:
		//	CTypeInt
		//	CTypeFloat
		//	CTypeBool
		//	CtypeString
		//	CTypeROI
		//
		// need to be copied. Instances of:
		//
		// CTypeIplImage
		//
		// need not to be copied, and instances of
		//
		// CTypeSDLSurface are not supported (in fact Clone method will fail)	 
		//
		static int ipl_img_id= m_cr->ResolveTypeID("iplimage");
		if (msg.GetTypeID()== ipl_img_id) 
			// img, Just send
			m_cr->SendMessageMainThreadAsync (msg, *this, CallbackAsync);
		else {
			// Copy and send
			m_message= msg.Clone(m_message.get(), true);
			m_cr->SendMessageMainThreadAsync (*m_message, *this, CallbackAsync);
		}
		return 0;
	}

	void OnCallbackReceived(const CTypeAny& msg) {
		//assert (msg->GetTypeID()== CTypeIplImage::getTypeID());
		assert (m_cr->IsMainThread());
		if (m_running) m_oPin->Send (SmartPtr<const CTypeAny>(&msg));
		m_pendingMessage= false;
	}

private:
	bool m_pendingMessage;
	bool m_running;
	boost::mutex m_mutex;
	SmartPtr<IOutputPin> m_oPin;
	ICoreRuntime* m_cr;
	SmartPtr<CTypeAny> m_message;
	
	virtual ~SendMainAsync() {
		Stop();
	}

	class InputPinAny : public CInputPinWriteOnly<CTypeAny, SendMainAsync > {
	public:
		InputPinAny (const char * name, SendMainAsync & component)
		: CInputPinWriteOnly<CTypeAny, SendMainAsync >(name, component) {}

		virtual int DoSend(const CTypeAny & msg) {
			return  m_component->OnSendReceived (msg);			
		}	
	};
};

typedef ComponentFactory<SendMainAsync> SendMainAsyncFactory;

static void CallbackAsync (IComponent* compo, const CTypeAny* msg)
{
	static_cast<SendMainAsync*>(compo)->OnCallbackReceived(*msg);	
}


/**
	print component - Dumps the contents of the type instance
		
	Input pins:
		in (CTypeAny)

	Command line
*/
class PrintComponent : public CComponentAdapter {
public:
	static const char* getTypeName() { return "print"; }
	virtual const char* GetTypeName() const { return PrintComponent::getTypeName(); }
	PrintComponent(const char * name, int argc, const char * argv[]) 
	: CComponentAdapter(name, argc, argv)	
	{
		if (RegisterInputPin(*SmartPtr<IInputPin>(new InputPinIn("in", *this), false))!= 0)
			throw std::runtime_error("error creating input pin");

		// Dump arguments
		if (argc) {
			stringstream ss;
			ss << "Arguments dump. argc: " << argc << "\t";
			for (int i= 0; i< argc; ++i) {
				ss << "argv[" << i << "]: \"" << argv[i] << "\" ";
			}
			getSpCoreRuntime()->LogMessage(ICoreRuntime::LOG_INFO, ss.str().c_str(), name);
		}
	}

private:
	virtual ~PrintComponent() {}

	class InputPinIn : public CInputPinWriteOnly<CTypeAny, PrintComponent> {
	public:
		InputPinIn (const char * name, PrintComponent & component)
		: CInputPinWriteOnly<CTypeAny, PrintComponent>(name, component) 
		{
		}

		void PrintInt (ostream& s, int val)
		{
			s << "\tint: " << val;
		}

		void PrintBool (ostream& s, bool val)
		{
			s << "\tbool: " << val;
		}

		void PrintFloat (ostream& s, float val)
		{
			s << "\tfloat: " << val;
		}

		void PrintString (ostream& s, const char* val)
		{
			s << "\tstring: " << val;
		}

		void PrintInstance (ostream& s, const CTypeAny & val)
		{
			int tid= val.GetTypeID();
			if (tid== CTypeFloat::getTypeID()) 
				PrintFloat(s, sptype_static_cast<CTypeFloat>(&val)->getValue());
			else if (tid== CTypeInt::getTypeID()) 
				PrintInt(s, sptype_static_cast<CTypeInt>(&val)->getValue());
			else if (tid== CTypeBool::getTypeID()) 
				PrintBool(s, sptype_static_cast<CTypeBool>(&val)->getValue());
			else if (tid== CTypeString::getTypeID()) 
				PrintString(s, sptype_static_cast<CTypeString>(&val)->get());
			else 
				s << "\tnon-printable:" << val.GetTypeID();

			// Recurse children
			SmartPtr<IIterator<CTypeAny *> > it= val.QueryChildren();
			if (it.get()) {
				s << "composite {";
				for (; !it->IsDone(); it->Next()) {
					PrintInstance (s, *(it->CurrentItem()));
					s << ", ";
				}
				s << "}";
			}
		}


		virtual int DoSend(const CTypeAny & message) {
			stringstream ss;

			PrintInstance (ss, message);

			getSpCoreRuntime()->LogMessage(ICoreRuntime::LOG_INFO, ss.str().c_str(), m_component->GetName());

			return 0;
		}
	};
};

typedef ComponentFactory<PrintComponent> PrintComponentFactory;

} // namespace spcore
