/*************************************************************************************************
** Project:     FSMGenerator
** Package:     FSMGenerator
** Module:      FSMGenerator::dbManager
** File:        dbManager.cpp
** Date:        2002.08.01
** Author:      Pavel Bekkerman (chpavel@tx.technion.ac.il)
** Copyright:   Copyright (C) 2002 Pavel Bekkerman
** License:
**              This program is free software; you can redistribute it and/or
**              modify it under the terms of the GNU General Public License as
**              published by the Free Software Foundation; either version 2 of
**              the License, or (at your option) any later version.
**
**              This program is distributed in the hope that it will be useful,
**              but WITHOUT ANY WARRANTY; without even the implied warranty of
**              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**              GNU General Public License for more details.
**
**              You should have received a copy of the GNU General Public License
**              along with this program; if not, write to the Free Software
**              Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
**
** Contact:     Pavel Bekkerman (chpavel@tx.technion.ac.il)
*************************************************************************************************/

#include "FSMDBManager.h"

#include <fstream>

using namespace FSMGenerator;
using namespace std;

// ********************************************************************* FSMDB

// *********************************************** Constructor
FSMGenerator::FSMDBManager::FSMDBManager(FSMDB& new_db)
	: m_db(new_db), m_idNextID(1)
{
	m_db.m_indStart = NULL_INDEX;
	m_db.m_dvCallbacks.push_back(Callback("",0,""));
}

// *********************************************** Destructor

FSMGenerator::FSMDBManager::~FSMDBManager()
{
}

// *********************************************** Name

const string&
FSMGenerator::FSMDBManager::Name()const
{
	return m_db.Name();
}

// *********************************************** _getNextID

ID
FSMGenerator::FSMDBManager::_generateID()
{
	return m_idNextID++;
}

// *********************************************** NameInsert
void
FSMGenerator::FSMDBManager::NewStringInsert(const string& strName, int idNew)
{	
	if(!m_mapNameToID.insert(map<string,ID>::value_type(strName,idNew)).second)
		throw DBException(DBException::DuplicatedNames,"Error: '"+strName+
																"' - name already exists.");
}

// *********************************************** NameInsert

void
FSMGenerator::FSMDBManager::NameSet(const string& strName)
{	
	m_db.m_strName = strName;
}

// *********************************************** StateInsert

void
FSMGenerator::FSMDBManager::StateInsert(const string& strName)
{
	ID idNew = _generateID();

	NewStringInsert(strName,idNew);	// add to NameToID map.

	INDEX indNew = m_db.m_dvStates.size();
	m_mapIDToState.insert(map<ID,INDEX>::value_type(idNew,indNew));	// add to IDToState map.

	m_db.m_dvStates.push_back(State(strName,idNew));	// add to States vector.
}

// *********************************************** EventInsert

void
FSMGenerator::FSMDBManager::EventInsert(const string& strName)
{
	ID idNew = _generateID();

	NewStringInsert(strName,idNew);	// add to NameToID map.

	INDEX indNew = m_db.m_dvEvents.size();
	m_mapIDToEvent.insert(map<ID,INDEX>::value_type(idNew,indNew));	// add to IDToEvent map.

	m_db.m_dvEvents.push_back(Event(strName,idNew));	// add to Events vector.
}

// *********************************************** TransInsert

