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

#include "DesTransition.h"

namespace DESpot
{
	class EventPool;
	class StatePool;
	class TransitionMapIterator;
	class DesTransitionIterator;
	class DesStateTransMapIterator;
	class DesStateTransIterator;
	class DesSelfTransIterator;


	//The transition functions keeps track of the transitions only in terms of IDs using DesTransition objects (not DesTransition)
	//A transition is a triplet of IDs (stateID; eventID; stateID)
	class DesTransitionFunction
	{
		public:
			DesTransitionFunction(StatePool& statePool, EventPool& eventPool, 
								  bool bAllowNonDeterminism = false);
			virtual ~DesTransitionFunction(void);

		//Iterator creator methods
		public:
			//Iterates through all the transitions in this function
			DesTransitionIterator* createIterator() const;

			//Iterates through all the inverse transitions in this function
			DesTransitionIterator* createInvIterator() const;

			//Iterates only through the transitions in the transition map, ignoring global self-transitions
			TransitionMapIterator* createTransMapIterator() const;

			//Iterates only through the inverse transitions in the transition map, ignoring global self-transitions
			TransitionMapIterator* createTransMapInvIterator() const;

			//Iterates through the transition leaving the given state including or not (depending on excludeSelfTrans)
			//the global self-transitions
			DesStateTransMapIterator* createStateTransMapIterator(const DesState& state) const;

			//Iterates through the transition entering the given state including or not (depending on excludeSelfTrans)
			//the global self-transitions
			DesStateTransMapIterator* createStateTransMapInvIterator(const DesState& state) const;

			//Iterates through the transition leaving the given state including or not (depending on excludeSelfTrans)
			//the global self-transitions
			DesStateTransIterator* createStateTransIterator(const DesState& state) const;

			//Iterates through the transition entering the given state including or not (depending on excludeSelfTrans)
			//the global self-transitions
			DesStateTransIterator* createStateTransInvIterator(const DesState& state) const;

			//Iterates through the global self-transitions
			DesSelfTransIterator* createSelfTransIterator() const;
			
		public:
			//Return the number of transitions
			DesTransition::Count getTransCount(bool includeSelfTrans = true) const;
			DesTransition::Count getSelfTransCount() const;

			void addTransition(const DesTransition& trans);

			//Adds a global self-loop transition to the transition function. If any self-loops are found having the
			//the same event label they are removed / replaced by the global one. The local-self loop transitions
			//are returned to the caller in the pLocalLoops array if requested. Note that DesTransition objects are
			//created for the local self-loops and the caller is responsible for deleting them
			void addGlobalSelfTransition(const DesEvent& selfLoopEvent, 
										 std::vector<DesTransition*>* o_pLocalLoops = null);

			void deleteTransition(const DesTransition& transToDelete);
			void deleteGlobalSelfTransition(const DesEvent& selfLoopEvent);
			void changeTransition(const DesTransition& trans, const DesTransition& change);

			//deletes all the transitions direct and reverse, reverting the transition function back to initial state after construction
			void clear();

			enum FindRes
			{
				eExactTransFound = 1, 
				eTransNotFound = 0,
				eOtherTransFound = -1,
				eOtherSelfTransFound = -2,
				eLocalSelfLoopFound = -3,
			};
			FindRes findTransition(const DesTransition& trans);
			FindRes findGlobalSelfTransition(const DesEvent& selfLoopEvent);

			//Checks if a transition with the label "event" leaves "fromState". If there is it returns true
			//and returns the destination state in the out parameter o_toState
			bool transExists(const DesState* fromState, const DesEvent* event, const DesState*& o_toState);

			bool isNonDeterminismAllowed() const { return m_nonDetAllowed; }

			//checks if the transition function is non deterministic
			bool isNonDeterministic() const;

			#ifdef _DEBUG_DESPOT
				void outputTransitionFunction();
			#endif //_DEBUG_DESPOT

