/*	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 "DesInterface.h"
#include "DesIterator.h"
#include "Des.h"
#include "ProjectEventPool.h"
#include "SyncAlgo.h"
#include "DesIntegrityAlgo.h"
#include "InstMapIterator.h"
#include "Instantiation.h"

namespace DESpot
{

DesInterface::DesInterface(const std::wstring& name, ProjectEventPool& eventPool):
			m_name(name),
                        m_isValid(eIntegNotVerified),
                        m_isInterfConsist(eIConsNotVerified),
                        m_syncDes(null),
                        m_eventPool(eventPool),
			m_bLoadInProgress(false)
{
	m_subsys=null;
}

//added Parul it is called when adding interface and link with the lowlevel subsystem
DesInterface::DesInterface(const std::wstring& name,const DesSubsystem* subsys, ProjectEventPool& eventPool):
			m_name(name),
                        m_isValid(eIntegNotVerified),
                        m_isInterfConsist(eIConsNotVerified),
                        m_syncDes(null),
                        m_eventPool(eventPool),
						m_subsys(subsys),
						m_bLoadInProgress(false)
{
}
//_________________________________________________________________________________________________
//When an interface is destroyed all its DES are destroyed as well
DesInterface::~DesInterface(void)
{
	if (m_syncDes)
	{
		delete m_syncDes;
		m_syncDes = null;
	}

	for(std::vector<DesNotificationSink*>::iterator sinkIt = m_desNotifSinks.begin(); sinkIt != m_desNotifSinks.end(); sinkIt++)
	{
		DesNotificationSink* sink = *sinkIt;
		delete sink;
	}

	for(DesMapIt desIt = m_supDesMap.begin(); desIt != m_supDesMap.end(); desIt++)
	{
		Des* pDes = desIt->second;
		delete pDes;
		pDes = null;
	}
	
	for(DesMapIt desIt = m_templateDesMap.begin(); desIt != m_templateDesMap.end(); desIt++)
	{
		Des* pDes = desIt->second;
		delete pDes;
		pDes = null;
	}
	
	for(InstMapIt instIt = m_InstMap.begin(); instIt != m_InstMap.end(); instIt++)
	{
		Instantiation* pInst = instIt->second;
		delete pInst;
		pInst = null;
	}
}

//_________________________________________________________________________________________________

std::wstring DesInterface::getName() const
{
	return m_name;
}

//_________________________________________________________________________________________________

void DesInterface::setName(const std::wstring& name)
{
	if (name.empty())
		throw EX("Cannot give an interface an empty name")

	m_name = name;
}

//_________________________________________________________________________________________________

bool DesInterface::isValid() const
{
	return m_isValid == eIntegYes;
}

//_________________________________________________________________________________________________

void DesInterface::setValid(bool setValid /*= true*/) const
{
	m_isValid = setValid ? eIntegYes : eIntegNo;
}

//_________________________________________________________________________________________________

void DesInterface::clearValid() const
{
	m_isValid = eIntegNotVerified;
}

//_________________________________________________________________________________________________

Integrity DesInterface::getValidProp() const
{
	return m_isValid;
}

//_________________________________________________________________________________________________(these methods are inline now)

/*void DesInterface::setSubsystem(const DesSubsystem& subsys)
{
	m_subsys = &subsys;
}

//_________________________________________________________________________________________________

const DesSubsystem& DesInterface::getSubsystem() const
{
	return *m_subsys;
}*/
//_________________________________________________________________________________________________

//Read/write the interface consistency property of this interface
bool DesInterface::isInterfConsist() const
{
	return m_isInterfConsist == eIConsYes;
}

//_________________________________________________________________________________________________

void DesInterface::setInterfConsist(bool isIConsist) const
{
	m_isInterfConsist = isIConsist ? eIConsYes : eIConsNo;
}

//_________________________________________________________________________________________________

void DesInterface::clearInterfConsist() const
{
	m_isInterfConsist = eIConsNotVerified;
}

//_________________________________________________________________________________________________

IConsistProp DesInterface::getIConsistProp() const
{
	return m_isInterfConsist;
}

//_________________________________________________________________________________________________

Des& DesInterface::getDes(const std::wstring& desName,DesLevel* out_desLevel /*= null*/)
{
	Des* pDes = null;

	if (findDes(desName, &pDes, out_desLevel) == false)
	{
		//Des wasn't found in any of the two maps
		throw EX("The interface has no DES with the given name")
	}

	return *pDes;
}

