/*	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 "ProjectEvent.h"
#include "DesSubsystem.h"
namespace DESpot
{

ProjectEvent::ProjectEvent(void)
{
}

//_________________________________________________________________________________________________

ProjectEvent::~ProjectEvent(void)
{
}

//_________________________________________________________________________________________________

void ProjectEvent::addSourceEvent(const DesEvent* desEvent, Des* eventOwner)
{
	SourceMapIt srcIt = m_srcMap.find(desEvent);
	if (srcIt != m_srcMap.end())
	{
		throw EX("Source event already part of this project event. Same event cannot be added twice.");
	}

	//if this is the first source set the project event properties
	if (m_srcMap.empty())
	{
		setName(desEvent->getName());
		setAlias(desEvent->getAlias());
		setControllable(desEvent->isControllable());
		setUsed(desEvent->isUsed());
	}
	else
	{
		//check the event properties to make sure they are consistent with the project event
		if (getName() != desEvent->getName())
		{
			throw EX("The name of the given DES event doesn't match the project event name. Cannot add it as a source for this project event.");
		}
	}
	
	m_srcMap[desEvent] = eventOwner;
}

//_________________________________________________________________________________________________

void ProjectEvent::removeSourceEvent(const DesEvent* desEvent)
{
	SourceMapIt srcIt = m_srcMap.find(desEvent);
	if (srcIt == m_srcMap.end())
	{
		throw EX("Source event not found.");
	}

	m_srcMap.erase(srcIt);
}

//_________________________________________________________________________________________________

bool ProjectEvent::findSourceEvent(const DesEvent* desEvent)
{
	SourceMapIt srcIt = m_srcMap.find(desEvent);
	return (srcIt != m_srcMap.end());
}

//_________________________________________________________________________________________________

ProjectEvent::SourceIteratorPtr ProjectEvent::createSourceIterator() const
{
	return SourceIteratorPtr(new SourceIterator(m_srcMap));
}

//_________________________________________________________________________________________________
//Changes all the event properties and the properties of all event sources to match to given event
//NOTE: the name of the event is is not changed. use changeEventName for changing the name
void ProjectEvent::changeEvent(const DesEvent& desEvent)
{
	setAlias(desEvent.getAlias());
	
	setControllable(desEvent.isControllable());

	setUsed(desEvent.isUsed());

	//set the type of event. If the type of the given evne is default the type of the project event has
	//to be set to high or low-level depending if who the owner is
	bool defaultTypeResolved = true;
	if (desEvent.getType() == eDefaultEvent)
	{
		//figure out if it is a high-level or low-level event
		defaultTypeResolved = false;
	}
	else
	{
		setType(desEvent.getType());
	}

	for (SourceMapCIt srcIt = m_srcMap.begin(); srcIt != m_srcMap.end(); srcIt++)
	{
		const DesEvent* srcEvent = srcIt->first;
		Des*      srcEventOwner = srcIt->second;
		
		//if the project event type has remain unresolved figure it out based on the 
		//source. Look for the first subsystem des and set the type based on the level
		//of that subsystem (high or low)
		if (defaultTypeResolved == false && srcEventOwner->getType() == eSubsystemDes)
		{
			const DesSubsystem* ownerSubsystem = srcEventOwner->getSubsystemOwner();
			if (ownerSubsystem->getLevel() == 0)
			{
				setType(eHighLevelEvent);
			}
			else
			{
				setType(eLowLevelEvent);
			}

			defaultTypeResolved = true;
		}
		
		//create a copy of the given event so that we can preserve the "Is Used" information
		DesEvent eventTemplate(desEvent);
		eventTemplate.setUsed(srcEvent->isUsed());
		srcEventOwner->changeEvent(srcEvent->getId(), eventTemplate);
	}

	if (defaultTypeResolved == false)
	{
		//the project event type is supposed to be "default" and could not be resolved based on sources
		//(which means the sources are all interface DES. By default the project event is set to high-level
		setType(eHighLevelEvent);
	}
}

//_________________________________________________________________________________________________

void ProjectEvent::changeEventName(const std::wstring& newName)
{
	//change the name of the project event first because the project event pool assumes
	//that all events exist in the pool already (one cannot have a DES event that is not in the project event pool)
	setName(newName);

	//change the name of the event in every source DES
	for (SourceMapCIt srcIt = m_srcMap.begin(); srcIt != m_srcMap.end(); srcIt++)
	{
		const DesEvent* srcEvent = srcIt->first;
		Des*      srcEventOwner = srcIt->second;
		srcEventOwner->changeEventName(srcEvent->getId(), newName);
	}
}


//_________________________________________________________________________________________________

unsigned int ProjectEvent::sourceCount()
{
	return m_srcMap.size();
}

//_________________________________________________________________________________________________

//checks if all the source events are consistent (have the same properties)
bool ProjectEvent::isValid(std::wstring* reason /*= null*/) const
{
	bool isValid = true;

	for (SourceMapCIt srcIt = m_srcMap.begin(); srcIt != m_srcMap.end(); srcIt++)
	{
		const DesEvent* srcEvent = srcIt->first;
		/*const Des*      srcEventOwner = */srcIt->second;
		
		if (srcEvent->getName() != getName())
		{
			if (reason)
			{
				*reason += L"The name of the source event from \"" + srcIt->second->getName() +
					       L"\" is inconsistent\n";
			}
			
			isValid = false;
		}

		if (srcEvent->getAlias() != getAlias())
		{
			if (reason)
			{
				*reason += L"The alias of the source event from \"" + srcIt->second->getName() +
					      L"\" is inconsistent\n";
			}
			
			isValid = false;
		}

		if (srcEvent->isControllable() != isControllable())
		{
			if (reason)
			{
				*reason += L"The controllability of the source event from \"" + srcIt->second->getName() +
					       L"\" is inconsistent\n";
			}
			
			isValid = false;
		}

		if (isTypeConsistent(srcEvent->getType()) == false)
		{
			if (reason)
			{
				*reason += L"The type of the source event from \"" + srcIt->second->getName() +
					       L"\" is inconsistent\n";
			}

			isValid = false;
		}
	}

	return isValid;
}

//_________________________________________________________________________________________________

bool ProjectEvent::isAliasValid()
{
	const DesEvent* firstSourceEvent = m_srcMap.begin()->first;
	return firstSourceEvent->getAlias() == getAlias();
}

//_________________________________________________________________________________________________

void ProjectEvent::updateAliasFromSources()
{
	const DesEvent* firstSourceEvent = m_srcMap.begin()->first;
	setAlias(firstSourceEvent->getAlias());

}

//_________________________________________________________________________________________________

bool ProjectEvent::isCtrlValid()
{
	const DesEvent* firstSourceEvent = m_srcMap.begin()->first;
	return firstSourceEvent->isControllable() == isControllable();
}

//_________________________________________________________________________________________________

void ProjectEvent::updateCtrlFromSources()
{
	const DesEvent* firstSourceEvent = m_srcMap.begin()->first;
	setControllable(firstSourceEvent->isControllable());
}

//_________________________________________________________________________________________________

bool ProjectEvent::isTypeConsistent(EventType srcType)	const
{
	switch(getType())
	{
		case eHighLevelEvent:			
		case eLowLevelEvent:
			return srcType == eDefaultEvent;
			
		default:
			return srcType == getType();
	}
}

} //end of namespace DESpot
