/*	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 "LwCtrlAlgo.h"
#include "HighSubsysCtrlAlgo.h"
#include "LowSubsysCtrlAlgo.h"

// for debug
 #include <iostream>



namespace DESpot
{

const std::wstring LwCtrlAlgo::cLwCtrlAlgoDesc = L"Level Wise Controllability Algo";
const std::wstring LwCtrlAlgo::cIntegrityNotConfirmed = L"Project integrity has not been checked";
const std::wstring LwCtrlAlgo::cProjectInvalid = L"Project is not valid";

//_________________________________________________________________________________________________

LwCtrlAlgo::LwCtrlAlgo(DesHierProject& project, bool runIncremental /*= true*/) : 
		m_project(project), 
		m_hCtrlAlgo(null), 
		m_isLwCtrl(false)		
{ 
	m_runIncremental = runIncremental;
	m_description = cLwCtrlAlgoDesc;
}

//_________________________________________________________________________________________________

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


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

	return true;
}



//_________________________________________________________________________________________________

void LwCtrlAlgo::clearAlgo()
{
	if (m_hCtrlAlgo)
	{
		delete m_hCtrlAlgo;
		m_hCtrlAlgo = null;
	}

	for(unsigned int i = 0; i < m_lCtrlAlgoList.size(); i++)
	{
		LowSubsysCtrlAlgo* algo = m_lCtrlAlgoList[i];
		if (algo)
		{
			delete algo;
			algo = null;
		}
	}
}

//_________________________________________________________________________________________________

bool LwCtrlAlgo::isLwControllable() const
{
	return m_isLwCtrl;
}

//_________________________________________________________________________________________________

void LwCtrlAlgo::overrideLwControllable(bool lwCtrl)
{
	m_isLwCtrl = lwCtrl;
}

//_________________________________________________________________________________________________

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

//_________________________________________________________________________________________________

void LwCtrlAlgo::prepareRun()
{
	m_isLwCtrl = false;
	clearAlgo();
}

//_________________________________________________________________________________________________

bool LwCtrlAlgo::runAlgo(std::map<std::wstring, bool> &subsysCtrlMap)
{
	
try
  {

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


     prepareRun();


	bool allSubsysCtrl = 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)
	//Also check if this subsys is being used as low level in another extraction system. 
	//If not(i.e if it is root in main multi-level project) then only perform the test.
	//if (m_project.getRootSubsys().isControllable() == false || !m_runIncremental)
	if ((m_project.getRootSubsys().isControllable() == false || !m_runIncremental) && m_project.getRootSubsys().extractionSystemSkipTest() == false)
	{
		m_hCtrlAlgo = new HighSubsysCtrlAlgo(m_project);
		if (m_hCtrlAlgo->runEx(m_progress))
		{
			m_project.getRootSubsys().setControllable();
			subsysCtrlMap[m_project.getRootSubsys().getName()]=true;
		}
		else
		{
			m_project.getRootSubsys().setControllable(false);
			addError(m_hCtrlAlgo->getErrorList());
			allSubsysCtrl = false;
			subsysCtrlMap[m_project.getRootSubsys().getName()]=false;
		}
	}
	//
	
	//throw EX("ffffffffffff")
//	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();
		if (subsys.getLevel() > 0)
		{
			if (subsys.isControllable() == false || !m_runIncremental)
			{
				LowSubsysCtrlAlgo* lCtrlAlgo = new LowSubsysCtrlAlgo(subsys);
				m_lCtrlAlgoList.push_back(lCtrlAlgo);

				if (lCtrlAlgo->runEx(m_progress))
				{
					subsys.setControllable();
					subsysCtrlMap[subsys.getName()]=true;
				}
				else
				{
					subsys.setControllable(false);
					addError( lCtrlAlgo->getErrorList());
					allSubsysCtrl = false;
					subsysCtrlMap[subsys.getName()]=false;
				}
			}
		}

	//	updateProgress();
	}



	return m_isLwCtrl = allSubsysCtrl;	  // = is intentional

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

