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

#ifndef __DES_SUBSYSTEM_H__
#define __DES_SUBSYSTEM_H__

#include <map>
#include <string>
#include <vector>

#include "Iterator.h"
#include "CommonDefinitions.h"
#include "DesNotifications.h"
#include "DesTypes.h"

namespace DESpot
{
	class Des;
	class DesInterface;
	class ProjectEventPool;

	class DesSubsystem
	{
		//Types____________________________________________________
		public:
			struct Dependency
			{
				DesInterface* interface;
				DesSubsystem* subsystem;
			};

			struct DependencyRef
			{
				std::wstring interfName;
				std::wstring subsysName;

				DependencyRef(const std::wstring& interfName, const std::wstring& subsysName)
				{
					this->interfName = interfName;
					this->subsysName = subsysName;
				}
			};

			//Des iterator
			typedef Iterator<Des&, const Des&> DesIterator;
			typedef std::auto_ptr<DesIterator> DesIteratorPtr;

			//Dependecies iterator
			typedef Iterator<Dependency&, const Dependency&> DependIterator;
			typedef std::auto_ptr<const DependIterator> DependIteratorPtr;

		public:
			DesSubsystem(const std::wstring& name, ProjectEventPool& eventPool);
			DesSubsystem(const std::wstring& name, const DesInterface* interf, ProjectEventPool& eventPool);
			~DesSubsystem(void);

		//Subsystem properties________________________________________
		public:
			//The name of the subsystem
			std::wstring getName() const;
			void setName(const std::wstring& name);

			//Correctness
			bool isValid() const;
			void setValid(bool setValid = true) const;
			void clearValid() const;
			Integrity getValidProp() const;

			//Non-blocking
			bool isNonBlocking() const;
			void setNonBlocking(bool isNonBlock = true) const;
			void clearNonBlocking() const;
			LwNonBlockProp getLwNonBlockProp() const;

			//Controllability
			bool isControllable() const;
			void setControllable(bool isCtrl = true) const;
			void clearControllable() const;
			LwCtrlProp getLwCtrlProp() const;

			//Interface-Consitent
			bool isInterfConsist() const;
			void setInterfConsist(bool isIConsist = true) const;
			void clearInterfConsist() const;
			IConsistProp getIConsistProp() const;

		public:
			bool isRoot() const; //the root has level zero
			int getLevel() const;
			void setLevel(int level);

		//Access to subsystem's interfaces
		public:
			void setInterface(const DesInterface& interf);
			const DesInterface& getInterface() const;
			bool implementsInterface() const;

		public:
			//Returns the sync product of the all the DES components. If it needs to be
			//calculated it calculates it
			Des& getSyncDes() const;

			//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
			//Deep cleaning will also clean the DES components. Non-deep cleaning will only clean
			//the properties of the subsystem
			void clean(bool deepClean = true);

		//Access to DES_________________________________________
		public:
			//returns the number of DES in the subsystem
			int getDesCount() const;

			//returns the number of supervisor DES in the subsystem
			int getSupDesCount() const;

			//returns the number of plant DES in the subystem
			int getPlantDesCount() const;

			bool findDes(const std::wstring& desName, Des** out_pDes = null, DesLevel* out_desLevel = null);
			bool findDes(const std::wstring& desName, const Des** out_pDes = null, DesLevel* out_desLevel = null) const;

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

			//Create an iterator that goes through all the internal Des of this subsystem
			DesIteratorPtr createDesIterator() const;

			//Create an iterator that goes through all the internal Des of this subsystem
			DesIteratorPtr createDesIterator();

			//Create an iterator that goes through either the plant or the supervisor Des
			DesIteratorPtr createDesIterator(DesLevel desLevel) const;

			//Create an iterator that goes through either the plant or the supervisor Des
			DesIteratorPtr createDesIterator(DesLevel desLevel);

		//Editing the Des______________________________________
		public:
			void addDes(Des* pDes, DesLevel desLevel);

			//locates the DES and changes it's name updating in the same time all internal
			//references to DES's name
			void changeDesName(Des& des, const std::wstring& newDesName);

			//Event sync from when a name of a DES that is part of this subsystem 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 subsystem's internal maps reference it by the old name
			//It is these internal maps that need to be updated
			void onDesNameChanged(Des* des, const std::wstring& oldName, const std::wstring& newName); 

			//removes the DES from the subsystem without destroying it
			void removeDes(Des* pDes);

			//removes the DES from the subsystem and destroys it
			void deleteDes(const std::wstring& desName);

		//Access to lower level subsystems____________________________
		public:
			DependIteratorPtr createDependsIterator() const;

		//Editing dependencies
		public:
			int getDependsCount() const;

