/*	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 <QtXml/QXmlSimpleReader>
#include <QFile>
#include <QVariant>

#include "DesSerializer.h"
#include "Des.h"
#include "DesStatePool.h"
#include "DesEventPool.h"
#include "DesTransitionFunction.h"
#include "GedDesScene.h"
#include "GedDesState.h"
#include "GedDesTrans.h"


namespace DESpot
{

const QString DesSerializer::cDesRootTag = "DES";
const QString DesSerializer::cDesFileVerAttr = "desVer";
const QString DesSerializer::cDesFileVerVal = "1.0";

const QString DesSerializer::cDesDefineTag = "Definition";
const QString DesSerializer::cDesDefineVerAttr = "desDefVer";
const QString DesSerializer::cDesDefineVerVal = "1.0";

const QString DesSerializer::cDesHeaderTag = "Header";
const QString DesSerializer::cDesNameAttr = "name";
const QString DesSerializer::cDesTypeAttr = "type";
const QString DesSerializer::cDesTypeVal_Regular = "regular";
const QString DesSerializer::cDesTypeVal_Subsystem = "subsystem";
const QString DesSerializer::cDesTypeVal_Interface = "interface";

const QString DesSerializer::cDesIntegTag = "Integrity";
const QString DesSerializer::cDesIntegStatusAttr = "status";
const QString DesSerializer::cDesIntegStatusVal_Yes = "valid";
const QString DesSerializer::cDesIntegStatusVal_No = "invalid";
const QString DesSerializer::cDesIntegStatusVal_NotVerified = "not-verified";

const QString DesSerializer::cDesIntegDateStampAttr = "date-stamp";

const QString DesSerializer::cDesReachTag = "Reachable";
const QString DesSerializer::cDesReachStatusAttr = "status";
const QString DesSerializer::cDesReachStatusVal_Yes = "yes";
const QString DesSerializer::cDesReachStatusVal_No = "no";
const QString DesSerializer::cDesReachStatusVal_NotVerified = "not-verified";
const QString DesSerializer::cDesReachDateStampAttr = "date-stamp";

const QString DesSerializer::cDesNonBlockTag = "Nonblocking";
const QString DesSerializer::cDesNonBlockStatusAttr = "status";
const QString DesSerializer::cDesNonBlockStatusVal_Yes = "yes";
const QString DesSerializer::cDesNonBlockStatusVal_No = "no";
const QString DesSerializer::cDesNonBlockStatusVal_NotVerified = "not-verified";
const QString DesSerializer::cDesNonBlockDateStampAttr = "date-stamp";

const QString DesSerializer::cDesStateListTag = "States";
const QString DesSerializer::cDesStateCountAttr = "count";
const QString DesSerializer::cDesStateMarkedCountAttr = "marked-count";

const QString DesSerializer::cDesInitStateTag = "InitSt";
const QString DesSerializer::cDesStateTag = "St";
const QString DesSerializer::cDesStateIdAttr = "id";
const QString DesSerializer::cDesStateNameAttr = "nm";
const QString DesSerializer::cDesStateAliasAttr = "al";
const QString DesSerializer::cDesStateMarkedAttr = "mk";
const QString DesSerializer::cDesStateMarkedVal_Yes = "1";
const QString DesSerializer::cDesStateMarkedVal_No = "0";
const QString DesSerializer::cDesStateReachableAttr = "rch";
const QString DesSerializer::cDesStateReachableVal_Yes = "1";
const QString DesSerializer::cDesStateReachableVal_No = "0";
const QString DesSerializer::cDesStateReachableVal_Unknown = "?";
//Graphic info of states
const QString DesSerializer::cDesStatePosXAttr = "sx";  //State position
const QString DesSerializer::cDesStatePosYAttr = "sy";
const QString DesSerializer::cDesStateLabelXAttr = "lx"; //Label position
const QString DesSerializer::cDesStateLabelYAttr = "ly";

const QString DesSerializer::cDesEventListTag = "Events";
const QString DesSerializer::cDesEventCountAttr = "count";
const QString DesSerializer::cDesEventCtrlCountAttr = "ctrl-count";
const QString DesSerializer::cDesEventUnctrlCountAttr = "unctrl-count";
const QString DesSerializer::cDesEventDefCountAttr = "def-count";
const QString DesSerializer::cDesEventAnsCountAttr = "ans-count";
const QString DesSerializer::cDesEventReqCountAttr = "req-count";
const QString DesSerializer::cDesEventLdCountAttr = "ld-count";

const QString DesSerializer::cDesEventTag = "Ev";
const QString DesSerializer::cDesEventIdAttr = "id";
const QString DesSerializer::cDesEventNameAttr = "nm";
const QString DesSerializer::cDesEventAliasAttr = "al";
const QString DesSerializer::cDesEventCtrlAttr = "ctrl";
const QString DesSerializer::cDesEventCtrlVal_Yes = "1";
const QString DesSerializer::cDesEventCtrlVal_No = "0";
const QString DesSerializer::cDesEventTypeAttr = "type";
const QString DesSerializer::cDesEventTypeVal_Def = "d";
const QString DesSerializer::cDesEventTypeVal_Ans = "a";
const QString DesSerializer::cDesEventTypeVal_Req = "r";
const QString DesSerializer::cDesEventTypeVal_Ld = "ld";

const QString DesSerializer::cDesTransFuncTag = "Trans-Function";
const QString DesSerializer::cDesTransCountAttr = "count";

const QString DesSerializer::cDesTransMapTag = "Transitions";
const QString DesSerializer::cDesTransMapCountAttr = "count";

const QString DesSerializer::cDesSelfTransListTag = "Self-Loops";
const QString DesSerializer::cDesSelfTransCountAttr = "count";

const QString DesSerializer::cDesTransTag = "Tr";
const QString DesSerializer::cDesSelfTransTag = "Sl"; //Self-Loop;
const QString DesSerializer::cDesTransFromStateIdAttr = "fID";
const QString DesSerializer::cDesTransEventIdAttr = "eID";
const QString DesSerializer::cDesTransToStateIdAttr = "tID";
//Graphic info for transition function
const QString DesSerializer::cDesTransLabelPosXAttr = "lx";
const QString DesSerializer::cDesTransLabelPosYAttr = "ly";
const QString DesSerializer::cDesTransPosCntAttr = "cnt";  //How many points in QPainterPath
const QString DesSerializer::cDesTransPosAngAttr = "ang";  //The angle in degree for self loop
const QString DesSerializer::cDesTransPosTag = "Pos";
const QString DesSerializer::cDesTransPosXAttr = "x";
const QString DesSerializer::cDesTransPosYAttr = "y";

//_________________________________________________________________________________________________

DesSerializer::DesSerializer(Des& des) : m_des(des), m_desAccess(null),
                                         m_pStatePool(null), m_pEventPool(null), m_pTransFunc(null),
										 m_pError(null), m_desHelper(&des),
										 m_integStatus(eIntegNotVerified),
										 m_reachStatus(eReachableNotVerified),
										 m_nonBlockStatus(eNonBlockNotVerified)
{
	mode=Tabular;
}

DesSerializer::DesSerializer(Des& des, DesSerializerAccess* desAccess) :
										m_des(des),
										m_desAccess(desAccess),
										m_pStatePool(desAccess->getStatePool()),
										m_pEventPool(desAccess->getEventPool()),
										m_pTransFunc(desAccess->getTransFunction()),
										m_pError(null),
										m_desHelper(&des),
										m_integStatus(eIntegNotVerified),
										m_reachStatus(eReachableNotVerified),
										m_nonBlockStatus(eNonBlockNotVerified)
{
  	mode=Tabular;
}

// Add by XMA to support graphics info
// All graphics info related code are branched by mode
DesSerializer::DesSerializer(Des& des,GedDesScene* /*scene*/,AccessMode mode) : 
										m_des(des), m_desAccess(null), m_pStatePool(null),
                                        m_pEventPool(null), m_pTransFunc(null),
										m_pError(null), m_desHelper(&des),
										m_integStatus(eIntegNotVerified),
										m_reachStatus(eReachableNotVerified),
										m_nonBlockStatus(eNonBlockNotVerified)
{
	//For now, this class read and write graphic info. The code can read/write tabular as well if
	// mode is set to Tabular.
	this->mode=mode;
}