void
FSMGenerator::FSMDBManager::TransInsert(const string& strName, const string& strFrom,
							const string& strEvent, const string& strTo)
{	
	

	ID idNew = _generateID();

	NewStringInsert(strName,idNew);	// add to NameToID map.

	INDEX indTrans = m_db.m_dvTransitions.size();
	m_mapIDToTransition.insert( map<ID,INDEX>::value_type(idNew,indTrans) ); // add to IDToTransition map.

	INDEX indFrom  = StateIndex(strFrom),
		  indEvent = EventIndex(strEvent);
	m_db.m_dvTransitions.push_back(Transition(strName,idNew,
										 indFrom,
										 indEvent,
										 StateIndex(strTo))
							 );	// add to Transitions vector.

	// add to the 'from'-State transition list (first check if the event was already handled here.
	list<INDEX>::const_iterator itr;
	list<INDEX>& trans_list = m_db.m_dvStates[indFrom].transitions;

	for(itr = trans_list.begin(); itr != trans_list.end(); ++itr)
		if(m_db.m_dvTransitions[*itr].WithEvent() == indEvent)
			throw DBException(DBException::DuplicatedEventHandler, "Error: '"+strEvent+"' event is already handled from '"+strFrom+"' state.");

	m_db.m_dvStates[indFrom].transitions.push_back(indTrans);
}

// *********************************************** CallbackInsert

void
FSMGenerator::FSMDBManager::CallbackInsert(const string& strCallName, const string& strFuncName)
{
	if(strFuncName == "")
		throw DBException(DBException::IllegalFunctionName,
				"Callback Error: Illegal function attached to callback '" + strCallName + "'.");

	ID idNew = _generateID();

	NewStringInsert(strCallName,idNew);	// add to NameToID map.

	INDEX indNew = m_db.m_dvCallbacks.size();
	m_mapIDToCallback.insert(map<ID,INDEX>::value_type(idNew,indNew));	// add to IDToCallback map.

	m_db.m_dvCallbacks.push_back(Callback(strCallName,idNew,strFuncName));	// add to Callbacks vector.
}


// *********************************************** StartInsert

void
FSMGenerator::FSMDBManager::StartSet(const string& strName)
{
	if(m_db.m_indStart != NULL_INDEX)
		throw DBException(DBException::DuplicatedStartState, "Start State Error: '"+strName+
		 "' - Start state was already defined as '"+m_db.m_dvStates[m_db.m_indStart].Name()+"'.");

	m_db.m_indStart = StateIndex(strName);
}

// *********************************************** FinalInsert

void
FSMGenerator::FSMDBManager::FinalInsert(const string& strName)
{
	m_db.m_dvStates[StateIndex(strName)].SetFinal();

	m_db.m_dvFinalID.push_back(m_mapNameToID.find(strName)->second);
}

// *********************************************** HookSet

void
FSMGenerator::FSMDBManager::HookSet(const string& strObjName,
						   const string& strCall1, const string& strCall2/*=STRNULL*/)
{
	// attach the 1st callback to the object.

	if(strCall2 != STRNULL && !IsState(strObjName))
		throw DBException(DBException::WrongNumberOfHooks,"Hook Error: To many hooks defined for '" + strObjName + "'.");

	if(IsEvent(strObjName))
	{
		Event& event = m_db.m_dvEvents[EventIndex(strObjName)];
		if(event.Callback() == NULL_INDEX)
			event.SetCallback(CallbackIndex(strCall1));
		else
			throw DBException(DBException::DuplicatedHook, "Hook Error: '"+strObjName+"' already hooked.");
	}
	else if(IsTrans(strObjName))
	{
		Transition& trans = m_db.m_dvTransitions[TransitionIndex(strObjName)];
		if(trans.Callback() == NULL_INDEX)
			trans.SetCallback(CallbackIndex(strCall1));
		else
			throw DBException(DBException::DuplicatedHook, "Hook Error: '"+strObjName+"' already hooked.");
	}
	else if(IsState(strObjName))
	{
		State& state = m_db.m_dvStates[StateIndex(strObjName)];
		if(state.EnterCall() == NULL_INDEX && state.ExitCall() == NULL_INDEX)
			state.SetCallbacks(CallbackIndex(strCall1), strCall2 != "" ? CallbackIndex(strCall2) :
																		 NULL_INDEX
							  );
		else
			throw DBException(DBException::DuplicatedHook, "Hook Error: '"+strObjName+"' already hooked.");
	}
	else
		throw DBException(DBException::UndefinedName, "Error: '"+strObjName+"' item wasn't defined.");

}