		//Implementation types____________________
		private:
			//state ID - transition destination state (for each event there could be a list of destination states
			//if this list has more than one state the transition function isn non-deterministic
			typedef std::map<const DesState::ID, const DesState*> StateMap;
			typedef StateMap::iterator StateMapIt;
			typedef StateMap::const_iterator StateMapCIt;
			
			//event label to destination state (for each state there is a list of events that leave the state; 
			//each event goes to one and only one state)
			typedef std::map<const DesEvent*, StateMap*>  EventMap;
			typedef EventMap::iterator EventMapIt;
			typedef EventMap::const_iterator EventMapCIt;

			//state to event map (for each state there is a list (implemented as an event map) of events that leave 
			//the states
			typedef std::map<const DesState*, EventMap*> TransitionMap;
			typedef TransitionMap::iterator TransMapIt;
			typedef TransitionMap::const_iterator TransMapCIt;

			//event id to event object map used for self-looped events
			typedef std::map<const DesEvent::ID, const DesEvent*>  SelfLoopEventMap;
			typedef SelfLoopEventMap::iterator SelfLoopEventIt;
			typedef SelfLoopEventMap::const_iterator SelfLoopEventCIt;

		//Implementation methods__________________
		private:
			//Internal function tha adds the given transition "trans" to the given transition map "transMap". The
			//addition will or will not allow non-determinism depending on the value of allowNonDet. This internal 
			//function is used to add a transition either to the direct transition function or the inverse one
			void addTransToMap(const DesTransition& trans, TransitionMap& transMap, bool allowNonDet);

			//Internal function that deletes the transition "trans" from the given map "transMap". This function is used
			//to delete a transition either from the regular map or from the inverse transition map
			void deleteTransFromMap(const DesTransition& trans, TransitionMap& transMap);

			//Internal function that changes "trans" into "change" in the given transition map. It is used to perform the change in both
			//the regular transition function and the inverse transition function.
			void changeTransInMap(const DesTransition& trans, const DesTransition& change, 
								  TransitionMap& transMap, bool allowNonDet);

			//Internal function used to add a transition to the map. No checking is actually done as the function
			//assumes there is no transition in the transition map that starts from the fromState of this transition
			void startNewTrans(TransitionMap& transMap, const DesTransition& trans);

			//Internal function used to add the toState of a transition to the event map when the event label
			//for this transition has not been used yet
			void startNewStateMap(EventMap& eventMap, const DesTransition& trans);

			//Internal function that adds a transition to an existing stateMap
			void addTransToStateMap(StateMap& stateMap, const DesTransition& trans, bool nonDetAllowed);

			//Internal function that makes sure the given transition map does not contain any conflicting transition
			//with the selfLoopEvent. The function doesn't consider local loops as conflicts cleaning them and returning
			//them to the user in the out parameter. The function returns the number of local loops found as return value
			DesTransition::Count checkAndCleanTransMap(TransitionMap& transMap, const DesEvent& selfLoopEvent,
									   std::vector<DesTransition*>* o_pLocalLoops = null);

			//Internal function that goes through all transitions looking for local loops with the same event as the 
			//selfLoopEvent. The check will fail (and an exception will be thrown) if a regular transition
			//is found having this event label.If only local loops are found having this event label they are returned
			//in the two list arguments.The function returns the number of local loops found as return value
			DesTransition::Count checkForLocalLoops(TransitionMap& transMap, const DesEvent& selfLoopEvent, std::vector<TransMapIt>& o_transList,
									std::vector<EventMapIt>& o_eventList);

			
			//Removes all transitions identified by the two given lists. transList identifies the start points
			//of the loops, the eventList identifies the event map for the self-loop. Note that the local loops
			//were identified using the checkForLocalLoops function. 
			DesTransition::Count cleanLocalLoops(TransitionMap& transMap, std::vector<TransMapIt>& transList, std::vector<EventMapIt>& eventList,
								 std::vector<DesTransition*>* o_pLocalLoops = null);

			
			//Completely replaces trans by change by deleting trans and adding change. If adding the "change"
			//transition fails the function attempts to put back the original transition
			void replaceTransition(TransitionMap& transMap, const DesTransition& trans, const DesTransition& change, bool allowNonDet);