// Add by XMA to support graphic info
// All graphics info related code are branched by mode
DesSerializer::DesSerializer(Des& des, DesSerializerAccess* desAccess,GedDesScene* /*scene*/,AccessMode mode) :
									m_des(des),
									m_desAccess(desAccess),
									m_pStatePool(desAccess->getStatePool()),
									m_pEventPool(desAccess->getEventPool()),
									m_pTransFunc(desAccess->getTransFunction()),
									m_pError(null),
									m_desHelper(&des),
								    m_integStatus(eIntegNotVerified),
									m_reachStatus(eReachableNotVerified),
									m_nonBlockStatus(eNonBlockNotVerified)
{
	this->mode=mode;
}

// Add by XMA to support graphic info
// All graphics info related code are branched by mode
DesSerializer::DesSerializer(Des& des, DesSerializerAccess* desAccess,AccessMode mode) :
									m_des(des),
									m_desAccess(desAccess),
									m_pStatePool(desAccess->getStatePool()),
									m_pEventPool(desAccess->getEventPool()),
									m_pTransFunc(desAccess->getTransFunction()),
									m_pError(null),
									m_desHelper(&des),
									m_integStatus(eIntegNotVerified),
									m_reachStatus(eReachableNotVerified),
									m_nonBlockStatus(eNonBlockNotVerified)
{
	this->mode=mode;
}

//_________________________________________________________________________________________________

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

//_________________________________________________________________________________________________

void DesSerializer::load(const std::wstring& fileName)
{
	if (m_pStatePool == null || m_pEventPool == null || m_pTransFunc == null)
		throw EX("Serializer was not initialized for loading. Cannot load")

	//open the file with the given file name
	QFile file(QString::fromStdWString(fileName));
	if (file.open(QIODevice::ReadOnly | QIODevice::Text) == false)
	{
		std::wstring error = QString("Cannot load DES. %1. \n(%2)").arg(file.errorString(),
						                QString::fromStdWString(fileName)).toStdWString();
		throw error;
	}

	//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 m_pError;
	}
}

//_________________________________________________________________________________________________

void DesSerializer::save(const std::wstring& fileName, const std::wstring& desName)
{
	m_alternateDesName = desName;
	save(fileName);
}

// added by rjl - remove when bddhisc andd bddsd
// integrated with despot. bool isHigh means DES is at high level
// If isHISC is false, then isHigh is ignored.
//_________________________________________________________________________________________________


