#include "artsbuilder.h"
#include "artsflow.h"
#include "connect.h"
#include "debug.h"
#include "flowsystem.h"
#include "stdsynthmodule.h"
#include "dynamicrequest.h"
#include <list>

#undef STRUCTURE_DEBUG

using namespace Arts;
using namespace std;

class StructureBuilder_impl : virtual public StructureBuilder_skel {
protected:
	list<ObjectFactory> factories;
public:
	void addFactory(ObjectFactory factory);
	Object createObject(StructureDesc structure);
};

REGISTER_IMPLEMENTATION(StructureBuilder_impl);

class Structure_impl : virtual public SynthModule_skel,
                       virtual public StdSynthModule {
protected:
	list<Object> structureObjects;

public:
	Structure_impl(StructureDesc structure, list<ObjectFactory>& factories);
	void streamInit();
	void streamEnd();
};

void StructureBuilder_impl::addFactory(ObjectFactory factory)
{
	factories.push_back(factory);
}

Object StructureBuilder_impl::createObject(StructureDesc structure)
{
	return Object::_from_base(new Structure_impl(structure, factories));
}

Structure_impl::Structure_impl(StructureDesc structureDesc,
									list<ObjectFactory>& factories)
{
	map<long, Object> moduleMap;
	vector<ModuleDesc> *modules = structureDesc.modules();
	vector<ModuleDesc>::iterator mi;

	// create each object
	for(mi = modules->begin(); mi != modules->end(); mi++)
	{
		ModuleDesc& md = *mi;

#ifdef STRUCTBUILDER_DEBUG
		cout << "create " << md.name() << endl;
#endif
		Object o = Object::null(); //SubClass(md.name());

		Object_skel *skel = 0;
		skel = ObjectManager::the()->create(md.name());
		if(skel) o = Object::_from_base(skel);

#ifdef STRUCTBUILDER_DEBUG
		if(o.isNull()) cout << "no local creator for " << md.name() << endl;
#endif
		list<ObjectFactory>::iterator fi = factories.begin();
		while(o.isNull() && fi != factories.end())
		{
			o = fi->createObject(md.name());
			fi++;
		}

#ifdef STRUCTBUILDER_DEBUG
		if(o.isNull()) cout << "no remote creator for " << md.name() << endl;
#endif
		assert(!o.isNull());
		moduleMap[md.ID()] = o;
		structureObjects.push_back(o);
	}

	// connect objects and set values
	for(mi = modules->begin(); mi != modules->end(); mi++)
	{
		Object& object = moduleMap[mi->ID()];

		vector<PortDesc> *ports = mi->ports();
		vector<PortDesc>::iterator pi;

		for(pi = ports->begin(); pi != ports->end(); pi++)
		{
			PortDesc& pd = *pi;
			const Arts::PortType& ptype = pd.type();

			if(pd.hasValue())
			{
				// set values
#ifdef STRUCTBUILDER_DEBUG
				cout << "value " << mi->name() << "." << pi->name() << endl;
#endif

				if(ptype.connType == conn_property)
				{
					DynamicRequest req(object);
					req.method("_set_"+pi->name());
					req.param(pd.value());

					bool requestOk = req.invoke();
					arts_assert(requestOk);
				}
				else
				{
					if(ptype.dataType == "float")
						setValue(object,pi->name(),pd.floatValue());
					else
						arts_warning("unexpected property type %s",	
													ptype.dataType.c_str());
						//setStringValue(object,pd.stringValue());
				}
			}
			else if(pd.isConnected() && ptype.direction == output)
			{
				// create connections

				vector<PortDesc> *connections = pd.connections();
				vector<PortDesc>::iterator ci;

				for(ci = connections->begin(); ci != connections->end(); ci++)
				{
					if(!ci->parent().isNull())	// structureport otherwise
					{
						Object& dest = moduleMap[ci->parent().ID()];
#ifdef STRUCTBUILDER_DEBUG
						cout << "connect " << mi->name() << "." << pi->name()
					     	 << " to " << ci->parent().name()
							 << "." << ci->name() << endl;
#endif
						connect(object,pd.name(),dest,ci->name());
					}
				}
				delete connections;
			}
		}
		delete ports;
	}
	delete modules;

	// create ports (should be done via dynamic impl class...)

	vector<StructurePortDesc> *ports = structureDesc.ports();
	vector<StructurePortDesc>::iterator pi;
	for(pi = ports->begin(); pi != ports->end(); pi++)
	{
		const Arts::PortType& type = pi->type();

		// This is a little tricky, as input ports (which are bringing data
		// from outside into the structure) are saved as output ports (and
		// output ports as input ports).

		if(type.direction == input && type.dataType == "float" &&
		   type.connType == conn_stream)
		{
			float **f = new (float *);
			_initStream(pi->name(),f,streamOut | attributeStream);
#ifdef STRUCTBUILDER_DEBUG
			cout << "structure: outport " << pi->name() << " created";
#endif
		}
		if(type.direction == output && type.dataType == "float" &&
		   type.connType == conn_stream)
		{
			float **f = new (float *);
			_initStream(pi->name(),f,streamIn | attributeStream);
#ifdef STRUCTBUILDER_DEBUG
			cout << "inport " << pi->name() << " created";
#endif
		}
	}

	for(pi = ports->begin(); pi != ports->end(); pi++)
	{
		Arts::StructurePortDesc& pd = *pi;
		if(pd.isConnected())
		{
			// create connections

			vector<PortDesc> *connections = pd.connections();
			vector<PortDesc>::iterator ci;

			for(ci = connections->begin(); ci != connections->end(); ci++)
			{
				Object& dest = moduleMap[ci->parent().ID()];
#ifdef STRUCTBUILDER_DEBUG
				cout << "virtualize " << pi->name()
				     << " to " << ci->parent().name() << "." << ci->name()
					 << endl;
#endif

				_node()->virtualize(pd.name(),dest._node(),ci->name());
			}
			delete connections;
		}
	}
	delete ports;
}

void Structure_impl::streamInit()
{
	list<Object>::iterator i;

#ifdef STRUCTBUILDER_DEBUG
	cout << "vstructure: got streamInit()" << endl;
#endif

	for(i=structureObjects.begin(); i != structureObjects.end(); i++)
	{
		if(i->_base()->_isCompatibleWith("Arts::SynthModule"))
			i->_node()->start();
	}
}

void Structure_impl::streamEnd()
{
	list<Object>::iterator i;

#ifdef STRUCTBUILDER_DEBUG
	cout << "vstructure: got streamEnd()" << endl;
#endif

	for(i=structureObjects.begin(); i != structureObjects.end(); i++)
		if(i->_base()->_isCompatibleWith("Arts::SynthModule"))
			i->_node()->stop();
}
