/*	Author:		Magdin Stoica
	Supervisor: Dr. Ryan Leduc
	
	Project created in conformity with the requirements for the Degree of Master of Engineering in Software Engineering, 
	Computing and Software Department, 
	McMaster University
	2003 - 2007
*/

#include <QGroupBox>
#include <QTreeWidget>
#include <qevent.h>
#include <QHeaderView>
#include <QMessageBox>

#include "CommonDefinitions.h"
#include "GedTransitionEditorUiPart.h"
#include "Des.h"
#include "DespotTreeWidget.h"


namespace DESpot
{

const short GedTransitionEditorUiPart::cTransEditorColumnCount		= 3;
const short GedTransitionEditorUiPart::cTransFromStateColumnIdx	= 0;
const short GedTransitionEditorUiPart::cTransEventColumnIdx		= 1;
const short GedTransitionEditorUiPart::cTransToStateColumnIdx		= 2;

const short GedTransitionEditorUiPart::cSelfTransEditorColumnCount = 2;
const short GedTransitionEditorUiPart::cSelfTransEventColumnIdx	= 0;
const short GedTransitionEditorUiPart::cSelfTransAliasColumnIdx	= 1;

GedTransitionEditorUiPart::GedTransitionEditorUiPart(Ui::GedDesEditorUI* pUiContainer, Des* pDes)
{
	if (pUiContainer == null)
		throw EX("Invalid (null) UI Container. Cannot create the transition editor UI part")

	if (pDes == null)
		throw EX("Invalid (null) DES pointer. Cannot create UI part without data")

	//Initialize the data shown in the UI part
	m_pDes = pDes;
	m_desListnerId = m_pDes->subscribe(this);

	//setup widget references
	m_pTransBoxWidg = pUiContainer->m_transFuncGroupBox;
	m_pTransViewWidg = pUiContainer->m_transitionViewWidg;
	m_pSelfTransViewWidg = pUiContainer->m_selfTransWidg;
	m_pTransSplitterWidg = pUiContainer->m_transSplitterWidg;

	m_pTransBoxWidg->installEventFilter(this);

	//initialize widgets
	initTransViewWidget();
	initSelfTransViewWidget();

	//connect widgets with the part's handlers
	connect(m_pTransViewWidg, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), 
		    this, SLOT(onChangedCurrentTransItem(QTreeWidgetItem*, QTreeWidgetItem*)));
	
	connect(m_pTransViewWidg, SIGNAL(onActiveItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), 
		    this, SLOT(onChangedActiveTransItem(QTreeWidgetItem*, QTreeWidgetItem*)));

	connect(m_pSelfTransViewWidg, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), 
		    this, SLOT(onChangedCurrentSelfTransItem(QTreeWidgetItem*, QTreeWidgetItem*)));

	connect(m_pSelfTransViewWidg, SIGNAL(onActiveItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), 
		    this, SLOT(onChangedActiveSelfTransItem(QTreeWidgetItem*, QTreeWidgetItem*)));

	connect(m_pTransViewWidg, SIGNAL(itemChanged(QTreeWidgetItem*, int)), 
		    this, SLOT(onTransItemChanged(QTreeWidgetItem*, int)));

	connect(m_pTransViewWidg, SIGNAL(onWidthChanged(int)), 
		    this, SLOT(resizeTransViewHeaders(int)));

	connect(m_pSelfTransViewWidg, SIGNAL(onWidthChanged(int)),
			this, SLOT(resizeSelfTransViewHeaders(int)));
}

//_________________________________________________________________________________________________

GedTransitionEditorUiPart::~GedTransitionEditorUiPart(void)
{
	if (m_pDes)
	{
		m_pDes->unsubscribe(m_desListnerId);
	}
}

//_________________________________________________________________________________________________

//returns the context menu for this UI Part
QMenu& GedTransitionEditorUiPart::getTransContextMenu()
{
	return m_transEditorContextMenu;
}

//_________________________________________________________________________________________________

