/*	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 "DesStatePool.h"
#include "DesState.h"
#include "DesStateIterator.h"
#include "DesMarkedStateIterator.h"

namespace DESpot
{

StatePool::StatePool(void)
{
	m_stateCount = 0;
	m_markedStateCount = 0;
	m_nextFreeID = 1;
	m_statePool.resize(10); //start with 10 states
}

//_________________________________________________________________________________________________

StatePool::~StatePool(void)
{
	try
	{
		clear();
	}
	catch(...) 
	{
		assert(false);
	}
}
//_________________________________________________________________________________________________

void StatePool::clear()
{
	//remove all states from the pool and clean memory
	DesStateIterator stateIt(m_statePool);
	for(stateIt.first(); stateIt.isDone() == false; stateIt.next())
	{
		DesState& crtState = stateIt.currentItem();
		delete &crtState;
	}

    m_stateCount = 0;
	m_markedStateCount = 0;
	m_nextFreeID = 1;
}

//_________________________________________________________________________________________________

DesState* StatePool::getState(const DesState::ID& stateId) const
{
	if (stateId >= m_statePool.size())
		throw EX("State ID not found in the state pool. Use find method if the state may not be in the pool")		

	DesState* pState = m_statePool[stateId];
	if (pState == null)
		throw EX("State ID not found in the state pool. Use find method if the state may not be in the pool")
	
	return pState;
}

//_________________________________________________________________________________________________

bool StatePool::findState(const DesState::ID& stateId, DesState** ppFoundState)	const
{
	if (stateId >= m_statePool.size())
		return false;		

	DesState* pState = m_statePool[stateId];
	if (pState == null)
		return false;
	
	//state found: return it to the caller if it was required
	if (ppFoundState)
	{
		assert(*ppFoundState == null);
		*ppFoundState = pState;
	}

	return true; //state found
}

//_________________________________________________________________________________________________

bool StatePool::findState(const std::wstring& stateName, const std::wstring& stateAlias, DesState** ppFoundState) const
{
	DesStateIterator stateIt(m_statePool);
	for(stateIt.first(); stateIt.isDone() == false; stateIt.next())
	{
		DesState& crtState = stateIt.currentItem();
		if (crtState.getName() == stateName || crtState.getAlias() == stateAlias)
		{
			//state found; return the state pointer if it was required
			if (ppFoundState)
			{
				assert(*ppFoundState == null);
				*ppFoundState = &crtState;
			}

			return true; //state found
		}
	}

	return false;
}

//_________________________________________________________________________________________________

bool StatePool::findState(const DesState& state, DesState** ppFoundState)	const
{
	DesState::ID stateId = state.getId();
	if (stateId >= m_statePool.size())
		return false;		

	DesState* pState = m_statePool[stateId];
	if (pState == null)
		return false;
	
	if (state == *pState)
	{
		//state found: return it to the caller if it was required
		if (ppFoundState)
		{
			assert(*ppFoundState == null);
			*ppFoundState = pState;
		}

		return true; //state found
	}
	else
	{
		return false;
	}
}

//_________________________________________________________________________________________________

DesState::Count StatePool::getStateCount()  const
{
	//we cannot rely on the size of the state pool as it may contain "null" states caused by removing states
	return m_stateCount;
}

//_________________________________________________________________________________________________

DesState::Count StatePool::getMarkedStateCount() const
{
	return m_markedStateCount;
}

//_________________________________________________________________________________________________

DesStateIterator* StatePool::createIterator() const
{
	return new DesStateIterator(m_statePool);
}

//_________________________________________________________________________________________________

DesMarkedStateIterator* StatePool::createMarkedStateIterator() const
{
	return new DesMarkedStateIterator(m_statePool);
}

//_________________________________________________________________________________________________
//returns the last used state ID 
DesState::ID StatePool::getLastUsedId() const
{
	return m_nextFreeID - 1;
}

//_________________________________________________________________________________________________

//Adds a new state ot the pool with the properties of the state template. Note that a new ID is assigned to the new state
const DesState& StatePool::addNewState(const DesState& stateTemplate)
{
	DesState::ID newStateId = reserveNewStateID();
	
	DesState* pNewDesState = new DesState(newStateId, stateTemplate);
	
	addState(pNewDesState);

	return *pNewDesState;
}

//_________________________________________________________________________________________________
//The state, if it was added it becomes the property of the pool who is now responsible for destorying it
void StatePool::addState(DesState* pStateToAdd)
{
	//Require: pStateToAdd cannot be null
	if (pStateToAdd == null)
		throw EX("Invalid (null) state to be added to pool")
	
	//Require: there is no state in the pool already having the name of the state to add
	if (findState(pStateToAdd->getName(), pStateToAdd->getAlias()))
		throw EX("A state with same name or alias already exists in DES. Cannot add state.")

	//Require: the ID of the state to not be already in use
	DesState::ID stateId = pStateToAdd->getId();
	if ( stateId < m_statePool.size())
	{
		if (m_statePool[pStateToAdd->getId()] != null)
			throw EX("A state with the same ID already exists")
	}

	//check to see if the state pool has space for another element
	if (stateId >= m_statePool.size())
	{
		//already at the end of the pool. resize the pool
		const int STATE_POOL_RESIZE_FACTOR = 2; //the classic factor is 2 but it maybe too slow
		m_statePool.resize((m_statePool.size()+1) * STATE_POOL_RESIZE_FACTOR); //+1 ensures it works when state pool is completely empty
	}
	
	//add the state to the pool with a new ID
	m_statePool[stateId] = pStateToAdd;

	//update the next available ID if necessary
	if (m_nextFreeID <=	stateId)
	{
		m_nextFreeID = stateId + 1;
	}

	//update state counters
	if (pStateToAdd->isMarked())
	{
		m_markedStateCount++;
	}	
	m_stateCount++;
}

//_________________________________________________________________________________________________

bool StatePool::changeState(const DesState::ID& stateId, const DesState& newState, const DesState** out_changedState/* = null*/)
{
	DesState* pState = getState(stateId);

	if (*pState == newState)
		return false; //no change 
	 
	//before making the change remember if the state was or not marked
	bool wasMarked = pState->isMarked();

	//change the state as asked by the user
	*pState = newState;

	//update the state counters
	if (wasMarked != pState->isMarked())
	{
		//the mark status changed -> increase or decrease accordingly
		m_markedStateCount = m_markedStateCount + (pState->isMarked() ? 1 : -1);
	}
	
	if (out_changedState != null)
	{
		*out_changedState = pState;
	}

	return true;
}