//_________________________________________________________________________________________________

//Returns the sync product of the all the DES components. If it needs to be 
//calculated it calculates it
Des& DesInterface::getSyncDes() const
{
	if (m_syncDes == null)
	{
		switch(m_supDesMap.size())
		{
			case 0:
			{
				throw EX("Interface is empty. Cannot calculate the SYNC product of its DES.");				
			}
			break;

			case 1:
			{
				//leave m_syncDes as null since we do not have to create a different DES we
				//can just return the single DES we have in the interface at this point
				return *(m_supDesMap.begin()->second);
			}
			break;	

			default:
			{
				//the sync product needs to be computed
				SyncAlgo syncAlgo(createDesIterator(), false);
				syncAlgo.runAlgo();

				m_syncDes = syncAlgo.returnResult();

				//check the integrity of the interface DES
				//DesIntegrityAlgo integAlgo(m_syncDes);
				//m_syncDes->checkIntegrity(integAlgo);
			}
			break;
		}
	}	

	return *m_syncDes;
}


//_________________________________________________________________________________________________

//Invalidates the sync product if it was computed already such that next time
//a client asks for it a new one is produced. Used primarly when the interface is modified
void DesInterface::clean(bool deepClean)
{
	//properties are reset when the interface changes. However change done while loading
	//the interface from a project file cannot be taken into consideration
	if (m_bLoadInProgress)
		return;

	clearValid();
	clearInterfConsist();

	if (m_syncDes)
	{
		delete m_syncDes;
		m_syncDes = null;
	}

	if (deepClean)
	{
		//reset the properties of all DES belonging to this interface
		for(DesMapIt desIt = m_supDesMap.begin(); desIt != m_supDesMap.end(); desIt++)
		{
			Des* pDes = desIt->second;
			pDes->clean();
		}
		
		for(DesMapIt desIt2 = m_templateDesMap.begin();desIt2!=m_templateDesMap.end();desIt2++)
		{
			Des* pDes = desIt2->second;
			pDes->clean();
		}
	}
}

//_________________________________________________________________________________________________

const Des& DesInterface::getDes(const std::wstring& desName,DesLevel* out_desLevel /*= null*/) const
{
	const Des* pDes = null;

	if (findDes(desName, &pDes, out_desLevel) == false)
	{
		//Des wasn't found in any of the two maps
		throw EX("The interface has no DES with the given name")
	}

	return *pDes;	
	
}

//_________________________________________________________________________________________________

//Create an iterator that goes through all the internal DES of this interface
DesInterface::DesIteratorPtr DesInterface::createDesIterator() const
{
	DesMapIterator* pDesIt = new DesMapIterator(m_supDesMap);
	return DesIteratorPtr(pDesIt);
}
//_________________________________________________________________________________________________
//Create an iterator that goes through either the plant or the supervisor DES
DesInterface::DesIteratorPtr DesInterface::createDesIterator(DesLevel desLevel) const
{
	DesMapIterator* pDesIt = new DesMapIterator(getDesMap(desLevel));
	return DesIteratorPtr(pDesIt);
}


//_________________________________________________________________________________________________
//Create an iterator that goes through either the plant or the supervisor DES
DesInterface::DesIteratorPtr DesInterface::createDesIterator(DesLevel desLevel)
{
	DesMapIterator* pDesIt = new DesMapIterator(getDesMap(desLevel));
	return DesIteratorPtr(pDesIt);
}
//_________________________________________________________________________________________________
//add by bini
//Create an iterator that goes through all the instantiation of this template
DesInterface::InstIteratorPtr DesInterface::createInstIterator()
{
	InstMapIterator* pInstIt = new InstMapIterator(m_InstMap);
        return InstIteratorPtr(pInstIt);
}
//_________________________________________________________________________________________________
//add by bini
//Create an iterator that goes through all the instantiation of this template
DesInterface::InstIteratorPtr DesInterface::createInstIterator() const
{
	InstMapIterator* pInstIt = new InstMapIterator(m_InstMap);
        return InstIteratorPtr(pInstIt);
}
//_________________________________________________________________________________________________

int DesInterface::getDesCount() const
{
	return getSupDesCount()+getTemplateDesCount();
}
//_________________________________________________________________________________________________

//returns the number of supervisor DES in the subsystem
int DesInterface::getSupDesCount() const
{
	return m_supDesMap.size();
}