QMenu& GedTransitionEditorUiPart::getSelfTransContextMenu()
{
	return m_selfTransEditorContextMenu;
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onChangedCurrentTransItem(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
	if (previous == current)
		return; //nothing really changed

	//previously there might not have been a selected transition so we need to check
	const DesTransition* oldCrtTrans = null;
	if (previous)
	{
		oldCrtTrans = &getTransFromUiItem(*previous);
	}

	const DesTransition* newCrtTrans = null;
	if (current)
	{
		//obtain the currently selected transition
		newCrtTrans = &getTransFromUiItem(*current);
	}

	emit onChangedCurrentTrans(newCrtTrans, oldCrtTrans);
}

void GedTransitionEditorUiPart::onChangedActiveTransItem(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
	if (previous == current)
		return; //nothing really changed

	//previously there might not have been a selected transition so we need to check
	const DesTransition* oldCrtTrans = null;
	if (previous)
	{
		oldCrtTrans = &getTransFromUiItem(*previous);
	}

	const DesTransition* newCrtTrans = null;
	if (current)
	{
		//obtain the currently selected transition
		newCrtTrans = &getTransFromUiItem(*current);
	}

	emit onChangedActiveTrans(newCrtTrans, oldCrtTrans);
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onChangedCurrentSelfTransItem(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
	if (previous == current)
		return; //nothing really changed

	//previously there might not have been a selected transition so we need to check
	const DesEvent* oldCrtSelfTransEvent = null;
	if (previous)
	{
		oldCrtSelfTransEvent = &getSelfTransEventFromUiItem(*previous);
	}

	const DesEvent* newCrtSelfTransEvent = null;
	if (current)
	{
		//obtain the currently selected transition
		newCrtSelfTransEvent = &getSelfTransEventFromUiItem(*current);
	}

	emit onChangedCurrentSelfTrans(newCrtSelfTransEvent, oldCrtSelfTransEvent);
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onChangedActiveSelfTransItem(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
	if (previous == current)
		return; //nothing really changed

	//previously there might not have been a selected transition so we need to check
	const DesEvent* oldCrtSelfTransEvent = null;
	if (previous)
	{
		oldCrtSelfTransEvent = &getSelfTransEventFromUiItem(*previous);
	}

	const DesEvent* newCrtSelfTransEvent = null;
	if (current)
	{
		//obtain the currently selected transition
		newCrtSelfTransEvent = &getSelfTransEventFromUiItem(*current);
	}

	emit onChangedActiveSelfTrans(newCrtSelfTransEvent, oldCrtSelfTransEvent);
}

//_________________________________________________________________________________________________
//This signal is received whenever the user changes the item in place (within the tree widget)
//The method ensures the data is validated and the corresponding transition is changed.   Once the transition
//is changed in the DES, the DES itself will send a changed transition notfication that this UI part 
//subscribes too
void GedTransitionEditorUiPart::onTransItemChanged(QTreeWidgetItem * item, int column )
{
	//Change the transition that corresponds to this item
	switch(column)
	{
		case cTransFromStateColumnIdx:
			onTransItemFromStateChanged(item, column);
			break;

		case cTransEventColumnIdx:
			onTransItemEventChanged(item, column);
			break;

		case cTransToStateColumnIdx:
			onTransItemToStateChanged(item, column);
			break;

		default:
			assert(false);
	}
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onTransItemFromStateChanged(QTreeWidgetItem * item, int /*column*/ )
{
	if (item == null)
		return;

	//get information about the old and the new name of the "from state" of the changed transition
	const DesTransition& changedTrans  = getTransFromUiItem(*item);
	const DesState& fromState = changedTrans.fromState();
	QString oldFromStateName = QString::fromStdWString(fromState.getName());	
	QString newFromStateName = item->text(cTransFromStateColumnIdx);

	//ensure that there was a change
	if (oldFromStateName == newFromStateName)
		return; //no change

	//validate the change and if valid  make it
	const DesState* pNewFromState = null;
	switch(validateStateName(newFromStateName, pNewFromState))
	{
		case eStateFound:
		{
			DesTransition change(*pNewFromState, changedTrans.event(), changedTrans.toState());
			bool changeDone = changeTransition(changedTrans, change);			
			if (changeDone == false)
			{
				//the change could not be applied (e.g. the transition change would lead to duplication or non-det)
				//revert to the previous state name
				item->setText(cTransFromStateColumnIdx, oldFromStateName);		
			}
			return;
		}			

		case eStateNameEmtpy:
		{
			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, STR_TRANS_FROM_STATE_MANDATORY);		
			item->setText(cTransFromStateColumnIdx, oldFromStateName);		
			return;
		}

		case eStateNotFound:
		{
			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, STR_TRANS_FROM_STATE_NOT_FOUND(newFromStateName));			
			item->setText(cTransFromStateColumnIdx, oldFromStateName);		
			break;
		}

		default:
			assert(false);

	}	
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onTransItemEventChanged(QTreeWidgetItem * item, int /*column*/ )
{
	if (item == null)
		return;

	//get information about the old and the new name of the "from state" of the changed transition
	const DesTransition& changedTrans  = getTransFromUiItem(*item);
	const DesEvent& oldEvent = changedTrans.event();
	QString oldEventName = QString::fromStdWString(oldEvent.getName());	
	QString newEventName =  item->text(cTransEventColumnIdx);

	//ensure that there was a change
	if (oldEventName == newEventName)
		return; //no change

	//validate the change and if valid  make it
	const DesEvent* pNewEvent = null;
	switch(validateEventName(newEventName, pNewEvent))
	{
		case eEventFound:
		{
			DesTransition change(changedTrans.fromState(), *pNewEvent, changedTrans.toState());
			bool changeDone = changeTransition(changedTrans, change);
			if (changeDone == false)
			{
				//the change could not be applied (e.g. the transition change would lead to duplication or non-det)
				//revert to previous event label
				item->setText(cTransEventColumnIdx, oldEventName);		
			}
			return;
		}			

		case eEventNameEmtpy:
		{
			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, STR_TRANS_EVENT_MANDATORY);		
			item->setText(cTransEventColumnIdx, oldEventName);		
			return;
		}

		case eStateNotFound:
		{
			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, STR_TRANS_EVENT_NOT_FOUND(newEventName));			
			item->setText(cTransEventColumnIdx, oldEventName);		
			break;
		}

		default:
			assert(false);

	}	
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onTransItemToStateChanged( QTreeWidgetItem * item, int /*column*/ )
{
	if (item == null)
		return;

	//get information about the old and the new name of the "from state" of the changed transition
	const DesTransition& changedTrans  = getTransFromUiItem(*item);
	const DesState& toState = changedTrans.toState();
	QString oldToStateName = QString::fromStdWString(toState.getName());	
	QString newToStateName = item->text(cTransToStateColumnIdx);

	//ensure that there was a change
	if (oldToStateName == newToStateName)
		return; //no change

	//validate the change and if valid  make it
	const DesState* pNewToState = null;
	switch(validateStateName(newToStateName, pNewToState))
	{
		case eStateFound:
		{
			DesTransition change(changedTrans.fromState(), changedTrans.event(), *pNewToState);
			bool changeDone = changeTransition(changedTrans, change);			
			if (changeDone == false)
			{
				//the change could not be applied (e.g. the transition change would lead to duplication or non-det)
				//revert to the previous state
				item->setText(cTransToStateColumnIdx, oldToStateName);		
			}
			return;
		}			

		case eStateNameEmtpy:
		{
			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, STR_TRANS_TO_STATE_MANDATORY);		
			item->setText(cTransToStateColumnIdx, oldToStateName);		
			return;
		}

		case eStateNotFound:
		{
			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, STR_TRANS_TO_STATE_NOT_FOUND(newToStateName));			
			item->setText(cTransToStateColumnIdx, oldToStateName);		
			break;
		}

		default:
			assert(false);

	}	
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::initTransViewWidget()
{
	//setup header: From State | Event | To State| 
	m_pTransViewWidg->setColumnCount(cTransEditorColumnCount);
	QStringList headers;
	headers << tr("From State") << tr("Event") << tr("To State");
	m_pTransViewWidg->setHeaderLabels(headers);

	resizeTransViewHeaders(m_pTransViewWidg->geometry().width());
	
	m_pTransViewWidg->header()->setSortIndicatorShown(false);
	
	m_pTransViewWidg->setRootIsDecorated(false);

	//disable context menu 
	//m_pTransViewWidg->setContextMenu(m_transEditorContextMenu);

	m_pTransViewWidg->setShareFocus();

	// disable editable xma [oct/8/08]
	//m_pTransViewWidg->setAllowEdit(false);
	m_pTransViewWidg->setSelectionMode(QAbstractItemView::SingleSelection);
    m_pTransViewWidg->setSelectionBehavior(QAbstractItemView::SelectRows);
	m_pTransViewWidg->setEditTriggers(QAbstractItemView::NoEditTriggers);

	m_pTransViewWidg->setCircularCursor();

	loadTransitions();
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::initSelfTransViewWidget()
{
	//setup header: Self-Looped Event | Alias 
	m_pSelfTransViewWidg->setColumnCount(cSelfTransEditorColumnCount);
	QStringList headers;
	headers << tr("Global Self-Loops") << tr("Alias");
	m_pSelfTransViewWidg->setHeaderLabels(headers);

	resizeSelfTransViewHeaders(m_pSelfTransViewWidg->geometry().width());
	
	m_pSelfTransViewWidg->header()->setSortIndicatorShown(false);
	
	m_pSelfTransViewWidg->setRootIsDecorated(false);

	//disable context menu
	//m_pSelfTransViewWidg->setContextMenu(m_selfTransEditorContextMenu);

	m_pSelfTransViewWidg->setShareFocus();

	m_pSelfTransViewWidg->setCircularCursor();

	loadSelfTransitions();
}

//_________________________________________________________________________________________________

bool GedTransitionEditorUiPart::eventFilter(QObject* target, QEvent* event)
{
	if (target == m_pTransBoxWidg && event->type() == QEvent::Resize)
	{
		//The box widget was resized so resize the tree widget itself as well to match
		QResizeEvent* resizeEvent = dynamic_cast<QResizeEvent*>(event);
		m_pTransSplitterWidg->resize(resizeEvent->size() - QSize(18,28));
	}

	return QObject::eventFilter(target, event);
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::resizeTransViewHeaders(int transViewWidgWidth)
{
	int headerWidth = transViewWidgWidth;
	
        int fromStateWidth = (int)(headerWidth / 3.0f);
	int fromStateOpt = m_pTransViewWidg->header()->sectionSizeHint(cTransFromStateColumnIdx);

        int eventNameWidth = (int)(headerWidth / 3.0f);
	int eventNameOpt = m_pTransViewWidg->header()->sectionSizeHint(cTransEventColumnIdx);

	
	int toStateWidth = headerWidth - (fromStateWidth + eventNameWidth) - 5;
	int toStateOpt = m_pTransViewWidg->header()->sectionSizeHint(cTransToStateColumnIdx);
	
	
	m_pTransViewWidg->header()->resizeSection(cTransFromStateColumnIdx, fromStateWidth < fromStateOpt ? fromStateOpt : fromStateWidth);
	m_pTransViewWidg->header()->resizeSection(cTransEventColumnIdx, eventNameWidth < eventNameOpt ? eventNameOpt: eventNameWidth);
	m_pTransViewWidg->header()->resizeSection(cTransToStateColumnIdx, toStateWidth < toStateOpt ? toStateOpt : toStateWidth);		
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::resizeSelfTransViewHeaders(int selfTransViewWidgWidth)
{
	int headerWidth = selfTransViewWidgWidth;
	
        int eventNameWidth = (int)(headerWidth * 4.0f / 5.0f);
	int eventNameOpt = m_pSelfTransViewWidg->header()->sectionSizeHint(cSelfTransEventColumnIdx);

	int eventAliasWidth = headerWidth  - eventNameWidth;
	int eventAliasOpt = m_pSelfTransViewWidg->header()->sectionSizeHint(cSelfTransAliasColumnIdx);
		
	m_pSelfTransViewWidg->header()->resizeSection(cSelfTransEventColumnIdx, eventNameWidth < eventNameOpt ? eventNameOpt: eventNameWidth);
	m_pSelfTransViewWidg->header()->resizeSection(cSelfTransAliasColumnIdx, eventAliasWidth < eventAliasOpt ? eventAliasOpt : eventAliasWidth);
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::loadTransitions()
{
	Des::TransIteratorPtr transIt = m_pDes->createTransIterator(true); //true - exclude global self-loops
	for(transIt->first(); transIt->isDone() == false; transIt->next())
	{
		const DesTransition& trans = transIt->currentItem();
		QTreeWidgetItem* pUiItem = createUiItemFromTrans(trans);	
		m_pTransViewWidg->addTopLevelItem(pUiItem);
	}
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::loadSelfTransitions()
{
	Des::SelfTransIteratorPtr selfTransIt = m_pDes->createSelfTransIterator();
	for(selfTransIt->first(); selfTransIt->isDone() == false; selfTransIt->next())
	{
		const DesEvent& selfTrans = selfTransIt->currentItem();
		QTreeWidgetItem* pUiItem = createUiItemFromSelfTrans(selfTrans);	
		m_pSelfTransViewWidg->addTopLevelItem(pUiItem);
	}
}

//_________________________________________________________________________________________________

GedTransitionEditorUiPart::StateValidation GedTransitionEditorUiPart::validateStateName(QString newToStateName, const DesState*& pNewToState)
{
	if (newToStateName.isEmpty())
	{
		return eStateNameEmtpy;
	}
	else
	{
		if (m_pDes->findState(newToStateName.toStdWString(), pNewToState))
		{
			return eStateFound;
		}
		else
		{
			return eStateNotFound;
		}
	}
}

//_________________________________________________________________________________________________

GedTransitionEditorUiPart::EventValidation GedTransitionEditorUiPart::validateEventName(QString newEventName, const DesEvent*& pNewEvent)
{
	if (newEventName.isEmpty())
	{
		return eEventNameEmtpy;
	}
	else
	{
		if (m_pDes->findEvent(newEventName.toStdWString(), pNewEvent))
		{
			return eEventFound;
		}
		else
		{
			return eEventNotFound;
		}
	}
}

//_________________________________________________________________________________________________

bool GedTransitionEditorUiPart::changeTransition(const DesTransition& trans, const DesTransition& change)
{	
	switch(m_pDes->changeTransition(trans, change))
	{
        case eTransOpOk:
			return true;

        case eDuplicatedTrans:
		{
			QString fromStateName = QString::fromStdWString(change.fromState().getName());
			QString eventName = QString::fromStdWString(change.event().getName());
			QString toStateName = QString::fromStdWString(change.toState().getName());


			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, 
								  STR_TRANSITION_ALREADY_EXISTS_CHANGE(fromStateName, eventName, toStateName));		
			return false;
		}

        case eNonDetTrans:
		{	
			QString fromStateName = QString::fromStdWString(change.fromState().getName());
			QString eventName = QString::fromStdWString(change.event().getName());
			QString toStateName = QString::fromStdWString(change.toState().getName());


			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, 
								  STR_NON_DETERMINISTIC_TRANS_CHANGE(fromStateName, eventName, toStateName));		
			return false;
		}

        case eTransNonDetWithSelf:
		{
			QString fromStateName = QString::fromStdWString(change.fromState().getName());
			QString eventName = QString::fromStdWString(change.event().getName());
			QString toStateName = QString::fromStdWString(change.toState().getName());


			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, 
								  STR_NON_DET_WITH_SELF_TRANS_CHANGE(fromStateName, eventName, toStateName));		
			return false;
		}

        case eInvalidFromState:
		{
			QString fromStateName = QString::fromStdWString(change.fromState().getName());
			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, STR_TRANS_FROM_STATE_NOT_FOUND(fromStateName));			
			assert(false); //this should not happen
			return false;
		}

        case eInvalidEvent:
		{
			QString eventName = QString::fromStdWString(change.event().getName());
			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, STR_TRANS_EVENT_NOT_FOUND(eventName));			
			assert(false); //this should not happen
			return false;
		}

        case eInvalidToState:
		{
			QString toStateName = QString::fromStdWString(change.toState().getName());
			QMessageBox::critical(m_pTransViewWidg->parentWidget(), STR_DESPOT_ERROR, STR_TRANS_TO_STATE_NOT_FOUND(toStateName));			
			assert(false); //this should not happen
			return false;
		}

		default:
		{
			assert (false);
			throw EX("Invalid result.")
		}
	}
}

//_________________________________________________________________________________________________

//called by Des whenever a transition is added to it. This event is called through the DesNotifications interface
//which this part implements. All parts are listeners through this interface
void GedTransitionEditorUiPart::onTransitionAdded(const DesTransition& addedTrans)
{
	//add the state information to the state editor
	QTreeWidgetItem* pTransUiItem = createUiItemFromTrans(addedTrans);
	m_pTransViewWidg->addTopLevelItem(pTransUiItem);
}

//_________________________________________________________________________________________________
//Called by Des whenever a self-transition is added to it. This event is called through the DesNotifications interface
//which this part implements. All parts are listeners through this interface but not all respond to all events
void GedTransitionEditorUiPart::onGlobalSelfTransitionAdded(const DesEvent& selfTransEvent)
{
	//add the state information to the state editor
	QTreeWidgetItem* pSelfTransUiItem = createUiItemFromSelfTrans(selfTransEvent);
	m_pSelfTransViewWidg->addTopLevelItem(pSelfTransUiItem);
}

//_________________________________________________________________________________________________

//called by Des whenever a transition is changed. This event is called through the DesNotifications interface
//which this part implements. All parts are listeners through this interface
void GedTransitionEditorUiPart::onTransitionChanged(const DesTransition& trans, const DesTransition& changedTrans)
{
	QTreeWidgetItem* pTransUiItem = getUiItemFromTrans(trans);

	//found UI item corresponding to the changed transition. update it
	fillTransUiItem(*pTransUiItem, changedTrans);

	//notify clients if this was the curently selected item
	if (pTransUiItem == m_pTransViewWidg->currentItem())
	{
		emit onCurrentTransChanged(changedTrans);
	}
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onRemovingTransition(const DesTransition& trans)
{
	QTreeWidgetItem* pTransItem = getUiItemFromTrans(trans);

	if (pTransItem == m_pTransViewWidg->currentItem())
	{
		//change the current item as this one will be deleted
		QModelIndex oldCrtItemIndex = m_pTransViewWidg->currentIndex();
		if (oldCrtItemIndex.isValid() == false)
			return; //nothing to delete; there is no current transition

		QModelIndex newCrtItemIndex;
		if (oldCrtItemIndex.row() == m_pTransViewWidg->topLevelItemCount() - 1)
		{
			//the item being deleted is the last one so the new current item must
			//be the one on the previous row (second last row)
			newCrtItemIndex = oldCrtItemIndex.sibling(oldCrtItemIndex.row() - 1, oldCrtItemIndex.column());
		}
		else
		{
			//the item being deleted is NOT the last one so select the next row
			newCrtItemIndex = oldCrtItemIndex.sibling(oldCrtItemIndex.row() + 1, oldCrtItemIndex.column());
		}
		m_pTransViewWidg->setCurrentIndex(newCrtItemIndex);
	}
	

	//Remove the item from the transition viewer which corresponds to the transition to be deleted
	m_pTransViewWidg->takeTopLevelItem(m_pTransViewWidg->indexOfTopLevelItem(pTransItem));
	const DesTransition* pUiTrans = &getTransFromUiItem(*pTransItem);

	//Delete the tree item and the transition record in DES as well as the transition object stored by the UI
	delete pTransItem;
	pTransItem = null;

	//The UI keeps its own transition object so delete that too
	delete pUiTrans;
	pUiTrans = null;
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onRemovingSelfTransition(const DesEvent& selfTransEvent)
{
	QTreeWidgetItem* pSelfTransItem = getUiItemFromSelfTrans(selfTransEvent);

	if (pSelfTransItem == m_pSelfTransViewWidg->currentItem())
	{
		//change the current item as this one will be deleted
		QModelIndex oldCrtItemIndex = m_pSelfTransViewWidg->currentIndex();
		if (oldCrtItemIndex.isValid() == false)
			return; //nothing to delete; there is no current self-transition

		QModelIndex newCrtItemIndex;
		if (oldCrtItemIndex.row() == m_pSelfTransViewWidg->topLevelItemCount() - 1)
		{
			//the item being deleted is the last one so the new current item must
			//be the one on the previous row (second last row)
			newCrtItemIndex = oldCrtItemIndex.sibling(oldCrtItemIndex.row() - 1, oldCrtItemIndex.column());
		}
		else
		{
			//the item being deleted is NOT the last one so select the next row
			newCrtItemIndex = oldCrtItemIndex.sibling(oldCrtItemIndex.row() + 1, oldCrtItemIndex.column());
		}
		m_pSelfTransViewWidg->setCurrentIndex(newCrtItemIndex);
	}	

	//Remove the item from the transition viewer which corresponds to the transition to be deleted
	m_pSelfTransViewWidg->takeTopLevelItem(m_pSelfTransViewWidg->indexOfTopLevelItem(pSelfTransItem));

	//Delete the tree item and the transition record in DES as well as the transition object stored by the UI
	delete pSelfTransItem;
	pSelfTransItem = null;
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onStateChanged(const DesState& changedState)
{
	//Update all transitions that contain the changed state either as the "fromState" or the "toState.
	//This has to be done in two steps. First all the Ui Items must be found and then they must be 
	//changed. This is because when we change the "fromState" the index of the item will change since they
	//are displayed alphabetically and thus we'll skip items
	QList<QTreeWidgetItem*> affectedItemList;
	for (int iRow = 0; iRow < m_pTransViewWidg->topLevelItemCount(); iRow++)
	{
		QTreeWidgetItem* pTransUiItem = m_pTransViewWidg->topLevelItem(iRow);
		assert(pTransUiItem != null);

		const DesTransition& crtTrans  = getTransFromUiItem(*pTransUiItem);
		if ((&(crtTrans.fromState()) == &changedState) || (&(crtTrans.toState()) == &changedState))
		{
			affectedItemList.push_back(pTransUiItem);			
		}
	}

	//now that we have all the items we need to update, update them
	for(int iItem = 0; iItem < affectedItemList.size(); iItem++)
	{
		QTreeWidgetItem* pTransUiItem = affectedItemList[iItem];
		const DesTransition& crtTrans  = getTransFromUiItem(*pTransUiItem);
		fillTransUiItem(*pTransUiItem, crtTrans);
	}
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::onEventChanged(const DesEvent& changedEvent)
{
	//update all transitions that use this event
	if (changedEvent.isUsed())
	{
		for (int iRow = 0; iRow < m_pTransViewWidg->topLevelItemCount(); iRow++)
		{
			QTreeWidgetItem* pTransUiItem = m_pTransViewWidg->topLevelItem(iRow);
			assert(pTransUiItem != null);

			const DesTransition& crtTrans  = getTransFromUiItem(*pTransUiItem);
			if (&(crtTrans.event()) == &changedEvent)
			{
				fillTransUiItem(*pTransUiItem, crtTrans);
			}
		}
	}
	
	QTreeWidgetItem* selfTransItem = null;
	if (findSelfTransItem(changedEvent, selfTransItem))
	{
		fillSelfTransUiItem(*selfTransItem, changedEvent);				
	}
}

//_________________________________________________________________________________________________

QTreeWidgetItem* GedTransitionEditorUiPart::createUiItemFromTrans(const DesTransition& trans)
{
	//add the transition information to the transition editor
	QTreeWidgetItem* pTransUiItem = new QTreeWidgetItem();

	//make the item editable
	pTransUiItem->setFlags(pTransUiItem->flags() | Qt::ItemIsEditable);

	//fill the UI item with the transition attributes (from state, event label, etc...)
	fillTransUiItem(*pTransUiItem, trans);

	return pTransUiItem;
}

//_________________________________________________________________________________________________
QTreeWidgetItem* GedTransitionEditorUiPart::createUiItemFromSelfTrans(const DesEvent& selfTransEvent)
{
	//add the transition information to the transition editor
	QTreeWidgetItem* pSelfTransUiItem = new QTreeWidgetItem();

	//make the item not-editable
	pSelfTransUiItem->setFlags(pSelfTransUiItem->flags() & ~Qt::ItemIsEditable);

	//fill the UI item with the transition attributes (event name, event alias, etc...)
	fillSelfTransUiItem(*pSelfTransUiItem, selfTransEvent);

	return pSelfTransUiItem;
}

//_________________________________________________________________________________________________
//Fills the information of the UI item with the given transition information. This means setting
//the text of the from state, event and to state. Also each ui item has a transition object associated with it
//This object needs to be updated as well
void GedTransitionEditorUiPart::fillTransUiItem(QTreeWidgetItem& transUiItem, const DesTransition& trans)
{
	/*It is important to first update the transition object associated with the Ui item BECAUSE any
	  change to the text of the tree control will generate an attempt to change the transition in DES
	  which has already been changed. This will trigger the following infinite loop
	
		Des::changeTransition(...)
			GedTransitionEditorUiPart::onTransitionChanged(...)
				GedTransitionEditorUiPart::fillTransUiItem(...)
					QTreeWidgetItem::setText(...)
						GedTransitionEditorUiPart::onTransItemChanged(...)	<-- this could be triggered directly 
																			by the user changing the item in-place
							Des::changeTransition(...) <<<-- recursive call
				
	  The solution is to ensure that when onTransItemChanged is called getTransFromUitItem will return the
	  already changed transition. This will happen if we first associated the item with the changed transition
	*/
	DesTransition* uiTrans = reinterpret_cast<DesTransition*>(transUiItem.data(0, Qt::UserRole).toULongLong());
	if (uiTrans == null)
	{
		//No transition object has been associated with this item - it must be a new item

		//Save the transition pointer in the item itsel for easy retrieval. Note that we cannot
		//save the received "trans" object as it is only transient. DES doesn't hold a list of
		//transition objects for now just the transition function. For the purpose of UI management of
		//transition it is the Transition Editor Ui Part that holds the list of transition objects. 
		//this pointer cannot be used to modify the trans but only to read things from it
		uiTrans = new DesTransition(trans);
		transUiItem.setData(cTransFromStateColumnIdx, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(uiTrans)));
	}
	else if (*uiTrans != trans)
	{
		//There is indeed a change: the transition associated with this UI item is different 
		*uiTrans = trans;
	}

	//fill the name of the "from state" for this transition
	QString fromStateName      = QString::fromStdWString(trans.fromState().getName());
	if (fromStateName != transUiItem.text(cTransFromStateColumnIdx))
	{
		transUiItem.setText(cTransFromStateColumnIdx, fromStateName);
	}
	
	//fill the name of the transition event label
	QString eventName     = QString::fromStdWString(trans.event().getName());
	if (eventName != transUiItem.text(cTransEventColumnIdx))
	{
		transUiItem.setText(cTransEventColumnIdx, eventName);
	}
	
	//fill the name of the "to state" for this transition
	QString toStateName      = QString::fromStdWString(trans.toState().getName());
	if (toStateName != transUiItem.text(cTransToStateColumnIdx))
	{
		transUiItem.setText(cTransToStateColumnIdx, toStateName);
	}

	QString transTooltip = fromStateName + " ; " + eventName + " ; " + toStateName;

	//set the tooltip for all columns if it changed
	//check if the computed tooltip is different then the one actually present in the UI item
	if (transTooltip != transUiItem.toolTip(cTransFromStateColumnIdx))
	{
		//current item has a different tooltip => update it
		for(int i = 0; i < cTransEditorColumnCount; i++)
		{
			transUiItem.setToolTip(i, transTooltip);
		}
	}
}

//_________________________________________________________________________________________________

void GedTransitionEditorUiPart::fillSelfTransUiItem(QTreeWidgetItem& selfTransUiItem, const DesEvent& selfTransEvent)
{
	//fill the name of the self-transition event label
	QString eventName     = QString::fromStdWString(selfTransEvent.getName());
	if (eventName != selfTransUiItem.text(cSelfTransEventColumnIdx))
	{
		selfTransUiItem.setText(cSelfTransEventColumnIdx, eventName);
	}
	
	//fill the name of the "to state" for this transition
	QString aliasName      = QString::fromStdWString(selfTransEvent.getAlias());
	if (aliasName != selfTransUiItem.text(cSelfTransAliasColumnIdx))
	{
		selfTransUiItem.setText(cSelfTransAliasColumnIdx, aliasName);
	}

	QString selfTransTooltip = "Self-looped event:" + eventName + " ( " + aliasName + " ) ";

	//set the tooltip for all columns if it changed
	//check if the computed tooltip is different then the one actually present in the UI item
	if (selfTransTooltip != selfTransUiItem.toolTip(0))
	{
		//current item has a different tooltip => update it
		for(int i = 0; i < cSelfTransEditorColumnCount; i++)
		{
			selfTransUiItem.setToolTip(i, selfTransTooltip);
		}
	}

	//Save a pointer to the self-looped event in the Ui item for easy access (e.g. when the item is deleted)
	selfTransUiItem.setData(cSelfTransEventColumnIdx, Qt::UserRole, 
		                    QVariant(reinterpret_cast<unsigned long long>(&selfTransEvent)));
}

//_________________________________________________________________________________________________

const DesTransition& GedTransitionEditorUiPart::getTransFromUiItem(QTreeWidgetItem& transUiItem)
{
	const DesTransition* pTransition = reinterpret_cast<DesTransition*>(transUiItem.data(0, Qt::UserRole).toULongLong());
	assert(pTransition != null);

	return *pTransition;
}

//_________________________________________________________________________________________________

const DesEvent& GedTransitionEditorUiPart::getSelfTransEventFromUiItem(QTreeWidgetItem& selfTransUiItem)
{
	const DesEvent* pSelfTransEvent = reinterpret_cast<DesEvent*>(selfTransUiItem.data(0, Qt::UserRole).toULongLong());
	assert(pSelfTransEvent != null);

	return *pSelfTransEvent;
}

//_________________________________________________________________________________________________

QTreeWidgetItem* GedTransitionEditorUiPart::getUiItemFromSelfTrans(const DesEvent& selfTransEvent)
{
	for (int iRow = 0; iRow < m_pSelfTransViewWidg->topLevelItemCount(); iRow++)
	{
		QTreeWidgetItem* pTransUiItem = m_pSelfTransViewWidg->topLevelItem(iRow);
		assert(pTransUiItem != null);

		const DesEvent& crtSelfTrans  = getSelfTransEventFromUiItem(*pTransUiItem);
		if (&crtSelfTrans == &selfTransEvent || crtSelfTrans == selfTransEvent)
		{
			return pTransUiItem;
		}
	}

	//if we got here this transition was not displayed. This should not happen
	throw EX("Self-Transition not found in the transition view widget")
}

//_________________________________________________________________________________________________

bool GedTransitionEditorUiPart::findSelfTransItem(const DesEvent& selfTransEvent, QTreeWidgetItem*& out_foundItem)
{
	for (int iRow = 0; iRow < m_pSelfTransViewWidg->topLevelItemCount(); iRow++)
	{
		QTreeWidgetItem* pTransUiItem = m_pSelfTransViewWidg->topLevelItem(iRow);
		assert(pTransUiItem != null);

		const DesEvent& crtSelfTrans  = getSelfTransEventFromUiItem(*pTransUiItem);
		if (&crtSelfTrans == &selfTransEvent)
		{
			out_foundItem = pTransUiItem;
			return true;
		}
	}

	return false;
}

//_________________________________________________________________________________________________

QTreeWidgetItem* GedTransitionEditorUiPart::getUiItemFromTrans(const DesTransition& trans)
{
	for (int iRow = 0; iRow < m_pTransViewWidg->topLevelItemCount(); iRow++)
	{
		QTreeWidgetItem* pTransUiItem = m_pTransViewWidg->topLevelItem(iRow);
		assert(pTransUiItem != null);

		const DesTransition& crtTrans  = getTransFromUiItem(*pTransUiItem);
		if (&crtTrans == &trans || crtTrans == trans)
		{
			return pTransUiItem;
		}
	}

	//if we got here this transition was not displayed. This should not happen
	throw EX("Transition not found in the transition view widget")
}

} //end of namespace DESpot