void DesSerializer::exportToBDDhiscORsd(const std::wstring& fileName, bool
isHigh, bool isHISC)
{

  QString DesFileName = QString::fromStdWString(fileName);

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

  //write out DES file
   QTextStream desOut(&file);

   desOut << "[States] \n";  

   int numStates = 0;
   // count the number of states
{
   Des::StateIteratorPtr stateIt = m_des.createStateIterator();
   for(stateIt->first(); stateIt->isDone() == false; stateIt->next())
     {
       numStates++;
     }
 }

   desOut << numStates << "\n"; 

   
   // output list of states
{
   Des::StateIteratorPtr stateIt = m_des.createStateIterator();
   for(stateIt->first(); stateIt->isDone() == false; stateIt->next())
     {
       const DesState& state = stateIt->currentItem();
       desOut << toString(state.getName()) << "\n"; 
     }
 }

   desOut <<  "\n[InitState]\n";

   if (m_des.hasInitialState())
     {
       const DesState& initState = m_des.getInitialState();
       desOut << toString(initState.getName()) << "\n\n"; 
     }

   desOut <<  "[MarkingStates]\n";  

   // output list of marking states
{
   Des::StateIteratorPtr stateIt = m_des.createStateIterator();
   for(stateIt->first(); stateIt->isDone() == false; stateIt->next())
     {
       const DesState& state = stateIt->currentItem();
       if (state.isMarked())
	 {
	   desOut << toString(state.getName()) << "\n";
	 }
     }
 }

  desOut <<  "\n[Events]\n";  

  //create list of events with cont and type info
  Des::EventIteratorPtr eventIt = m_des.createEventIterator();
  for(eventIt->first(); eventIt->isDone() == false; eventIt->next())
    {
      const DesEvent& event = eventIt->currentItem();
    
      QString contString = "N";
      QString eTypeString = "";

      if (event.isControllable())
	{
	  contString = "Y";
	}
    

      //if we are exporting for bddsd, we just set the event type to
      //low as bddsd will ignore this.

      if (!(isHISC)) 
	{
	  eTypeString = "L";
	} else 	{

	  switch(event.getType())
	    {
	    case eDefaultEvent:
	      if (isHigh) 
		{
		  eTypeString = "H";
		}
	      else 
		{
		  eTypeString = "L";
		}
	      break;
	    case eAnswerEvent:
	      eTypeString = "A";
	      break;
	    case eRequestEvent:
	      eTypeString = "R";
	      break;
	    case eLDataEvent:
	      eTypeString = "D";
	      break;
	    case eHighLevelEvent:
	      eTypeString = "H";
	      break;
	    case eLowLevelEvent:
	      eTypeString = "L";
	      break;
	    default:
	      assert(false); //unknown event type
	      break;
	    }
	}
    
      desOut << toString(event.getName()) << "\t" << contString  << "\t" <<  eTypeString  << "\n";  

    }				 	    

  desOut <<  "\n[Transitions]\n";  


  // create list of transitions
{
   Des::StateIteratorPtr stateIt = m_des.createStateIterator();
   for(stateIt->first(); stateIt->isDone() == false; stateIt->next())
     {
       const DesState& state = stateIt->currentItem();
       bool firstTime = true;

       Des::TransIteratorPtr transIt = m_des.createTransIterator(false);   //false - include global self-loops

       for(transIt->first(); !transIt->isDone(); transIt->next())
	 {
	   const DesTransition& trans = transIt->currentItem();
	   const DesState& fromState = trans.fromState();
	   const DesState& toState = trans.toState();
	   const DesEvent& event=  trans.event();

	   if (state != fromState)
	     {
	       continue;
	     }

	   if (firstTime) 
	     {
	       firstTime = false;
	       desOut << toString(state.getName()) << "\n";
	     }

	   desOut << "(" << toString(event.getName())  << "\t" << toString(toState.getName()) << ")" << "\n";
	 }
     }
}


// that should be it!


}


 //  remove to here - RJL



//_________________________________________________________________________________________________

void DesSerializer::save(const std::wstring& fileName)
{
	// We are going to try and save information into a temp file
	// and if successful overwrite the original. If an error occurs during
	// the save operation original file won't be lost.
	QString orgFileName = QString::fromStdWString(fileName);
	std::wstring tmpFileName = fileName + L".swp";

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

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

		//start writing the file
		xw.writeOpenTag(cDesRootTag, AttrMap(cDesFileVerAttr, cDesFileVerVal));

		//write the definition of DES
		writeDefinition(xw);

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

		// We ignore result from file remove because this could be a Save As
		// operation where original file does not exist so we are going to
		// get false when trying to remove it.
		QFile::remove(orgFileName);
		bool success = file.rename(orgFileName);

		if (!success)
		{
			std::wstring msg = L"Failed to save changes to " + fileName;
			msg += L". Your changes are saved in " + tmpFileName;
			throw msg;
		}
	}
	catch (...)
	{
		std::wstring partFileName = fileName + L".part";
		file.rename(QString::fromStdWString(partFileName));
		throw EX("There was an error with saving your file. Partial information can be found in " 
				+ partFileName)
	}
}

//_________________________________________________________________________________________________

void DesSerializer::writeDefinition(XmlWriter& xw)
{
	//start the definition of DES. The definition contains the structure of the DES: states, events and transitions
	xw.writeOpenTag(cDesDefineTag, AttrMap(cDesDefineVerAttr, cDesDefineVerVal));

	writeHeader(xw);
	writeStates(xw);
	writeEvents(xw);
	writeTransitions(xw);

	//end the DES definition
	xw.writeCloseTag(cDesDefineTag);
}

//_________________________________________________________________________________________________

void DesSerializer::writeHeader(XmlWriter& xw)
{
	QString desName = m_alternateDesName.size() > 0 ? toString(m_alternateDesName) : toString(m_des.getName());
	xw.writeOpenTag(cDesHeaderTag, AttrMap(cDesNameAttr, desName, cDesTypeAttr, getDesType()));

	xw.writeAtomTag(cDesIntegTag, AttrMap(cDesIntegStatusAttr, getDesIntegrity(),
										  cDesIntegDateStampAttr, toString(m_des.getIntegrityStamp())));

	xw.writeAtomTag(cDesReachTag, AttrMap(cDesReachStatusAttr, getDesReachableProp(),
										  cDesReachDateStampAttr, toString(m_des.getReachableStamp())));

	xw.writeAtomTag(cDesNonBlockTag, AttrMap(cDesNonBlockStatusAttr, getDesNonBlockProp(),
										  cDesNonBlockDateStampAttr, toString(m_des.getNonBlockingStamp())));

	xw.writeCloseTag(cDesHeaderTag);
}

//_________________________________________________________________________________________________