//_________________________________________________________________________________________________
//add by bini
//returns the number of supervisor DES in the subsystem
int DesInterface::getTemplateDesCount() const
{
	return m_templateDesMap.size();
}
//_________________________________________________________________________________________________

void DesInterface::addDes(Des* pDes, DesLevel desLevel)
{
	if (pDes == null)
		throw EX("Invalid argument. Cannot add null DES.");

	DesMap& desMap = getDesMap(desLevel);

    if ((pDes->getType() == eInterfaceDes)||(pDes->getType() == eInterfaceTemplateDes))
    {
    }
    else if(pDes->getLevel() == eTemplateDes)
    {
    }
    else
    {
		throw EX("Cannot add a non-interface DES to an interface");
    }
	//make sure the DES is not there already
	if (desMap.find(pDes->getName()) != desMap.end())
		throw EX("A DES with the same name is already part of the interface. Cannot add duplicates")

	desMap[pDes->getName()] = pDes;
	
	//set the owner of the DES to be this
	pDes->setOwner(this);

	//update the project event pool
	m_eventPool.addDesEvents(pDes, this);

	sinkDesEvents(pDes);

	clean(false);
}
//_________________________________________________________________________________________________
//add by bini
void DesInterface::addInstantiation(Instantiation* pInstantiation)
{
	if (pInstantiation == null)
		throw EX("Invalid argument. Cannot add null Instantiation.")

	//InstMap& instMap = getInstMap();

	//make sure the DES is not there already
	if (m_InstMap.find(pInstantiation->getName()) != m_InstMap.end())
		throw EX("An Instantiation with the same name is already part of the interface. Cannot add duplicates")


	
	m_InstMap[pInstantiation->getName()] = pInstantiation;

	clean(false);
}
//_________________________________________________________________________________________________

void DesInterface::changeDesName(Des& des, const std::wstring& newDesName)
{
		Des* pLocatedDes = null;
	DesMap* pDesMap = null;
	DesMapIt desMapIt;
	if (findDes(des.getName(), &pLocatedDes, &pDesMap, &desMapIt) == false)
	{
		throw EX("Cannot find a DES with the given name in this subsystem");
	}

		//The DES was found. Erase it from the map...
	if (&des != pLocatedDes)
	{
		assert(&des == pLocatedDes);
		throw EX("A different DES with the same name was found. Cannot rename DES")
	}

	//erase the DES from its map since we need to add it under the new name
	pDesMap->erase(desMapIt);

	//change the des name
	des.setName(newDesName);

	//add the des in the map under the new name
	(*pDesMap)[newDesName] = &des;
}

//_________________________________________________________________________________________________

//Event sync from when a name of a DES that is part of this interface is changed using
//the DES itself instead of the changeDesName(...) method. In this case the name  of 
//the DES has already been changed but the interface's internal maps reference it by the old name
//It is these internal maps that need to be updated
void DesInterface::onDesNameChanged(Des* des, const std::wstring& oldName, const std::wstring& newName)
{
	Des* pLocatedDes = null;
	DesMap* pDesMap = null;
	DesMapIt desMapIt;
	if (findDes(oldName, &pLocatedDes, &pDesMap, &desMapIt) == false)
	{
		if (findDes(newName, &pLocatedDes, &pDesMap, &desMapIt))
		{
			//the name has already been updated
			return;
		}
		else
		{
			throw EX("Cannot find a DES with the given name in this subsystem");
		}
	}

	//The DES was found. Erase it from the map...
	if (des != pLocatedDes)
	{
		assert(des == pLocatedDes);
		throw EX("A different DES with the same name was found. Cannot rename DES")
	}

	//erase the DES from its map since we need to add it under the new name
	pDesMap->erase(desMapIt);

	//NOTE, the name of the DES has already been changed so no need to do anything about the DES
	assert(des->getName() == newName);

	//add the des in the map under the new name
	(*pDesMap)[newName] = des;

}

//_________________________________________________________________________________________________

//removes the DES from the subsystem without destroying it
/*void DesSubsystem::removeDes(Des* pDes)
{
	//try to find a DES with this name either in the supervisor map or the plant map
	Des* pLocatedDes = null;
	DesMap* pDesMap = null;
	DesMapIt desMapIt;
	if (findDes(pDes->getName(), &pLocatedDes, &pDesMap, &desMapIt) == false)
	{
		throw EX("Des could not be found. Cannot delete")
	}

	//The DES was found. Erase it from the map...
	if (pDes != pLocatedDes)
	{
		assert(pDes == pLocatedDes);
		throw EX("A different DES with the same name was found. Cannot delete DES")
	}

	//erase the DES from its map
	pDesMap->erase(desMapIt);

	//update the project event pool
	m_eventPool.removeDesEventSet(pDes);

	removeDesSink(pDes);
	clean(false);
}
*/
//_________________________________________________________________________________________________

