/*	Author: Magdin Stoica
	Supervisor: Dr. Ryan Leduc

	Project created in conformity with the requirements for the Degree of Master of Engineering in Software Engineering,
	Computing and Software Department,
	McMaster University
	2003 - 2007
*/

#include <QVariant>
#include <QFileInfo>
#include <QDir>

#include "SimConfigSerializer.h"
#include "DesFlatProject.h"
#include "DesHierProject.h"
#include "Des.h"
#include "xmlwriter.h"

namespace DESpot
{

const QString SimConfigSerializer::cProjRootTag = "Project";
const QString SimConfigSerializer::cProjFileVerAttr = "projVer";
const QString SimConfigSerializer::cProjFileVerVal = "1.0.0";

const QString SimConfigSerializer::cProjHeaderTag = "Header";
const QString SimConfigSerializer::cProjNameAttr = "name";
const QString SimConfigSerializer::cProjTypeAttr = "type";
const QString SimConfigSerializer::cProjTypeFlatVal = "flat";
const QString SimConfigSerializer::cProjTypeHierVal = "HISC";
const QString SimConfigSerializer::cProjDegreeAttr = "degree";

const QString SimConfigSerializer::cProjIntegrityTag = "Integrity";
const QString SimConfigSerializer::cProjIntegStatusAttr = "status";
const QString SimConfigSerializer::cProjIntegStatusYesVal = "yes";
const QString SimConfigSerializer::cProjIntegStatusNoVal = "no";
const QString SimConfigSerializer::cProjIntegStatusNotVerifVal = "not-verified";

const QString SimConfigSerializer::cProjIntegStampAttr =  "dateStamp";

const QString SimConfigSerializer::cProjPropsTag = "Properties";
const QString SimConfigSerializer::cProjCtrlPropAttr = "controllable";
const QString SimConfigSerializer::cProjCtrlPropYesVal = "yes";
const QString SimConfigSerializer::cProjCtrlPropNoVal = "no";
const QString SimConfigSerializer::cProjCtrlPropNotVerifVal = "not-verified";
const QString SimConfigSerializer::cProjCtrlStampAttr = "ctrl-date-stamp";


const QString SimConfigSerializer::cProjNonBlockPropAttr = "non-blocking";
const QString SimConfigSerializer::cProjNonBlockPropYesVal = "yes";
const QString SimConfigSerializer::cProjNonBlockPropNoVal = "no";
const QString SimConfigSerializer::cProjNonBlockPropNotVerifVal = "not-verified";
const QString SimConfigSerializer::cProjNonBlockStampAttr = "non-block-stamp";

const QString SimConfigSerializer::cSubsysTag = "Subsystem";
const QString SimConfigSerializer::cSubsysNameAttr = "name";
const QString SimConfigSerializer::cSubsysLevelAttr = "level";
const QString SimConfigSerializer::cSubsysLwCtrlProp = "lw-ctrl";
const QString SimConfigSerializer::cSubsysLwCtrlYesVal = "yes";
const QString SimConfigSerializer::cSubsysLwCtrlNoVal = "no";
const QString SimConfigSerializer::cSubsysLwNonBlockProp = "lw-non-blocking";
const QString SimConfigSerializer::cSubsysLwNonBlockYesVal = "yes";
const QString SimConfigSerializer::cSubsysLwNonBlockNoVal = "no";
const QString SimConfigSerializer::cSubsysIConsistProp = "i-consist";
const QString SimConfigSerializer::cSubsysIConsistYesVal = "yes";
const QString SimConfigSerializer::cSubsysIConsistNoVal = "no";

const QString SimConfigSerializer::cSubsysImplTag = "Implements";

const QString SimConfigSerializer::cInterfaceTag = "Interface";
const QString SimConfigSerializer::cInterfRefTag = "InterfaceRef";
const QString SimConfigSerializer::cInterfNameAttr = "name";
const QString SimConfigSerializer::cInterfProviderAttr = "provider";

const QString SimConfigSerializer::cSubsysSupDesTag = "Supervisor";
const QString SimConfigSerializer::cSubsysPlantDesTag = "Plant";

const QString SimConfigSerializer::cSubsysUsesTag = "Uses";


const QString SimConfigSerializer::cDesTag = "Des";
const QString SimConfigSerializer::cDesNameAttr = "name";
const QString SimConfigSerializer::cDesLocationAttr = "location";


//_________________________________________________________________________________________________

SimConfigSerializer::SimConfigSerializer() :
		m_project(null),
		m_crtSubsys(null),
		m_crtInterf(null),
        m_readingDesType(eUnknownDes),
		m_pError(null)
{
}

//_________________________________________________________________________________________________

SimConfigSerializer::SimConfigSerializer(DesProject& project) :
		m_project(&project),
		m_crtSubsys(null),
		m_crtInterf(null),
        m_readingDesType(eUnknownDes),
		m_pError(null)
{
}

//_________________________________________________________________________________________________

SimConfigSerializer::~SimConfigSerializer(void)
{
	if (m_pError)
	{
		delete m_pError;
		m_pError = null;
	}
}

//_________________________________________________________________________________________________

DesProject* SimConfigSerializer::load(const std::wstring& fileName)
{
	m_fileName = QString::fromStdWString(fileName);

	//open the file with the given file name
	QFile file(QString::fromStdWString(fileName));
	if (file.open(QIODevice::ReadOnly|QIODevice::Text) == false)
		throw EX("Cannot open file in read-only mode. Cannot load project.")

	//create and configure an XML Reader
	QXmlSimpleReader xmlReader;
	xmlReader.setContentHandler(this);
	xmlReader.setErrorHandler(this);

	//...and have it parse the opened file
	QXmlInputSource* xmlSource = new QXmlInputSource(&file);
	bool sourceParsed = xmlReader.parse(xmlSource);

	if (sourceParsed == false)
	{
		throw new QXmlParseException(m_pError->message(), m_pError->columnNumber(), m_pError->lineNumber());
	}

	//the project was created and loaded
	return m_project;
}

//_________________________________________________________________________________________________

void SimConfigSerializer::save(const std::wstring& fileName)
{
	m_fileName = QString::fromStdWString(fileName);

	//open the file with the given file name
	QFile file(QString::fromStdWString(fileName));
	if (file.open(QIODevice::WriteOnly|QIODevice::Text) == false)
		throw EX("Cannot open file in write-only mode. Cannot save DES.")

	//create an XML serializer
	XmlWriter xw(file);
	xw.setAutoNewLine(true);

	//start writing the file
	xw.writeOpenTag(cProjRootTag, AttrMap(cProjFileVerAttr, cProjFileVerVal));

	//write the definition of the project
	writeProjDefinition(xw);

	//write the end of the file
	xw.writeCloseTag(cProjRootTag);
}

//_________________________________________________________________________________________________

void SimConfigSerializer::configure(ProjSerializerAccess* projAccess)
{
	m_projectAccess = projAccess;
}

//_________________________________________________________________________________________________

//the parsing is starting
bool SimConfigSerializer::startDocument()
{
	//clean any errors from previous parses
	if (m_pError)
	{
		delete m_pError;
		m_pError = null;
	}

	//clear any information that may have been previously loaded
	m_projFileVer.clear();

	return true;
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::endDocument()
{
	if (m_project == null)
	{
		throw EX("Invalid project file. Cannot load project");
	}

    if (m_project->getType() == eHierProject)
	{
		resolveInterfImplementations();
		resolveSubsysDependencies();
	}

	return true;
}

//_________________________________________________________________________________________________

//element and attribute parsing
bool SimConfigSerializer::startElement(const QString& /*namespaceURI*/, const QString& localName,
						  const QString& /*qName*/, const QXmlAttributes& atts)
{
	if (localName == cProjRootTag)
	{
		return readProjectRoot(localName, atts);
	}
	else
	{
		return readProjectDefinition(localName, atts);
	}
}

//_________________________________________________________________________________________________

//error handling
bool SimConfigSerializer::warning(const QXmlParseException& exception)
{
	qWarning(exception.message().toAscii().data());
	return true; //continue
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::error(const QXmlParseException& exception)
{
	assert(m_pError == null);
	m_pError = new QXmlParseException(exception.message(), exception.columnNumber(), exception.lineNumber());

	qWarning(exception.message().toAscii().data());

	return false; //stop parsing
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::fatalError(const QXmlParseException& exception)
{
	assert(m_pError == null);
	m_pError = new QXmlParseException(exception.message(), exception.columnNumber(), exception.lineNumber());

	qWarning(exception.message().toAscii().data());

	return false; //stop parsing
}

//_________________________________________________________________________________________________

void SimConfigSerializer::createProject(ProjectType projType, QString projName)
{
	switch(projType)
	{
        case eFlatProject:
			m_project = new DesFlatProject(projName.toStdWString());
			break;

        case eHierProject:
			m_project = new DesHierProject(projName.toStdWString());
			break;
	}

//	m_project->configureSerializer(this);
}

//_________________________________________________________________________________________________

DesHierProject* SimConfigSerializer::hierProject()
{
	assert(m_project != null);
    assert(m_project->getType() == eHierProject);
	DesHierProject* hierProject = dynamic_cast<DesHierProject*>(m_project);

	if (hierProject == null)
	{
		throw EX("The project beeing loaded is not a HISC project");
	}

	return hierProject;
}

//_________________________________________________________________________________________________

HierSerializerAccess* SimConfigSerializer::hierProjAccess()
{
	assert(m_projectAccess != null);
    assert(m_project->getType() == eHierProject);

	HierSerializerAccess* hierAccess = dynamic_cast<HierSerializerAccess*>(m_projectAccess);
	if (hierAccess == null)
	{
		throw EX("The project being loaded does not provide serializer with HISC access");
	}

	return hierAccess;
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::isHeaderTag(const QString& tagName)
{
	return ((tagName == cProjHeaderTag) || (tagName == cProjIntegrityTag) || (tagName == cProjPropsTag));
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::isSubsystemTag(const QString& tagName)
{
	return ((tagName == cSubsysTag) || (tagName == cSubsysImplTag) || (tagName == cSubsysSupDesTag) ||
		(tagName == cSubsysPlantDesTag) || (tagName == cSubsysUsesTag) || (tagName == cInterfRefTag));
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::isInterfaceTag(const QString& tagName)
{
	return (tagName == cInterfaceTag);
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::isDesTag(const QString& tagName)
{
	return (tagName == cDesTag);
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::readProjectRoot(const QString& /*tagName*/, const QXmlAttributes& atts)
{
	m_projFileVer = atts.value(cProjFileVerAttr);
	if (m_projFileVer > cProjFileVerVal)
	{
		//the file version is greater than the one supported by this serializer
		qWarning("Des file version (%s) is greater than the one supported (%s). The loading may fail.",
                                 m_projFileVer.toAscii().data(), cProjFileVerVal.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::readProjectDefinition(const QString& tagName, const QXmlAttributes& atts)
{
	if (isHeaderTag(tagName))
	{
		return readProjectHeader(tagName, atts);
	}
	else if (isSubsystemTag(tagName))
	{
		return readSubsystem(tagName, atts);
	}
	else if (isInterfaceTag(tagName))
	{
		return readInterface(tagName, atts);
	}
	else if (isDesTag(tagName))
	{
		return readDes(tagName, atts);
	}
	else
	{
		assert(true);
		qWarning("Unknown tag \"%s\". Tag will be ingored", tagName.toAscii().data());
		return true;
	}
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::readProjectHeader(const QString& tagName, const QXmlAttributes& atts)
{
	if (tagName == cProjHeaderTag)
	{
		//read the name of the project
		QString projName = atts.value(cProjNameAttr);

		//read the type of the project
		QString projTypeName = atts.value(cProjTypeAttr);
        ProjectType projType = getProjectType(projTypeName);

		createProject(projType, projName);
	}
	else if (tagName == cProjIntegrityTag)
	{
		//read the integrity status ...
		QString integStatusName = atts.value(cProjIntegStatusAttr);
		m_projectAccess->setIntegrity(getProjIntegrity(integStatusName));

		//... and the time stamp for the integrity check
		QString integTimeStamp = atts.value(cProjIntegStampAttr);
		m_projectAccess->setIntegrityStamp(integTimeStamp.toStdWString());
	}
	else if (tagName == cProjPropsTag)
	{
		//read the controllable property and date stamp
		QString ctrlPropName = atts.value(cProjCtrlPropAttr);
		m_projectAccess->setControllableProp(getCtrlProp(ctrlPropName));

		QString ctrlStamp = atts.value(cProjCtrlStampAttr);
		m_projectAccess->setCtrlStamp(ctrlStamp.toStdWString());

		//read the non-blocking property and date stamp
		QString nonBlockPropName = atts.value(cProjNonBlockPropAttr);
		m_projectAccess->setNonBlockingProp(getNonBlockProp(nonBlockPropName));

		QString nonBlockStamp = atts.value(cProjNonBlockStampAttr);
		m_projectAccess->setNonBlockingStamp(nonBlockStamp.toStdWString());
	}
	else
	{
		assert(true);
		qWarning("Unknown tag \"%s\". Tag will be ingored", tagName.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::readSubsystem(const QString& tagName, const QXmlAttributes& atts)
{
	if (tagName == cSubsysTag)
	{
        if (m_project->getType() == eHierProject)
		{
			//create the subsystem and read its properties
			QString subsysName = atts.value(cSubsysNameAttr);
			m_crtSubsys = new DesSubsystem(subsysName.toStdWString(), m_projectAccess->getEventPool());

			int level = atts.value(cSubsysLevelAttr).toInt();
			m_crtSubsys->setLevel(level);

			QString lwNonBlockName = atts.value(cSubsysLwNonBlockProp);
			m_crtSubsys->setNonBlocking(getLwNonBlockProp(lwNonBlockName));

			QString lwCtrlName = atts.value(cSubsysLwCtrlProp);
			m_crtSubsys->setControllable(getLwCtrlProp(lwCtrlName));

			QString lwIConsistName = atts.value(cSubsysIConsistProp);
			m_crtSubsys->setInterfConsist(getIConsistProp(lwIConsistName));

			if (m_crtSubsys->isRoot())
			{
				hierProjAccess()->addHighLevelSubsys(m_crtSubsys);
			}
			else
			{
				hierProjAccess()->addLowLevelSubsys(m_crtSubsys);
			}
		}
		else
		{
			//flat project the subsystem has already been created just access it
			m_crtSubsys = &(m_project->getRootSubsys());
		}
	}
	else if (tagName == cSubsysSupDesTag)
	{
        m_readingDesType = eSubsystemDes;
        m_readingDesLevel = ePlantDes;
    }
	else if (tagName == cSubsysPlantDesTag)
	{
        m_readingDesType = eSubsystemDes;
        m_readingDesLevel = eSupervisorDes;
    }
	else if (tagName == cSubsysImplTag)
	{
        assert(m_project->getType() == eHierProject);
		m_readingInterfRefType = eImplInterface;
	}
	else if (tagName == cSubsysUsesTag)
	{
        assert(m_project->getType() == eHierProject);
		m_readingInterfRefType = eUsesInterface;
	}
	else if (tagName == cInterfRefTag)
	{
        assert(m_project->getType() == eHierProject);

		std::wstring interfName = atts.value(cInterfNameAttr).toStdWString();

		switch(m_readingInterfRefType)
		{
			case eImplInterface:
				m_unresImpls.push_back(InterfImpl(m_crtSubsys, interfName));
				break;

			case eUsesInterface:
			{
				std::wstring interfProvider = atts.value(cInterfProviderAttr).toStdWString();
				m_unresDepends.push_back(SubsysDependency(m_crtSubsys, DesSubsystem::DependencyRef(interfName, interfProvider)));
				break;
			}

			default:
				assert(false); //unknown
				qWarning("Unknown interface reference type for tag \"%s\". Tag will be ingored", tagName.toAscii().data());
		}
	}
	else
	{
		assert(true);
		qWarning("Unknown tag \"%s\". Tag will be ingored", tagName.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::readInterface(const QString& tagName, const QXmlAttributes& atts)
{
    assert(m_project->getType() == eHierProject);

	if (tagName == cInterfaceTag)
	{
		QString interfName = atts.value(cInterfNameAttr);
		m_crtInterf = new DesInterface(interfName.toStdWString(), m_projectAccess->getEventPool());

		hierProjAccess()->addInterface(m_crtInterf);

		m_readingDesType = eInterfaceDes;
	}
	else
	{
		assert(true);
		qWarning("Unknown tag \"%s\". Tag will be ingored", tagName.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::readDes(const QString& tagName, const QXmlAttributes& atts)
{
	if (tagName == cDesTag)
	{
		//obtain the DES information and create a DES
		QString desName = atts.value(cDesNameAttr);
		QString desFileName = convertToAbsolute(atts.value(cDesLocationAttr));
		Des* des = new Des(desName.toStdWString(), desFileName.toStdWString(), true/*delay load*/);

		//TODO: Before adding the des to the project we need to load it since the project will need
		//to update the project event pool. Delay loading needs a background thread mechanism that
		//loads DES in the background and updates the project event pool synchronized with the projec UI
		//such as not to allow any action until all DES have been loaded
		try
		{
			//make sure this is not a new DES. If you save the project with new DES but don't save DES
			//you get a name but not a file. No point in trying to load nothing
			if (desFileName.isEmpty() == false)
			{
				des->load();
			}
		}
		catch(...)
		{
			qWarning("Errors loading DES \"%s\" from file \"%s\". DES will be ignored.", desName.toAscii().data(), desFileName.toAscii().data());
		}

		//add it to the proper container
		switch(m_readingDesType)
		{
            case eSubsystemDes:
                if (des->isNew())
                {
                    //this DES is not comming from a file so we can assume the right type
                    des->setType(eSubsystemDes);
                }

                m_crtSubsys->addDes(des, m_readingDesLevel);
                break;

			case eInterfaceDes:
				if (des->isNew())
				{
					//this DES is not comming from a file so we can assume the right type
                    des->setType(eInterfaceDes);
				}
				m_crtInterf->addDes(des);
				break;

                 default:
                    assert(false);
                    qWarning("Unknown or regular des type");
                }


	}
	else
	{
		assert(true);
		qWarning("Unknown tag \"%s\". Tag will be ingored", tagName.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

void SimConfigSerializer::writeProjDefinition(XmlWriter& xw)
{
	//The definition contains the project structure: header, subsystems and interfaces
	writeProjHeader(xw);

	//write the root subsystem first
	writeSubsystem(xw, m_project->getRootSubsys());

    if (m_project->getType() == eHierProject)
	{
		writeProjInterfaces(xw);
	}

	writeProjSubsystems(xw, false /*exclude root subsystem*/);
}

//_________________________________________________________________________________________________

void SimConfigSerializer::writeProjHeader(XmlWriter& xw)
{
	AttrMap headerAttrs(cProjNameAttr, toString(m_project->getName()),
						cProjTypeAttr, getProjectType());

    if (m_project->getType() == eHierProject)
	{
		headerAttrs.add(cProjDegreeAttr, toString(hierProject()->getDegree()));
	}

	xw.writeOpenTag(cProjHeaderTag, headerAttrs);

	xw.writeAtomTag(cProjIntegrityTag, AttrMap(cProjIntegStatusAttr, getProjIntegrity(),
											   cProjIntegStampAttr, toString(m_project->getIntegrityStamp())));

	xw.writeAtomTag(cProjPropsTag, AttrMap(cProjCtrlPropAttr, getCtrlProp(),
										   cProjCtrlStampAttr, toString(m_project->getCtrlStamp()),
										   cProjNonBlockPropAttr, getNonBlockProp(),
										   cProjNonBlockStampAttr, toString(m_project->getNonBlockingStamp())));
	xw.writeCloseTag(cProjHeaderTag);
}

//_________________________________________________________________________________________________

void SimConfigSerializer::writeProjSubsystems(XmlWriter& xw, bool includeRoot)
{
	if (includeRoot)
	{
		//save the root subsystem
		DesSubsystem& rootSubsys = m_project->getRootSubsys();
		writeSubsystem(xw, rootSubsys);
	}

	//if the project is HISC save all other subsystems
    if (m_project->getType() == eHierProject)
	{
		DesHierProject::SubsysIteratorPtr subsysIt = hierProject()->createSubsysIterator();
		for(subsysIt->first(); subsysIt->notDone(); subsysIt->next())
		{
			const DesSubsystem& subsys = subsysIt->currentItem();

			if (subsys.isRoot() == false)
			{
				writeSubsystem(xw, subsys);
			}
		}
	}
}

//_________________________________________________________________________________________________

void SimConfigSerializer::writeSubsystem(XmlWriter& xw, const DesSubsystem& subsys)
{
	//Write the opening subsystem tag:
	//e.g. <Subsystem name = "High Level Subsystem" level="0" lw-ctrl ="yes" lw-non-blocking ="yes" i-consist ="yes">
	AttrMap subsysAttrMap(cSubsysNameAttr, toString(subsys.getName()));
    if (m_project->getType() == eHierProject)
	{
		subsysAttrMap.add(cSubsysLevelAttr, getLevelValue(subsys));
		subsysAttrMap.add(cSubsysLwCtrlProp, getLwCtrlProp(subsys));
		subsysAttrMap.add(cSubsysLwNonBlockProp, getLwNonBlockProp(subsys));
		subsysAttrMap.add(cSubsysIConsistProp, getIConsistProp(subsys));
	}
	xw.writeOpenTag(cSubsysTag, subsysAttrMap);
	{
        if (m_project->getType() == eHierProject)
		{
			//write the implements tag with the interface the subsystem implements
			//the root subsystem doesn't implement any interface
			{
				if (subsys.implementsInterface())
				{
					xw.writeOpenTag(cSubsysImplTag);
					{
						const DesInterface& interf = subsys.getInterface();
						xw.writeAtomTag(cInterfRefTag, AttrMap(cInterfNameAttr, toString(interf.getName())));
					}
					xw.writeCloseTag(cSubsysImplTag);
				}
				else
				{
					//write an empty implementation tag
					xw.writeAtomTag(cSubsysImplTag);
				}
			}
		}

		//write the list of supervisor DES
		xw.writeOpenTag(cSubsysSupDesTag);
		{
            DesSubsystem::DesIteratorPtr desIt = subsys.createDesIterator(eSupervisorDes);
			for(desIt->first(); desIt->notDone(); desIt->next())
			{
				const Des& des = desIt->currentItem();

				xw.writeAtomTag(cDesTag, AttrMap(cDesNameAttr, toString(des.getName()),
								cDesLocationAttr, convertToRelative(toString(des.getFileName()))));
			}
		}
		xw.writeCloseTag(cSubsysSupDesTag);

		//write the list of plant DES
		xw.writeOpenTag(cSubsysPlantDesTag);
		{
            DesSubsystem::DesIteratorPtr desIt = subsys.createDesIterator(ePlantDes);
			for(desIt->first(); desIt->notDone(); desIt->next())
			{
				const Des& des = desIt->currentItem();
				xw.writeAtomTag(cDesTag, AttrMap(cDesNameAttr, toString(des.getName()),
								cDesLocationAttr, convertToRelative(toString(des.getFileName()))));
			}
		}
		xw.writeCloseTag(cSubsysPlantDesTag);

        if (m_project->getType() == eHierProject)
		{
			//write the list of dependent subsystems
			if (subsys.getDependsCount() != 0)
			{
				xw.writeOpenTag(cSubsysUsesTag);
				{
					//<InterfaceRef name = "Interface 1" provider ="Low Level Subsystem 1"/>
					DesSubsystem::DependIteratorPtr depIt = subsys.createDependsIterator();
					for(depIt->first(); depIt->notDone(); depIt->next())
					{
						const DesSubsystem::Dependency& depend = depIt->currentItem();
						xw.writeAtomTag(cInterfRefTag, AttrMap(cInterfNameAttr, toString(depend.interface->getName()),
										cInterfProviderAttr, toString(depend.subsystem->getName())));
					}
				}
				xw.writeCloseTag(cSubsysUsesTag);
			}
			else
			{
				xw.writeAtomTag(cSubsysUsesTag);
			}
		}
	}
	xw.writeCloseTag(cSubsysTag);
}

//_________________________________________________________________________________________________

void SimConfigSerializer::writeProjInterfaces(XmlWriter& xw)
{
	DesHierProject::InterfIteratorPtr interfIt = hierProject()->createInterfIterator();
	for(interfIt->first(); interfIt->notDone(); interfIt->next())
	{
		const DesInterface& interf = interfIt->currentItem();
		writeInterface(xw, interf);
	}
}

//_________________________________________________________________________________________________

void SimConfigSerializer::writeInterface(XmlWriter& xw, const DesInterface& interf)
{
	//Write the interface tag: e.g. <Interface name = "Interface 1">
	xw.writeOpenTag(cInterfaceTag, AttrMap(cInterfNameAttr, toString(interf.getName())));
	{
		//write all DES that are part of the interface
		//e.g. <Des name = "I1-Des-1" location="FullPath\I1-Des-1.des"/>
		DesInterface::DesIteratorPtr desIt = interf.createDesIterator();
		for(desIt->first(); desIt->notDone(); desIt->next())
		{
			const Des& des = desIt->currentItem();
			xw.writeAtomTag(cDesTag, AttrMap(cDesNameAttr, toString(des.getName()),
											 cDesLocationAttr, convertToRelative(toString(des.getFileName()))));
		}
	}
	xw.writeCloseTag(cInterfaceTag);
}

//_________________________________________________________________________________________________

ProjectType SimConfigSerializer::getProjectType(const QString& projTypeName)
{
	if (projTypeName == cProjTypeFlatVal)
	{
        return eFlatProject;
	}
	else if (projTypeName == cProjTypeHierVal)
	{
        return eHierProject;
	}
	else
	{
		assert(true);
		qWarning("Unknown value \"%s\". Value will default to flat project", projTypeName.toAscii().data());
        return eFlatProject;
	}
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::getProjectType()
{
	switch(m_project->getType())
	{
        case eFlatProject:
			return cProjTypeFlatVal;

        case eHierProject:
			return cProjTypeHierVal;

		default:
			assert(true);
			qWarning("Unknown project type value.");
			return "ERROR: Unknown project type";
	}
}

//_________________________________________________________________________________________________

Integrity SimConfigSerializer::getProjIntegrity(const QString& integStatusName)
{
	if (integStatusName == cProjIntegStatusYesVal)
	{
        return eIntegYes;
	}
	else if (integStatusName == cProjIntegStatusNoVal)
	{
        return eIntegNo;
	}
	else if ( integStatusName == cProjIntegStatusNotVerifVal)
	{
        return eIntegNotVerified;
	}
	else
	{
		assert(true);
		qWarning("Unknown value \"%s\". Value will default to integrity-not-verified", integStatusName.toAscii().data());
        return eIntegNotVerified;
	}
}

//_________________________________________________________________________________________________

QString	SimConfigSerializer::getProjIntegrity()
{
	switch(m_project->getIntegrity())
	{
        case eIntegYes:
			return cProjIntegStatusYesVal;

        case eIntegNo:
			return cProjIntegStatusNoVal;

        case eIntegNotVerified:
			return cProjIntegStatusNotVerifVal;

		default:
			assert(true);
			qWarning("Unknown project integrity value.");
			return "ERROR: Unknown integrity value";
	}
}

//_________________________________________________________________________________________________

ControllableProp SimConfigSerializer::getCtrlProp(const QString& ctrlPropName)
{
	if (ctrlPropName == cProjCtrlPropYesVal)
	{
        return eCtrlYes;
	}
	else if (ctrlPropName == cProjCtrlPropNoVal)
	{
        return eCtrlNo;
	}
	else if ( ctrlPropName == cProjCtrlPropNotVerifVal)
	{
        return eCtrlNotVerified;
	}
	else
	{
		assert(true);
		qWarning("Unknown value \"%s\". Value will default to not-verified", ctrlPropName.toAscii().data());
        return eCtrlNotVerified;
	}
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::getCtrlProp()
{
	switch(m_project->getControllableProp())
	{
        case eCtrlYes:
			return cProjCtrlPropYesVal;

        case eCtrlNo:
			return cProjCtrlPropNoVal;

        case eCtrlNotVerified:
			return cProjCtrlPropNotVerifVal;

		default:
			assert(true);
			qWarning("Unknown project controllable property value.");
			return "ERROR: Unknown controllable property value";
	}
}

//_________________________________________________________________________________________________

NonBlockingProp SimConfigSerializer::getNonBlockProp(const QString& nonBlockPropName)
{
	if (nonBlockPropName == cProjNonBlockPropYesVal)
	{
        return eNonBlockYes;
	}
	else if (nonBlockPropName == cProjNonBlockPropNoVal)
	{
        return eNonBlockNo;
	}
	else if ( nonBlockPropName == cProjNonBlockPropNotVerifVal)
	{
        return eNonBlockNotVerified;
	}
	else
	{
		assert(true);
		qWarning("Unknown value \"%s\". Value will default to not-verified", nonBlockPropName.toAscii().data());
        return eNonBlockNotVerified;
	}
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::getNonBlockProp()
{
	switch(m_project->getNonBlockingProp())
	{
        case eNonBlockYes:
			return cProjNonBlockPropYesVal;

        case eNonBlockNo:
			return cProjNonBlockPropNoVal;

        case eNonBlockNotVerified:
			return cProjNonBlockPropNotVerifVal;

		default:
			assert(true);
			qWarning("Unknown value nonblocking property value");
			return "ERROR: Unknown non-blocking value";
	}
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::getLevelValue(const DesSubsystem& subsys)
{
	return QVariant(subsys.getLevel()).toString();
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::getLwCtrlProp(const QString& lwCtrlPropName)
{
	if (lwCtrlPropName == cSubsysLwCtrlYesVal)
	{
		return true;
	}
	else if (lwCtrlPropName == cSubsysLwCtrlNoVal)
	{
		return false;
	}
	else
	{
		assert(true);
		qWarning("Unknown value \"%s\". Value will default to -no-", lwCtrlPropName.toAscii().data());
		return false;
	}
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::getLwCtrlProp(const DesSubsystem& subsys)
{
	return subsys.isControllable() ? cSubsysLwCtrlYesVal : cSubsysLwCtrlNoVal;
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::getLwNonBlockProp(const QString& lwNonBlockPropName)
{
	if (lwNonBlockPropName == cSubsysLwNonBlockYesVal)
	{
		return true;
	}
	else if (lwNonBlockPropName == cSubsysLwNonBlockNoVal)
	{
		return false;
	}
	else
	{
		assert(true);
		qWarning("Unknown value \"%s\". Value will default to -no-", lwNonBlockPropName.toAscii().data());
		return false;
	}
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::getLwNonBlockProp(const DesSubsystem& subsys)
{
	return subsys.isNonBlocking() ? cSubsysLwNonBlockYesVal : cSubsysLwNonBlockNoVal;
}

//_________________________________________________________________________________________________

bool SimConfigSerializer::getIConsistProp(const QString& iConsistPropName)
{
	if (iConsistPropName == cSubsysIConsistYesVal)
	{
		return true;
	}
	else if (iConsistPropName == cSubsysIConsistNoVal)
	{
		return false;
	}
	else
	{
		assert(true);
		qWarning("Unknown value \"%s\". Value will default to -no-", iConsistPropName.toAscii().data());
		return false;
	}
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::getIConsistProp(const DesSubsystem& subsys)
{
	return subsys.isInterfConsist() ? cSubsysIConsistYesVal : cSubsysIConsistNoVal;
}

//_________________________________________________________________________________________________

void SimConfigSerializer::resolveInterfImplementations()
{
	for(unsigned int i = 0; i < m_unresImpls.size(); i++)
	{
		InterfImpl& unresImpl = m_unresImpls[i];
		DesInterface* interf = hierProjAccess()->getInterface(unresImpl.interfName);
		unresImpl.subsys->setInterface(*interf);
	}
}

//_________________________________________________________________________________________________

void SimConfigSerializer::resolveSubsysDependencies()
{
	for(unsigned int i = 0; i < m_unresDepends.size(); i++)
	{
		SubsysDependency& unresDepend = m_unresDepends[i];
		DesInterface* interf = hierProjAccess()->getInterface(unresDepend.dependRef.interfName);
		DesSubsystem* lSubsys = hierProjAccess()->getSubsystem(unresDepend.dependRef.subsysName);
		unresDepend.subsys->addDependency(interf, lSubsys);
	}
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::toString(int intVal)
{
	return QVariant(intVal).toString();
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::toString(const std::wstring stdStr)
{
	return QString::fromStdWString(stdStr);
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::convertToRelative(const QString& desFileName)
{
	QFileInfo projFileInfo(m_fileName);
	QDir projDir = projFileInfo.dir();
	return projDir.relativeFilePath(desFileName);
}

//_________________________________________________________________________________________________

QString SimConfigSerializer::convertToAbsolute(const QString& desRelativeFileName)
{
	if (desRelativeFileName.isEmpty())
		return QString();

	QFileInfo projFileInfo(m_fileName);
	QDir projDir = projFileInfo.dir();
	return QDir::cleanPath(projDir.absoluteFilePath(desRelativeFileName));
}

} // end of namespace DESpot