void DesSerializer::writeStates(XmlWriter& xw)
{
	//start the list of states
	xw.writeOpenTag(cDesStateListTag, AttrMap(cDesStateCountAttr, toString(m_des.getStateCount()),
											  cDesStateMarkedCountAttr, toString(m_des.getMarkedStateCount())));

	//write the initial state
	if (m_des.hasInitialState())
	{
		const DesState& initState = m_des.getInitialState();

		AttrMap stateAttr(cDesStateIdAttr, toString(initState.getId()),
						  cDesStateNameAttr, toString(initState.getName()),
						  cDesStateAliasAttr, toString(initState.getAlias()),
						  cDesStateMarkedAttr, getStateMarkStatus(initState),
						  cDesStateReachableAttr, getStateReachStatus(initState)
						  );
		addGedInfo(initState, stateAttr);
		xw.writeAtomTag(cDesInitStateTag, stateAttr);
	}
	//iterate through all the states and serialize them
	Des::StateIteratorPtr stateIt = m_des.createStateIterator();
	for(stateIt->first(); stateIt->isDone() == false; stateIt->next())
	{
		const DesState& state = stateIt->currentItem();

		if (state.isInit())
			continue; //skip the initial state as it was previously written

		AttrMap stateAttr(cDesStateIdAttr, toString(state.getId()),
						  cDesStateNameAttr, toString(state.getName()),
						  cDesStateAliasAttr, toString(state.getAlias()),
						  cDesStateMarkedAttr, getStateMarkStatus(state),
						  cDesStateReachableAttr, getStateReachStatus(state)
						  );
		addGedInfo(state, stateAttr);
		xw.writeAtomTag(cDesStateTag, stateAttr);
	}

	//end the list of states
	xw.writeCloseTag(cDesStateListTag);
}

//_________________________________________________________________________________________________

void DesSerializer::writeEvents(XmlWriter& xw)
{
	//create the list of attributes for the event list; start with the common ones
	AttrMap attrMap(cDesEventCountAttr,			toString(m_des.getEventCount()),
				    cDesEventCtrlCountAttr,		toString(m_des.getEventCountByCtrl(true)),
				    cDesEventUnctrlCountAttr,	toString(m_des.getEventCountByCtrl(false)));

	//depending on the type of DES add the proper event counters
	switch(m_des.getType())
	{
		case eRegularDes:
			attrMap.add(cDesEventDefCountAttr, toString(m_des.getEventCountByType(eDefaultEvent)));
			break;

		case eSubsystemDes:
			attrMap.add(cDesEventDefCountAttr, toString(m_des.getEventCountByType(eDefaultEvent)));
			attrMap.add(cDesEventAnsCountAttr, toString(m_des.getEventCountByType(eAnswerEvent)));
			attrMap.add(cDesEventReqCountAttr, toString(m_des.getEventCountByType(eRequestEvent)));
			break;

		case eInterfaceDes:
			attrMap.add(cDesEventAnsCountAttr, toString(m_des.getEventCountByType(eAnswerEvent)));
			attrMap.add(cDesEventReqCountAttr, toString(m_des.getEventCountByType(eRequestEvent)));
			attrMap.add(cDesEventLdCountAttr, toString(m_des.getEventCountByType(eLDataEvent)));
			break;

		default:
			assert(false); //unknown DES type
			attrMap.add(cDesEventDefCountAttr, toString(m_des.getEventCountByType(eDefaultEvent)));
			break;
	}

	xw.writeOpenTag(cDesEventListTag, attrMap);

	//iterate through all events and serialize them
	Des::EventIteratorPtr eventIt = m_des.createEventIterator();
	for(eventIt->first(); eventIt->isDone() == false; eventIt->next())
	{
		const DesEvent& event = eventIt->currentItem();

		xw.writeAtomTag(cDesEventTag, AttrMap(cDesEventIdAttr, toString(event.getId()),
											  cDesEventNameAttr, toString(event.getName()),
											  cDesEventAliasAttr, toString(event.getAlias()),
											  cDesEventCtrlAttr, getEventCtrlStatus(event),
											  cDesEventTypeAttr, getEventType(event)
											  ));
	}

	xw.writeCloseTag(cDesEventListTag);
}

//_________________________________________________________________________________________________

void DesSerializer::writeTransitions(XmlWriter& xw)
{
	//start the transition function
	xw.writeOpenTag(cDesTransFuncTag, AttrMap(cDesTransCountAttr, toString(m_des.getTransCount())));

		//serialize the transition map
		xw.writeOpenTag(cDesTransMapTag, AttrMap(cDesTransMapCountAttr, toString(m_des.getTransCount(false))));

			//iterate through all transitions in the map (excluding global self-transitions)
			Des::TransIteratorPtr transIt = m_des.createTransIterator(true); //true - exclude global self-loops
			for(transIt->first(); !transIt->isDone(); transIt->next())
			{
				const DesTransition& trans = transIt->currentItem();

				AttrMap transAttr(cDesTransFromStateIdAttr, toString(trans.fromState().getId()),
						  cDesTransEventIdAttr, toString(trans.event().getId()),
						  cDesTransToStateIdAttr, toString(trans.toState().getId()));

				if (m_des.hasGedInfo())
				{
					const GedDesTrans* gedTrans = m_desHelper.gedTrans(&trans);
					if (gedTrans)
					{
						QPointF labelPos = gedTrans->labelPos();
						transAttr.add(cDesTransLabelPosXAttr, QString::number(labelPos.x()));
						transAttr.add(cDesTransLabelPosYAttr, QString::number(labelPos.y()));
						if(trans.fromState()!=trans.toState())
						{
							QList<QPointF> pt_lst = gedTrans->getPos();
							transAttr.add(cDesTransPosCntAttr, QString::number(pt_lst.count()));
							xw.writeOpenTag(cDesTransTag, transAttr);
							for(int i=0; i<pt_lst.count(); i++)
							{
								xw.writeAtomTag(cDesTransPosTag,
										AttrMap(cDesTransPosXAttr, QString::number(pt_lst[i].x()),
												cDesTransPosYAttr, QString::number(pt_lst[i].y())));
							}
						}
						else // self loop
						{
							qreal angle = gedTrans->angle();
							transAttr.add(cDesTransPosAngAttr, QString::number(angle));
							xw.writeOpenTag(cDesTransTag, transAttr);
						}
						xw.writeCloseTag(cDesTransTag);
					}
					else
					{
						xw.writeAtomTag(cDesTransTag, transAttr);
					}
				}
				else
				{
					xw.writeAtomTag(cDesTransTag, transAttr);
				}
			}

		//close the transition map
		xw.writeCloseTag(cDesTransMapTag);

		//serialize the global self-transitions
		xw.writeOpenTag(cDesSelfTransListTag, AttrMap(cDesSelfTransCountAttr, toString(m_des.getSelfTransCount())));

			Des::SelfTransIteratorPtr selfTransIt = m_des.createSelfTransIterator();
			for(selfTransIt->first(); selfTransIt->isDone() == false; selfTransIt->next())
			{
				const DesEvent& selfTrans = selfTransIt->currentItem();

				xw.writeAtomTag(cDesSelfTransTag, AttrMap(cDesTransEventIdAttr, toString(selfTrans.getId())));
			}

		//close the list of self transitions
		xw.writeCloseTag(cDesSelfTransListTag);

	//end the transition function
	xw.writeCloseTag(cDesTransFuncTag);
}

