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

#pragma once

#include "CommonDefinitions.h"
#include "ProjectEvent.h"
#include "DesInterface.h"
#include "DesSubsystem.h"

namespace DESpot
{
	class InterfaceEvent : public ProjectEvent
	{
		public:
			InterfaceEvent(void) : m_owner(null) {}
			InterfaceEvent(const DesInterface& owner) : m_owner(&owner) {}
                        virtual ~InterfaceEvent(void) {}

		public:
			const DesInterface& owner() const { return *m_owner; }
			void setOwner(const DesInterface& owner) { m_owner = &owner; }
			bool hasOwner() const { return m_owner != null; }

			//checks if the type of project event is valid. When types of source events change
			//it is possible that if the project event pool would be re-created this would become
			//an interface event
			virtual bool isProjectEventTypeValid() const
			{
				const DesEvent* firstSrcEvent = m_srcMap.begin()->first;
				return (firstSrcEvent->getType() == eAnswerEvent ||
						firstSrcEvent->getType() == eRequestEvent ||
						firstSrcEvent->getType() == eLDataEvent);
			}

			virtual bool isValid(std::wstring* reason = null) const 
			{ 
				bool isValid = ProjectEvent::isValid(reason); 
				
				if (m_owner == null)
				{
					if (reason)
					{
						*reason += L"Event doesn't have an owner\n";
					}

					isValid = false;
				}
				else
				{
					//Check that the owner subsystem or interface of every source of this event is correct
					for (SourceMapCIt srcIt = m_srcMap.begin(); srcIt != m_srcMap.end(); srcIt++)
					{
						const Des* eventDesOwner = srcIt->second;
						checkSourceDes(eventDesOwner, isValid, reason);
					}
				}

				return isValid;
			}

			bool checkSourceDes(const Des* sourceDes, bool& isValid, std::wstring* reason) const
			{
				switch(sourceDes->getType())
				{
					case eInterfaceDes:
					{
						//every interface event can only be used in one interface (interface event sets
						//are mutually exclusive
						const DesInterface* sourceInterf = sourceDes->getInterfaceOwner();
						if (sourceInterf != m_owner)
						{
							if (reason)
							{
								*reason += L"Event is owned by " + m_owner->getName() + L" interface" + 
									       L" and is shared with another interface: " + sourceInterf->getName() +
										   L" through DES: " + sourceDes->getName() + L"\n";
							}

							isValid = false; 
						}
					}
					break;

					case eSubsystemDes:
					{
						bool interfBelowIsOwner = false;
						//a subsystem DES is using this interface event: it should either by the high-level
						//or the low-level that implements the interface
						const DesSubsystem* sourceSubsys = sourceDes->getSubsystemOwner();
						DesSubsystem::DependIteratorPtr depIt = sourceSubsys->createDependsIterator();
						for(depIt->first(); depIt->notDone(); depIt->next())
						{
						const DesSubsystem::Dependency& depend = depIt->currentItem();
						if(depend.interface == m_owner)
							{
							interfBelowIsOwner = true;
							}
						}
						//if ((sourceSubsys->isRoot() == false) && !(&sourceSubsys->getInterface() != m_owner))
						if ((sourceSubsys->isRoot() == false) && !(&sourceSubsys->getInterface() == m_owner || interfBelowIsOwner))
						{
							if (reason)
							{
								//*reason += L"Event is used in a low-level subsystem that does not implement this interface: " + 
								//			 sourceSubsys->getName() + L"\n";
								*reason += L"Event is used in a subsystem that does neither implement nor use this interface: " + 
											 sourceSubsys->getName() + L"\n";
							}

							isValid = false;

						}
					}
					break;

					case eRegularDes:
					{
						if (reason)
						{
							*reason += L"Event is used in a regular DES: " + sourceDes->getName() + L". Only interface or subsystem DES allowed.";
						}

						isValid = false;
					}
					break;

					case eInterfaceTemplateDes:
					{
					}
						break;

					default:
						assert(false);
						break;
				}

				return isValid;
			}
		
		private:
			const DesInterface* m_owner;
	};
} //end of namespace DESpot