//  Should never reach here.

return  false;

}
//_________________________________________________________________________________________________

bool LwCtrlAlgo::runAlgo()
{
	
try
  {

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


     prepareRun();


	bool allSubsysCtrl = 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)
	//Also check if this subsys is being used as low level in another extraction system. 
	//If not(i.e if it is root in main multi-level project) then only perform the test.
	//if (m_project.getRootSubsys().isControllable() == false || !m_runIncremental)
	const DesSubsystem& temp= m_project.getRootSubsys();
	if ((m_project.getRootSubsys().isControllable() == false || !m_runIncremental) && m_project.getRootSubsys().extractionSystemSkipTest() == false)
	{
		m_hCtrlAlgo = new HighSubsysCtrlAlgo(m_project);
		if (m_hCtrlAlgo->runEx(m_progress))
		{
			m_project.getRootSubsys().setControllable();
		}
		else
		{
			m_project.getRootSubsys().setControllable(false);
			addError(m_hCtrlAlgo->getErrorList());
			allSubsysCtrl = false;
			//Added by Zain for Counter Example generation
			if( LwCtrlAlgo::IsFirstErrorSubSystem)
			{
				 LwCtrlAlgo::ErrorSubSystem=&m_project.getRootSubsys();
				 LwCtrlAlgo::IsFirstErrorSubSystem=false;
				 this->CE_tuplemap=m_hCtrlAlgo->CE_tuplemap;
				 LwCtrlAlgo::isfirsterror=false;
			}
			
		}
	}
	//
	
	//throw EX("ffffffffffff")
//	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();
		if (subsys.getLevel() > 0)
		{
			if (subsys.isControllable() == false || !m_runIncremental)
			{
				LowSubsysCtrlAlgo* lCtrlAlgo = new LowSubsysCtrlAlgo(subsys);
				m_lCtrlAlgoList.push_back(lCtrlAlgo);

				if (lCtrlAlgo->runEx(m_progress))
				{
					subsys.setControllable();
				}
				else
				{
					subsys.setControllable(false);
					addError( lCtrlAlgo->getErrorList());
					allSubsysCtrl = false;

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

	//	updateProgress();
	}



	return m_isLwCtrl = allSubsysCtrl;	  // = is intentional

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

//  Should never reach here.

return  false;

}
//-----This method is only called when runnig checkProect( on Multi level project) to run controllable algo on each extraction system--------
bool LwCtrlAlgo::runAlgoOnExtractionSystem()
{
	if (verifyProjectIntegrity() == false)
	{
		//	  	    throw EX("Either project is not valid, or integrity \
		//	  	  has not yet been checked.");
		return m_isLwCtrl = false; // = is intentional
	}
	std::map<std::wstring, bool> subsysCtrlMap;
	std::map<std::wstring, bool>::iterator subsysCtrlMapIt;
	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);
			LwCtrlAlgo lwCtrlAlgo(*extractedProj);	

			lwCtrlAlgo.provideProgress(NULL);

			extractedProj->checkLwCtrlProp(lwCtrlAlgo);

			if(extractedProj->isLwControllable()==false)
			{
				DesHierProject::SubsysIteratorPtr subsysNonBlockIt = m_project.createSubsysIterator();
				for(subsysNonBlockIt->first(); subsysNonBlockIt->notDone(); subsysNonBlockIt->next())
				{
					subsysCtrlMapIt = subsysCtrlMap.find(subsysNonBlockIt->currentItem().getName());
					if (subsysCtrlMapIt != subsysCtrlMap.end())
					{
						subsysNonBlockIt->currentItem().setControllable(subsysCtrlMapIt->second);
					}

				}
				addError(lwCtrlAlgo.getErrorList());
				return  m_isLwCtrl = 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.setControllable(true);
	}
	return  m_isLwCtrl = true;
}

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

} //end of namespace DESpot