//_________________________________________________________________________________________________

QString DesSerializer::getDesType()
{
	switch(m_des.getType())
	{
		case eRegularDes:
			return cDesTypeVal_Regular;

		case eSubsystemDes:
			return cDesTypeVal_Subsystem;

		case eInterfaceDes:
			return cDesTypeVal_Interface;

		default:
			assert(false);
			return cDesTypeVal_Regular;
	}
}

//_________________________________________________________________________________________________

DesType DesSerializer::getDesType(const QString& desTypeName)
{
	if (desTypeName == cDesTypeVal_Regular)
	{
		return eRegularDes;
	}
	else if (desTypeName == cDesTypeVal_Subsystem)
	{
		return eSubsystemDes;
	}
	else if (desTypeName ==	cDesTypeVal_Interface)
	{
		return eInterfaceDes;
	}
	else
	{
		assert(false);
		return eRegularDes; //unknown type name -> return regular DES (the default)
	}
}

//_________________________________________________________________________________________________

QString DesSerializer::getDesIntegrity()
{
	switch(m_des.getIntegrity())
	{
		case eIntegYes:
			return cDesIntegStatusVal_Yes;

		case eIntegNo:
			return cDesIntegStatusVal_No;

		case eIntegNotVerified:
		default:
			return cDesIntegStatusVal_NotVerified;
	}
}

//_________________________________________________________________________________________________

Integrity DesSerializer::getDesIntegrity(const QString& desIntegName)
{
	if (desIntegName == cDesIntegStatusVal_Yes)
	{
		return eIntegYes;
	}
	else if (desIntegName == cDesIntegStatusVal_NotVerified)
	{
		return eIntegNotVerified;
	}
	else if (desIntegName == cDesIntegStatusVal_No)
	{
		return eIntegNo;
	}
	else
	{
		assert(false);
		return eIntegNotVerified; //unknown name return default
	}
}

//_________________________________________________________________________________________________

QString DesSerializer::getDesReachableProp()
{
	switch(m_des.getReachableProp())
	{
		case eReachableYes:
			return cDesReachStatusVal_Yes;

		case eReachableNotVerified:
			return cDesReachStatusVal_NotVerified;

		case eReachableNo:
			return cDesReachStatusVal_No;

		default:
			assert(false);
			return cDesReachStatusVal_NotVerified;
	}
}

//_________________________________________________________________________________________________

ReachableProp DesSerializer::getDesReachableProp(const QString& desReachName)
{
	if (desReachName == cDesReachStatusVal_Yes)
	{
		return eReachableYes;
	}
	else if (desReachName == cDesReachStatusVal_NotVerified)
	{
		return eReachableNotVerified;
	}
	else if (desReachName == cDesReachStatusVal_No)
	{
		return eReachableNo;
	}
	else
	{
		assert(false);
		return eReachableNotVerified; //unknown name return default
	}
}

//_________________________________________________________________________________________________

QString DesSerializer::getDesNonBlockProp()
{
	switch(m_des.getNonBlockingProp())
	{
		case eNonBlockYes:
			return cDesNonBlockStatusVal_Yes;

		case eNonBlockNotVerified:
			return cDesNonBlockStatusVal_NotVerified;

		case eNonBlockNo:
			return cDesNonBlockStatusVal_No;

		default:
			assert(false);
			return cDesNonBlockStatusVal_NotVerified;
	}
}

//_________________________________________________________________________________________________

NonBlockingProp DesSerializer::getDesNonBlockProp(const QString& desNonBlockName)
{
	if (desNonBlockName == cDesNonBlockStatusVal_Yes)
	{
		return eNonBlockYes;
	}
	else if (desNonBlockName == cDesNonBlockStatusVal_NotVerified)
	{
		return eNonBlockNotVerified;
	}
	else if (desNonBlockName == cDesNonBlockStatusVal_No)
	{
		return eNonBlockNo;
	}
	else
	{
		assert(false);
		return eNonBlockNotVerified; //unknown name return default
	}
}

//_________________________________________________________________________________________________

QString DesSerializer::getStateMarkStatus(const DesState& state)
{
	return (state.isMarked() ? cDesStateMarkedVal_Yes : cDesStateMarkedVal_No);
}

//_________________________________________________________________________________________________

QString DesSerializer::getStateReachStatus(const DesState& state)
{
	return state.isReachKnown() ?
				(state.isReachable() ? cDesStateReachableVal_Yes : cDesStateReachableVal_No) :
				cDesStateReachableVal_Unknown;
}

//_________________________________________________________________________________________________

bool DesSerializer::getStateMarkStatus(const QString& markStatus)
{
	return (markStatus == cDesStateMarkedVal_Yes);
}

//_________________________________________________________________________________________________

