/*************************************************************************
 * This file is part of the Distributed Computation feature
 *
 * Project created in conformity with the requirements for the Degree of
 * Master of Engineering in Software Engineering, Computing and Software
 * Department, McMaster University 2012
 *
 * Author:	David Kirby
 * Supervisor: Dr. Ryan Leduc
 ************************************************************************/
#include "HISCHandler.h"

#ifdef __ENABLE_DIST__

namespace DESpot 
{

	HISCHandler::HISCHandler(DesProject* project, AlgEnum alg, int subSystem):
		m_project(*(dynamic_cast<DesHierProject*>(project))),
		m_alg(alg),
		m_subSystem(subSystem)
	{
	}

//______________________________________________________________________________________________

	DesAlgo::ErrorList HISCHandler::getErrorList()
	{
		return m_errList;
	}

//______________________________________________________________________________________________

	bool HISCHandler::HighCtrl()
	{
		HighSubsysCtrlAlgo hCtrlAlgo(m_project);

		if (hCtrlAlgo.runAlgo())
		{
			return true;
		}
		else
		{
			m_errList = hCtrlAlgo.getErrorList();
			return false;
		}
	}

//______________________________________________________________________________________________

	bool HISCHandler::LowCtrl()
	{
		DesHierProject::SubsysIteratorPtr subsysIt = m_project.createSubsysIterator();
		
		// iterate to correct subsystem
		int i = 0;
		for(subsysIt->first(); i < m_subSystem && subsysIt->notDone(); i++)
		{
			subsysIt->next();
		}

		// ensure that a valid subsystem number was passed in
		if (i < m_subSystem || subsysIt->isDone())
			throw EX("Invalid subsystem number");

		const DesSubsystem& subsys = subsysIt->currentItem();
		if (subsys.getLevel() > 0)
		{
			LowSubsysCtrlAlgo* lCtrlAlgo = new LowSubsysCtrlAlgo(subsys);
			
			if (lCtrlAlgo->runAlgo()) 
			{		
				delete(lCtrlAlgo);
				return true;
			}
			else
			{
				m_errList = lCtrlAlgo->getErrorList();
				delete(lCtrlAlgo);
				return false;
			}
		}
		else
		{
			throw EX("Subsystem is not low level");
		}
	}

//______________________________________________________________________________________________

	bool HISCHandler::HighNB()
	{
		HighSubsysNonBlockAlgo hNonBlockAlgo(m_project);
		if (hNonBlockAlgo.runAlgo())
		{
			return true;
		}
		else
		{
			m_errList = hNonBlockAlgo.getErrorList();
			return false;
		}
	}

//______________________________________________________________________________________________

	bool HISCHandler::LowNB()
	{
		DesHierProject::SubsysIteratorPtr subsysIt = m_project.createSubsysIterator();
		
		// iterate to correct subsystem
		int i = 0;
		for(subsysIt->first(); i < m_subSystem && subsysIt->notDone(); i++)
		{
			subsysIt->next();
		}

		// ensure that a valid subsystem number was passed in
		if (i < m_subSystem || subsysIt->isDone())
			throw EX("Invalid subsystem number");

		const DesSubsystem& subsys = subsysIt->currentItem();
		if (subsys.getLevel() > 0)
		{
			LowSubsysNonBlockAlgo* lNonBlockAlgo = new LowSubsysNonBlockAlgo(subsys);
			
			if (lNonBlockAlgo->runAlgo()) 
			{	
				delete(lNonBlockAlgo);
				return true;
			}
			else
			{
				m_errList = lNonBlockAlgo->getErrorList();
				delete(lNonBlockAlgo);
				return false;
			}
		}
		else
		{
			throw EX("Subsystem is not low level");
		}
	}

//______________________________________________________________________________________________

	bool HISCHandler::InterfLDConsist()
	{
		DesHierProject::InterfIteratorPtr interfIt = m_project.createInterfIterator();

		// iterate to correct subsystem
		int i = 0;
		for (interfIt->first(); i < m_subSystem && interfIt->notDone(); i++)
		{
			interfIt->next();
		}

		// ensure that a valid subsystem number was passed in
		if (i < m_subSystem  || interfIt->isDone())
			throw EX("Invalid subsystem number");

		const DesInterface& interf = interfIt->currentItem();
		
		LdInterfaceCheckAlgo interfCheckAlgo(interf);
		if (interfCheckAlgo.runAlgo())
		{
			return true;
		}
		else
		{
			m_errList = interfCheckAlgo.getErrorList();
			m_errList.push_back(L"Interface " + interf.getName() + L" is not an LD interface");
			return false;
		}
	}

//______________________________________________________________________________________________

	bool HISCHandler::HighInterf()
	{
		IConsistAnsAcceptAlgo ansAcceptAlgo(m_project);
		if (ansAcceptAlgo.runAlgo())
		{
			return true;
		}
		else
		{
			m_errList = DesAlgo::ErrorList();
			m_errList.push_back(ansAcceptAlgo.getError());
			return false;
		}
	}

//______________________________________________________________________________________________

	bool HISCHandler::LowInterf()
	{
		DesHierProject::SubsysIteratorPtr subsysIt = m_project.createSubsysIterator();
		
		// iterate to correct subsystem
		int i = 0;
		for(subsysIt->first(); i < m_subSystem && subsysIt->notDone(); i++)
		{
			subsysIt->next();
		}

		// ensure that a valid subsystem number was passed in
		if (i < m_subSystem || subsysIt->isDone())
			throw EX("Invalid subsystem number");

		const DesSubsystem& subsys = subsysIt->currentItem();
		if (subsys.getLevel() > 0)
		{
			LowSubsysIConsistAlgo* lowSubsyIConsAlgo = new LowSubsysIConsistAlgo(subsys);
			
			if (lowSubsyIConsAlgo->runAlgo()) 
			{		
				delete(lowSubsyIConsAlgo);
				return true;
			}
			else
			{
				m_errList = lowSubsyIConsAlgo->getErrorList();
				delete(lowSubsyIConsAlgo);
				return false;
			}
		}
		else
		{			
			throw EX("Subsystem is not low level");
		}
	}

//______________________________________________________________________________________________

	bool HISCHandler::runAlg()
	{
		try {

			HierProjIntegrityAlgo integAlgo(false);

			switch (m_alg)
			{
				case HISCHighCtrl:
					return this->HighCtrl();

				case HISCLowCtrl:
					return this->LowCtrl();

				case HISCHighNB:
					return this->HighNB();

				case HISCLowNB:
					return this->LowNB();

				case HISCInterfLDConsist:
					return this->InterfLDConsist();
					
				case HISCHighInterf:
					return this->HighInterf();

				case HISCLowInterf:
					return this->LowInterf();
			}

		} 
		catch(const std::wstring& e)
		{
			m_errList = DesAlgo::ErrorList();
			m_errList.push_back(e);
			return false;
		}
		catch(...)
		{
			//unkown exception occurred
			m_errList = DesAlgo::ErrorList();
			m_errList.push_back(L"Unknown exception");			
			return false;
		}

		// This should never be reached
		assert(false);
		return false;

	}

}

#endif
