/*	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 "LwNonBlockingAlgo.h"
#include "HighSubsysNonBlockAlgo.h"
#include "LowSubsysNonBlockAlgo.h"
#include "ProjIntegrityAlgo.h"
namespace DESpot
{

const std::wstring LwNonBlockingAlgo::cLwNonBlockAlgoDesc = L"Level Wise Nonblocking Algo";
const std::wstring LwNonBlockingAlgo::cIntegrityNotConfirmed = L"Project integrity has not been checked";
const std::wstring LwNonBlockingAlgo::cProjectInvalid = L"Project is not valid";

//_________________________________________________________________________________________________

LwNonBlockingAlgo::LwNonBlockingAlgo(DesHierProject& project, bool runIncremental /*= true*/) : 
			m_project(project),  
			m_hNonBlockAlgo(null), 
			m_isLwNonBlock(false)
{
	m_description = cLwNonBlockAlgoDesc;
	m_runIncremental = runIncremental;
	//isfirsterror=true;
	//IsFirstErrorSubSystem=true;
	
}

//_________________________________________________________________________________________________

LwNonBlockingAlgo::~LwNonBlockingAlgo(void)
{
	clearAlgo();
}

//_________________________________________________________________________________________________

DesHierProject& LwNonBlockingAlgo::getProject()
{
	return m_project;
}

//_________________________________________________________________________________________________

void LwNonBlockingAlgo::clearAlgo()
{
	if (m_hNonBlockAlgo!=null)
	{
		delete m_hNonBlockAlgo;
		m_hNonBlockAlgo = null;
	}

	for(unsigned int i = 0; i < m_lNonBLockAlgoList.size(); i++)
	{
		LowSubsysNonBlockAlgo* algo = m_lNonBLockAlgoList[i];
		if (algo)
		{
			delete algo;
			algo = null;
		}
	}
}
//_________________________________________________________________________________________________

bool LwNonBlockingAlgo::isLwNonBlocking() const
{
	return m_isLwNonBlock;
}

//_________________________________________________________________________________________________

void LwNonBlockingAlgo::overrideLwNonBlocking(bool lwNonBlock)
{
	m_isLwNonBlock = lwNonBlock;
}

//_________________________________________________________________________________________________

void LwNonBlockingAlgo::prepareRun()
{
	m_isLwNonBlock = false;
	clearAlgo();
}



bool LwNonBlockingAlgo::verifyProjectIntegrity()
{

		if (m_project.getIntegrity() == eIntegNotVerified)
	{
		addError(cIntegrityNotConfirmed);
		return false;
	}
	else if (m_project.getIntegrity() == eIntegNo)
	{
		addError(cProjectInvalid);
		return false;
	}

	return true;
}
//_________________________________________________________________________________________________