ReachableProp DesSerializer::getStateReachStatus(const QString& reachStatus)
{
	if (reachStatus == cDesStateReachableVal_Yes)
	{
        return eReachableYes;
	}
	else if (reachStatus == cDesStateReachableVal_No)
	{
        return eReachableNo;
	}
	else
	{
        return eReachableNotVerified;
	}
}

//_________________________________________________________________________________________________

QString DesSerializer::getEventCtrlStatus(const DesEvent& event)
{
	return (event.isControllable() ? cDesEventCtrlVal_Yes : cDesEventCtrlVal_No);
}

//_________________________________________________________________________________________________

bool DesSerializer::getEventCtrlStatus(const QString& status)
{
	return (status == cDesEventCtrlVal_Yes);
}

//_________________________________________________________________________________________________

QString DesSerializer::getEventType(const DesEvent& event)
{
	switch(event.getType())
	{
		case eDefaultEvent:
			return cDesEventTypeVal_Def;

		case eRequestEvent:
			return cDesEventTypeVal_Req;

		case eAnswerEvent:
			return cDesEventTypeVal_Ans;

		case eLDataEvent:
			return cDesEventTypeVal_Ld;

		default:
			assert(false);
			return cDesEventTypeVal_Def;
	}
}

//_________________________________________________________________________________________________

EventType DesSerializer::getEventType(const QString& type)
{
	if (type == cDesEventTypeVal_Def)
	{
		return eDefaultEvent;
	}
	else if (type == cDesEventTypeVal_Req)
	{
		return eRequestEvent;
	}
	else if (type == cDesEventTypeVal_Ans)
	{
		return eAnswerEvent;
	}
	else if (type == cDesEventTypeVal_Ld)
	{
		return eLDataEvent;
	}
	else
	{
		assert(false);
		return eDefaultEvent;
	}
}

//_________________________________________________________________________________________________

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

//_________________________________________________________________________________________________

QString DesSerializer::toString(unsigned long long longlongVal)
{
	return QVariant(longlongVal).toString();
}

//_________________________________________________________________________________________________

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

//_________________________________________________________________________________________________

//the parsing is starting
bool DesSerializer::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_desFileVer.clear();
	m_desDefVer.clear();

	return true;
}
//_________________________________________________________________________________________________

//the parsing is starting
bool DesSerializer::endDocument()
{
	if (mode == CheckGraph)
	{
		// No modifications in CheckGraph mode
		return true;
	}

	//set all properties loaded at the beginning of the parsing. This must be
	//done now at the end because otherwise they are reset when the DES is being loaded
	//with states, events and transitions
	m_desAccess->setIntegrity(m_integStatus);
	m_desAccess->setIntegrityStamp(m_integDateStamp);

	m_desAccess->setReachableProp(m_reachStatus);
	m_desAccess->setReachableStamp(m_reachDateStamp);

	m_desAccess->setNonBlockingProp(m_nonBlockStatus);
	m_desAccess->setNonBlockingStamp(m_nonBlockDateStamp);

	return true;
}

//_________________________________________________________________________________________________

//element and attribute parsing
bool DesSerializer::startElement(const QString& /*namespaceURI*/, const QString& localName,
						  const QString& /*qName*/, const QXmlAttributes& atts)
{
	if (localName == cDesRootTag)
	{
		return readDesRoot(localName, atts);
	}
	else
	{
		return readDesDefinition(localName, atts);
	}
}

bool DesSerializer::endElement(const QString& /*namespaceURI*/, const QString& /*localName*/,
						  const QString& qName)
{
	if (qName == cDesTransTag && mode != CheckGraph && hasGedInfo)
	{
		bool isNew;
		if (isSelfLoopTrans)
			m_desHelper.insertTrans(m_pCurTrans, angle, transLabelPos, isNew);
		else if (transPos.size() > 0)
			m_desHelper.insertTrans(m_pCurTrans, transPos, transLabelPos, isNew);
	}

	return true;
}

//_________________________________________________________________________________________________

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

//_________________________________________________________________________________________________

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

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

	return false; //stop parsing
}

//_________________________________________________________________________________________________

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

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

	return false; //stop parsing
}

//_________________________________________________________________________________________________

bool DesSerializer::isHeaderTag(const QString& tagName)
{
	return (tagName == cDesHeaderTag) || (tagName == cDesIntegTag) || (tagName == cDesNonBlockTag) ||
		   (tagName == cDesReachTag);
}

//_________________________________________________________________________________________________

bool DesSerializer::isStateTag(const QString& tagName)
{
	return (tagName == cDesStateListTag) || (tagName == cDesInitStateTag) || (tagName == cDesStateTag);
}

//_________________________________________________________________________________________________

bool DesSerializer::isEventTag(const QString& tagName)
{
	return (tagName == cDesEventListTag) || (tagName == cDesEventTag);
}

//_________________________________________________________________________________________________

bool DesSerializer::isTransTag(const QString& tagName)
{
	return (tagName == cDesTransFuncTag) || (tagName ==	cDesTransMapTag) ||
		   (tagName == cDesSelfTransListTag) || (tagName == cDesTransTag) ||
		   (tagName == cDesSelfTransTag) || (tagName == cDesTransPosTag);
}

//_________________________________________________________________________________________________