void DesInterface::deleteDes(const std::wstring& desName)
{
	Des* pDes = null;
	DesMap* pDesMap = null;
	DesMapIt desMapIt;
	if (findDes(desName, &pDes, &pDesMap, &desMapIt) == false)
	{
		throw EX("Des could not be found. Cannot delete")
	}

	//The DES was found. Erase it from the map...
	pDesMap->erase(desMapIt);

	//update the project event pool
	m_eventPool.removeDesEventSet(pDes);

	removeDesSink(pDes);
	clean(false);

	//...and then destroy the DES itself
	delete pDes;
	pDes = null;
}
//_________________________________________________________________________________________________
//add by bini
void DesInterface::deleteInstantiation(const std::wstring& instName)
{
	//try to find a DES with this name
	Instantiation* pInst = null;
	InstMapIt instMapIt = m_InstMap.find(instName);
	if (instMapIt == m_InstMap.end())
	{
		throw EX("Instantiation could not be found. Cannot delete")
	}
	pInst=instMapIt->second;
	//The DES was found. Erase it from the map...
	m_InstMap.erase(instMapIt);


	//removeDesSink(pDes);
	clean(false);

	//...and then destroy the DES itself
	delete pInst;
	pInst = null;
}

//_________________________________________________________________________________________________

bool DesInterface::findDes(const std::wstring& desName, Des** out_pDes /*= null*/, DesLevel* out_desLevel /*= null*/)
{
	//try to find it in the supervisor map
	DesMapIt supDesIt = m_supDesMap.find(desName);
	if (supDesIt != m_supDesMap.end())
	{
		//DES was found in the supervisor map
		if (out_desLevel)
		{
			*out_desLevel = eSupervisorDes;
		}

		if (out_pDes)
		{
			*out_pDes = supDesIt->second;
		}

		return true;
	}

	
//add by bini
	//Des was not found in the supervisor map and plant map try to find it in the template map
	DesMapIt templateDesIt = m_templateDesMap.find(desName);
	if (templateDesIt != m_templateDesMap.end())
	{
		//DES was found in the plant map
		if (out_desLevel)
		{
			*out_desLevel = eTemplateDes;
		}

		if (out_pDes)
		{
			*out_pDes = templateDesIt->second;
		}

		return true;
	}
//bini
	

	return false;
}

//_________________________________________________________________________________________________

bool DesInterface::findDes(const std::wstring& desName, const Des** out_pDes /*= null*/, DesLevel* out_desLevel /*= null*/) const
{
	//try to find it in the supervisor map
	DesMapCIt supDesIt = m_supDesMap.find(desName);
	if (supDesIt != m_supDesMap.end())
	{
		//DES was found in the supervisor map
		if (out_desLevel)
		{
			*out_desLevel = eSupervisorDes;
		}

		if (out_pDes)
		{
			*out_pDes = supDesIt->second;
		}

		return true;
	}

	
//add by bini
	//Des was not found in the supervisor map and plant map try to find it in the template map
	DesMapCIt templateDesIt = m_templateDesMap.find(desName);
	if (templateDesIt != m_templateDesMap.end())
	{
		//DES was found in the template map
		if (out_desLevel)
		{
			*out_desLevel = eTemplateDes;
		}

		if (out_pDes)
		{
			*out_pDes = templateDesIt->second;
		}

		return true;
	}
//bini
	

	return false;
}
//_________________________________________________________________________________________________
//add by bini
bool DesInterface::findInst(const std::wstring& instName, Instantiation** out_pInst /*= null*/)
{

	//try to find it in the supervisor map
	InstMapIt instIt = m_InstMap.find(instName);
	
	if (instIt != m_InstMap.end())
	{
		if (out_pInst)
		{
			*out_pInst = instIt->second;
		}
		
		return true;
	}
	return false;
}
//_________________________________________________________________________________________________
//add by bini
bool DesInterface::findInst(const std::wstring& instName, Instantiation** out_pInst /*= null*/) const
{
	//try to find it in the supervisor map
	InstMapCIt instIt = m_InstMap.find(instName);
	if (instIt != m_InstMap.end())
	{
		if (out_pInst)
		{
			*out_pInst = instIt->second;
		}
		return true;
	}
	return false;
}

