/*************************************************************************
 * This file is part of DES simulation project    
 *
 * Project created in conformity with the requirements for the Degree of
 * Master of Engineering in Software Engineering, Computing and Software
 * Department, McMaster University 2004 - 2008
 *
 * Author:	Xiao Ma
 * Supervisor: Dr. Ryan Leduc
*************************************************************************/

/* 
 NAME
   SimAlg.cpp - Simulation algorithm 
 FUNCTION
   Implement simulation algorithm
 NOTES
   
 MODIFIED
   xma		04/01/08 - Integrate to DESpot
   xma	    09/01/07 - CREATION. 
*/

#include <QTime>
#include <QWaitCondition>
#include "SimAlg.h"
#include "SimWorkspace.h"
#include<cassert>
#include<iostream>
#include<sstream>

namespace DESpot
{

SimAlg::SimAlg( DesProject* desProject,						//Project 
			    SimWorkspace* configWorkspace,
			    SimCompState::StateTuple initStateTuple,	//Init state
			    SimConfig::ProjectEventSet eventSet,		//Event set
                            SimConfig::SimMode simMode,
                            SimConfig::EventSetMode eventSetMode,
                            SimStopCondition stopCondition):			//Stop condition
                                traceEnabled(true),
                                projectEventSet(eventSet),
                                eventSetMode(eventSetMode),
                                simMode(simMode),
                                stopCondition(stopCondition),
                                desProject(desProject),
                                workSpace(configWorkspace)

{

	
	currentState = new SimCompState(initStateTuple,this); 

	//Status 
	currentStep = 0;
	sessionStep = 0;
	trcStepsForLongStateName=DEFAULT_TRC_DES_PREFIX_STEPS;
	stepsToFlush=DEFAULT_FLUSH_TRACE_STEPS;
	stepSize = 1; //this is default

	createTransTab();
	//printTransTab();
	status = Runable;
	elapsedMilliseconds=0;

    if(desProject->getType()==eHierProject){
		hierProject =  dynamic_cast<DesHierProject*>(const_cast<DesProject*>(desProject));
        projectType = eHierProject;
		}
	else {
		flatProject =  dynamic_cast<DesFlatProject*>(const_cast<DesProject*>(desProject));
        projectType = eFlatProject;
	}

	//create eventset if the mode is SET
	if(eventSetMode == SimConfig::SET)
		foreach (unsigned long key, projectEventSet.keys()) {
			foreach (const ProjectEvent* value, projectEventSet.values(key)) {
			simEventSet.insert(const_cast<ProjectEvent*>(value));
		}
	}
	else  //If SEQ, we use all events in the project
	{
		SimConfig::ProjectEventSet evMap = workSpace->config()->getCompleteProjEventSet();
		foreach (unsigned long key, evMap.keys()) 
		{
			foreach (const ProjectEvent* value, evMap.values(key)) 
				simEventSet.insert(const_cast<ProjectEvent*>(value));
		}

		if (projectEventSet.size() < 1)
			status = end_of_seq;
	}


	if (simMode == SimConfig::HIER)
		initHISCOptim();

	pringTraceHeader();


}

void SimAlg::pringTraceHeader()
{
	if(traceEnabled)
	{
		QDateTime tm;
		QString tmstr = tm.currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
		writeTrace(QString("*** New simulation session:  %1").arg(tmstr));
		QString trcFileName = "Trace file: ";
		trcFileName += workSpace->config()->getTraceFileName();
		writeTrace(trcFileName);
		printSimConfig();

		if(eventSetMode == SimConfig::SET)
		{
			writeTrace("Event set to simulate:");
			writeEvSetToTrace(getSimEvents());
			workSpace->onFlushTraceFile(true);
		}
		else
		{
			writeTrace("Event seq to simulate:");
			writeTrace(QString("Number of events in seq = %1").arg(projectEventSet.size()));
			if (projectEventSet.size() > 500)
			{
				writeTrace(QString("Simulator did not write event sequence into trace because there are more than 500 events in the seq"));
				writeTrace(QString("Dump of large number of events into trace significantly impacts loading performance"));
			}
			else
			{
				writeEvSeqToTrace(projectEventSet);
				workSpace->onFlushTraceFile(true);
			}
			
		}
		writeTrace("Initial state:");
		currentState->printState(currentState->getCurrentState());

		// Too slow for AIP, remove it for now
		/*
		if (simMode == SimConfig::HIER)
		{
			printLevelDesMap();
			workSpace->onFlushTraceFile(true);
			printLevelEventMap();
			workSpace->onFlushTraceFile(true);
			printEventLevelMap();
			workSpace->onFlushTraceFile(true);
			printEventLevelCtlMap();
			workSpace->onFlushTraceFile(true);

		}
		*/
	}
}

void SimAlg::printSimConfig()
{
	writeTrace("[Configuration]");

	QString simTypeDesc = "Simulation Mode: ";
	if(simMode == SimConfig::HIER)
		simTypeDesc += "HISC";
	else 
		if (simMode == SimConfig::HIERASFLAT)
			simTypeDesc += "HISC as FLAT";
		else 
			simTypeDesc += "FLAT";
	writeTrace(simTypeDesc);

	writeTrace(QString("Event Set Mode:  %1").arg((eventSetMode == SimConfig::SEQ)?"Sequence":"Set"));
	
	QString stopStr("Stop condition: ");
	if (stopCondition.option == SimStopCondition::UNLIMITED)
		stopStr.append(QString(" unlimited"));
	if (stopCondition.option == SimStopCondition::STEP)
		stopStr.append(QString(" step = %1").arg(stopCondition.step));
	if (stopCondition.option == SimStopCondition::REACH_INIT)
		stopStr.append("Stop at next initial state");
	if (stopCondition.option == SimStopCondition::REACH_MARKED)
		stopStr.append("Stop at next marked state");
	writeTrace(stopStr);
}

void SimAlg::createTransTab()
{

	//writeTrace("*** Create Transition Table ***");
	DesProject::DesIteratorPtr desIt = desProject->createDesIterator();
	for(desIt->first(); desIt->isDone() == false; desIt->next())
	{

		const Des& des = desIt->currentItem();
		Des* pdes = const_cast<Des*>(&des);
		
		const DesState::Count stateCount = pdes->getStateCount();
		const DesEvent::Count eventCount = pdes->getEventCount();
		
		/* debug only
		QString str = QString::fromStdWString(pdes->getName());
		str.append("[");
		str.append(QString::number(stateCount));
		str.append(",");
		str.append(QString::number(eventCount));
		str.append("]");
		writeTrace(str);
		str.clear();
		*/

		TransTab **transTab = new TransTab*[stateCount];
                for (int i=0; i<(int)stateCount; i++){
			transTab[i] = new TransTab[eventCount];
		}

		Des::StateIteratorPtr stateIt = pdes->createStateIterator();
		Des::EventIteratorPtr eventIt = pdes->createEventIterator();
		StateIdxMap stateIdx;
		EventIdxMap eventIdx;
	
		int stateIndex=0;
		for(stateIt->first(); stateIt->isDone() == false; stateIt->next())
		{
			const DesState& state = stateIt->currentItem();
			DesState* sta = const_cast<DesState*>(&state);

			int eventIndex=0;
			for(eventIt->first(); eventIt->isDone() == false; eventIt->next())
			{
				const DesEvent& ev = eventIt->currentItem();
				DesEvent* evt = const_cast<DesEvent*>(&ev);

				const DesState* toState;
				if (pdes->transExists(sta,evt,toState))
					transTab[stateIndex][eventIndex]=const_cast<DesState*>(toState);
				else 
					transTab[stateIndex][eventIndex]=0;
/*
				//debug
				str.append(" [");
				str.append(QString::number(stateIndex));
				str.append(",");
				str.append(QString::number(eventIndex));
				str.append("] = ");
				str.append(transTab[stateIndex][eventIndex]==0?QString(" "):
					QString::fromStdWString(transTab[stateIndex][eventIndex]->getName()));
*/

				eventIdx.insert(std::make_pair(evt,eventIndex));
				eventIndex++;
			}
			stateIdx.insert(std::make_pair(sta,stateIndex));
			stateIndex++;

			//writeTrace(str);
			//str.clear();
		}
/*
		//debug
		StateIdxMap::iterator stateIdxIt=stateIdx.begin();
		EventIdxMap::iterator eventIdxIt=eventIdx.begin();
		QString debug_str="State Index: ";
		while(stateIdxIt != stateIdx.end()){
			debug_str.append("[");
			debug_str.append(QString::fromStdWString(stateIdxIt->first->getName()));
			debug_str.append(",");
			debug_str.append(QString::number(stateIdxIt->second));
			debug_str.append("] ");
			stateIdxIt++;
		}
		writeTrace(debug_str);

		debug_str="Event Index: ";
		while(eventIdxIt != eventIdx.end()){
			debug_str.append("[");
			debug_str.append(QString::fromStdWString(eventIdxIt->first->getName()));
			debug_str.append(",");
			debug_str.append(QString::number(eventIdxIt->second));
			debug_str.append("] ");
			eventIdxIt++;
		}
		writeTrace(debug_str);
*/
		transTabStateIdxMap.insert(std::make_pair(pdes,stateIdx));	//index into 2-d array
		transTabEventIdxMap.insert(std::make_pair(pdes,eventIdx));	//
		desTransTabMap.insert(std::make_pair(pdes,transTab));
	} // des loop
}

void SimAlg::printTransTab()
{
	writeTrace("[Transition function table]");

	std::map<Des*, TransTab**>::iterator transMapIt = desTransTabMap.begin();

	for(;transMapIt != desTransTabMap.end(); transMapIt++)
	{
		Des* des = transMapIt->first;
		writeTrace(QString::fromStdWString(des->getName()));

		/*TransTab **transTab = */transMapIt->second;

		StateIdxMap stateIdx = transTabStateIdxMap[des];
		EventIdxMap eventIdx = transTabEventIdxMap[des];
/*
		//debug 
		StateIdxMap::iterator stateIdxIt=stateIdx.begin();
		EventIdxMap::iterator eventIdxIt=eventIdx.begin();
		QString debug_str="State Index: ";
		while(stateIdxIt != stateIdx.end()){
			debug_str.append("[");
			debug_str.append(QString::fromStdWString(stateIdxIt->first->getName()));
			debug_str.append(",");
			debug_str.append(QString::number(stateIdxIt->second));
			debug_str.append("] ");
			stateIdxIt++;
		}
		writeTrace(debug_str);

		debug_str="Event Index: ";
		while(eventIdxIt != eventIdx.end()){
			debug_str.append("[");
			debug_str.append(QString::fromStdWString(eventIdxIt->first->getName()));
			debug_str.append(",");
			debug_str.append(QString::number(eventIdxIt->second));
			debug_str.append("] ");
			eventIdxIt++;
		}
		writeTrace(debug_str);
*/

		const DesState::Count stateCount = des->getStateCount();
		const DesEvent::Count eventCount = des->getEventCount();

		StateIdxMap::iterator stateIt = stateIdx.begin();
		EventIdxMap::iterator eventIt = eventIdx.begin();

		QString trans;
		for(;stateIt != stateIdx.end(); stateIt++){
			/*DesState* st = */stateIt->first;
                        int stateIndex = (int)stateIt->second;

						//Remove unused warning in release
						stateIndex = stateIndex;

                        assert(stateIndex < (int)stateCount);
						//Remove unused warning in release
						if (stateIndex < (int)stateCount) {}
			
			for(eventIt=eventIdx.begin(); eventIt != eventIdx.end(); eventIt++){
				/*DesEvent* ev = */eventIt->first;
                                int eventIndex = (int)eventIt->second;
								
								//remove unused warning in release
								eventIndex = eventIndex;

                                assert(eventIndex < (int)eventCount);
								//Remove unused warning in release
								if (eventIndex < (int)eventCount) {}
/*
				//if(transTab[stateIndex][eventIndex]!=0)
				//{
					trans.append("[");
					trans.append(QString::fromStdWString(st->getName()));
					trans.append(",");
					trans.append(QString::fromStdWString(ev->getName()));
					trans.append("] = ");
					trans.append(transTab[stateIndex][eventIndex]==0?
						QString(" "):QString::fromStdWString(transTab[stateIndex][eventIndex]->getName()));
					trans.append(",  ");
*/
				//}
			}
			//writeTrace(trans);
			//trans.clear();
		} //state loop
	} // des loop
}


bool SimAlg::isTransDefined(Des* des, DesState* state, const DesEvent* ev)
{
	const DesState* toState;
	return des->transExists(state,ev,toState);
	
}

DesState* SimAlg::getNextState(Des* des, DesState* state, const DesEvent* ev)
{

	TransTab** transTab = desTransTabMap[des];

	StateIdxMap stateIdx = transTabStateIdxMap[des];
	EventIdxMap eventIdx = transTabEventIdxMap[des];

	int stateIndex = stateIdx[state];
	int eventIndex = eventIdx[const_cast<DesEvent*>(ev)];
	
	return transTab[stateIndex][eventIndex];
}




//Choose event randomly
ProjectEvent* SimAlg::chooseEvent()
{
	if (eventSetMode == SimConfig::SEQ)
		return getSeqSimEvent();

	// Choose event randomly
	std::set<ProjectEvent*> eligibleEventSet = currentState->getEligEvents();

	int maxRand = eligibleEventSet.size();
	/* initialize random seed: */
	qsrand ( QTime::currentTime().second() );
	/* generate secret number: */
	int row = qrand() % maxRand;

	std::set<ProjectEvent*>::iterator it = eligibleEventSet.begin();
	while( row > 0) {
		it++; row--;
		}
	ProjectEvent *ev= *it;
	assert(ev != 0);
    return ev;  
}


/* update current state with the choosen event. 
 * 1) update state with event choosen
 * 2) create a new next state
 * 3) link current, next in linked list
 * 4) push current in history link
 * 5) std::set next to current
 */
void SimAlg::updateCurrentState(ProjectEvent* ev)
{
	
    //dbg("******* Update current states ********");
    
    currentState->setChoosenEvent(ev);
	currentState->computeNextState(ev);

	if (traceEnabled){
		writeTrace(QString("Step=%1").arg(currentStep + 1));
		if ((currentStep % trcStepsForLongStateName) == 0)	
			currentState->printLongState("Previous State");
		else
			currentState->printShortState("Previous State");
		currentState->printEligEvents();
		writeTrace(QString("Event chosen: %1").arg
			(QString::fromStdWString(ev->getName())));
	}
	
    //get next state, create new composite state
    SimCompState::StateTuple nextState=currentState->getNextStateTuple(ev);
    SimCompState *nextCompState = new SimCompState(nextState,this);

    currentState->setNextState(nextCompState);
    nextCompState->setPrevState(currentState);
    nextCompState->setRevEvent(ev);
    
	if(histEnabled)
	{
	    stateHist.push_back(currentState);
	}
	else
	{
		SimCompState* prevState = currentState->getPrevState();
		if(prevState)
			delete prevState;
	}

    currentState=nextCompState;
	
	if(traceEnabled)
	{
		if ((currentStep % trcStepsForLongStateName) == 0)	
		{
			currentState->printLongState("Current State");
		}
		else
		{
			currentState->printShortState("Current State");
		}
	}

}

//To support sequence. This will only be called from workspace::getChooseEvent
//to get the event in the set
std::set<const ProjectEvent*> SimAlg::getSeqSimEvents()
{

	if(eventSetMode == SimConfig::SET)
	{
		return simEventSet;
	}
	else
	{
		std::set<const ProjectEvent*> evSet;
		//This might be further enhanced to support multiple choice of events in each step
		//Note, currentStep + 1 here
		foreach (const ProjectEvent* value, projectEventSet.values(currentStep + 1)) {
			evSet.insert(const_cast<ProjectEvent*>(value));
			}
		return evSet;
	}
}

ProjectEvent* SimAlg::getSeqSimEvent()
{
	std::set<const ProjectEvent*> evSet;
		//This might be further enhanced to support multiple choice of events in each step
		//Note, currentStep + 1 here
	foreach (const ProjectEvent* value, projectEventSet.values(currentStep + 1)) 
	{
		evSet.insert(const_cast<ProjectEvent*>(value));
	}

	return const_cast<ProjectEvent*>(*(evSet.begin()));
}

std::set<const ProjectEvent*>& SimAlg::getSimEvents()
{

	return simEventSet;
	/* SEQ mode now calculate all events 
	if(eventSetMode == SimConfig::SET)
		return simEventSet;
	else
	{
		simEventSet.clear();
		//This might be further enhanced to support multiple choice of events in each step
		foreach (const ProjectEvent* value, projectEventSet.values(currentStep)) {
			simEventSet.insert(const_cast<ProjectEvent*>(value));
			}
		return simEventSet;
	}
	*/
}

/* old version 
std::set<ProjectEvent*> SimAlg::getSimEvents()
{
	std::set<ProjectEvent*> evSet;
	if(eventSetMode == SimConfig::SEQ)
	{
		//This might be further enhanced to support multiple choice of events in each step
		foreach (const ProjectEvent* value, projectEventSet.values(currentStep+1)) {
			evSet.insert(const_cast<ProjectEvent*>(value));
			}
	}
	else
	{
		foreach (unsigned long key, projectEventSet.keys()) {
			foreach (const ProjectEvent* value, projectEventSet.values(key)) {
				evSet.insert(const_cast<ProjectEvent*>(value));
			}
		}
	}
	return evSet;
}
*/

std::set<const ProjectEvent*>& SimAlg::getSimEvents(const DesSubsystem* subsys)
{

	return *levelEventMap[subsys];
	/* SEQ mode now calculate all events 
	if(eventSetMode == SimConfig::SET)
		return *levelEventMap[subsys];
	else
	{
		simEventSet.clear();
		//This might be further enhanced to support multiple choice of events in each step
		foreach (const ProjectEvent* value, projectEventSet.values(currentStep)) {
			simEventSet.insert(const_cast<ProjectEvent*>(value));
			}
		return simEventSet;
	}
	*/
}

//This check is after current step increment so that there is no -1 
void SimAlg::checkSeqRange()
{
	if (eventSetMode == SimConfig::SEQ)
	{
		if (currentStep > (unsigned long)projectEventSet.size() -1)
		{
			writeTrace(QString("Next step = %1  # events in seq = %2")
				.arg(QString::number(currentStep+1),QString::number(projectEventSet.size())));
			writeTrace("End of the event sequence. Stop.");
			status = end_of_seq;
		}
	}
}

bool SimAlg::timeToStop()
{
	std::set<ProjectEvent*> eligibleEvSet = currentState->getEligEvents();

	if (eventSetMode == SimConfig::SEQ)
	{
		if (currentStep > (unsigned long)projectEventSet.size() - 1)
		{
			writeTrace(QString("Next step = %1  # events in seq = %2")
				.arg(QString::number(currentStep+1),QString::number(projectEventSet.size())));
			writeTrace("End of the event sequence. Stop.");
			status = end_of_seq;
			return true;
		}
		else
		{
			bool eventAvail = false;
			std::set<const ProjectEvent*> evSet = getSeqSimEvents();
			ProjectEvent* ev = 0;
			assert(evSet.size()<=1);            //One or empty
			if (evSet.size()>0)
			{
				ev = const_cast<ProjectEvent*>(*(evSet.begin()));
				if (eligibleEvSet.count(ev) > 0)//this test failed
					eventAvail = true;
			}

			if (eventAvail)
				return false;
			else
			{
				writeTrace("The current event in sequence is not eligible. Stop.");
				writeTrace(QString("Additional info: next step = %1   next event in seq = %2")
					.arg(QString::number(currentStep+1),QString::fromStdWString(ev->getName())));
				status = no_elig_seqev;
				return true;
			}
		}

	}

	// Run out of eligible events.
	if (eligibleEvSet.empty()){
		writeTrace("No eligible event available. Stop.");
		status=Stop;
		return true;
	}

	switch (stopCondition.option){
		case SimStopCondition::STEP:
			if (stopCondition.step <= currentStep){
				writeTrace(QString("predefined number of steps = %1 reached. Stop.").arg(currentStep));
				status=Reach_condition;
				return true;
				}
			break;
		case SimStopCondition::REACH_INIT:
			if (sessionStep > 0)
				if (currentState->isInit()){
					writeTrace("Reach init state. Stop.");
					status=Reach_condition;
					return true;
				}
			break;
		case SimStopCondition::REACH_MARKED:
			if (sessionStep>0)
				if (currentState->isMarked() && currentStep > 1){
					writeTrace("Reach marked state. Stop.");
					status=Reach_condition;
					return true;
					}
			break;

                default:
                         break;
	}

	if (status == User_request_stop){
			writeTrace("User requested simulation stop. Stop.");
			status = User_request_stop;
			return true;
		}	

	if (status == user_exit){
			writeTrace("User requested simulation stop and exit. Stop.");
			return true;
		}	

	return false;
}

void SimAlg::computeEligEventsFlat(bool computeNextState)
{
	currentState->computeEligEventFlat(computeNextState);
}

void SimAlg::computeEligEventsHISC(bool computeAllEligEvent)
{
	currentState->computeEligEventHISC(computeAllEligEvent);
}

void SimAlg::computeEligEventsSeqHISC()
{
	currentState->computeEligEventSeqHISC();
}


void SimAlg::writeStateToTrace(SimCompState* /*state*/)
{
	currentState->printState(currentState->getCurrentState());
}

void SimAlg::writeEvSetToTrace(std::set<const ProjectEvent*> eventSet)
{
	QString line;
	std::set<const ProjectEvent *>::const_iterator it=eventSet.begin();
    for(; it != eventSet.end() ; ++it){
		const DesEvent* ev = *it;
		line += QString::fromStdWString(ev->getName());
		line += ",";
	}
	line.remove(line.count()-1,1);
	writeTrace(line);
}

void SimAlg::writeEvSeqToTrace(SimConfig::ProjectEventSet &evSeq)
{
	QString line;
	SimConfig::ProjectEventSet::const_iterator it=evSeq.begin();
    for(; it != evSeq.end() ; ++it){
		line += QString::number(it.key());
		line += " ";
		const DesEvent* ev = it.value();
		line += QString::fromStdWString(ev->getName());
		line += ",";
	}
	line.remove(line.count()-1,1);
	writeTrace(line);
}

//deprcate
std::set<DesEvent*> SimAlg::getDesEventSet(const ProjectEvent* ev)
{
	std::set<DesEvent*> eventSet;
	ProjectEvent::SourceIteratorPtr srcIt = ev->createSourceIterator();
	for(srcIt->first(); srcIt->notDone(); srcIt->next())
	{
		const ProjectEvent::Source& crtSource = srcIt->currentItem();
		eventSet.insert(const_cast<DesEvent*>(crtSource.event));
	}
	return eventSet;
}

std::set<Des*> SimAlg::getDefinedDesSet(const ProjectEvent* ev)
{
	if (defDesSet.count(ev)>0)
	{
		return defDesSet[ev];
	}
	else
	{
		std::set<Des*> desSet;
		ProjectEvent::SourceIteratorPtr srcIt = ev->createSourceIterator();
		for(srcIt->first(); srcIt->notDone(); srcIt->next())
		{
			const ProjectEvent::Source& crtSource = srcIt->currentItem();
			desSet.insert(crtSource.des);
		}
		defDesSet.insert(std::make_pair(ev,desSet));
		return desSet;
	}
}

std::set<Des*> SimAlg::getUndefinedDesSet(const ProjectEvent* ev)
{
	if (undefDesSet.count(ev)>0)
	{
		return undefDesSet[ev];
	}
	else
	{
		std::set<Des*> definedSet=getDefinedDesSet(ev);
		std::set<Des*> undefinedSet;

		DesProject::DesIteratorPtr desIt = desProject->createDesIterator();
		for(desIt->first(); desIt->isDone() == false; desIt->next())
		{
			const Des& des = desIt->currentItem();
			Des* pdes = const_cast<Des*>(&des);
			if(definedSet.count(pdes)==0)
				undefinedSet.insert(pdes);
		}
		undefDesSet.insert(std::make_pair(ev,undefinedSet));
		return undefinedSet;
	}
}

//These two overloaded functions are to support HISC optmization. They are level wise
std::set<Des*> SimAlg::getDefinedDesSet(const ProjectEvent* ev, const DesSubsystem *subsys)
{
	if (defDesSet.count(ev)>0)
	{
		return defDesSet[ev];
	}
	else
	{
		std::set<Des*> desSet;
		ProjectEvent::SourceIteratorPtr srcIt = ev->createSourceIterator();
		for(srcIt->first(); srcIt->notDone(); srcIt->next())
		{
			const ProjectEvent::Source& crtSource = srcIt->currentItem();
			if(levelDesMap[subsys]->count(crtSource.des) > 0)
				desSet.insert(crtSource.des);
		}
		defDesSet.insert(std::make_pair(ev,desSet));
		return desSet;
	}
}

std::set<Des*> SimAlg::getUndefinedDesSet(const ProjectEvent* ev, const DesSubsystem* /*subsys*/)
{
	if (undefDesSet.count(ev)>0)
	{
		return undefDesSet[ev];
	}
	else
	{
		std::set<Des*> definedSet=getDefinedDesSet(ev);
		std::set<Des*> undefinedSet;

		DesProject::DesIteratorPtr desIt = desProject->createDesIterator();
		for(desIt->first(); desIt->isDone() == false; desIt->next())
		{
			const Des& des = desIt->currentItem();
			Des* pdes = const_cast<Des*>(&des);
			if(definedSet.count(pdes)==0)
				undefinedSet.insert(pdes);
		}

		undefDesSet.insert(std::make_pair(ev,undefinedSet));
		return undefinedSet;
	}
}


std::set<ProjectEvent*> SimAlg::getEligibleEventSet()
{
	//have to duplicate computeEligEvents here otherwise
	//workspace cannot get the new eligStates.
	/*
	std::set<ProjectEvent*> emptySet;
	computeEligEvents();
	if (!timeToStop())   //To check if the elig ev set is empty
		return currentState->getEligEvents();
	else
		return emptySet;
	*/
	if (simMode == SimConfig::HIER)
		computeEligEventsHISC(true);
	else
		computeEligEventsFlat(true);

	return currentState->getEligEvents();

}

SimCompState::StateTuple* SimAlg::getCurrentState()
{
	//currentState->printState(currentState->getCurrentState());
	return currentState->getCurrentState();
}

SimCompState* SimAlg::getPrevState()
{
	return currentState->getPrevState();
}

SimCompState* SimAlg::getCurState()
{
	return currentState;
}

SimCompState::StateTuple SimAlg::getNextStateTuple(ProjectEvent* ev)
{
	SimCompState::StateTuple nextState=currentState->getNextStateTuple(ev);
	return nextState;
}

//This is fast/flat mode, so delay the compute of next state
void SimAlg::runFlat()
{
	sessionStep=0;
	QTime timer;
	timer.start();

	while(1)
	{
		qApp->processEvents();

		//replace the above code with
		if(!oneStepForwardFlat(false))
			break;

		if (currentStep % stepsToFlush == 0 ) 
			workSpace->onFlushTraceFile(false);		

		workSpace->updateRunStatus();

	}
	elapsedMilliseconds += timer.elapsed();

}

void SimAlg::runFastHISC()
{
	sessionStep=0;
	QTime timer;
	timer.start();

	while(1)
	{
		qApp->processEvents();

		//replace the above code with
		if(!oneStepForwardHISC(false))
			break;

		if (currentStep % stepsToFlush == 0 ) 
			workSpace->onFlushTraceFile(false);		

		workSpace->updateRunStatus();

	}
	elapsedMilliseconds += timer.elapsed();

}

//When running in fast mode HISC, we do not calulate 
// all eligible events. This will cause problem when
// check if the event in seq is eligible.
// For performance purpose, we do not add the extra 
// check in runFastHISC. Rather, we use another function
// to do this. 
void SimAlg::runFastSeqHISC()
{
	sessionStep=0;
	QTime timer;
	timer.start();

	while(1)
	{
		qApp->processEvents();

		//replace the above code with
		if(!oneStepForwardSeqHISC())
			break;

		if (currentStep % stepsToFlush == 0 ) 
			workSpace->onFlushTraceFile(false);		

		workSpace->updateRunStatus();

	}
	elapsedMilliseconds += timer.elapsed();

}

//The choosenEvent will only be used in the first step
//If steps > 1, the 2nd steps and on will randomly choose
//events to occur. 
void SimAlg::stepForwardFlat(ProjectEvent* choosenEvent)
{
	//sessionStep=0;
	QTime timer;
	timer.start();

	unsigned long numSteps = stepSize;
	bool continueRun = true;

	continueRun = oneStepForwardFlat(choosenEvent);
	numSteps--;

	//dbg(QString("******** stepForward %1 steps ********").arg(numSteps));
	while( numSteps > 0 && continueRun == true){
		//this is step forward so compute all state
		continueRun = oneStepForwardFlat(true);
		numSteps--;
	}
	//dbg(QString("******** stepForward completed********"));

	elapsedMilliseconds += timer.elapsed();

}

void SimAlg::stepForwardHISC(ProjectEvent* choosenEvent)
{
	//sessionStep=0;
	QTime timer;
	timer.start();

	unsigned long numSteps = stepSize;
	bool continueRun = true;

	continueRun = oneStepForwardHISC(choosenEvent);
	numSteps--;

	//dbg(QString("******** stepForward %1 steps ********").arg(numSteps));
	while( numSteps > 0 && continueRun == true){
		continueRun = oneStepForwardHISC(true);
		numSteps--;
	}
	//dbg(QString("******** stepForward completed********"));

	elapsedMilliseconds += timer.elapsed();

}

bool SimAlg::oneStepForwardFlat(bool computeNextState)
{
	computeEligEventsFlat(computeNextState);

	//must after compute elig 
	if (timeToStop())
	{
		sessionStep = 0;
		return false;
	}

	ProjectEvent* ev=chooseEvent();
	updateCurrentState(ev);
	currentStep++;
	sessionStep++;
	checkSeqRange();

	return true;
}

bool SimAlg::oneStepForwardHISC(bool compAllEligEvent)
{
	computeEligEventsHISC(compAllEligEvent);
	//computeEligEvents_HISC();

	//must after compute elig 
	if (timeToStop())
	{
		sessionStep = 0;
		return false;
	}

	ProjectEvent* ev=chooseEvent();
	updateCurrentState(ev);
	currentStep++;
	sessionStep++;
	checkSeqRange();

	return true;
}

bool SimAlg::oneStepForwardHISC(ProjectEvent* ev)
{
	computeEligEventsHISC(true);
	//computeEligEvents_HISC();

	//must after compute elig 
	if (timeToStop())
	{
		sessionStep = 0;
		return false;
	}

	updateCurrentState(ev);
	currentStep++;
	sessionStep++;
	checkSeqRange();


	return true;
}

bool SimAlg::oneStepForwardSeqHISC()
{
	computeEligEventsSeqHISC();
	//computeEligEvents_HISC();

	//must after compute elig 
	if (timeToStop())
	{
		sessionStep = 0;
		return false;
	}

	ProjectEvent* ev=chooseEvent();
	updateCurrentState(ev);
	currentStep++;
	sessionStep++;
	checkSeqRange();

	return true;
}

bool SimAlg::oneStepForwardFlat(ProjectEvent* choosenEvent)
{

	//dbg("******** oneStepForward ********");

	computeEligEventsFlat(true);

	if (timeToStop())
	{
		sessionStep = 0;
		return false;
	}

	updateCurrentState(choosenEvent);

	currentStep++;
	sessionStep++;
	checkSeqRange();


	//dbg("******** oneStepForard return true ********");
	return true;
}

void SimAlg::stepBackward()
{
	std::vector<SimCompState*>::size_type sz = stateHist.size();
	unsigned int numSteps = stepSize;
	if(traceEnabled){
		writeTrace(QString("*** Step back ***"));
		writeTrace(QString("Original next step = %1").arg(currentStep+1));
		writeTrace(QString("History size = %1, requested steps to go back = %2").arg(sz).arg(numSteps));
	}

	if (numSteps > sz)
		numSteps = sz;

	if(numSteps<1) return;

	currentStep -= numSteps;
	
	if (sessionStep < numSteps )
	{
		sessionStep = 0;
	}
	else
	{
		sessionStep -= numSteps;
	}

	while (numSteps > 1 ){
		//core dump if doing this
		//but might cause leak if not doing so
		//SimCompState *st = stateHist[stateHist.size()-1];
		//delete st;
		stateHist.pop_back();
		--numSteps;
	}

	assert(numSteps==1);
	if (stateHist.size()>0 && numSteps > 0 ){
		//core dump if doing this
		//but might cause leak if not doing so
		//SimCompState *st = stateHist[stateHist.size()-1];
		//delete st;
		currentState = stateHist[stateHist.size()-1];
		stateHist.pop_back();
	}
	if(traceEnabled)
		writeTrace(QString("New next step = %1").arg(currentStep+1));
	

}

void SimAlg::stepBackward(int stepsToBack)
{
	std::vector<SimCompState*>::size_type sz = stateHist.size();
	unsigned int numSteps = stepsToBack;
	if(traceEnabled){
		writeTrace(QString("*** Step back ***"));
		writeTrace(QString("Original next step = %1").arg(currentStep+1));
		writeTrace(QString("History size = %1, requested steps to go back = %2").arg(sz).arg(numSteps));
	}

	if (numSteps > sz)
		numSteps = sz;

	if(numSteps<1) return;

	currentStep -= numSteps;
	while (numSteps > 1 ){
		//core dump if doing this
		//but might cause leak if not doing so
		//SimCompState *st = stateHist[stateHist.size()-1];
		//delete st;
		stateHist.pop_back();
		--numSteps;
	}

	assert(numSteps==1);
	if (stateHist.size()>0 && numSteps > 0 ){
		//core dump if doing this
		//but might cause leak if not doing so
		//SimCompState *st = stateHist[stateHist.size()-1];
		//delete st;
		currentState = stateHist[stateHist.size()-1];
		stateHist.pop_back();
	}
	if(traceEnabled)
		writeTrace(QString("New next step = %1").arg(currentStep+1));
	

}

//deprecated
bool SimAlg::oneStepBackward()
{
	return false;
}

SimConfig::ProjectEventSet SimAlg::getHistEventSeq()
{
	SimConfig::ProjectEventSet eventSet;
	std::vector<SimCompState*>::const_iterator it = stateHist.begin();
	for(int i=1 ;it != stateHist.end(); ++it){
		SimCompState* state = *it;
		eventSet.insert(i,state->getChoosenEvent());
		++i;
	}
	return eventSet;	
}

// Initalize data structure for HISC optimization
void SimAlg::initHISCOptim()
{
	createLevelDesMap();
	createInterSubsysMap();
	createLevelEventMap();

}


void SimAlg::createLevelDesMap()
{
	/////// Hihg level ///////
	const DesSubsystem& highLevelSubsys=hierProject->getRootSubsys();
    DesSubsystem::DesIteratorPtr supDesIt = highLevelSubsys.createDesIterator(eSupervisorDes);
	std::set<const Des*> *hDesSet = new std::set<const Des*>;
	for(supDesIt->first(); supDesIt->isDone() == false; supDesIt->next())
	{
		const Des& des = supDesIt->currentItem();
		hDesSet->insert(&des);		
	}

	DesSubsystem* subsys = const_cast<DesSubsystem*>(&highLevelSubsys);

	//Add high level plants
    DesSubsystem::DesIteratorPtr plantDesIt = highLevelSubsys.createDesIterator(ePlantDes);
	for(plantDesIt->first(); plantDesIt->isDone() == false; plantDesIt->next())
	{
		const Des& des = plantDesIt->currentItem();
		hDesSet->insert(&des);		
	}

    // Add interface DES
	DesHierProject::InterfIteratorPtr interfIt = hierProject->createInterfIterator();
	for(interfIt->first(); interfIt->isDone() == false; interfIt->next())
	{
		const DesInterface& interf = interfIt->currentItem();				
		DesInterface::DesIteratorPtr desIt = interf.createDesIterator();
		for(desIt->first(); desIt->isDone() == false; desIt->next())
		{
			const Des& des = desIt->currentItem();
			hDesSet->insert(&des);		
		}
	}
	levelDesMap.insert(std::make_pair(subsys,hDesSet));
	levelSet.insert(subsys);


	/////  Low level //////
	//DesSubsystem::DependIteratorPtr lowSubsysIt = hierProject->getRootSubsys().createDependsIterator();
	DesHierProject::SubsysIteratorPtr lowSubsysIt = hierProject->createSubsysIterator();
	for(lowSubsysIt->first(); lowSubsysIt->isDone() == false; lowSubsysIt->next())
	{
		if(lowSubsysIt->currentItem().isRoot() == false)
		{
		std::set<const Des*> *lDesSet = new std::set<const Des*>;
		lDesSet->clear();

		const DesInterface&  interf = lowSubsysIt->currentItem().getInterface();
		DesInterface::DesIteratorPtr desIt = interf.createDesIterator();
		for(desIt->first(); desIt->isDone() == false; desIt->next())
		{
			const Des& des = desIt->currentItem();
			lDesSet->insert(&des);		
		}

		const DesSubsystem&  lowSubsys = lowSubsysIt->currentItem();
		DesSubsystem* subsys = const_cast<DesSubsystem*>(&lowSubsys);

        DesSubsystem::DesIteratorPtr supDesIt = lowSubsys.createDesIterator(eSupervisorDes);
		for(supDesIt->first(); supDesIt->isDone() == false; supDesIt->next())
		{
			const Des& des = supDesIt->currentItem();
			lDesSet->insert(&des);		
		}

        DesSubsystem::DesIteratorPtr plantDesIt = lowSubsys.createDesIterator(ePlantDes);
		for(plantDesIt->first(); plantDesIt->isDone() == false; plantDesIt->next())
		{
			const Des& des = plantDesIt->currentItem();
			lDesSet->insert(&des);		
		}

		levelDesMap.insert(std::make_pair(subsys,lDesSet));
		levelSet.insert(subsys);
	}
}
}

//See HierProjStructureUiPart::loadProjectStruct() how this is implemented
void SimAlg::createInterSubsysMap()
{
	//load all low-level subsystems dependent on the high-level one
	//DesSubsystem::DependIteratorPtr lowSubsysIt = hierProject->getRootSubsys().createDependsIterator();
	DesHierProject::SubsysIteratorPtr lowSubsysIt = hierProject->createSubsysIterator();
	for(lowSubsysIt->first(); lowSubsysIt->isDone() == false; lowSubsysIt->next())
	{
		const DesInterface*  interf = &(lowSubsysIt->currentItem().getInterface());
		const DesSubsystem*  lowSubsys = &(lowSubsysIt->currentItem());
		interSubsysMap.insert(std::make_pair(interf,lowSubsys));
	}
}

// See ProjEventSetWidget.cpp to understand how this func is implmented
// The events that a level controls are
// 1. High level controls :  high level events + all request events
// 2. low level controls:    low level events + answer events + low data events
void SimAlg::createLevelEventMap()
{
	const DesSubsystem* highLevel= &(hierProject->getRootSubsys());

	DesProject::EventIteratorPtr eventIt = desProject->createProjEventIterator();
	for(eventIt->first(); eventIt->isDone() == false; eventIt->next())
	{		
		const ProjectEvent* event = &(eventIt->currentItem());

		//We only care about the subset of the events to be simulated
		if(eventSetMode == SimConfig::SET)        //We should not get to here if SEQ
			if(simEventSet.count(const_cast<ProjectEvent*>(event))<=0)
				continue;

		const InterfaceEvent* interfEvent=0;
		const SubsystemEvent* subsysEvent=0; 
		const DesSubsystem* subsys=0;
		std::set<const ProjectEvent*> *evSet;
		std::set<const DesSubsystem*> *subsysSet;
        EventType eventType = event->getType();

		switch(eventType)
		{
			//Interface events
            case eAnswerEvent:
            case eRequestEvent:
            case eLDataEvent:
				interfEvent = static_cast<const InterfaceEvent*>(event);
				if (interfEvent->hasOwner())
				{
					//Construct level-event map, that is, given a level, find all events it controls
					const DesInterface& interf = interfEvent->owner();
					assert (interSubsysMap.count(&interf)>0);
					// The low level controls answer and low data events
                    if (eventType == eAnswerEvent ||
                        eventType == eLDataEvent)
					{
						//low level the interface controls
						if (levelEventMap.count(interSubsysMap[&interf])==0)
						{
							evSet = new std::set<const ProjectEvent*>;
							evSet->insert(event);
							levelEventMap.insert(std::make_pair(interSubsysMap[&interf],evSet));
						}
						else
						{
							evSet = levelEventMap[interSubsysMap[&interf]];
							evSet->insert(event);
						}
					}
					else //request event
					{
						//An interface event is always a high level event
						if (levelEventMap.count(highLevel)==0)
						{
							evSet = new std::set<const ProjectEvent*>;
							evSet->insert(event);
							levelEventMap.insert(std::make_pair(highLevel,evSet));
						}
						else
						{
							evSet = levelEventMap[highLevel];
							evSet->insert(event);
						}
					}

					// Construct Event-level map. This is the level that the event impacts
					// interface event impacts both high and low level
					if (eventLevelMap.count(event)==0)
					{
						subsysSet = new std::set<const DesSubsystem*>;
						subsysSet->insert(interSubsysMap[&interf]);
						subsysSet->insert(highLevel);  //always impact high level
						eventLevelMap.insert(std::make_pair(event,subsysSet));
					}
					else
					{
						subsysSet = eventLevelMap[event];
						subsysSet->insert(interSubsysMap[&interf]);
						subsysSet->insert(highLevel);
					}

				}
				else
				{
					//This is an orphaned interface event. This means that it is used in subsystems but not defined
					//in any interface in the project. The orphaned events are displayed at the root of all interface
					//events signifying that they do not belong to any interface
					///// IGNORE IT? ////
				}
				break;

            case eHighLevelEvent:
            case eLowLevelEvent:
				subsysEvent = static_cast<const SubsystemEvent*>(event);
				subsys = &(subsysEvent->owner());
				if (levelEventMap.count(subsys)==0)
					{
						evSet = new std::set<const ProjectEvent*>;
						evSet->insert(event);
						levelEventMap.insert(std::make_pair(subsys,evSet));
					}
					else
					{
						evSet = levelEventMap[subsys];
						evSet->insert(event);
					}

				if (eventLevelMap.count(event)==0)
					{
						subsysSet = new std::set<const DesSubsystem*>;
						subsysSet->insert(subsys);
						eventLevelMap.insert(std::make_pair(event,subsysSet));
					}
					else
					{
						subsysSet = eventLevelMap[event];
						subsysSet->insert(subsys);
					}
				break;

			default:
				assert(false);
				throw EX("Invalid event type. Cannot set type of event.")
		}
	}

	//Create event-level-control map. Given an event, find the levels that control it
	std::map<const DesSubsystem*,std::set<const ProjectEvent*>*>::iterator 
		levelEvIt = levelEventMap.begin();
	for(;levelEvIt != levelEventMap.end(); levelEvIt++)
	{
		const DesSubsystem* subsys = levelEvIt->first;

		std::set<const ProjectEvent*>* evSet = levelEvIt->second;
		std::set<const ProjectEvent*>::iterator evIt = evSet->begin();
		for(;evIt != evSet->end(); evIt++)
		{
			const ProjectEvent* ev = *evIt;
			eventLevelCtlMap.insert(std::make_pair(ev,subsys));
		}
	}

}


void SimAlg::printLevelDesMap()
{
	writeTrace("[Level DES table]");

	std::map<const DesSubsystem*,std::set<const Des*>*>::iterator 
		levelMapIt = levelDesMap.begin();

	QString line;
	for(;levelMapIt != levelDesMap.end(); levelMapIt++)
	{
		const DesSubsystem* subsys = levelMapIt->first;
		line = QString::fromStdWString(subsys->getName());
		line += ": ";

		std::set<const Des*>* desSet = levelMapIt->second;
		std::set<const Des*>::iterator desIt = desSet->begin();
		for(;desIt != desSet->end(); desIt++){
			const Des* des = *desIt;
			line += QString::fromStdWString(des->getName());
			line += " ";
		}
		writeTrace(line);
	}
}


void SimAlg::printLevelEventMap()
{
	writeTrace("[Level Event table]");

	std::map<const DesSubsystem*,std::set<const ProjectEvent*>*>::iterator 
		levelEvIt = levelEventMap.begin();

	QString line;
	for(;levelEvIt != levelEventMap.end(); levelEvIt++)
	{
		const DesSubsystem* subsys = levelEvIt->first;
		line = QString::fromStdWString(subsys->getName());
		line += ": ";

		std::set<const ProjectEvent*>* evSet = levelEvIt->second;
		std::set<const ProjectEvent*>::iterator evIt = evSet->begin();
		for(;evIt != evSet->end(); evIt++){
			const ProjectEvent* ev = *evIt;
			line += QString::fromStdWString(ev->getName());
			QString ts;
			switch(ev->getType())
			{
                case eAnswerEvent:
					ts = "A";
					break;
                case eRequestEvent:
					ts = "R";
					break;
                case eLDataEvent:
					ts = "LD";
					break;
                case eHighLevelEvent:
					ts = "H";
					break;
                case eLowLevelEvent:
					ts = "L";
					break;
				default:
					ts = "U";
					break;
			}
			line += "[";
			line += ts;
			line += "] ";
		}
		writeTrace(line);
	}
}

void SimAlg::printEventLevelMap()
{
	writeTrace("[Event Level table] Levels the event impacts");

	std::map<const ProjectEvent*,std::set<const DesSubsystem*>*>::iterator 
		levelEvIt = eventLevelMap.begin();

	QString line;
	for(;levelEvIt != eventLevelMap.end(); levelEvIt++)
	{
		const ProjectEvent* ev = levelEvIt->first;
		line = QString::fromStdWString(ev->getName());
		line += ": ";

		std::set<const DesSubsystem*>* subsysSet = levelEvIt->second;
		std::set<const DesSubsystem*>::iterator subsysIt = subsysSet->begin();
		for(;subsysIt != subsysSet->end(); subsysIt++){
			const DesSubsystem* subsys = *subsysIt;
			line += QString::fromStdWString(subsys->getName());
			line += ",";
		}
		line.remove(line.count()-1,1);
		writeTrace(line);
	}
}

void SimAlg::printEventLevelCtlMap()
{
	writeTrace("[Event Level Control Table] The level that control the event");

	std::map<const ProjectEvent*,const DesSubsystem*>::iterator 
		levelEvIt = eventLevelCtlMap.begin();

	QString line;
	for(;levelEvIt != eventLevelCtlMap.end(); levelEvIt++)
	{
		const ProjectEvent* ev = levelEvIt->first;
		line = QString::fromStdWString(ev->getName());
		line += ": ";

		const DesSubsystem* subsys = levelEvIt->second;
		line += QString::fromStdWString(subsys->getName());
		writeTrace(line);
	}
}

void SimAlg::writeTrace(const QString &str)
{
	workSpace->writeTrace(str);
}

void SimAlg::dbg(const QString &str)
{
	workSpace->writeTrace(str);
}

ProjectEvent* SimAlg::getPrevEvent()
{
	return currentState->getReverseEvent();
}

ProjectEvent* SimAlg::getNextEvent()
{
	return currentState->getNextEvent();
}

ProjectEvent* SimAlg::getPrevEvent(int row)
{
	SimCompState *stat = stateHist[row];  //must exist and valid
	return stat->getReverseEvent();
}

ProjectEvent* SimAlg::getNextEvent(int row)
{
	SimCompState *stat = stateHist[row];  //must exist and valid
	return stat->getNextEvent();
}

ProjectEvent* SimAlg::getNextEventInSeq()
{
	std::set<const ProjectEvent*> evSet;
		//This might be further enhanced to support multiple choice of events in each step
		//Note, currentStep + 1 here
	foreach (const ProjectEvent* value, projectEventSet.values(currentStep + 1)) 
	{
		evSet.insert(const_cast<ProjectEvent*>(value));
	}

	if (evSet.size() > 0)
		return const_cast<ProjectEvent*>(*(evSet.begin()));
	else 
		return 0;
}

} //namespace