bool LwNonBlockingAlgo::runAlgo()
{

try
  {
	ProgressUpdater updater(m_progress);

	if (verifyProjectIntegrity() == false)
	{
	  //		throw EX("Either project is not valid, or integrity \
	  // has not yet been checked.");
		return m_isLwNonBlock = false; // = is intentional
	}
	
	prepareRun();



	bool allSubsysNonBlock = true;

	//create and run the algo for the high level if the high level subsystem is not
	//already shown to be non-blocking OR if we don't care about previous calculations (we're not running incremental)
	if (m_project.getRootSubsys().isNonBlocking() == false || !m_runIncremental)
	{
		m_hNonBlockAlgo = new HighSubsysNonBlockAlgo(m_project);
		if (m_hNonBlockAlgo->runEx(m_progress) == false  )
		{
			
			addError(m_hNonBlockAlgo->getErrorList());
			allSubsysNonBlock = false;
			//Added by Zain for Counter Example generation
			if( LwNonBlockingAlgo::IsFirstErrorSubSystem)
			{
				 LwNonBlockingAlgo::ErrorSubSystem=&m_project.getRootSubsys();
				 LwNonBlockingAlgo::IsFirstErrorSubSystem=false;
				 this->CE_tuplemap=m_hNonBlockAlgo->CE_tuplemap;
				 LwNonBlockingAlgo::isfirsterror=false;
			}
		}
	}
   delete  m_hNonBlockAlgo;
   m_hNonBlockAlgo=NULL;
	//updateProgress();

	//create and run the algo for the low level
	DesHierProject::SubsysIteratorPtr subsysIt = m_project.createSubsysIterator();
	for(subsysIt->first(); subsysIt->notDone(); subsysIt->next())
	{
		const DesSubsystem& subsys = subsysIt->currentItem();
	//Check if this subsys is treated as highlevel(i.e it is non-leaf in main project) in an extraction system then no need to
	//perform the low level non-blocking test as high level non-blocking test will be performed on it. If so skip test for this subsys.
		if(subsys.extractionSystemSkipTest()==true)
		{
			continue;
		}
		if (subsys.getLevel() > 0)
		{
			if (subsys.isNonBlocking() == false || !m_runIncremental)
			{
				LowSubsysNonBlockAlgo* lNonBlockAlgo = new LowSubsysNonBlockAlgo(subsys);
				m_lNonBLockAlgoList.push_back(lNonBlockAlgo);

				if (lNonBlockAlgo->runEx(m_progress) == false)
				{
					addError(lNonBlockAlgo->getErrorList());
					allSubsysNonBlock = false;

					//Added by Zain for Counter Example generation
					if( LwNonBlockingAlgo::IsFirstErrorSubSystem)
					{	
						LwNonBlockingAlgo::ErrorSubSystem=&subsys;
						LwNonBlockingAlgo::IsFirstErrorSubSystem=false;
						this->CE_tuplemap=lNonBlockAlgo->CE_tuplemap;				   	    
				        LwNonBlockingAlgo::isfirsterror=false;
					}
				}
			}
		}

		//updateProgress();
	}

	return m_isLwNonBlock = allSubsysNonBlock;	  // = is intentional

}
	catch(const std::wstring& e)
	{
	 addError(e);
	 return m_isLwNonBlock = false; // = is intentional
	}
catch(...){
  //unkown exception occurred
  addError(L"Unknown exception occurred.");
  return m_isLwNonBlock = false;  // '=' intended
}
// Should never reach here.

return  false;

}
//------------------overloading method for extraction system --------------
//_________________________________________________________________________________________________

