/*************************************************************************
 * 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
 *
 * Description: This is the main control module for the slave processes. It
 * loops through retrieving commands, performing calculations, and returning
 * the results until it is told to stop
 ************************************************************************/

#include "ProjectSerializer.h"

#include <QApplication>
#include <QString>

#include <iostream>
#include <sstream>
#include <iterator>
#include <algorithm>
#include <string>

#include "DistCommon.h"

#include "CommHandler.h"
#include "FlatHandler.h"
#include "HISCHandler.h"
#include "BDDHandler.h"

#ifndef _WIN32

using namespace DESpot;

int main(int argc, char* argv[])
{
	try
	{
		Q_INIT_RESOURCE(DESpot);		
		QApplication app(argc, argv, false);

	// Set up communication protocol
		CommHandler com = new CommHandler(true, 0);		

	// Get Command
	//   Command Structure
	//   Project_Path Command_Number Subsystem_Index	
		string cmd = com.recvString();

		istringstream iss(cmd);
		vector<string> tokens;
		copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter<vector<string> >(tokens));

	// Get Project_Path
		wstring proj(tokens[0].length(), L' ');
		copy(tokens[0].begin(), tokens[0].end(), proj.begin());
		
	// Decode any spaces in file path
		int loc = proj.find(L"///");
		while (loc != wstring::npos)
		{			
			proj.replace(loc, 3, L" ");
			loc = proj.find(L"///");
		} 

	// Immediately bail out if the KillProcess command is recieved.
	// This will occur if a node is not used for a computation
		if (atoi(tokens[1].c_str()) == KillProcess)
		{
			CommHandler::closeConnection();	
			return 0;
		}

	// Determine Project Type
		DistCommon::GUI_Enabled = false;
		DesProject* project = DesProject::load(proj);

		bool isFlat;
		if (project->getType() == eFlatProject)
		{
			isFlat = true;
		}
		else if (project->getType() == eHierProject)
		{
			isFlat = false;
		}
		else
		{
			return -1;
		}

	// Begin command loop
		while (atoi(tokens[1].c_str()) != KillProcess)
		{

		// Get Algorithm		
			AlgEnum algCmd = (AlgEnum)(atoi(tokens[1].c_str()));
			bool isBDD = (algCmd >= BDDHighAll && algCmd <= BDDLowSyntho) ? true : false;

		// Perform check		
			if (isFlat)
			{
				// Pass control to Flat Algorithm handler			
				FlatHandler FlatCheck(project, algCmd);
				bool result = FlatCheck.runAlg();

				// pass back result
				com.send(result ? 0 : -1);

				DesAlgo::ErrorList errList = FlatCheck.getErrorList();

				// pass back the number of errors
				com.send(errList.size());

				// pass back error list
				while (!errList.empty())
				{		
					string errStr;
					wstring errWStr = errList.front();

					errStr.assign(errWStr.begin(), errWStr.end());

					com.send(errStr);

					errList.pop_front();
				}			
			}
			else
			{
				// Determine subsystem
				int subSystem = atoi(tokens[2].c_str());

				if (isBDD)
				{

					// Pass control to the BDD Algorithm handler
					BDDHandler BDDCheck(project, algCmd, subSystem);
					int result = BDDCheck.runAlg();

					// pass back result
					com.send(result);

					// send back that a single string will follow
					com.send(1);

					// pass back msg string
					string msgStr;
					wstring msgWStr = BDDCheck.getMsg();
					msgStr.assign(msgWStr.begin(), msgWStr.end());

					// filter out the "total computation time" line, if it exists
					int index = msgStr.find("Total");
					if (index != string::npos)
					{
						msgStr = msgStr.substr(0, index);
					}

					// filter out the "Level-wise synthesis complete" line, if it exists
					index = msgStr.find("Level-wise synthesis complete.");
					if (index != string::npos)
					{
						// remove the first 30 characters, ie. the above string
						// as well as the last 2 \n (too much space)
						msgStr = msgStr.substr(30, msgStr.length() - 32);
					}

					com.send(msgStr);
				}
				else
				{
					// Pass control to HISC Algorithm handler
					HISCHandler HISCCheck(project, algCmd, subSystem);
					bool result = HISCCheck.runAlg();

					// pass back result
					com.send(result ? 0 : -1);

					DesAlgo::ErrorList errList = HISCCheck.getErrorList();

					// pass back the number of errors
					com.send(errList.size());

					// pass back error list
					while (!errList.empty())
					{		
						string errStr;
						wstring errWStr = errList.front();

						errStr.assign(errWStr.begin(), errWStr.end());

						com.send(errStr);

						errList.pop_front();
					}
				}
			}

		// Get next command
		//   Command Structure
		//   Project_Path Command_Number Subsystem_Index
		//		Project Path ignored. Only included for consistancy of token numbering
			cmd = com.recvString();			
			delete(iss);
			istringstream iss(cmd);
			tokens.clear();
			copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter<vector<string> >(tokens));
		}

		CommHandler::closeConnection();

		return 0;
	}
	catch(...)
	{
		return -1;
	}
}

#endif