//_________________________________________________________________________________________________

bool StatePool::changeStateName(const DesState::ID& stateId, const std::wstring& newName, const DesState** out_changedState /*= null*/)
{
	DesState* pState = getState(stateId);

	if (pState->getName() == newName)
		return false;

	pState->setName(newName);

	if (out_changedState != null)
	{
		*out_changedState = pState;
	}

	return true;
}

//_________________________________________________________________________________________________

bool StatePool::changeStateAlias(const DesState::ID& stateId, const std::wstring& newAlias, const DesState** out_changedState /*= null*/)
{
	DesState* pState = getState(stateId);

	if (pState->getAlias() == newAlias)
		return false;

	pState->setAlias(newAlias);

	if (out_changedState != null)
	{
		*out_changedState = pState;
	}

	return true;
}

//_________________________________________________________________________________________________

bool StatePool::changeStateInit(const DesState::ID& stateId, bool isInit, const DesState** out_changedState /*= null*/)
{
	DesState* pState = getState(stateId);

	if (pState->isInit() == isInit)
		return false;

	pState->setInit(isInit);

	if (out_changedState != null)
	{
		*out_changedState = pState;
	}

	return true;
}

//_________________________________________________________________________________________________

bool StatePool::changeStateMarking(const DesState::ID& stateId, bool isMarked, const DesState** out_changedState /*= null*/)
{
	DesState* pState = getState(stateId);

	if (pState->isMarked() == isMarked)
		return false;

	pState->setMarked(isMarked);

	//the mark status changed -> increase or decrease accordingly
	m_markedStateCount = m_markedStateCount + (isMarked ? 1 : -1);

	if (out_changedState != null)
	{
		*out_changedState = pState;
	}

	return true;
}

//_________________________________________________________________________________________________

bool StatePool::changeStateReachable(const DesState::ID& stateId, bool isReachable, const DesState** out_changedState /*= null*/)
{
	DesState* pState = getState(stateId);

	pState->setReachable(isReachable);

	if (out_changedState != null)
	{
		*out_changedState = pState;
	}

	return true;
}

//_________________________________________________________________________________________________

//resets the reachability of all the states
void StatePool::resetReachability(ReachableProp resetValue /*= eReachableNotVerified*/)
{
	DesStateIterator stateIt(m_statePool);
	for(stateIt.first(); stateIt.isDone() == false; stateIt.next())
	{
		DesState& crtState = stateIt.currentItem();
		crtState.setReachabilityState(resetValue);
	}
}

//_________________________________________________________________________________________________

void StatePool::removeState(DesState* pStateToRemove)
{
	if (pStateToRemove == null)
		throw EX("Invalid (null) state. Cannot remove.")

	DesState* pStateRemoved = removeState(pStateToRemove->getId());
	assert(pStateRemoved == pStateToRemove);
	if (pStateRemoved != pStateToRemove)
	{
		//The state that was removed has the same ID with the one caller requested to be removed
		//but it was actually a different object. This should not occurr
		throw EX("Unexpected error. Two different states with the same ID were found")
	}
}

//_________________________________________________________________________________________________

DesState* StatePool::removeState(DesState::ID stateId)
{	
	if (stateId > m_statePool.size())
		throw EX("State not found. Cannot remove")

	DesState* pRemState = m_statePool[stateId];

	//remove the state from the pool. At this point the responsibility for the state is left to the caller
	//who is now is solely responsible for freeing the memory. We cannot free the memory here as the caller
	//has a pointer to the state
	m_statePool[stateId] = null;
	
	//update state counters
	if (pRemState->isMarked())
	{
		m_markedStateCount--;
	}
	m_stateCount--;
	
	return pRemState;
}


//_________________________________________________________________________________________________
//																								 //
//									PRIVATE SECTION												 //
//_______________________________________________________________________________________________//

DesState::ID StatePool::reserveNewStateID()
{
	return DesState::ID(m_nextFreeID++);
}

} //end of namespace DESpot
