#include "MultiNonBlockingAlgo.h"
#include "Des.h"

namespace DESpot
{

const std::wstring MultiNonBlockingAlgo::cMultiNonBlockingAlgoDesc = L"the MultiNonBlockingAlgoAlgorithm";
const std::wstring
MultiNonBlockingAlgo::cMultiNonBlockingAlgoErr=L"State '%ls' is blocking.";


//_________________________________________________________________________________________________


MultiNonBlockingAlgo::MultiNonBlockingAlgo(bool checkIntegrity /*= true*/) : DesAlgo(cMultiNonBlockingAlgoDesc)
{
	m_checkDesIntegrity = checkIntegrity;	
	nonblocknumber=0;
	isnonblocked=false;
	m_integrity=false;

tranMap=NULL;
 turpleStorage=NULL;
 main_pendinglist=NULL;

}

MultiNonBlockingAlgo::~MultiNonBlockingAlgo(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;
		}

}

//_________________________________________________________________________________________________
void MultiNonBlockingAlgo::addInputDes(const Des* inDes)
{
	//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.")
		}
	}
	m_inDesSet.push_back(inDes);
}
//_________________________________________________________________________________________________

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

       //load turpleStorage
		turpleStorage=new NonblockTrie(m_inDesSet);
		main_pendinglist=new PendingVector(DesNum);

	}

void MultiNonBlockingAlgo::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);
		main_pendinglist->PushPending(tuple);

	}



//_________________________________________________________________________________________________
void MultiNonBlockingAlgo::prepareRun()
{

	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(cMultiNonBlockingAlgoErr, crtDes->getName());
				throw EX("Either all DES are not valid, or integrity \
has not yet been checked.");
			}
		}
	}	

	loadProductDes();
    createInitialStateTuple();
}

//_________________________________________________________________________________________________

//--------change one---(done)-----------------
bool MultiNonBlockingAlgo::runAlgo()
{

//ProgressUpdater updater(m_progress);
	if (m_integrity == false)
	{
		throw EX("Either project is not valid, or integrity \
has not yet been checked.");
		 return false;
	}


	prepareRun();
	if(DesNum > 1)
	{
		reachableCheck();
        nonblockingCheck();
        BlockedStateCheck();
		return isnonblocked;
	}
	else
	{
		m_non_algo.setInputDes(const_cast<Des*>(m_inDesSet[0]));

		isnonblocked = 	m_non_algo.runAlgo();
		return isnonblocked;

		 

	}
}


void MultiNonBlockingAlgo::overrideNonBlocking(bool nonBlocking)
{	
	isnonblocked = nonBlocking;
}




int MultiNonBlockingAlgo::getDesNum()
{
	return DesNum;
}



//-------------------------nonblocking operation


void MultiNonBlockingAlgo::reachableCheck()
{
		
		
     //----------------------1:check and fill table----------------------
		while(main_pendinglist->isEmpty()==false)
	 {
		
		 
		  SrcStateTuple crtSrcStateTuple(DesNum);
		  main_pendinglist->PopPending(crtSrcStateTuple);
		  int m_id=turpleStorage->getStateId(&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
				  {
					  break;
				  }

				  if(j==DesNum-1)
				  {  // at end of the entry; the whole tuple is finished
					 bool useEvent=canUseTransition(i);
					  if(turpleStorage->search(&temptuple,m_id,useEvent)==false)
					  {
						  turpleStorage->insert(&temptuple,m_id,useEvent);
                         // pushPending(&temptuple);
						  main_pendinglist->PushPending(temptuple);
					  }
				  }
			  }
		  }
		//  delete crtSrcStateTuple;
	 }
	 turpleStorage->destoryMemory();
	 //delete main_pendinglist;
	// main_pendinglist=null;
}

int MultiNonBlockingAlgo::nonblockingCheck()
{
	     //---------2:remove the memory and do the reverse check-----------------
	 int state_count=turpleStorage->getStateCount();
	 markchecker=new bool[state_count];
	 for(int i=0;i<state_count;i++)
	 {
       markchecker[i]=false;
	 }

	 while(turpleStorage->m_queue.empty()==false)
	 {
		 int mark_id=turpleStorage->m_queue.front();
		 turpleStorage->m_queue.pop_front();
		 nonblocknumber++;
		markchecker[mark_id]=true;
		m_nonblocklist.push_back(mark_id);
	 }


	 int state_id;
	 State *pend_state;

	 while(m_nonblocklist.empty()==false)
	 {
		 state_id=m_nonblocklist.front();
		 m_nonblocklist.pop_front();
		 pend_state=turpleStorage->getState(state_id);

		 for(int i=0;i<=pend_state->returnUpperBound();i++)
		 {
			 int temp_id=pend_state->getValue(i);
			 if(markchecker[temp_id]==false)
			 {
				 nonblocknumber++;
				 markchecker[temp_id]=true;
				 m_nonblocklist.push_back(temp_id);
			 }
		 }

		 if(nonblocknumber==state_count)
		 {
			 isnonblocked=true;
			 turpleStorage->destoryAllState();
			 turpleStorage->destoryAllStateList();
			 return 0;
		 }
	 }
	 isnonblocked=false;
	 turpleStorage->destoryAllState();
	 turpleStorage->destoryAllStateList();
	 return 0;
}



void MultiNonBlockingAlgo::BlockedStateCheck()
{
	//-------3:if its blocked return the blocked state info----------
	if(isnonblocked==false)
	{
		int state_count=turpleStorage->getStateCount();
		for(int i=0;i<state_count;i++)
		{
			if(markchecker[i]==false)
			{// get the blocking id;
				//m_blockstatelist.insert(i);
				SrcStateTuple temp(DesNum);
				main_pendinglist->GetTurple(i,temp);
				fillErrorList(temp);
			}
		}
	}
    delete main_pendinglist;
	main_pendinglist=null;
}

void MultiNonBlockingAlgo::setIntegrity(bool in_m_integrity)
{
	m_integrity = in_m_integrity;
}



const DesAlgo::ErrorList& MultiNonBlockingAlgo::getErrorList()
{
	
	for(ErrorListIt it=m_inErrorList.begin();it!=m_inErrorList.end();it++)
	{
       addError(errorMessage(cMultiNonBlockingAlgoErr, *it));
	}
	return m_errorList;
}


void MultiNonBlockingAlgo::fillErrorList(SrcStateTuple &in_tuple)
{

  //RJL:  state name formatting
    std::wstringstream s;
    s<<L"(";
    for(int i=0;i<DesNum;i++)
      {
	const Des*  blockingDes = m_inDesSet[i];
	if (i > 0)
	  {
	    s<< L", ";
	  }
	
	s<< blockingDes->getName() << L"/";
	s<<blockingDes->getState(in_tuple[i]).getName();
      }

    s << L")";

    std::wstring temp=s.str();
    m_inErrorList.push_back(temp);

}



bool MultiNonBlockingAlgo::canUseTransition(short in_eventId)
{
	in_eventId=in_eventId;
	return true;
}




bool MultiNonBlockingAlgo::isNonBlocking() const
{
	return isnonblocked;
}



}//End of namespace DESpot