bool DesSerializer::readDesRoot(const QString& /*tagName*/, const QXmlAttributes& atts)
{
	m_desFileVer = atts.value(cDesFileVerAttr);
	if (m_desFileVer > cDesFileVerVal)
	{
		//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_desFileVer.toAscii().data(), cDesFileVerVal.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

bool DesSerializer::readDesDefinition(const QString& tagName, const QXmlAttributes& atts)
{
	if (tagName == cDesDefineTag)
	{
		//read the root DES definition tag containing the version of the DES definition
		m_desDefVer = atts.value(cDesDefineVerAttr);
		if (m_desDefVer > cDesDefineVerVal)
		{
			qWarning("Des definition version (%s) is greater than the one supported (%s). The loading may fail.",
                                         m_desDefVer.toAscii().data(), cDesDefineVerVal.toAscii().data());
		}

		return true;
	}
	else if (isHeaderTag(tagName))
	{
		return readDesHeader(tagName, atts);
	}
	else if (isStateTag(tagName))
	{
		return readDesStates(tagName, atts);
	}
	else if (isEventTag(tagName))
	{
		return readDesEvents(tagName, atts);
	}
	else if (isTransTag(tagName))
	{
		return readDesTransitions(tagName, atts);
	}
	else
	{
		assert(true);
		qWarning("Unknown tag %s. Tag will be ingored", tagName.toAscii().data());
		return true;
	}
}

//_________________________________________________________________________________________________

bool DesSerializer::readDesHeader(const QString& tagName, const QXmlAttributes& atts)
{
	if (mode == CheckGraph)
	{
		// No modifications to des in this mode.
		return true;
	}

	if (tagName == cDesHeaderTag)
	{
		//read the name of DES
		QString desName = atts.value(cDesNameAttr);
		m_des.setName(desName.toStdWString());

		//read the type of DES
		QString desTypeName = atts.value(cDesTypeAttr);
		m_des.setType(getDesType(desTypeName));
	}
	else if (tagName == cDesIntegTag)
	{
		//read the integrity status ...
		QString integStatusName = atts.value(cDesIntegStatusAttr);
		m_integStatus = getDesIntegrity(integStatusName);

		//... and the time stamp for the integrity check
		QString integTimeStamp = atts.value(cDesIntegDateStampAttr);
		m_integDateStamp = integTimeStamp.toStdWString();
	}
	else if (tagName == cDesReachTag)
	{
		//read the reachability status ...
		QString reachStatusName = atts.value(cDesReachStatusAttr);
		m_reachStatus = getDesReachableProp(reachStatusName);

		//... and the time stamp for the reachability status
		QString reachTimeStamp = atts.value(cDesReachDateStampAttr);
		m_reachDateStamp = reachTimeStamp.toStdWString();
	}
	else if (tagName == cDesNonBlockTag)
	{
		//read the nonblocking status ...
		QString nonBlockStatusName = atts.value(cDesNonBlockStatusAttr);
		m_nonBlockStatus = getDesNonBlockProp(nonBlockStatusName);

		//... and the time stamp for the non-blocking status
		QString nonBlockTimeStamp = atts.value(cDesNonBlockDateStampAttr);
		m_nonBlockDateStamp = nonBlockTimeStamp.toStdWString();
	}
	else
	{
		assert(true);
		qWarning("Unknown tag %s. Tag will be ingored", tagName.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

bool DesSerializer::readDesStates(const QString& tagName, const QXmlAttributes& atts)
{
	if (tagName == cDesStateListTag)
	{
		QVariant stateCount = atts.value(cDesStateCountAttr);
		QVariant markedStateCount = atts.value(cDesStateMarkedCountAttr);

		//TODO: do something with this counters. there is not point in setting these because
		//they are obtained by counting actual states which are not there yet. Hoever it
		//maybe useful to get them only partial-loading a des for viewing in a project. Partial
		//loading is to be determined later
	}
	else if (tagName == cDesInitStateTag || tagName == cDesStateTag)
	{
		// Read DES values
		QVariant stateId = atts.value(cDesStateIdAttr);
		QString  stateName = atts.value(cDesStateNameAttr);
		QString  stateAlias = atts.value(cDesStateAliasAttr);
		bool     isMarked = getStateMarkStatus(atts.value(cDesStateMarkedAttr));
		ReachableProp isReach = getStateReachStatus(atts.value(cDesStateReachableAttr));
		bool     isInit = (tagName == cDesInitStateTag);

		// Read GED values
		QString posx = atts.value(cDesStatePosXAttr);  //State position
		QString posy = atts.value(cDesStatePosYAttr);
		QString labx = atts.value(cDesStateLabelXAttr); //Label position
		QString laby = atts.value(cDesStateLabelYAttr);

		hasGedInfo = !(posx.isEmpty() || posy.isEmpty() ||
				labx.isEmpty() || laby.isEmpty());

		if (mode == CheckGraph)
		{
			// Return whether or not graphical information is available.
			// In either case we don't modify m_des in CheckGraph mode.
			return hasGedInfo;
		}

		DesState* pState = new DesState(stateId.toULongLong());
		pState->setName(stateName.toStdWString());
		pState->setAlias(stateAlias.toStdWString());
		pState->setMarked(isMarked);
		pState->setReachabilityState(isReach);
		pState->setInit(isInit);

		statePool().addState(pState);
		if (isInit)
		{
			m_des.setInitialState(*pState);
		}

		if (hasGedInfo)
		{
			// Only add ged info if available
			QPointF stPos = QPointF(posx.toFloat(),posy.toFloat());
			QPointF lblPos = QPointF(labx.toFloat(),laby.toFloat());

			m_desHelper.insertState(pState, stPos, lblPos);
		}
	}
	else
	{
		assert(false);   //unknown tag
		qWarning("Unknown tag %s. Tag will be ingored", tagName.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

bool DesSerializer::readDesEvents(const QString& tagName, const QXmlAttributes& atts)
{
	if (mode == CheckGraph)
	{
		// We don't care about events during CheckGraph mode and
		// we don't want any modifications to m_des in this mode either.
		return true;
	}

	if (tagName == cDesEventListTag)
	{
		QVariant eventCount = atts.value(cDesEventCountAttr);
		QVariant ctrlEventCount = atts.value(cDesEventCtrlCountAttr);
		QVariant unctrlEventCount = atts.value(cDesEventUnctrlCountAttr);
		QVariant defaultEventCount = atts.value(cDesEventDefCountAttr);
		QVariant ansEventCount = atts.value(cDesEventAnsCountAttr);
		QVariant reqEventCount = atts.value(cDesEventReqCountAttr);
		QVariant ldEventCount = atts.value(cDesEventLdCountAttr);

		//TODO: do something with this counters. there is not point in setting these because
		//they are obtained by counting actual events which are not there yet. Hoever it
		//maybe useful to get them only partial-loading a des for viewing in a project. Partial
		//loading is to be determined later
	}
	else if (tagName == cDesEventTag)
	{
		QVariant eventId = atts.value(cDesEventIdAttr);
		QString eventName = atts.value(cDesEventNameAttr);
		QString eventAlias = atts.value(cDesEventAliasAttr);
		bool     isCtrl = getEventCtrlStatus(atts.value(cDesEventCtrlAttr));
		EventType type = getEventType(atts.value(cDesEventTypeAttr));

		//create an event with the above properties
		DesEvent* pEvent = new DesEvent(eventId.toULongLong());
		pEvent->setName(eventName.toStdWString());
		pEvent->setAlias(eventAlias.toStdWString());
		pEvent->setControllable(isCtrl);
		pEvent->setType(type);

		eventPool().addEvent(pEvent);
	}
	else
	{
		assert(false);   //unknown tag
		qWarning("Unknown tag %s. Tag will be ingored", tagName.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

bool DesSerializer::readDesTransitions(const QString& tagName, const QXmlAttributes& atts)
{
	if (tagName == cDesTransFuncTag)
	{
		QVariant totalTransCount = atts.value(cDesTransCountAttr);
	}
	else if (tagName == cDesTransMapTag)
	{
		QVariant transCount = atts.value(cDesTransMapCountAttr);

		//TODO: do something with this counters. there is no point in setting these because
		//they are obtained by counting actual transitions which are not there yet. Hoever it
		//maybe useful to get them only partial-loading a des for viewing in a project. Partial
		//loading is to be determined later
	}
	else if (tagName == cDesSelfTransListTag)
	{
		QVariant selfTransCount = atts.value(cDesSelfTransCountAttr);

		//TODO: do something with this counters. there is no point in setting these because
		//they are obtained by counting actual transitions which are not there yet. Hoever it
		//maybe useful to get them only partial-loading a des for viewing in a project. Partial
		//loading is to be determined later
	}
	else if (tagName == cDesTransTag)
	{
		QVariant fromStateId = atts.value(cDesTransFromStateIdAttr);
		QVariant eventId = atts.value(cDesTransEventIdAttr);
		QVariant toStateId = atts.value(cDesTransToStateIdAttr);

		QString labx = atts.value(cDesTransLabelPosXAttr); //Label position
		QString laby = atts.value(cDesTransLabelPosYAttr);

		QString angleStr = atts.value(cDesTransPosAngAttr);

		const DesState& fromState = m_des.getState(fromStateId.toULongLong());
		const DesEvent& event = m_des.getEvent(eventId.toULongLong());
		const DesState& toState = m_des.getState(toStateId.toULongLong());

		isSelfLoopTrans = (fromStateId == toStateId);
		hasGedInfo = !angleStr.isEmpty();

		if (mode == CheckGraph)
		{
			// We want to check angle information for self loop or
			// quit otherwise since we don't want to modify des.
			if (isSelfLoopTrans)
			{
				return hasGedInfo;
			}
			else
			{
				return true;
			}
		}

		m_pCurTrans = new DesTransition(fromState, event, toState);
		m_pTransFunc->addTransition(*m_pCurTrans);

		angle = angleStr.toFloat();
		transLabelPos = QPointF(labx.toFloat(),laby.toFloat());
		transPos.clear();
	}
	else if (tagName == cDesTransPosTag)
	{
		QString posx = atts.value(cDesTransPosXAttr);  //State position
		QString posy = atts.value(cDesTransPosYAttr);
		hasGedInfo = !(posx.isEmpty() && posy.isEmpty());

		if (mode == CheckGraph)
		{
			// Stop parsing if no ged info available otherwise quit so
			// we don't modify des information in CheckGraph mode.
			return hasGedInfo;
		}

		if (hasGedInfo)
		{
			transPos.append(QPointF(posx.toFloat(),posy.toFloat()));
		}
	}
	else if (tagName == cDesSelfTransTag)
	{
		if (mode == CheckGraph)
		{
			// Don't modify des in CheckGraph mode and there is no
			// position information in this tag so quit
			return true;
		}

		QVariant eventId = atts.value(cDesTransEventIdAttr);
		const DesEvent& event = m_des.getEvent(eventId.toULongLong());
		m_pTransFunc->addGlobalSelfTransition(event);
	}
	else
	{
		assert(false);   //unknown tag
		qWarning("Unknown tag %s. Tag will be ingored", tagName.toAscii().data());
	}

	return true;
}

//_________________________________________________________________________________________________

StatePool& DesSerializer::statePool()
{
	if (m_pStatePool == null)
		throw EX("State pool not initialized. Cannot return")

	return *m_pStatePool;
}

//_________________________________________________________________________________________________

EventPool& DesSerializer::eventPool()
{
	if (m_pEventPool == null)
		throw EX("Event pool not initialized. Cannot return")

	return *m_pEventPool;
}

void DesSerializer::addGedInfo(const DesState& state, AttrMap& stateAttr)
{
	if (!m_des.hasGedInfo())
	{
		// No graphics info available
		return;
	}

	Des::GedInfo& stateMap = m_des.getGedInfo();
	const GedDesState* gedState = stateMap.value(&state, 0);
	if (!gedState)
	{
		// State not found, can't do anything, so quit
		return;
	}

	QPointF pos = gedState->pos();
	QPointF labelPos = gedState->labelPos();

	stateAttr.add(cDesStatePosXAttr, QString::number(pos.x()));
	stateAttr.add(cDesStatePosYAttr, QString::number(pos.y()));
	stateAttr.add(cDesStateLabelXAttr, QString::number(labelPos.x()));
	stateAttr.add(cDesStateLabelYAttr, QString::number(labelPos.y()));
}

} //namespace DESPot

