/*	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"

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)
{
}

//_________________________________________________________________________________________________
//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_desMap.begin(); desIt != m_desMap.end(); desIt++)
	{
		Des* pDes = desIt->second;
		delete pDes;
		pDes = 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;
}

//_________________________________________________________________________________________________

//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)
{
	//try to find it in the supervisor map
	DesMapIt desIt = m_desMap.find(desName);
	if (desIt != m_desMap.end())
	{
		return *(desIt->second);
	}

	//Des wasn't found in the interface
	throw EX("The interface has no DES with the given name")
}

//_________________________________________________________________________________________________

//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_desMap.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_desMap.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_desMap.begin(); desIt != m_desMap.end(); desIt++)
		{
			Des* pDes = desIt->second;
			pDes->clean();
		}
	}
}

//_________________________________________________________________________________________________

const Des& DesInterface::getDes(const std::wstring& desName) const
{
	//try to find it in the supervisor map
	DesMapCIt desIt = m_desMap.find(desName);
	if (desIt != m_desMap.end())
	{
		return *(desIt->second);
	}

	//Des wasn't found in the interface
	throw EX("The interface has no DES with the given name")
}

//_________________________________________________________________________________________________

//Create an iterator that goes through all the internal DES of this interface
DesInterface::DesIteratorPtr DesInterface::createDesIterator() const
{
	DesMapIterator* pDesIt = new DesMapIterator(m_desMap);
	return DesIteratorPtr(pDesIt);
}

//_________________________________________________________________________________________________

int DesInterface::getDesCount() const
{
	return m_desMap.size();
}

//_________________________________________________________________________________________________

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

	if (pDes->getType() != eInterfaceDes)
		throw EX("Cannot add a non-interface DES to an interface");

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

	m_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);
}

//_________________________________________________________________________________________________

void DesInterface::changeDesName(Des& des, const std::wstring& newDesName)
{
	//try to find a DES with this name
	Des* pFoundDes = null;
	DesMapIt desMapIt = m_desMap.find(des.getName());
	if (desMapIt == m_desMap.end())
	{
		throw EX("Des could not be found. Cannot rename DES")
	}

	//The DES was found. Erase it from the map...
	pFoundDes = desMapIt->second;
	if (pFoundDes != &des)
	{
		throw EX("The found DES doesn't match the one in this interface. Cannot rename")
	}

	//remove it from the map cause it will be added under a new name
	m_desMap.erase(desMapIt);

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

	//add it to the map under the new name
	m_desMap[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)
{
	//try to find a DES with this name
	Des* pFoundDes = null;
	DesMapIt desMapIt = m_desMap.find(oldName);
	if (desMapIt == m_desMap.end())
	{
		if (desMapIt == m_desMap.find(newName))
		{
			//the name has already been updated
			return;
		}
		else
		{
			throw EX("Des could not be found. Cannot rename DES")
		}
	}

	//The DES was found. Erase it from the map...
	pFoundDes = desMapIt->second;
	if (pFoundDes != des)
	{
		throw EX("The found DES doesn't match the one in this interface. Cannot rename")
	}

	//remove it from the map cause it will be added under a new name
	m_desMap.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 it to the map under the new name
	m_desMap[newName] = des;
}

//_________________________________________________________________________________________________

void DesInterface::deleteDes(const std::wstring& desName)
{
	//try to find a DES with this name
	Des* pDes = null;
	DesMapIt desMapIt = m_desMap.find(desName);
	if (desMapIt == m_desMap.end())
	{
		throw EX("Des could not be found. Cannot delete")
	}

	//The DES was found. Erase it from the map...
	pDes = desMapIt->second;
	m_desMap.erase(desMapIt);

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

	removeDesSink(pDes);

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

	clean(false);
}

//_________________________________________________________________________________________________

bool DesInterface::findDes(const std::wstring& desName, Des** out_pDes /*= null*/)
{
	DesMapIt desMapIt = m_desMap.find(desName);
	if (desMapIt == m_desMap.end())
	{
		return false;
	}
	else
	{
		if (out_pDes != null)
		{
			*out_pDes = desMapIt->second;
		}

		return true;
	}
}

//_________________________________________________________________________________________________

bool DesInterface::findDes(const std::wstring& desName, const Des** out_pDes /*= null*/) const
{
	DesMapCIt desMapIt = m_desMap.find(desName);
	if (desMapIt == m_desMap.end())
	{
		return false;
	}
	else
	{
		if (out_pDes != null)
		{
			*out_pDes = desMapIt->second;
		}

		return true;
	}
}

//_________________________________________________________________________________________________

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);
}

//_________________________________________________________________________________________________

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

}; //end of namespace DESpot
