/*	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 "DesFlatProject.h"
#include "Des.h"
#include "DesSubsystem.h"
#include "ProjectEventPool.h"
#include "ProjectIndex.h"
#include <iostream>
#include <QString>
//add by bini
#include "TemplateNameParser.h"
//#include "TemplateValidation.h"
#include "NameValidator.h"
#include "InstantiateTemplate.h"
#include "Instantiation.h"
#include <QMessageBox>


namespace DESpot
{

DesFlatProject::DesFlatProject(const std::wstring& name): DesProject(name, eFlatProject) 
{
	//automatically create the root subsystem with the same name as the project. Flat
	//projects have only one subsystem which is the system itself
	m_pRootSubsys = new DesSubsystem(name, *m_eventPool);
}

//_________________________________________________________________________________________________


DesFlatProject::~DesFlatProject(void)
{
}

//_________________________________________________________________________________________________

//returns the number of supervisor DES in the project
int DesFlatProject::getSupDesCount()
{
	return m_pRootSubsys->getSupDesCount();
}

//_________________________________________________________________________________________________

//returns the number of template DES in the project
int DesFlatProject::getTemplateDesCount()
{
	return m_pRootSubsys->getTemplateDesCount();
}

//_________________________________________________________________________________________________
//returns the number of plant DES in the project
int DesFlatProject::getPlantDesCount()
{
	return m_pRootSubsys->getPlantDesCount();
}


//_________________________________________________________________________________________________

//adds a DES with the given type
void DesFlatProject::addDes(Des* pDes, DesLevel desLevel)
{
	switch(desLevel)
	{
		case eSupervisorDes:
			addSupDes(pDes);
			break;

		case ePlantDes:
			addPlantDes(pDes);
			break;

		case eTemplateDes:
			addTemplateDes(pDes);
			break;

		default:
			assert(false);
			throw EX("Unknown DES type")
	}
}

//_________________________________________________________________________________________________

//adds a supervisor DES
void DesFlatProject::addSupDes(Des* pSupDes)
{
	//make sure the name is not already used
	
//add by bini
	std::wstring name = pSupDes->getName();
	if(m_nameValidator.validate(name)==false)
	{
		throw EX("This supervisor name contains '%'.  Only template names can contain '%'.  Cannot add DES.")
	}

	if (m_nameIndex.findName(pSupDes->getName()))
	{
		throw EX("This name is already in use. Cannot add DES.")
	}

	m_pRootSubsys->addDes(pSupDes, eSupervisorDes);
	
	//index the name
	m_nameIndex.addComp(*pSupDes);

	onDesAdded(*pSupDes, eSupervisorDes);
}

//_________________________________________________________________________________________________

//adds a plant DES
void DesFlatProject::addPlantDes(Des* pPlantDes)
{
	//make sure the name is not already used
//add by bini
	std::wstring name = pPlantDes->getName();
	if(m_nameValidator.validate(name)==false)
	{
		throw EX("This plant name contains '%'.  Only template names can contain '%'. Cannot add DES.")
	}	
	if (m_nameIndex.findName(pPlantDes->getName()))
	{
		throw EX("This name is already in use. Cannot add DES.")
	}

	m_pRootSubsys->addDes(pPlantDes, ePlantDes);

	//index the na
	m_nameIndex.addComp(*pPlantDes);
	
	onDesAdded(*pPlantDes, ePlantDes);
}

//_________________________________________________________________________________________________
//add by bini
//add a template DES
void DesFlatProject::addTemplateDes(Des* pTemplateDes)
{
	
        std::wstring name = pTemplateDes->getName();
	TemplateNameParser* NameParser=new TemplateNameParser(name);
	if (NameParser->isTemplateName()==false)
	{
		throw EX("This is not a valid template name. Cannot add DES.")
	}
	
	if (m_nameIndex.findName(pTemplateDes->getName()))
	{
		throw EX("This name is already in use. Cannot add DES.")
	}

	

	m_pRootSubsys->addDes(pTemplateDes, eTemplateDes);

	//index the name
	m_nameIndex.addComp(*pTemplateDes);
	
	onDesAdded(*pTemplateDes, eTemplateDes);
	delete NameParser;
}
//_________________________________________________________________________________________________
//add by bini
//add an instantiation
bool DesFlatProject::addInstantiation(Instantiation* pInstantiation,Des* template_crt)
{


      // 	const std::wstring name = pInstantiation->getName()
	
	if (m_nameIndex.findName(pInstantiation->getName()))
	{

		InstantiateTemplate::InstantiatedTemplate* result=new InstantiateTemplate::InstantiatedTemplate;
		if(pInstantiation->getInputType()==eRange)
		{
			result=InstantiateTemplate::generateTemplateParameterFromTemplate(*(pInstantiation->getParameter()));
			InstantiateTemplate::InstantiatedTemplateItr it=result->begin();
			while(result->end()!=it)
		{	
			TemplateNameParser::TemplateParameter* parameter=new TemplateNameParser::TemplateParameter;					
			TemplateNameParser::TemplateParameterItr iterator=(&*it)->begin();
			while (iterator != (&*it)->end())
         		{
      				parameter->insert(TemplateNameParser::TemplateParameter::value_type(iterator->first,iterator->second));
        			iterator++;
      			 }
			 std::wstring name =*(InstantiateTemplate::getNameFromInstantiatedTemplate(template_crt->getName(),*(parameter)));
			 deleteDes(name);
		         it++;
		}
		}
		else
		{
			result=InstantiateTemplate::generateTemplateParameterFromTuple(*(pInstantiation->getParameter()));
InstantiateTemplate::InstantiatedTemplateItr it=result->begin();
		
		while(result->end()!=it)
		{	
			TemplateNameParser::TemplateParameter* parameter=new TemplateNameParser::TemplateParameter;					
			TemplateNameParser::TemplateParameterItr iterator=(&*it)->begin();
			while (iterator != (&*it)->end())
         		{
      				parameter->insert(TemplateNameParser::TemplateParameter::value_type(iterator->first,iterator->second));
        			iterator++;
      			 }
			 std::wstring name =*(InstantiateTemplate::getNameFromInstantiatedTemplate(template_crt->getName(),*(parameter)));
			 deleteDes(name);
		         it++;
		}
		}
		QMessageBox::information(NULL, "DESpot","Error: This instantiation name is already in use. Cannot add Instantiation.");
		return false;
	}

	m_pRootSubsys->addInstantiation(pInstantiation);

	//index the name
	m_nameIndex.addComp(*pInstantiation);
	
	onInstAdded(*pInstantiation,*template_crt);
	
	return true;

}
//bini
//______________________________________________________________________________________________

void DesFlatProject::changeDesName(Des& des, const std::wstring& newDesName)
{
	//make sure the name is not already used
	if (m_nameIndex.findName(newDesName))
	{
		throw EX("This name is already in use. Cannot change the name of DES.")
	}
	TemplateNameParser* NameParser2=new TemplateNameParser(newDesName);
	if (des.isTemplate()&&(!NameParser2->isTemplateName()))
	{
		throw EX("This name is not a Template DES name");
	}
	//ask the subsystem that owns this DES to change the name and update itself
	std::wstring oldName = des.getName();
	m_pRootSubsys->changeDesName(des, newDesName);

	//reindex the DES
	m_nameIndex.reIndex(oldName, des);

	onDesNameChanged(des, oldName);
}

//_________________________________________________________________________________________________

//removes a DES (either plant or supervisor)
void DesFlatProject::removeDes(Des* pDes)
{	
	Des* des = null;
	DesLevel desLevel;
	if (m_pRootSubsys->findDes(pDes->getName(), &des, &desLevel))
	{
		//let everybody know the des is being removed. This will give clients opportunity to remove
		//any references to it
		onRemovingDes(*des, DesLevel(desLevel));
		
		if (pDes != des)
		{
			throw EX("Des is not part of the project. Cannot remove.")
		}

		//remove
		m_pRootSubsys->removeDes(des);
		
		//let everybody know that the des doesn't exist anymore
		onDesRemoved(pDes->getName(), DesLevel(desLevel));
	}
}   

//_________________________________________________________________________________________________
//modified by bini
//removes a DES from the project and destroys it
void DesFlatProject::deleteDes(const std::wstring& desName)
{
	Des* des = null;
	DesLevel desLevel;
	if (m_pRootSubsys->findDes(desName, &des, &desLevel))
	{
		//let everybody know the des is being removed. This will give clients opportunity to remove
		//any references to it
		if(des->isInstantiated())
		{
			DesProject::InstIteratorPtr InstIt = m_pRootSubsys->createInstIterator();
			
			for(InstIt->first();InstIt->notDone();InstIt->next())
			{
			if(InstIt->currentItem().getTemplateDes()==des)
			{
				
				InstantiateTemplate::InstantiatedTemplate* result=new InstantiateTemplate::InstantiatedTemplate;
				if(InstIt->currentItem().getInputType()==eRange)
				{
					result=InstantiateTemplate::generateTemplateParameterFromTemplate(*(InstIt->currentItem().getParameter()));
				}
				else
				{
					result=InstantiateTemplate::generateTemplateParameterFromTuple(*(InstIt->currentItem().getParameter()));
				}
				InstantiateTemplate::InstantiatedTemplateItr it=result->begin();
				
				while(result->end()!=it)
				{	
					TemplateNameParser::TemplateParameter* parameter=new TemplateNameParser::TemplateParameter;
					
					TemplateNameParser::TemplateParameterItr iterator=(&*it)->begin();
					while (iterator != (&*it)->end())
            			 	{
               	 				parameter->insert(TemplateNameParser::TemplateParameter::value_type(iterator->first,iterator->second));
                				iterator++;
            				 }
					
					const Instantiation& inst=InstIt->currentItem();
					
					std::wstring name =*(InstantiateTemplate::getNameFromInstantiatedTemplate(desName,*(parameter)));
					
					deleteInstantiation(inst.getName());
					
					deleteDes(name);
					
					it++;
				}
			}
			}
		}
					
			onRemovingDes(*des, DesLevel(desLevel));
		
			//remove and destroy the DES
			m_pRootSubsys->deleteDes(desName);
		
			//remove DES name from index
			m_nameIndex.erase(desName);
	
			//let everybody know that the des doesn't exist anymore
			onDesRemoved(desName, DesLevel(desLevel));
		
	}
}
//_________________________________________________________________________________________________
//add by bini
//removes a DES from the project and destroys it
void DesFlatProject::deleteInstantiation(const std::wstring& instName)
{
	
	Instantiation* inst = null;
	DesLevel desLevel;
	if (m_pRootSubsys->findInst(instName, &inst))
	{
		//let everybody know the des is being removed. This will give clients opportunity to remove
		//any references to it
		onRemovingInstantiation(*inst);
		
		
		//remove and destroy the DES
		m_pRootSubsys->deleteInstantiation(instName);
		
		//remove DES name from index
		m_nameIndex.erase(instName);

		//let everybody know that the des doesn't exist anymore
		onInstantiationRemoved(instName);
		
	}
}

//_________________________________________________________________________________________________

void DesFlatProject::setFlags(resultStruct rs) 
{
	if (rs.NBChecked)
	{
		m_nonBlockProp = (NonBlockingProp)rs.nonBlocking;
		m_nonBlockStamp = now();
	}

	if (rs.ctrlChecked)
	{
		m_ctrlProp = (ControllableProp)rs.controllable;
		m_ctrlStamp = now();
	}
}

//_________________________________________________________________________________________________
//add by bini
void DesFlatProject::instantiatetemplate( const std::wstring& templateName,Instantiation* inst)
{
	
	DesLevel level=inst->getLevel();	
	Des* templatedes=null;
	DesLevel desLevel;
	if (m_pRootSubsys->findDes(templateName,&templatedes,&desLevel))
	{
		if (templatedes->isTemplate()==false)
		{
			QMessageBox::information(NULL, "DESpot","Error: This cannot be instantiated");
                        setSuccessful(false);
			return;
		}
		TemplateNameParser::TemplateParameter* parameter=inst->getParameter();
		if(inst->getInputType()==eRange)
		{
			InstantiateTemplate::DesArray* manydes=InstantiateTemplate::generateDesFromTemplate(templatedes,InstantiateTemplate::generateTemplateParameterFromTemplate(*parameter));
			InstantiateTemplate::DesArrayItr itr=manydes->begin();
			std::vector<std::wstring> name_vec;
		while (itr != manydes->end())
       		{
			name_vec.push_back((*itr)->getName());
           		if (m_nameIndex.findName((*itr)->getName()))
            		{
              		 	QMessageBox::information(NULL, "DESpot","Error: One of the DES names is already in use.  Can not add instantiation.");
                                setSuccessful(false);
				return;
           		}
           		 itr++;
        	}
		if(name_vec.size()>1)
		{
		for(int i =0;i<name_vec.size();i++)
		{
			for(int j=1;j<name_vec.size();j++)
			{
                if(i==j)
				{
					continue;
				}
				if(name_vec[i]==name_vec[j])
				{
					QMessageBox::information(NULL, "DESpot","Error: One of the DES names is already in use.  Can not add instantiation.");
					setSuccessful(false);
					return;
				}
				
			}
		}
		}
		itr=manydes->begin();
		while(itr!=manydes->end())
		{
			addDes(*itr,level);
			itr++;
		}
		
		templatedes->setInstantiated(true);
		setSuccessful(true);
		}
		else
		{
			InstantiateTemplate::DesArray* manydes=InstantiateTemplate::generateDesFromTemplate(templatedes,InstantiateTemplate::generateTemplateParameterFromTuple(*parameter));
		
		
		InstantiateTemplate::DesArrayItr itr=manydes->begin();
		std::vector<std::wstring> name_vec;
		while (itr != manydes->end())
       		{
			name_vec.push_back((*itr)->getName());
           		if (m_nameIndex.findName((*itr)->getName()))
            		{
              			QMessageBox::information(NULL, "DESpot","Error: One of the DES names is already in use.  Can not add instantiation.");
                                setSuccessful(false);
				return;
           		}
           		 itr++;
        	}
		if(name_vec.size()>1)
		{
		for(int i =0;i<name_vec.size();i++)
		{
			for(int j=1;j<name_vec.size();j++)
			{
                if(name_vec[i]==name_vec[j]&&(i!=j))
				{
					QMessageBox::information(NULL, "DESpot","Error:One of the DES names is already in use.  Can not add instantiation.");
                                        setSuccessful(false);
					return;
				}
				
			}
		}
		}
		itr=manydes->begin();
		while(itr!=manydes->end())
		{
			addDes(*itr,level);
			itr++;
		}
		
		templatedes->setInstantiated(true);
		setSuccessful(true);
		}
		/*std::pair<Des*,InstantiateTemplate> instinfomap;
		instinfomap=make_pair(templatedes,instantiatedlg);
		InstantiateTemplate::InstInfoMap_Vector* instinfomap_vector;
		instinfomap_vector->push_back(instinfomap);
		instantiatedlg->setInstInfoMap(instinfomap_vector);
		onInstInfoDlgAdded(instantiatedlg,instinfomap);*/
		
	}
}

}; //end of namespace DESpot
