
#include "MeetAlgo.h"
#include "Des.h"

// for debug
// #include <iostream>

namespace DESpot
{

const std::wstring MeetAlgo::cMeetDesc = L"the Meet Algorithm";
const std::wstring MeetAlgo::cMeetIntegError = L"Input DES '%ls' is either not valid or not verified to be valid.";

//_________________________________________________________________________________________________


MeetAlgo::MeetAlgo(bool checkIntegrity /*= true*/) : DesAlgo(cMeetDesc),m_outDes(null)
{
	m_checkDesIntegrity = checkIntegrity;
	tranMap = null;
	turpleStorage = null;
	main_pendinglist = null;
	// should mPoutDes be set to null?
	m_outDes = null;
}
MeetAlgo::MeetAlgo(const Des* des1, const Des* des2, bool checkIntegrity /*= true*/) : DesAlgo(cMeetDesc), m_outDes(null)
{
	m_checkDesIntegrity = checkIntegrity;
	addInputDes(des1);
	addInputDes(des2);
	tranMap = null;
	turpleStorage = null;
	main_pendinglist = null;
	// should mPoutDes be set to null?
	m_outDes = null;
}
MeetAlgo::MeetAlgo(DesProject::DesIteratorPtr desIterator, bool checkIntegrity /*= true*/): DesAlgo(cMeetDesc), m_outDes(null)
{
	m_checkDesIntegrity = checkIntegrity;	
	addInputDes(desIterator);
	tranMap = null;
	turpleStorage = null;
	main_pendinglist = null;
	// should mPoutDes be set to null?
	m_outDes = null;
}
MeetAlgo::~MeetAlgo(void)
{
		if(tranMap!=null)
		{
			delete tranMap;
			tranMap=null;
		}
		if(turpleStorage!=null)
		{
			delete turpleStorage;
			turpleStorage=null;
		}
		if(main_pendinglist!=null)
		{
			delete main_pendinglist;
			main_pendinglist=null;
		}
		if(m_outDes!=null)
		{
			delete m_outDes;
			m_outDes=null;
		}

}

//_________________________________________________________________________________________________

void MeetAlgo::addInputDes(DesProject::DesIteratorPtr desIterator)
{
	for(desIterator->first(); desIterator->notDone(); desIterator->next())
	{
		MeetAlgo::addInputDes(&desIterator->currentItem());
	}
}
void MeetAlgo::addInputDes(const Des* inDes)
{

  //std::cout << "entering MeetAlg - add des .\n";


	//Make sure the same Des is not added twice
	for (unsigned int iDes = 0; iDes < m_inDesSet.size(); ++iDes)
	{
		if (inDes == m_inDesSet[iDes])
		{
			throw EX("A DES with the same name already exists in the list of input DES. Cannot add.")
		}
	}


	if ((m_description == MeetAlgo::cMeetDesc) && 	(m_inDesSet.size() > 0))  
	  {
	    // For the meet, make sure the DES have the same event sets.
	    long size1 = 0;
	    long size2 = 0;
	    bool found = false;

	    Des::EventIteratorPtr eventIt1 = m_inDesSet[0]->createEventIterator();
	    for(eventIt1->first(); eventIt1->notDone(); eventIt1->next())
	      {
		size1++;
	      }

	    Des::EventIteratorPtr eventIt2 = inDes->createEventIterator();
	    for(eventIt2->first(); eventIt2->notDone(); eventIt2->next())
	      {
		size2++;
	      }

	    if (size1 >= size2) {
	      for(eventIt1->first(); eventIt1->notDone(); eventIt1->next())
		{
		  const DesEvent& event1 = eventIt1->currentItem();
		  found = false;
		  for(eventIt2->first(); eventIt2->notDone(); eventIt2->next())
		    {
		      const DesEvent& event2 = eventIt2->currentItem();
		      if (event1.getName() == event2.getName()) {
			found = true;
			break;
		      }
		    }
		  
		  if (!found) {
		    std::wstring error = L"DES '" + m_inDesSet[0]->getName() + L"' contains event '" + event1.getName() +   L"' but this event is not present in all other input DES.  All DES must have the same event set.";
		    throw error;
		      }
		}
	    } else {
	      for(eventIt2->first(); eventIt2->notDone(); eventIt2->next())
		{
		  const DesEvent& event2 = eventIt2->currentItem();
		  found = false;
		  for(eventIt1->first(); eventIt1->notDone(); eventIt1->next())
		    {
		      const DesEvent& event1 = eventIt1->currentItem();
		      if (event1.getName() == event2.getName()) {
			found = true;
			break;
		      }
		    }

		  if (!found) {
		    std::wstring error = L"DES '" + inDes->getName() + L"' contains event '" + event2.getName() +   L"' but this event is not present in all other input DES. All DES must have the same event set.";
		    throw error;
		  }
		}
	    }

	    /*
	    for(eventIt1->first(); eventIt1->notDone(); eventIt1->next())
	      {
		const DesEvent& crtEvent = eventIt1->currentItem();
		
	      }	

	    */

	  }

	m_inDesSet.push_back(inDes);
}





Des* MeetAlgo::getOutputDes()
{
	return m_outDes;
}



Des* MeetAlgo::returnResult()
{
	Des* result = m_outDes;
	m_outDes = null;
	return result;

}

std::wstring MeetAlgo::composeOutputDesName()
{
	return L"MeetAlgo-Result";
}
void MeetAlgo::createOutputDes()
{
	if (m_outDes!=null)
	{
		delete m_outDes;
		m_outDes = null;
	}

	m_outDes = new Des(composeOutputDesName());
}

//_________________________________________________________________________________________________

void MeetAlgo::loadProductDes() //--1
	{
		createOutputDes();
		//load ProductTransitionMap
		tranMap= new ProductTransitionMap(m_inDesSet,m_outDes,true);
        DesNum=tranMap->getDesNum();
		EventNum=tranMap->getEventNum();
        StateMaxNum=tranMap->getStateMaxNum();
        transitionMatrix=tranMap->getTransitionMatrix();

       //load turpleStorage
		turpleStorage=new MeetSynTrie(m_inDesSet);
		main_pendinglist=new PendingList(DesNum);

		
	}

void MeetAlgo::createInitialStateTuple() //--2
	{
		SrcStateTuple tuple(DesNum);
		for(short i=0;i<DesNum;i++)
		{
			const unsigned long long itmp=m_inDesSet[i]->getInitialState().getId().id();
            unsigned long long tmp=itmp;
			tuple[i]=(short)tmp;
		}

	   //put into finishlist
        turpleStorage->insert(&tuple);
       //put into the pending list:
		
		//pendinglistClear();
		//pushPending(&tuple);
		addStateToOutputDes(&tuple,true);
		main_pendinglist->PushPending(tuple);

		

	}



//_________________________________________________________________________________________________
void MeetAlgo::prepareRun()
{
	//startAlgo();

	if (m_inDesSet.size() < 2)
	{
		throw EX("The MEET algorithm requires at least two input DES.");
	}
	if (m_checkDesIntegrity)
	{
		for (unsigned int iDes = 0; iDes < m_inDesSet.size(); ++iDes)
		{
			const Des* crtDes = m_inDesSet[iDes];
			if (crtDes->getIntegrity() != eIntegYes)
			{			
				throw errorMessage(cMeetIntegError, crtDes->getName());
			}
		}
	}	

	loadProductDes();
    createInitialStateTuple();
	
}

//_________________________________________________________________________________________________

//--------change one---(done)-----------------
bool MeetAlgo::runAlgo()
{
	//ProgressUpdater updater(m_progress);
	prepareRun();
		
     //----------------------major loop---------------------------
	while(main_pendinglist->isEmpty()==false)
	 {
		  SrcStateTuple crtSrcStateTuple(DesNum);
		  main_pendinglist->PopPending(crtSrcStateTuple);
		  for(short i=1;i<EventNum;i++)
		  { 
			  SrcStateTuple temptuple(DesNum);
			  for(short j=0;j<DesNum;j++)
			  {
				  short from_state=crtSrcStateTuple[j];
				  short to_state=transitionMatrix[j][i][from_state];
				 
				  if(to_state!=-1)
				  {
					  temptuple[j]=to_state;
				  }
				  else
				  {
					  //need to be created
				
					  break;
				  }

				  if(j==DesNum-1)
				  {  // at end of the entry; the whole tuple is finished
					 
					  if(turpleStorage->search(&temptuple)==false)
					  {
						  turpleStorage->insert(&temptuple);
						  addStateToOutputDes(&temptuple,false);
						  main_pendinglist->PushPending(temptuple);
					  }

                      DesState& fromState=getStateFromOutputDes(&crtSrcStateTuple);
					  const DesEvent& temp_event=m_outDes->getEvent(i);
					  DesEvent&the_event=const_cast<DesEvent&>(temp_event);

					  DesState& toState=getStateFromOutputDes(&temptuple);

					  DesTransition temp_transition(fromState,the_event,toState);
					  m_outDes->addTransition(temp_transition);

				  }

			  }
		  }
		
		  
	 }
	 //updateProgress();
     //------------------------------------------------------------------------------
		    delete main_pendinglist;
	    main_pendinglist=null;
	return true;
}

void MeetAlgo::addStateToOutputDes( SrcStateTuple *sl, bool isInitial)
{

	bool isMarked = true;
	for(int iDes = 0; iDes < DesNum; ++iDes)
	{
		short DesId=(*sl)[iDes];
		const DesState& srcState = m_inDesSet[iDes]->getState(DesId);
		isMarked &= srcState.isMarked();
	}
	DesState template_state;
	template_state.setMarked(isMarked);

	if(isInitial==true)
	{
       template_state.setInit(true);
	}


    const DesState& outState = m_outDes->addState(template_state);
	DesState &newstate=const_cast<DesState&>(outState);
	newstate.setIDasName();
	newstate.resetAlias();
	
	

}

DesState& MeetAlgo::getStateFromOutputDes(SrcStateTuple *sl)
{
	int State_ID=turpleStorage->getStateId(sl);
	const DesState& temp=m_outDes->getState(State_ID);
	DesState &newstate=const_cast<DesState&>(temp);
	return newstate;
}




}//End of namespace DESpot
