#ifndef PRODUCTTRANSITIONMAP_H
#define PRODUCTTRANSITIONMAP_H




#include <vector>
#include <map>
#include "Des.h"
#include "DesState.h"
#include "DesEvent.h"
#include "DesTypes.h"
#include <deque>
namespace DESpot
{


class ProductTransitionMap
{
public:
	typedef std::map<std::wstring,short> GlobalEventMap;
	typedef std::map<std::wstring,short>::iterator GlobalEventMapIt;


	typedef std::map<short,DesEvent*> ConEventSet;
	typedef std::map<short,DesEvent*>::iterator ConEventSetIt;
	typedef std::vector<const Des*> DesSet;
	typedef std::vector<const Des*> DesSetIt;

	

private:
GlobalEventMap m_eventmap;
ConEventSet m_coneventset;



GlobalEventMap meet_evenCountSet;
ConEventSet meet_id_eventMap;
GlobalEventMap meet_name_idMap;


private:
DesSet &m_desset;
short DesNum;
short EventNum;
short StateMaxNum;
short ***transitionMatrix;

short **eventInDesMatrix;

Des *out_des;


public:
ProductTransitionMap(DesSet &in_desset):m_desset(in_desset) //for regular algorithm
{

DesNum=m_desset.size();
StateMaxNum=calculateMaxStateCount()+1;
EventNum=calculateTotalEventCount()+1;

createTransitionMapMatrix(DesNum,EventNum,StateMaxNum);
createEventInDesMatrix(DesNum,EventNum);
preprocess(DesNum,EventNum);
}



ProductTransitionMap(DesSet &in_desset, Des* out):m_desset(in_desset),out_des(out)//for syn algorithm
{

DesNum=m_desset.size();
StateMaxNum=calculateMaxStateCount()+1;
EventNum=calculateTotalEventCount()+1;

createTransitionMapMatrix(DesNum,EventNum,StateMaxNum);
createEventInDesMatrix(DesNum,EventNum);
preprocess(DesNum,EventNum);

fillEventToOutputDes();//for sync algo
}


ProductTransitionMap(DesSet &in_desset, Des* out,bool formeet):m_desset(in_desset),out_des(out)// for meet algorithm
{
bool t=formeet;
t=t;

DesNum=m_desset.size();
StateMaxNum=calculateMaxStateCount()+1;
EventNum=calculateMeetEventCount()+1;

createTransitionMapMatrix(DesNum,EventNum,StateMaxNum);
createEventInDesMatrix(DesNum,EventNum);
process(DesNum);

fillEventToOutputDes();


}





short calculateMeetEventCount()
{

	short descout=(short)(m_desset.size());
	for(short i=0;i<descout;i++)
	{
		Des::EventIteratorPtr eventIt = m_desset[i]->createEventIterator();
		for(eventIt->first(); eventIt->notDone(); eventIt->next())
		{
			 const DesEvent& crtEvent = eventIt->currentItem();
		    MeetEventCountSetBuilder(&crtEvent);
		}
	}

	fillMeetEventMap();
	return (short)m_eventmap.size();

}

void MeetEventCountSetBuilder(const DesEvent *event)
{
	GlobalEventMapIt eit=meet_evenCountSet.find(event->getName()); //<name, number>

	if(eit==meet_evenCountSet.end())
	{
		//fill the output event map with the new id
		short id=((short)meet_evenCountSet.size())+1;


		meet_evenCountSet[event->getName()]=1;
		meet_name_idMap[event->getName()]=id;
        meet_id_eventMap[id]=new DesEvent(*event);
	}
	else
	{
		short temp=eit->second;
		++temp;
		eit->second=temp;
	}
}


void fillMeetEventMap()
{
	for(GlobalEventMapIt it=meet_evenCountSet.begin();it!=meet_evenCountSet.end();it++)
	{
		if(it->second==DesNum)
		{
			std::wstring event_name=it->first;
			short event_id=meet_name_idMap[event_name];
			DesEvent* e_event=meet_id_eventMap[event_id];

		    short id=((short)m_eventmap.size())+1;
		    m_eventmap[e_event->getName()]=id;
		    m_coneventset[id]=new DesEvent(id,*e_event);

		}
	}
}
void fillEventToOutputDes()
{

	for(ConEventSetIt it=m_coneventset.begin();it!=m_coneventset.end();it++)
	{
		out_des->addEvent(*(it->second));
	}
}

void process(short numDes)
{
		//prepossessing the matrix
	for(short i=0;i<numDes;i++)
	{
		Des::StateIteratorPtr StateIt =m_desset[i]->createStateIterator();
		for(StateIt->first(); StateIt->notDone(); StateIt->next())
		{
			const DesState& state = StateIt->currentItem();
			short stateid=(short)(state.getId().id());
	        Des::TransIteratorPtr  transIt = m_desset[i]->createStateTransIterator(state);
			for(transIt->first(); transIt->notDone(); transIt->next())
			{
			    const DesTransition& crtTrans = transIt->currentItem();
				
				short eventid=getEventId(&(crtTrans.event()));


                if(eventid==-1)
					continue;

				short tostateid=(long)(crtTrans.toState().getId().id());

				//fill the transitionMatrix map
                transitionMatrix[i][eventid][stateid]=tostateid;
			}
		}
	}
}







~ProductTransitionMap()
{

deleteTransitionMapMatrix(DesNum,EventNum);
deleteEventInDesMatrix(DesNum);


for(ConEventSetIt it=m_coneventset.begin();it!=m_coneventset.end();it++)
{
	delete it->second;
}
for(ConEventSetIt it=meet_id_eventMap.begin();it!=meet_id_eventMap.end();it++)
{
	delete it->second;
}



}












private:
//-----------------get the des attribute---------------------------------------------
// get the max state size for all the Des, the output should be add by one for the matrix  +1
short calculateMaxStateCount()
{
	unsigned long long temp=0;

	short descout=(short)(m_desset.size());
	for(short i=0;i<descout;i++)
	{
		unsigned long long stemp=m_desset[i]->getLastUsedStateId()+1;
		if(stemp>temp)
		{
			temp=stemp;
		}
	}
	return (short)temp;
}

short calculateTotalEventCount()
{
short descout=(short)(m_desset.size());
	for(short i=0;i<descout;i++)
	{
		Des::EventIteratorPtr eventIt = m_desset[i]->createEventIterator();
		for(eventIt->first(); eventIt->notDone(); eventIt->next())
		{
			const DesEvent& crtEvent = eventIt->currentItem();

			OutputEventMapBuilder(&crtEvent);

			/*   if(eventChecker(m_desset[i],crtEvent) == true)  
			{
			     OutputEventMapBuilder(&crtEvent);
			}  */

		}
	}

	return (short)m_eventmap.size();
}


//  Don't need this 
/*
bool eventChecker(const Des* m_des, const DesEvent& m_event)
{

  Des::StateIteratorPtr StateIt =m_des->createStateIterator();
  for(StateIt->first(); StateIt->notDone(); StateIt->next())
    {
      const DesState& state = StateIt->currentItem();
      Des::TransIteratorPtr  transIt = m_des->createStateTransIterator(state);
      for(transIt->first(); transIt->notDone(); transIt->next())
	{
	  const DesTransition& crtTrans = transIt->currentItem();
	  
	  if(crtTrans.event().getId() == m_event.getId())
	    {
	      return true;
	    }
	  
	}
    }
  
  return false;
  
}
*/

void OutputEventMapBuilder(const DesEvent *event)
{
	GlobalEventMapIt eit=m_eventmap.find(event->getName());
	if(eit==m_eventmap.end())
	{
		//fill the output event map with the new id
		short id=((short)m_eventmap.size())+1;
		m_eventmap[event->getName()]=id;
		m_coneventset[id]=new DesEvent(id,*event);


	}
}
//-----------------------------------------------------------------------------------

//-----------------operations on the Transition Map Matrix----------------------------
// create the transition map and initial it with 0

void createTransitionMapMatrix(short x,short y, short z)// desNum,eventNum,StateMaxNum
{
    //  Allocate and initialize transitionMatrix
    transitionMatrix = new short**[x];
    for(short i = 0; i < x; i++)
    {
        transitionMatrix[i] = new short*[y];

        for(short j = 0; j < y; j++)
        {
            transitionMatrix[i][j] = new short[z];
			for(short k=0;k<z;k++)
			{
				transitionMatrix[i][j][k]=-1;
			}
        }
    }

}

void deleteTransitionMapMatrix(short x,short y)// desNum,eventNum,stateMaxNum
{
    //  Deallocate transitionMatrix
    for(short i = 0; i < x; i++)
    {
        for(short j = 0; j < y; j++)
        {
            delete[] transitionMatrix[i][j];
        }

        delete[] transitionMatrix[i];
    }
    delete[] transitionMatrix;
}

//----------------------------------------------------------------------------------------



//--------------operations on the Event in Des Matrix----------------------------------

void createEventInDesMatrix(short x,short y)// desNum,eventNum
{
	//  Allocate and initialize eventInDesMatrix
	eventInDesMatrix=new short* [x];
	for(short i=0;i<x;i++)
	{
		eventInDesMatrix[i]=new short[y];
		for(short j=0;j<y;j++)
		{
			eventInDesMatrix[i][j]=0;
		}
	}
}

void deleteEventInDesMatrix(short x)// desNum,eventNum
{
	//  Deallocate eventInDesMatrix
	for(short i=0;i<x;i++)
	{
		delete [] eventInDesMatrix[i];
	}
	delete [] eventInDesMatrix;
}

//----------------------------------------------------------------------------------------


//--------------------preprocessing the matrix-------------------------------------------


void preprocess(short numDes,short numEvent) //desNum, eventNum
{
  //prepossessing the matrix
  for(short i=0;i<numDes;i++)
    {

      // need to add every event in the DES' event set
      // to eventInDesMatrix, not just  those with transitions!
      {
	Des::EventIteratorPtr eventIt = m_desset[i]->createEventIterator();
	for(eventIt->first(); eventIt->notDone(); eventIt->next())
	  {
	    const DesEvent& crtEvent = eventIt->currentItem();
	    short theEventId=getEventId(&crtEvent);

	    eventInDesMatrix[i][theEventId]=1;
	  }	    
      }

      Des::StateIteratorPtr StateIt =m_desset[i]->createStateIterator();
      for(StateIt->first(); StateIt->notDone(); StateIt->next())
	{
	  const DesState& state = StateIt->currentItem();
	  short stateid=(short)(state.getId().id());
	  Des::TransIteratorPtr  transIt = m_desset[i]->createStateTransIterator(state);
	  for(transIt->first(); transIt->notDone(); transIt->next())
	    {
	      const DesTransition& crtTrans = transIt->currentItem();
	      
	      short eventid=getEventId(&(crtTrans.event()));
	      short tostateid=(long)(crtTrans.toState().getId().id());
	      
	      //fill the transitionMatrix map
	      transitionMatrix[i][eventid][stateid]=tostateid;
	      
	      //fill the eventInDesMatrix table - probably can remove now
	      // eventInDesMatrix[i][eventid]=1;
	    }
	}
    }

  //postpressing the matrix

  for(short j=0;j<numDes;j++)
    {
      for(short k=1;k<numEvent;k++)
	{
	  if(eventInDesMatrix[j][k]==0)// the event k dont in the des[j], but in other des
	    {
	      
	      // get the valid states;
	      Des::StateIteratorPtr StateIt =m_desset[j]->createStateIterator();
	      for(StateIt->first(); StateIt->notDone(); StateIt->next())
		{
		  const DesState& state = StateIt->currentItem();
		  short stateid=(short)(state.getId().id());
		  transitionMatrix[j][k][stateid]=stateid; //add selfloop
		}
	    }
	}
      
    }
	

}












//---------------------------------------------------------------------------------------


public:

short getEventId(const DesEvent *event)
{
	GlobalEventMapIt eit=m_eventmap.find(event->getName());
	if(eit!=m_eventmap.end())
	{
		return eit->second;
	}
	else
	{
		//throw EX("ERROR NO SUCH EVENT EXIT");
		return -1;
	}

}


bool isControllable(short id)
{
	ConEventSetIt eit=m_coneventset.find(id);
    if(eit==m_coneventset.end())
    {
		throw EX("INVLAID");
    }
  
	return eit->second->isControllable();
}

bool isNotLDataEvent(short id)
{
   ConEventSetIt eit=m_coneventset.find(id);
   if(eit==m_coneventset.end())
    {
		throw EX("INVLAID");
    }

   if(eit->second->getType()==eLDataEvent)
   {
	   return false;
   }
   else
   {
	   return true;
   }
}

bool isLowEvent(short id)
{
	ConEventSetIt eit=m_coneventset.find(id);
   if(eit==m_coneventset.end())
    {
		throw EX("INVLAID");
    }

   if(eit->second->getType()==eDefaultEvent||eit->second->getType()==eLowLevelEvent)
   {
	   return true;
   }
   else
   {
	   return false;
   }
}


bool isAnswerEvent(short id)
{
		ConEventSetIt eit=m_coneventset.find(id);
   if(eit==m_coneventset.end())
    {
		throw EX("INVLAID");
    }

   if(eit->second->getType()==eAnswerEvent)
   {
	   return true;
   }
   else
   {
	   return false;
   }
}


bool isRequestEvent(short id)
{
			ConEventSetIt eit=m_coneventset.find(id);
   if(eit==m_coneventset.end())
    {
		throw EX("INVLAID");
    }

   if(eit->second->getType()==eRequestEvent)
   {
	   return true;
   }
   else
   {
	   return false;
   }
}





DesEvent* getEvent(short id)
{
	ConEventSetIt eit=m_coneventset.find(id);
    if(eit==m_coneventset.end())
    {
		throw EX("INVLAID");
    }
	return eit->second;
}





public:

short getDesNum()
{
	return DesNum;
}

short getEventNum()
{
	return EventNum;
}

short getStateMaxNum()
{
	return StateMaxNum;
}

short *** getTransitionMatrix()
{
	return transitionMatrix;
}


};
}

#endif
