/*	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 <map>
#include <string>
#include "ProjectEvent.h"
#include "Iterator.h"
#include "DesNotifications.h"

namespace DESpot
{
	class ProjectEventIterator;
	class DesSubsystem;
	class DesInterface;
	class DesProject;
	class EventPoolAccess;

	class ProjectEventPool : private DesNotifications
	{
		public:	
			typedef std::auto_ptr<ProjectEventIterator> EventIteratorPtr;
			
		public:
			ProjectEventPool(DesProject* proj, EventPoolAccess* projAccess);
                        virtual ~ProjectEventPool(void);

		//Event access
		public:
			//Returns a event that must exist in the pool. Throws exception if the eventId is not found in the pool
			const ProjectEvent& getEvent(const std::wstring& eventName) const;
			ProjectEvent& getEvent(const std::wstring& eventName);

			//Searches for a project event with the given name
			bool findEvent(const std::wstring& eventName, ProjectEvent*& out_projEvent);

			//Searches for a DES event in the project event pool
			bool findEvent(const DesEvent& desEvent, ProjectEvent*& out_projEvent);

		//Pool operations
		public:
			//Adds a new event to the pool with the properties of the event template. 
			void addEvent(ProjectEvent* projEvent);

			//changes the type of project event that is in the pool form subsystem event to interface event
			//or vice-versa. The new type is determined based on the input project event (the invalido one)
			//all teh sources of the input event are going to be tranfered to the new event and the input
			//event will be removed from the pool
			ProjectEvent* changeProjectEventType(ProjectEvent* invalidProjEvent);

			//Removes an existing event
			void removeEvent(const std::wstring& eventName);

			//changes the event name and properties. If the type of event has changed then the project event
			//object has to be re-created
			void changeEvent(ProjectEvent* projectEvent, const DesEvent& newEvent);

			//Changes the name of an existing event
			void changeEventName(const std::wstring& oldName, const std::wstring& newName);

			//Adds all the events of the given DES to the event pool. Used by the project when a DES is
			//being added to the project
			void addDesEvents(Des* des, DesSubsystem* desOwner);
			void addDesEvents(Des* des, DesInterface* desOwner);			
			
			//Removes all the events of the given DES to the event pool. Used by the project when a DES is
			//being removed from the project
			void removeDesEventSet(Des* des);
			void removeDesEvent(const DesEvent* desEvent);
			

			EventIteratorPtr createIterator() const;

			//Factory methods that can create an event matching teh template event and the owner
			ProjectEvent* createProjectEvent(const DesEvent& templateEvent, const DesSubsystem& owner);
			ProjectEvent* createProjectEvent(const DesEvent& templateEvent, const DesInterface& owner);

			
			void notifyPoolUpdated();

		private:
			typedef std::map<std::wstring, ProjectEvent*> EventMap;
			typedef EventMap::iterator EventMapIt;
			typedef EventMap::const_iterator EventMapCIt;

		private:
			void removeNotificationSink(Des* des);

		private:
			DesProject* m_project;
			EventPoolAccess* m_projAccess;

			EventMap m_eventMap;

			class DesNotificationSink;

			std::vector<DesNotificationSink*> m_desNotifSinks;

			bool m_processNotifications;

		//__________________________________________________________________________________________________________
	
		private: class DesNotificationSink : private DesNotifications
		{
			public:
				DesNotificationSink(ProjectEventPool& eventPool, bool& poolProcessNotifications, Des* source, DesProject* project) : 
                                        m_project(project), m_eventPool(eventPool), m_des(source), m_poolProcessNotifications(poolProcessNotifications)
				{
					m_desListenerId = m_des->subscribe(this);
				}
					
                                virtual ~DesNotificationSink()
				{
					//can't unsubscribe here because all DES could be already destroyed if the project was closed
				}
				
				Des* des() { return m_des; }

				void unsubscribe()
				{
					m_des->unsubscribe(m_desListenerId);
				}

				ProjectEvent* createProjectEvent(const DesEvent& desEvent);

			private:
				virtual void onEventAdded(const DesEvent& addedEvent);
				virtual void onEventChanged(const DesEvent& changedEvent);
				virtual void onRemovingEvent(const DesEvent& event);

			private:
				DesProject* m_project;

				ProjectEventPool& m_eventPool;
				
				Des* m_des;
				
				unsigned int m_desListenerId;

				bool& m_poolProcessNotifications;
		};
	};

	//__________________________________________________________________________________________________________

	class ProjectEventIterator : public Iterator<ProjectEvent&, const ProjectEvent&>
	{
		public:
			ProjectEventIterator(const std::map<std::wstring, ProjectEvent*>& eventMap):
						m_eventMap(eventMap)
			{
			}
		
			virtual ~ProjectEventIterator(void)
			{
			}
	
		public:
			virtual void first() const
			{
				m_crtEventIt = m_eventMap.begin();
			}
			
			virtual void next() const
			{
				m_crtEventIt++;
			}
			
			virtual bool isDone() const
			{
				return (m_crtEventIt == m_eventMap.end());
			}
			
			virtual ProjectEvent& currentItem()
			{
				if (m_crtEventIt == m_eventMap.end())
					throw EX("Iterator is already done. Call isDone() before.")

				return const_cast<ProjectEvent&>(*(m_crtEventIt->second));
			}
			
			virtual const ProjectEvent& currentItem() const
			{
				if (m_crtEventIt == m_eventMap.end())
					throw EX("Iterator is already done. Call isDone() before.")

				return *(m_crtEventIt->second);
			}


		private:
			//no copying allowed
			ProjectEventIterator(const ProjectEventIterator& other) : m_eventMap(other.m_eventMap) {}
			ProjectEventIterator& operator=(const ProjectEventIterator& /*other*/) {return *this;}

		private:
			typedef std::map<std::wstring, ProjectEvent*> EventMap;
			typedef EventMap::const_iterator EventMapIt;

		private:
			const EventMap m_eventMap;
			mutable EventMapIt m_crtEventIt;	
	};	
} //end of namespace DESpot