//_________________________________________________________________________________________________
const DesInterface::DesMap& DesInterface::getDesMap(DesLevel desLevel) const
{
	switch(desLevel)
	{
		case eSupervisorDes:
			return m_supDesMap;

//add by bini
		case eTemplateDes:
			return m_templateDesMap;
//bini
		default:
			assert(false);
			throw EX("Unknown DES type")
	}
}

//_________________________________________________________________________________________________

DesInterface::DesMap& DesInterface::getDesMap(DesLevel desLevel)
{
	switch(desLevel)
	{
		case eSupervisorDes:
			return m_supDesMap;


		//add by bini
		case eTemplateDes:
			return m_templateDesMap;
		//bini

		default:
			assert(false);
			throw EX("Unknown DES type")
	}
}

//_______________________________________________________________________________________________//
//add by bini
const DesInterface::InstMap& DesInterface::getInstMap() const
{
	return m_InstMap;
}

//_________________________________________________________________________________________________
//add by bini
DesInterface::InstMap& DesInterface::getInstMap() 
{
	return m_InstMap;
}
//_________________________________________________________________________________________________
//Tries to find a DES with the given name either in the map of supervisor DES or in the
//plant ones. If the DES is found, the function returns true as a result as well as the DES
//itself, the map it was found in as well as the precise iterator pointint to it. The out parameters are
//all optional
bool DesInterface::findDes(const std::wstring& desName,
								 Des** out_des /*= null*/,
								 DesMap** out_desMap /*= null*/,
								 DesMapIt* out_desIt /*= null*/)
{
	//Try the supervisor map
	DesMapIt supDesIt = m_supDesMap.find(desName);
	if (supDesIt != m_supDesMap.end())
	{
		//found the DES in the list of supervisor DES
		if (out_des)
		{
			*out_des = supDesIt->second;
		}

		if (out_desMap)
		{
			*out_desMap = &m_supDesMap;
		}

		if (out_desIt)
		{
			*out_desIt = supDesIt;
		}

		return true;
	}

	//The DES wasn't found in the supervisor list -> try the plant map
	
//add by bini
//The DES wasn't found in the supervisor and plant list -> try the template map
	DesMapIt templateDesIt = m_templateDesMap.find(desName);
	if (templateDesIt != m_templateDesMap.end())
	{
		//found the DES in the list of supervisor DES
		if (out_des)
		{
			*out_des = templateDesIt->second;
		}

		if (out_desMap)
		{
			*out_desMap = &m_templateDesMap;
		}

		if (out_desIt)
		{
			*out_desIt = templateDesIt;
		}

		return true;
	}

	//DES not found
	return false;
}

//_________________________________________________________________________________________________
//add by bini
bool DesInterface::findInstantiation(const std::wstring& instName,
								 Instantiation** out_inst /*= null*/,
								 InstMap** out_instMap /*= null*/,
								 InstMapIt* out_instIt /*= null*/)
{
	//Try the supervisor map
	InstMapIt instIt = m_InstMap.find(instName);
	if (instIt != m_InstMap.end())
	{
		//found the DES in the list of supervisor DES
		if (out_inst)
		{
			*out_inst = instIt->second;
		}

		if (out_instMap)
		{
			*out_instMap = &m_InstMap;
		}

		if (out_instIt)
		{
			*out_instIt = instIt;
		}

		return true;
	}
	return false;
}
//_________________________________________________________________________________________________
void DesInterface::sinkDesEvents(Des* des)
{
	//create a notification listener for this des so we can tell when the des changed
	m_desNotifSinks.push_back(new DesNotificationSink(this, des));
}

//_________________________________________________________________________________________________

void DesInterface::removeDesSink(Des* des)
{
	for(std::vector<DesNotificationSink*>::iterator sinkIt = m_desNotifSinks.begin(); sinkIt != m_desNotifSinks.end(); sinkIt++)
	{
		DesNotificationSink* sink = *sinkIt;
		if (des == sink->des())
		{
			m_desNotifSinks.erase(sinkIt);
			
			sink->unsubscribe();
			delete sink;
			
			return;
		}
	}

	assert(false);
}

//_________________________________________________________________________________________________
////section added by parul begins
//section added by parul ends
//_________________________________________________________________________________________________

void DesInterface::setLoadInProgress(bool loadInProgress /*= true*/)
{
	m_bLoadInProgress = loadInProgress;
}

}; //end of namespace DESpot
