//---------------------------------------------------------------------------
#include <algorithm>
#include <map>
#include "fsm.h"
#include "hub.h"
#include "err.h"
using namespace std;
using namespace hub;
//---------------------------------------------------------------------------
FsmDescr::FsmDescr(int inpN, int outN, std::list<hub::StateTableRow>& table, std::string reset,
                        int stateCount, int productCount)
{
  FsmState* rs;
  inputNo_ = inpN;
  outputNo_ = outN;
  stateNo_ = stateCount;
  productNo_ = productCount;

  rs = CreateMealyFromTable(table);

  if( reset == "" ) {
    resetState_ = rs;
  } else {
    resetState_ = FindOrCreateState(reset);
  }
}
//---------------------------------------------------------------------------
//return first current state from table - if reset state is not specified, then reset state is the first current state
FsmState* FsmDescr::CreateMealyFromTable(std::list<StateTableRow>& table)
{
  FsmState* firstState;
  FsmState* currentState;
  FsmState* nextState;
  bool first = true;
  FsmProduct* product;
  std::list<StateTableRow>::iterator it;

  for( it= table.begin(); it != table.end(); ++it ) {
	if( it->nextState == "*" ) {
	  product = FindOrCreateProduct(it->input, it->output, NULL);
	  dontcareNextStates_.push_back(product);
	  continue;
	}
	if( it->currState == "*" ) {
	  nextState = FindOrCreateState(it->nextState);
	  product = FindOrCreateProduct(it->input, it->output, nextState);
	  dontcareCurrStates_.push_back(product);
	  continue;
	}
    currentState = FindOrCreateState(it->currState);
    nextState = FindOrCreateState(it->nextState);
    product = FindOrCreateProduct(it->input, it->output, nextState);
	currentState->products_.push_back(product);
    if( first ) {
      firstState = currentState;
      first = false;
    }
  }
  return firstState;
}
//---------------------------------------------------------------------------
FsmState* FsmDescr::FindOrCreateState(std::string& stateName)
{
  std::list<FsmState*>::iterator it;
  FsmState* newState;

  for( it= states_.begin(); it != states_.end(); ++it ) {
    if( (*it)->name() == stateName ) return *it;
  }
  newState = new FsmState(stateName);
  states_.push_back(newState);

  return newState;
}
//---------------------------------------------------------------------------
FsmProduct* FsmDescr::FindOrCreateProduct(std::string& input, std::string& output, FsmState* nextState)
{
  std::list<FsmProduct*>::iterator it ;
  FsmProduct* newProduct;

  for( it= products_.begin(); it != products_.end(); ++it ) {
    if( ((*it)->input() == input)&&((*it)->output() == output)&&((*it)->nextState() == nextState) ) return *it;
  }
  newProduct = new FsmProduct(input, output, nextState);
  products_.push_back(newProduct);

  return newProduct;
}
//---------------------------------------------------------------------------
void FsmDescr::CreateTable(std::list<StateTableRow> &table)
{
	std::list<FsmState*> stack;
	StateTableRow row;
	FsmState* actualState;
	std::list<FsmProduct*>::iterator it;
	std::list<FsmState*> finishedStates;

	//dont-care current states
	for( it = dontcareCurrStates_.begin(); it != dontcareCurrStates_.end(); ++it ) {
		row.input = (*it)->input();
		row.output = (*it)->output();
		row.currState = "*";
		row.nextState = (*it)->nextState()->name();
		table.push_back(row);
	}

	stack.push_back(resetState_);
	while( !stack.empty() ) {
		actualState = stack.back();
		stack.pop_back();
		finishedStates.push_back(actualState);
		for( it = actualState->products_.begin(); it != actualState->products_.end(); ++it ) {
			row.input = (*it)->input();
			row.currState = actualState->name();
			row.nextState = (*it)->nextState()->name();
			row.output = (*it)->output();
			if( find(finishedStates.begin(), finishedStates.end(), (*it)->nextState()) == finishedStates.end() ) {
				stack.push_back((*it)->nextState());
				finishedStates.push_back((*it)->nextState());
			}
			table.push_back(row);
		}
	}

	//unused states
	for( it = dontcareNextStates_.begin(); it != dontcareNextStates_.end(); ++it ) {
		row.input = (*it)->input();
		row.output = (*it)->output();
		row.currState = "*";
		row.nextState = "*";
		table.push_back(row);
	}
}
//---------------------------------------------------------------------------
FsmDescr& FsmDescr::copy (const FsmDescr& that) {             // deep copy, assignment JS
    std::map <FsmState*, FsmState*> stateMap;
    std::map <FsmProduct*, FsmProduct*> productMap;
    
    clear();            // discard old data if any
    *this = that;       // make a shallow copy using generated assignment operator
    
    std::list<FsmState*>::const_iterator thatState;
    std::list<FsmState*>::iterator thisState;
    
    thatState = that.states_.begin();
    thisState = states_.begin();
    while (thisState != states_.end()) {                    // copy states
        *thisState = new FsmState (**thatState);            // again a shallow copy by generated copy ctor
        stateMap.insert (std::pair <FsmState*, FsmState*> (*thatState, *thisState));// record the correspondence
        thatState++;
        thisState++;
    }
    
    std::list<FsmProduct*>::const_iterator thatProd;
    std::list<FsmProduct*>::iterator thisProd;
    std::map <FsmState*, FsmState*> :: const_iterator stateEntry;
    std::map <FsmProduct*, FsmProduct*> :: const_iterator prodEntry;
        
    thatProd = that.products_.begin();                      // copy products
    thisProd = products_.begin();
    while (thisProd != products_.end()) {
        *thisProd = new FsmProduct (**thatProd);            // again a shallow copy by generated copy ctor
        stateEntry = stateMap.find ((*thisProd)->nextState());
        if (stateEntry == stateMap.end()) {
            throw CException <std::logic_error, InconsReason> (
                InconsReason ("ptr translation failure"));
        }
        (*thisProd)->nextState (stateEntry->second);       // translate next state ptr
        productMap.insert (std::pair <FsmProduct*, FsmProduct*> (*thatProd, *thisProd));// record the correspondence
        thatProd++;
        thisProd++;
    }
    
    thatProd = that.dontcareNextStates_.begin();            // copy next dontcare products
    thisProd = dontcareNextStates_.begin();
    while (thisProd != dontcareNextStates_.end()) {         // nextState is NULL
        *thisProd = new FsmProduct (**thatProd);            // again a shallow copy by generated copy ctor
        productMap.insert (std::pair <FsmProduct*, FsmProduct*> (*thatProd, *thisProd));// record the correspondence
        thatProd++;
        thisProd++;
    }
    
    thatProd = that.dontcareCurrStates_.begin();            // copy current dontcare products
    thisProd = dontcareCurrStates_.begin();
    while (thisProd != dontcareCurrStates_.end()) {
        *thisProd = new FsmProduct (**thatProd);            // again a shallow copy by generated copy ctor
        stateEntry = stateMap.find ((*thisProd)->nextState());
        if (stateEntry == stateMap.end()) {
            throw CException <std::logic_error, InconsReason> (
                InconsReason ("ptr translation failure"));        
        }
        (*thisProd)->nextState (stateEntry->second);       // translate next state ptr
        productMap.insert (std::pair <FsmProduct*, FsmProduct*> (*thatProd, *thisProd));// record the correspondence
        thatProd++;
        thisProd++;
    }

    thisState = states_.begin();                            // translate product ptrs in states
    while (thisState != states_.end()) {
        thisProd = (*thisState)->products_.begin();
        while (thisProd != (*thisState)->products_.end()) {
            prodEntry = productMap.find (*thisProd);
            if (prodEntry == productMap.end()) {
                throw CException <std::logic_error, InconsReason> (
                    InconsReason ("ptr translation failure"));
            }
            (*thisProd) = prodEntry->second;
            thisProd++;
        }
        thisState++;
    }

    return *this;
}
//---------------------------------------------------------------------------
void FsmDescr::clear (void) {                           // as if the defaulr ctor executed
    type_="Mealy"; inputNo_ = 0; outputNo_ = 0; stateNo_ = 0; productNo_=0;
    resetState_ = NULL;
    
    std::list <FsmState*>::iterator backState;
    std::list <FsmProduct*>::iterator backProd;
    while (!states_.empty()) {
        backState = --states_.end();
        delete *backState;
        states_.pop_back();
    }
    while (!products_.empty()) {
        backProd = --products_.end();
        delete *backProd;
        products_.pop_back();
    }
    while (!dontcareNextStates_.empty()) {
        backProd = --dontcareNextStates_.end();
        delete *backProd;
        dontcareNextStates_.pop_back();
    }
    while (!dontcareCurrStates_.empty()) {
        backProd = dontcareCurrStates_.end();
        delete *backProd;
        dontcareCurrStates_.pop_back();
    }
}
//---------------------------------------------------------------------------
FsmDescr::~FsmDescr () {                                 // JS
    clear();
}
//---------------------------------------------------------------------------
bool FsmAgent::Eq (const BehavDescr*) const
{
  return false;
}
//---------------------------------------------------------------------------
BehavDescr* FsmAgent::CreateDescr (void) const
{
  return new FsmDescr(repr_);
}
//---------------------------------------------------------------------------