			void addDependency(DesInterface* pInterface, DesSubsystem* pSubsystem);

			//Verifies if the subsystem is depenent on the given interface
			bool isDependentOn(const DesInterface& interf) const;

			void deleteDependency(DesInterface* pInterface);

			//Finds the dependency to a subsystem with the given name, deletes the dependency
			void deleteDependency(DesSubsystem* pSubsys);

			//removes all the dependencies this subsystem has on others
			void clearDependencies();

			//returns a lowever level subsystem depenent on this through the given interface
			bool getDependent(DesInterface* interf, DesSubsystem*& out_subsys);

		//Internal types
		protected:
			//Defines the map of dependencies of this subsystem to others. Every dependency
			//is possible through an interface thus each dependecies is a pair of interface <-> subsystem
			//where the interface is the key in the map and the subsystem is the value
			typedef std::map<DesInterface*, DesSubsystem*> SubsystemDependsMap;
			typedef SubsystemDependsMap::iterator SubsystemDependsMapIt;

			typedef std::map<std::wstring, Des*> DesMap;
			typedef DesMap::iterator DesMapIt;
			typedef DesMap::const_iterator DesMapCIt;

		//Implemention methods
		protected:
			DesMap& getDesMap(DesLevel desLevel);
			const DesMap& getDesMap(DesLevel desLevel) const;

			//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 findDes(const std::wstring& desName, Des** out_des,
							   DesMap** out_desMap = null, DesMapIt* out_desIt = null);

			void sinkDesEvents(Des* des);
			void removeDesSink(Des* des);


			//serializer access
		public:
			void setLoadInProgress(bool loadInProgress = true);

		//Data
		protected:
			//The name of the subsystem
			std::wstring m_name;

			//True if the subsystem is valid (all integrity checks for this subsystem are successful)
			mutable Integrity m_isValid;

			//Subsystem interface consistency property
			mutable IConsistProp m_isInterfConsist;

			//Subsystem is or not non-blocking
			mutable LwNonBlockProp m_isNonBlocking;

			//Subsystem controllability property
			mutable LwCtrlProp m_isControllable;

			//The subsystem level in the dependency tree
			int m_level;

			//The map of dependecies to other subsystems
			SubsystemDependsMap m_dependsMap;

			//The list internal supervisor Des
			DesMap m_supDesMap;

			//The list of internal plant Des
			DesMap m_plantDesMap;

			//The synchronized product of all the DES components
			mutable Des* m_syncDes;

			//The interface the subsystem implements. In the future this may become a list of interfaces
			const DesInterface* m_interf;

			//The event pool of the project this subsystem belong to
			ProjectEventPool& m_eventPool;

			//True when the serializer is loading the subsystem
			bool m_bLoadInProgress;

			//the list of sinks from all DES of the interface
			class DesNotificationSink;
			std::vector<DesNotificationSink*> m_desNotifSinks;

		//__________________________________________________________________________________________________________

		protected: class DesNotificationSink : private DesNotifications
		{
			public:
				DesNotificationSink(DesSubsystem* subsys, Des* source) :
                                        m_subsys(*subsys), m_des(source)
				{
					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);
				}

			private:
				virtual void onNameChanged(const std::wstring& oldName, const std::wstring& newName) {m_subsys.onDesNameChanged(m_des, oldName, newName);}
				virtual void onStateAdded(const DesState& /*addedState*/) { m_subsys.clean();}
				virtual void onStateChanged(const DesState& /*changedState*/) { m_subsys.clean();}
				virtual void onRemovingState(const DesState& /*state*/) { m_subsys.clean();}
				virtual void onEventAdded(const DesEvent& /*addedEvent*/) { m_subsys.clean();}
				virtual void onEventChanged(const DesEvent& /*changedEvent*/){ m_subsys.clean();}
				virtual void onEventRemoved(const DesEvent::ID& /*eventId*/) { m_subsys.clean();}
				virtual void onTransitionAdded(const DesTransition& /*addedTrans*/) { m_subsys.clean();}
				virtual void onGlobalSelfTransitionAdded(const DesEvent& /*selfTransEvent*/) { m_subsys.clean();}
				virtual void onTransitionChanged(const DesTransition& /*trans*/, const DesTransition& /*change*/) { m_subsys.clean();}
				virtual void onTransitionRemoved(const DesTransition& /*removedTrans*/) { m_subsys.clean();}
				virtual void onSelfTransitionRemoved(const DesEvent& /*selfTransEvent*/) { m_subsys.clean();}

			private:
				DesSubsystem& m_subsys;

				Des* m_des;

				unsigned int m_desListenerId;
		};
	};

} //end of namespace DESpot

#endif // __DES_SUBSYSTEM_H__
