#include "baseplugincontainer.h"

#include <iostream>

#include "plugin.h"
#include "iprovider.h"
#include "ipluginfactory.h"

#include "helpers.h"

namespace NPlugin
{

BasePluginContainer::~BasePluginContainer()
{
}


void BasePluginContainer::unloadAllPlugins()
{
	for (PluginMap::iterator it = _plugins.begin(); it != _plugins.end(); ++it)
	{
		if (it->second != 0)
			_pluginInformer.informRemovePlugin(it->second);
	}
	// delete the plugins only, after everyone was informed about all remoals, because
	// there may be interdependencies between the plugins (one plugin using the other)
	// that might otherwise still be used
	for (PluginMap::iterator it = _plugins.begin(); it != _plugins.end(); ++it)
	{
		delete it->second;
	}
}


/////////////////////////////////////////////////////
// PluginContainer Interface
/////////////////////////////////////////////////////

bool BasePluginContainer::init(IProvider* pProvider, IPluginFactory* pFactory)
{
	_pFactory = pFactory;
	_pProvider = pProvider;
	return true;
}

Plugin* BasePluginContainer::requestPlugin(const string& name)
{
	if (_pProvider==0 || _pFactory==0)
	{
		cerr << "Programming error: Requesting plugin while (_pProvider==0 || _pFactory==0) "
			<< " please file a bug report." << endl;
		return 0;
	}
	// find the entry for the plugin with this name
	PluginMap::iterator it = _plugins.find(name);
	if (it == _plugins.end())
		return 0;
	Plugin* pPlugin = it->second;
	if (!pPlugin)
	{
		pPlugin = _pFactory->createPlugin(name);
		if (pPlugin == 0)
			qDebug("Warning, plugin %s could not be created", name.c_str());
		it->second = pPlugin;
	}
	else
		return pPlugin;
	if (pPlugin)
	{
		pPlugin->init(_pProvider);
		_pluginInformer.informAddPlugin(pPlugin);
	}
	return pPlugin;
}

void BasePluginContainer::releasePlugin(Plugin* pPlugin)
{
	// find the plugin in the _plugins map
	PluginMap::iterator it = _plugins.begin();
	while (it != _plugins.end() && it->second != pPlugin)
		++it;
	if (it == _plugins.end())
		return;
	_pluginInformer.informRemovePlugin(pPlugin);
	it->second = 0;
	delete pPlugin;
}

vector<Plugin*> BasePluginContainer::getLoadedPlugins() const
{
	vector<Plugin*> result;
	for (PluginMap::const_iterator it = _plugins.begin(); it != _plugins.end(); ++it)
	{
		if (it->second != 0)
			result.push_back(it->second);
	}
	return result;
}

/////////////////////////////////////////////////////
// IXmlStorable Interface
/////////////////////////////////////////////////////

QDomElement BasePluginContainer::loadSettings(const QDomElement source)
{
	// if the settings are not for this node
	if (source.tagName() != toQString(name()))
		return source;
	float settingsVersion;
	NXml::getAttribute(source, settingsVersion, "settingsVersion", 0.0);
	// for old settings treat the element as we used to
	if (settingsVersion < 0.1)
	{
		return loadContainerSettings(source);
	}
	QDomElement pluginContainerElement = NXml::getFirstElement(source.firstChild());
	QDomElement singlePluginElement = loadContainerSettings(pluginContainerElement);
	// iterate over all SinglePlugin entries and load them
	while (!singlePluginElement.isNull())
	{
		QString pluginName;
		NXml::getAttribute(singlePluginElement, pluginName, "name", "");
// 		qStrDebug("Loaded <SinglePlugin name=\"" + pluginName + "\"> settings");
		if (_plugins.find(toString(pluginName)) != _plugins.end())
		{
			Plugin* pPlugin = _plugins[toString(pluginName)];
			pPlugin->loadSettings( NXml::getFirstElement(singlePluginElement.firstChild()) );
		}
		else
		{
			qStrWarning("found unknown <SinglePlugin name=\"" + pluginName + "\"> settings entry");
		}
		singlePluginElement = NXml::getNextElement(singlePluginElement);
	}
	return NXml::getNextElement(source);
}

void BasePluginContainer::saveSettings(NXml::XmlData& outData, QDomElement parent) const
{
	QDomElement pluginContainerElement = outData.addElement(parent, toQString(name()));
	outData.addAttribute(pluginContainerElement, 0.1f, "settingsVersion");
	saveContainerSettings(outData, pluginContainerElement);
	vector<Plugin*> plugins = getLoadedPlugins();
	for (vector<Plugin*>::iterator it = plugins.begin(); it != plugins.end(); ++it)
	{
		Plugin* pPlugin = *it;
		QDomElement singlePluginElement = outData.addElement(pluginContainerElement, "SinglePlugin");
		outData.addAttribute(singlePluginElement, pPlugin->name(), "name");
		pPlugin->saveSettings(outData, singlePluginElement);
	}
}

/////////////////////////////////////////////////////
// Helper Methods
/////////////////////////////////////////////////////

QDomElement BasePluginContainer::loadContainerSettings(const QDomElement source)
{
	return source;
}

void BasePluginContainer::saveContainerSettings(NXml::XmlData&, QDomElement) const
{
}

void BasePluginContainer::addPlugin(const string& name)
{
	_plugins[name] = 0;
	_pluginNames.push_back(name);
}


}	// namespace NPlugin