			//changes the transition trans to change when the events are different. Their fromState is assumed the same
			//and the event map corresponds to this to state. If the change cannot be done it throws exception.
			void changeTransitionEvent(TransitionMap& transMap, const DesTransition& trans, 
									   const DesTransition& change, EventMap& eventMap, bool allowNonDet);

			//change the transition "trans" to "change" when only the destination states are different. Their start state
			//and event are assumed to be the same. If the change cannto be done it throws exception.
			void changeTransitionToState(const DesTransition& trans, const DesTransition& change, EventMap& eventMap);

			//Internal functions that removes the toState from the given state map identifid by eventIt. The function
			//will properly destroy the state map if empty and remove the event entry from the event map.
			void removeToStateFromMap(const DesState& toState, EventMap& eventMap, EventMapIt& eventIt);

			//Internal function that finds all the iterators that precises point to the given transition
			bool findTransition(TransitionMap& transMap, const DesTransition& trans, TransMapIt* o_pFromStateIt, 
								EventMapIt* o_pEventIt = null, StateMapIt* o_pToStateIt = null);

			//Internal function that attempts to find  the event map for the source state of "trans" - the event
			//map where the given transition would have to be placed. Returns true if the event map is found. The
			//actual event map is returned in the out parameter - eventMap
			bool findEventMapForTrans(TransitionMap& transMap, const DesTransition& trans, EventMap*& o_eventMap);

			//Internal function that attempts to find the state map for the toState of "trans". It assumes the 
			//event map has already been found but we do not know if the event label of "trans" is in it. If 
			//it is then we want the list of states associated with that event - the state map
			bool findStateMapForTrans(EventMap& eventMap, const DesTransition& trans, StateMap*& o_stateMap);


			//helper methods that return the components of a transition in the proper type
			const DesState* transFromState(const DesTransition& trans) const
			{
				return &trans.fromState();
			}

			const DesEvent* transEvent(const DesTransition& trans) const
			{
				return &trans.event();
			}

			const DesState* transToState(const DesTransition& trans) const
			{
				return &trans.toState();
			}

			//Check to see if an event is used or not. Returns true if no transition uses this event
			bool notUsed(const DesEvent& event) ;

			//checks to see if an event is self-looped globally and returns the iterator in the 
			//map of self loop events if required
			bool isEventSelfLooped(const DesEvent& event, SelfLoopEventIt* pEventIt = null);

			//Marks the given event as being used or unused depending whether the event is used
			//in the transition function either by locals transitions or global self-loops
			void markEventUsed(const DesEvent& event, bool isUsed = true);

			#ifdef _DEBUG_DESPOT
				void outputTransitionFunction(TransitionMap& transMap);
			#endif //_DEBUG_DESPOT

		//Data
		private:
			//The map of transitions excluding the global self-looped transitions
			TransitionMap m_transMap;

			//The number of transitions in the transition map - does not include global self-loops 
			DesTransition::Count m_transCount;

			//The map of transitions excluding the global self-looped transitions
			TransitionMap m_invTransMap;

			//True if the inverse transition function is also available
			bool m_calcInverseFunc;

			//The map of self-looped transitions
			SelfLoopEventMap m_globalSelfLoopEventMap;

			//A reference to the state pool of the DES that has this transition function. The state pool
			//is used for iterating through self transitions
			StatePool& m_statePool;
			
			//A reference to the event pool of the DES that has this transition function. The transition
			//function uses the event pool to modify the "used" status of events. Any time an event is 
			//used in a transition it becomes "used"
			EventPool&     m_eventPool;

			//True if the transition function can be non-deterministic and false if not
			bool m_nonDetAllowed;
	};

} //end of namespace DESpot

