/*************************************************************************
 * 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 "BDDHandler.h"

#include <iostream>
using namespace std;

#ifdef __ENABLE_DIST__

namespace DESpot 
{
	BDDHandler::BDDHandler(DesProject* project, AlgEnum alg, int subSystem):
		m_project(*project),
		m_hierProject(*(dynamic_cast<DesHierProject*>(project))),
		m_alg(alg),
		m_subSystem(subSystem)
	{
		m_msg = L"";
	}

//______________________________________________________________________________________________

	std::wstring BDDHandler::getMsg()
	{
		return m_msg;
	}

//______________________________________________________________________________________________

	QString BDDHandler::getSubSysName()
	{
		DesHierProject::SubsysIteratorPtr subsysIt = m_hierProject.createSubsysIterator();

		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();

		return QString::fromStdWString(subsys.getName());
	}

//______________________________________________________________________________________________

	std::wstring BDDHandler::getInterf()
	{
		DesHierProject::InterfIteratorPtr interfIt = m_hierProject.createInterfIterator();

		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();
		
		return interf.getName();
	}

//______________________________________________________________________________________________

	int BDDHandler::CheckAll(bool isHigh)
	{
		QString subSysName = this->getSubSysName();
		BDDHISC::Hisc_ChkInfo checkInfo("Project is LD interface consistent, LD level-wise nonblocking and LD level-wise controllable.");

		/*while (subSysName.contains(" "))
		{
			subSysName.remove(' ');
		}
		*/
		BDDHISC::HISC_TESTTYPE testType = BDDHISC::HISC_DOALL;

		int iret = BDDHISC::BddHiscCheckProp(m_project, subSysName, isHigh, testType, checkInfo);

		if (checkInfo.m_chkPassed)
		{
			m_msg = checkInfo.m_succStr.toStdWString();
			iret = 0;	// all pass, as in the table blow
		}
		else
		{
			m_msg = checkInfo.m_errStr.toStdWString();
			
			// determine what the outcome was specifically by performing additions.
			// 
			// Result | Meaning
			//    0   | All Pass
			//    1   | NB Failed
			//    2   | Controllable Failed
			//    3   | Interface Consistent Failed
			iret = 0;

			if (checkInfo.m_LDNBChkFail)
			{
				iret += 1;
			}
			else if (checkInfo.m_LDCtrlChkFail)
			{
				iret += 2;
			}
			else if (checkInfo.m_iConsisChkFail)
			{
				iret += 3;
			}
		}

		return iret;
	}

//______________________________________________________________________________________________

	int BDDHandler::Ctrl(bool isHigh) 
	{
		QString subSysName = this->getSubSysName();	
		BDDHISC::Hisc_SubLWContChkInfo checkInfo("Subsystem '" + subSysName + "' is LD controllable.", subSysName); 

		/*while (subSysName.contains(" "))
		{
		  subSysName.remove(' ');
		}*/
		
		int iret =  BDDHISC::BddHiscCheckLWCtrl(m_project, subSysName, isHigh, checkInfo);

		if (iret == 0)
			m_msg = checkInfo.m_succStr.toStdWString();
		else
			m_msg = checkInfo.m_errStr.toStdWString();

		return iret;
	}

//______________________________________________________________________________________________
	
	int BDDHandler::NonBlock(bool isHigh) 
	{
		QString subSysName = this->getSubSysName();	
		BDDHISC::Hisc_SubLWNBChkInfo checkInfo("Subsystem '" + subSysName + "' is LD nonblocking.", subSysName);

		/*while (subSysName.contains(" "))
		{
		  subSysName.remove(' ');
		}*/

		int iret =  BDDHISC::BddHiscCheckLWNonBlk(m_project, subSysName, isHigh, checkInfo);

		if (iret == 0)
			m_msg = checkInfo.m_succStr.toStdWString();
		else
			m_msg = checkInfo.m_errStr.toStdWString();

		return iret;
	}