// *********************************************** Index Retrivals
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INDEX
FSMGenerator::FSMDBManager::StateIndex(const string& strName)const
{
	citrNameToID itr = m_mapNameToID.find(strName);
	if(itr == m_mapNameToID.end())
		throw DBException(DBException::UndefinedName, "Error: '"+strName+"' state wasn't defined.");

	citrIDToIndex itrFind = m_mapIDToState.find(itr->second);
	if(itrFind == m_mapIDToState.end())
		throw DBException(DBException::UndefinedName, "Error: '"+strName+"' wasn't defined as state.");

	return itrFind->second;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INDEX
FSMGenerator::FSMDBManager::EventIndex(const string& strName)const
{
	citrNameToID itr = m_mapNameToID.find(strName);
	if(itr == m_mapNameToID.end())
		throw DBException(DBException::UndefinedName, "Error: '"+strName+"' event wasn't defined.");

	citrIDToIndex itrFind = m_mapIDToEvent.find(itr->second);
	if(itrFind == m_mapIDToEvent.end())
		throw DBException(DBException::UndefinedName, "Error: '"+strName+"' wasn't defined as event.");

	return itrFind->second;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INDEX
FSMGenerator::FSMDBManager::TransitionIndex(const string& strName)const
{
	citrNameToID itr = m_mapNameToID.find(strName);
	if(itr == m_mapNameToID.end())
		throw DBException(DBException::UndefinedName, "Error: '"+strName+"' transition wasn't defined.");

	citrIDToIndex itrFind = m_mapIDToTransition.find(itr->second);
	if(itrFind == m_mapIDToTransition.end())
		throw DBException(DBException::UndefinedName, "Error: '"+strName+"' wasn't defined as transition.");

	return itrFind->second;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INDEX
FSMGenerator::FSMDBManager::CallbackIndex(const string& strName)const
{
	citrNameToID itr = m_mapNameToID.find(strName);
	if(itr == m_mapNameToID.end())
		throw DBException(DBException::UndefinedName, "Error: '"+strName+"' callback wasn't defined.");

	citrIDToIndex itrFind = m_mapIDToCallback.find(itr->second);
	if(itrFind == m_mapIDToCallback.end())
		throw DBException(DBException::UndefinedName, "Error: '"+strName+"' wasn't defined as callback.");

	return itrFind->second;
}

// ************************************************************ Validation Interface
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
FSMGenerator::FSMDBManager::IsState(const string& strName)const
{
	citrNameToID itr = m_mapNameToID.find(strName);
	if(itr == m_mapNameToID.end())	// no object with such name.
		return false;

	return m_mapIDToState.find(itr->second) != m_mapIDToState.end();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
FSMGenerator::FSMDBManager::IsEvent(const string& strName)const
{
	citrNameToID itr = m_mapNameToID.find(strName);
	if(itr == m_mapNameToID.end())	// no object with such name.
		return false;

	return m_mapIDToEvent.find(itr->second) != m_mapIDToEvent.end();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
FSMGenerator::FSMDBManager::IsTrans(const string& strName)const
{
	citrNameToID itr = m_mapNameToID.find(strName);
	if(itr == m_mapNameToID.end())	// no object with such name.
		return false;

	return m_mapIDToTransition.find(itr->second) != m_mapIDToTransition.end();
	
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
FSMGenerator::FSMDBManager::IsCallback(const string& strName)const
{
	citrNameToID itr = m_mapNameToID.find(strName);
	if(itr == m_mapNameToID.end())	// no object with such name.
		return false;

	return m_mapIDToCallback.find(itr->second) != m_mapIDToCallback.end();

}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
FSMGenerator::FSMDBManager::IsObject(const string& strName)const
{
	return m_mapNameToID.find(strName) != m_mapNameToID.end();
}