bool LwNonBlockingAlgo::runAlgo(std::map<std::wstring, bool> &subsysNonBlockMap)
{

try
  {
	ProgressUpdater updater(m_progress);

	if (verifyProjectIntegrity() == false)
	{
	  //		throw EX("Either project is not valid, or integrity \
	  // has not yet been checked.");
		return m_isLwNonBlock = false; // = is intentional
	}
	
	prepareRun();



	bool allSubsysNonBlock = true;

	//create and run the algo for the high level if the high level subsystem is not
	//already shown to be non-blocking OR if we don't care about previous calculations (we're not running incremental)
	if (m_project.getRootSubsys().isNonBlocking() == false || !m_runIncremental)
	{
		m_hNonBlockAlgo = new HighSubsysNonBlockAlgo(m_project);
		if (m_hNonBlockAlgo->runEx(m_progress) == false  )
		{
			addError(m_hNonBlockAlgo->getErrorList());
			allSubsysNonBlock = false;
		}
		subsysNonBlockMap[m_project.getRootSubsys().getName()]= allSubsysNonBlock;
	}
   delete  m_hNonBlockAlgo;
   m_hNonBlockAlgo=NULL;
	//updateProgress();

	//create and run the algo for the low level
	DesHierProject::SubsysIteratorPtr subsysIt = m_project.createSubsysIterator();
	for(subsysIt->first(); subsysIt->notDone(); subsysIt->next())
	{
		const DesSubsystem& subsys = subsysIt->currentItem();
	//Check if this subsys is treated as highlevel(i.e it is non-leaf in main project) in an extraction system then no need to
	//perform the low level non-blocking test as high level non-blocking test will be performed on it. If so skip test for this subsys.
		if(subsys.extractionSystemSkipTest()==true)
		{
			continue;
		}
		if (subsys.getLevel() > 0)
		{
			if (subsys.isNonBlocking() == false || !m_runIncremental)
			{
				LowSubsysNonBlockAlgo* lNonBlockAlgo = new LowSubsysNonBlockAlgo(subsys);
				m_lNonBLockAlgoList.push_back(lNonBlockAlgo);

				if (lNonBlockAlgo->runEx(m_progress) == false)
				{
					addError(lNonBlockAlgo->getErrorList());
					allSubsysNonBlock = false;
					subsysNonBlockMap[subsys.getName()]= false;
				}
				else
					subsysNonBlockMap[subsys.getName()]= true;
			}
		}

		//updateProgress();
	}

	return m_isLwNonBlock = allSubsysNonBlock;	  // = is intentional

}
	catch(const std::wstring& e)
	{
	 addError(e);
	 return m_isLwNonBlock = false; // = is intentional
	}
catch(...){
  //unkown exception occurred
  addError(L"Unknown exception occurred.");
  return m_isLwNonBlock = false;  // '=' intended
}

// Should never reach here.

return  false;

}
//-----This method is only called when runnig checkProect( on Multi level project) to run non-Blocking algo on each extraction system--------
bool LwNonBlockingAlgo::runAlgoOnExtractionSystem()
	{
		if (verifyProjectIntegrity() == false)
		{
			return m_isLwNonBlock = false; // = is intentional
		}
		std::map<std::wstring, bool> subsysNonBlockMap;
		std::map<std::wstring, bool>::iterator subsysNonBlockMapIt;
		DesHierProject::SubsysIteratorPtr subsysIt = m_project.createSubsysIterator();
		for(subsysIt->first(); subsysIt->notDone(); subsysIt->next())
		{
			bool nonLeaf = false;
			DesSubsystem::DependIteratorPtr depIt = subsysIt->currentItem().createDependsIterator();
					for(depIt->first(); depIt->notDone(); depIt->next())
					{
						nonLeaf = true;
					}
				//call extraction against non-leaf node only
					if(nonLeaf)
					{
						const DesSubsystem& subsysExtraction = subsysIt->currentItem();
						DesProject* desProj = m_project.extractionSystem(&subsysExtraction);
						DesHierProject* extractedProj = dynamic_cast<DesHierProject*>(desProj);
						LwNonBlockingAlgo lwNonBlockAlgo(*extractedProj);	

						lwNonBlockAlgo.provideProgress(NULL);

						bool isNonBloc = extractedProj->checkLwNonBlockProp(lwNonBlockAlgo,subsysNonBlockMap);

						if(isNonBloc==false)
						{
							DesHierProject::SubsysIteratorPtr subsysNonBlockIt = m_project.createSubsysIterator();
							for(subsysNonBlockIt->first(); subsysNonBlockIt->notDone(); subsysNonBlockIt->next())
							{
								subsysNonBlockMapIt = subsysNonBlockMap.find(subsysNonBlockIt->currentItem().getName());
								if (subsysNonBlockMapIt != subsysNonBlockMap.end())
								{
									subsysNonBlockIt->currentItem().setNonBlocking(subsysNonBlockMapIt->second);
								}
								
							}
							addError(lwNonBlockAlgo.getErrorList());
							//m_project.setLwNonBlockPropMainProj(false);
							//output().show(lwNonBlockAlgo);
							return m_isLwNonBlock = false;;
						}
					}
			}
			DesHierProject::SubsysIteratorPtr subsysIt1 = m_project.createSubsysIterator();
			for(subsysIt1->first(); subsysIt1->notDone(); subsysIt1->next())
			{
				const DesSubsystem& subsysMainProj = subsysIt1->currentItem();
				DesSubsystem& subsysNc= const_cast<DesSubsystem &>(subsysMainProj);
				subsysNc.setNonBlocking(true);
			}
			//m_project.setLwNonBlockPropMainProj(true);
			//output().show(eLwNonBlock);
			return m_isLwNonBlock = true;		
		}

void LwNonBlockingAlgo::run()
{
	if(m_project.isBilevel())
	runAlgo();
	else
		runAlgoOnExtractionSystem();
}


} //end of namespace DESpot