//______________________________________________________________________________________________
	
	int BDDHandler::ICheck() 
	{
		std::wstring intfName = this->getInterf();
		QString interfName = QString::fromStdWString(intfName);
		QString subSysName;

		BDDHISC::Hisc_LDIntfChkInfo checkInfo("Interface '" + interfName +  "' is an LD interface.", interfName); 

		DesHierProject::SubsysIteratorPtr subsysIt = m_hierProject.createSubsysIterator();
		for(subsysIt->first(); subsysIt->notDone(); subsysIt->next())
		{
			const DesSubsystem& lowSubsys = subsysIt->currentItem();
	  
			if ((lowSubsys.isRoot() == false) && (lowSubsys.implementsInterface()))
			{
				const DESpot::DesInterface& interf =  lowSubsys.getInterface();

				if (interf.getName() == intfName)
				{
					subSysName = QString::fromStdWString(lowSubsys.getName());
					// remove spaces from name
					/*while (subSysName.contains(" "))
					{
						subSysName.remove(' ');
					}*/
					break;
				}
			}
		}

		if (subSysName == "")
		{
			throw EX("Bddhisc requires interfaces to be associated with a subsystem");
		}

		int iret =  BDDHISC::BddHiscCheckIntf(m_project, subSysName, false, checkInfo);

		if (iret == 0)
			m_msg = checkInfo.m_succStr.toStdWString();
		else
			m_msg = checkInfo.m_errStr.toStdWString();

		return iret;
	}

//______________________________________________________________________________________________

	int BDDHandler::IConsis(bool isHigh) 
	{
		QString subSysName = this->getSubSysName();
		BDDHISC::Hisc_SubIConsisChkInfo checkInfo("Subsystem '" + subSysName + "' is LD interface consistent.", subSysName);

		/*while (subSysName.contains(" "))
		{
		  subSysName.remove(' ');
		}*/

		int iret =  BDDHISC::BddHiscCheckIConsist(m_project, subSysName, isHigh, checkInfo); 

		if (iret == 0)
			m_msg = checkInfo.m_succStr.toStdWString();
		else
			m_msg = checkInfo.m_errStr.toStdWString();

		return iret;
	}

//______________________________________________________________________________________________

	int BDDHandler::Synth(char choice, bool isHigh)
	{
		QString absoluteFileName = m_project.isNew() ? QString::fromStdWString(m_project.getName()) : QString::fromStdWString(m_project.getFileName());
		QString absPath = QFileInfo(absoluteFileName).absolutePath();
	  
		QString succMessage = "";

		// need to determine which subsystem to synthesize
		QString DESpotSubName = this->getSubSysName();
		QString subSysName = this->getSubSysName();

		// remove spaces from name
		/*while (subSysName.contains(" "))
	    {
	      subSysName.remove(' ');
	    }*/

	  BDDHISC::Hisc_SubLWSynthChkInfo checkInfo("Synthesis successful for subsystem '" + DESpotSubName + ".'", DESpotSubName); 

        int iret = BDDHISC::BddHiscLWSynth(m_project, choice, absPath, subSysName, isHigh, checkInfo);

		m_msg = checkInfo.m_succStr.toStdWString();
		//		m_msg = succMessage.toStdWString();

		return iret;
	}

//______________________________________________________________________________________________

	int BDDHandler::runAlg() 
	{
		try {
			int iret = -99;

			switch (m_alg)
			{
				case BDDHighAll:
					iret = this->CheckAll(true);
					break;

				case BDDLowAll:
					iret = this->CheckAll(false);
					break;

				case BDDHighCtrl:				
					iret = this->Ctrl(true);
					break;

				case BDDLowCtrl:
					iret = this->Ctrl(false);
					break;

				case BDDHighNB:
					iret = this->NonBlock(true);
					break;

				case BDDLowNB:
					iret = this->NonBlock(false);
					break;

				case BDDICheck:
					iret = this->ICheck();
					break;

				case BDDHighIConsis:
					iret = this->IConsis(true);
					break;

				case BDDLowIConsis:
					iret = this->IConsis(false);
					break;

				case BDDHighSynthb:
					iret = this->Synth('b', true);
					break;

				case BDDLowSynthb:
					iret = this->Synth('b', false);
					break;

				case BDDHighSynthd:
					iret = this->Synth('d', true);
					break;

				case BDDLowSynthd:
					iret = this->Synth('d', false);
					break;

				case BDDHighSyntho:
					iret = this->Synth('o', true);
					break;

				case BDDLowSyntho:
					iret = this->Synth('o', false);
					break;
			}

			return iret;
		}
		catch(const std::wstring& e)
		{
			m_msg = e;
			return -99;
		}
		catch(...)
		{
			//unkown exception occurred
			m_msg = L"Unknown exception";
			return -99;
		}

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

#endif
