/*	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 "CommonDefinitions.h"
#include "GedStateQuickLookUiPart.h"
#include "Des.h"
#include "DesState.h"


namespace DESpot
{

GedStateQuickLookUiPart::GedStateQuickLookUiPart(Ui::GedDesEditorUI* pUiContainer, Des* pDes):
			m_stateRootItem(null),
			m_enterEventsItem(null),
			m_loopEventsItem(null), 
			m_exitEventsItem(null),
                        m_pCrtState(null),
                        m_pDes(pDes)
{
	if (pUiContainer == null)
		throw EX("Invalid (null) UI Container. Cannot create the state quick look UI part")

	//subscribe to des notifications
	m_desListnerId = m_pDes->subscribe(this);

	//setup widget references
	m_pStateQuickLookGroupBox = pUiContainer->m_stateQuickLookGroupBox;
	m_pStateQuickLookWidg	  = pUiContainer->m_stateQuickLookWidg;

	connect(m_pStateQuickLookWidg, SIGNAL(onWidthChanged(int)), 
		    this, SLOT(resizeQuickLookHeaders(int)));

	//setup event filters
	m_pStateQuickLookGroupBox->installEventFilter(this);

	//initialize widgets
	initStateQuickLookWidget();
}

//_________________________________________________________________________________________________

GedStateQuickLookUiPart::~GedStateQuickLookUiPart(void)
{
	m_pDes->unsubscribe(m_desListnerId);
	
	//no need to release memory - these are just references
	m_pStateQuickLookGroupBox = null;
	m_pStateQuickLookWidg = null;
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::showQuickLookFor(const DesState& state)
{
	if (m_pCrtState != &state)
	{
		m_pCrtState = &state;
		updateStateQuickLookWidget();
	}
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::initStateQuickLookWidget()
{
	m_pStateQuickLookWidg->setColumnCount(1);
	m_pStateQuickLookWidg->header()->setStretchLastSection(true);
	m_pStateQuickLookWidg->header()->hide();
	m_pStateQuickLookWidg->setShareFocus();

	//create a top-level widget for the current state
	m_stateRootItem = new QTreeWidgetItem(m_pStateQuickLookWidg);
	QFont font = m_stateRootItem->font(0);
	font.setItalic(true);
	m_stateRootItem->setFont(0, font);
	m_stateRootItem->setText(0, STR_NO_STATE_SELECTED);
	m_pStateQuickLookWidg->expandItem(m_stateRootItem);

	
	//create the UI items for that categorized the state's transitions: enter events, self-events (loops), exit events
	m_enterEventsItem = new QTreeWidgetItem(m_stateRootItem);
	m_enterEventsItem->setText(0, "Enter Events");
	m_enterEventsItem->setTextColor(0, Qt::darkGray);
	m_pStateQuickLookWidg->expandItem(m_enterEventsItem);

	m_loopEventsItem = new QTreeWidgetItem(m_stateRootItem);
	m_loopEventsItem->setText(0, "Looped Events");
	m_loopEventsItem->setTextColor(0, Qt::darkGray);
	m_pStateQuickLookWidg->expandItem(m_loopEventsItem);

	m_exitEventsItem = new QTreeWidgetItem(m_stateRootItem);
	m_exitEventsItem->setText(0, "Exit Events");
	m_exitEventsItem->setTextColor(0, Qt::darkGray);
	m_pStateQuickLookWidg->expandItem(m_exitEventsItem);
}

//_________________________________________________________________________________________________
//Updates the widget with the transitions entering and existing m_state. The transitions are grouped
//into 3 categories: entering, loops, exiting
void GedStateQuickLookUiPart::updateStateQuickLookWidget()
{
	//first clear the widget
	clearStateQuickLookWidget();

	if (m_pCrtState == null)
	{
		//assert(false);
		return;
	}

	//update the name of the state for which we are showing the quick look
	QFont font = m_stateRootItem->font(0);
	font.setBold(true);
	font.setItalic(false);
	m_stateRootItem->setFont(0, font);
	QString lookStateName = QString::fromStdWString(m_pCrtState->getName());
	m_stateRootItem->setText(0, lookStateName);

	//loop through the enter transitions by using an inverse state transition iterator. Make sure
	//the global events are not considered. Note: that an inverse iterator is using the inverse transition function 
	//so the "fromState" of the transition is m_pCrtState while in reality m_pCrtState is the "toState". 
	Des::TransIteratorPtr enterTransIt = m_pDes->createStateInvTransIterator(*m_pCrtState, true);
	for(enterTransIt->first(); enterTransIt->isDone() == false; enterTransIt->next())
	{
		const DesTransition& enterTrans = enterTransIt->currentItem();
		
		QString enterTransSource = QString::fromStdWString(enterTrans.toState().getName());
		QString eventName = QString::fromStdWString(enterTrans.event().getName());
		QString eventAlias = QString::fromStdWString(enterTrans.event().getAlias());
		
		if (enterTransSource != lookStateName)
		{
			//This is an enter-transitions that is not a loop since the source and destination are different
			QString eventItemText = eventName + " <- " + enterTransSource;
			QString tooltip = enterTransSource + " -> " + eventName + " -> " + lookStateName;

			QTreeWidgetItem* eventItem = new QTreeWidgetItem(m_enterEventsItem);
			eventItem->setText(0, eventItemText);
			eventItem->setToolTip(0, tooltip);
			
			//associate a transition object with the new ui item so we can identify it later
			DesTransition invEnterTrans;
			addTransToUiItem(*eventItem, enterTrans.inverse(invEnterTrans));
		}
	}
	//now that all enter events have been added set the tool tip of hte enter events item to specify their number
	m_enterEventsItem->setToolTip(0, "Enter Events (" + QVariant(m_enterEventsItem->childCount()).toString() + ")");
	
	//loop through the exit transitions making sure to consider the global loops
	Des::TransIteratorPtr exitTransIt = m_pDes->createStateTransIterator(*m_pCrtState);
	for(exitTransIt->first(); exitTransIt->isDone() == false; exitTransIt->next())
	{
		const DesTransition& exitTrans = exitTransIt->currentItem();
		
		QString exitTransDest = QString::fromStdWString(exitTrans.toState().getName());
		QString eventName = QString::fromStdWString(exitTrans.event().getName());
		QString eventAlias = QString::fromStdWString(exitTrans.event().getAlias());
		
		if (lookStateName != exitTransDest)
		{
			//This is an exit-transitions that is not a loop since the source and destination are different
			QString eventItemText = eventName + " -> " + exitTransDest;
			QString tooltip = lookStateName + " -> " + eventName + " -> " + exitTransDest;

			QTreeWidgetItem* eventItem = new QTreeWidgetItem(m_exitEventsItem);
			eventItem->setText(0, eventItemText);
			eventItem->setToolTip(0, tooltip);

			//associate a transition object with the new ui item so we can identify it later
			addTransToUiItem(*eventItem, exitTrans);
		}
		else
		{
			//this is a loop so add the item to the loopEventsItem
			QString eventItemText = eventName + " ( " + eventAlias + " )";
			QString tooltip = lookStateName + " <-> " + eventName + " ( " + eventAlias + " )";

			QTreeWidgetItem* eventItem = new QTreeWidgetItem(m_loopEventsItem);
			eventItem->setText(0, eventItemText);
			eventItem->setToolTip(0, tooltip);

			//associate a transition object with the new ui item so we can identify it later
			const DesTransition selfLoop(*m_pCrtState, exitTrans.event(), *m_pCrtState);
			addTransToUiItem(*eventItem, selfLoop);
		}
	}
	//now that all loop and exit events have been added set the tool tip of the exit and looped events 
	//items to specify their number
	m_loopEventsItem->setToolTip(0, "Looped Events (" + QVariant(m_loopEventsItem->childCount()).toString() + ")");
	m_exitEventsItem->setToolTip(0, "Exit Events (" + QVariant(m_exitEventsItem->childCount()).toString() + ")");

	//also specify the tooltip of the root item
	m_stateRootItem->setToolTip(0, lookStateName + QString (" - " + 
									   QVariant(m_enterEventsItem->childCount()).toString() + " enter events, "+ 
									   QVariant(m_loopEventsItem->childCount()).toString() + " looped events and " +
									   QVariant(m_exitEventsItem->childCount()).toString() + " exit events"));
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::clearStateQuickLookWidget()
{
	if (m_pStateQuickLookWidg->topLevelItemCount() == 0)
	{
		//The widget is empty; this is valid when the widget is created
		return;
	}
	
	//reset the root item
	assert(m_pStateQuickLookWidg->topLevelItemCount() == 1);
	QFont font = m_stateRootItem->font(0);
	font.setBold(false);
	font.setItalic(true);
	m_stateRootItem->setFont(0, font);
	m_stateRootItem->setText(0, STR_NO_STATE_SELECTED);
	
	for(int iEventCat = 0; iEventCat < 3; iEventCat++)
	{
		QTreeWidgetItem* eventCatItem = m_stateRootItem->child(iEventCat);

		//iterate through the items in the category and remove them
		QList<QTreeWidgetItem*> eventItemList = eventCatItem->takeChildren();
		for(QList<QTreeWidgetItem*>::iterator eventIt = eventItemList.begin();
			eventIt != eventItemList.end(); eventIt++)
		{
			QTreeWidgetItem* eventItem = *eventIt;
			
			//delete the transition object associated with the UI item
			delTransFromUiItem(*eventItem);

			delete eventItem;
			eventItem = null;
		}
	}
}

//_________________________________________________________________________________________________

bool GedStateQuickLookUiPart::eventFilter(QObject* target, QEvent* event)
{	
	if (target == m_pStateQuickLookGroupBox && event->type() == QEvent::Resize)
	{
		QResizeEvent* resizeEvent = dynamic_cast<QResizeEvent*>(event);
		m_pStateQuickLookWidg->resize(resizeEvent->size() - QSize(18,28));
	}

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

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::resizeQuickLookHeaders(int quickLookWidgWidth)
{
	m_pStateQuickLookWidg->header()->resizeSection(0, quickLookWidgWidth);
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::onEventChanged(const DesEvent& /*changedEvent*/)
{
	updateStateQuickLookWidget();	
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::onTransitionRemoved(const DesTransition& removedTrans)
{
	findAndRemoveUiItem(removedTrans);
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::onSelfTransitionRemoved(const DesEvent& selfTransEvent)
{
	DesTransition selfTrans(*m_pCrtState, selfTransEvent, *m_pCrtState);
	findAndRemoveUiItem(selfTrans);
}

//_________________________________________________________________________________________________

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

	return *pTransition;
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::addTransToUiItem(QTreeWidgetItem& transUiItem, const DesTransition& trans)
{
	DesTransition* uiTrans = new DesTransition(trans);
	transUiItem.setData(0, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(uiTrans)));
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::delTransFromUiItem(QTreeWidgetItem& transUiItem)
{
	DesTransition* uiTrans = reinterpret_cast<DesTransition*>(transUiItem.data(0, Qt::UserRole).toULongLong());
	delete uiTrans;
	uiTrans = null;
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::findAndRemoveUiItem(const DesTransition& trans)
{
	for(int iEventCat = 0; iEventCat < 3; iEventCat++)
	{
		QTreeWidgetItem* eventCatItem = m_stateRootItem->child(iEventCat);

		//iterate through the items in the category and remove them
		for(int iTrans = eventCatItem->childCount() - 1; iTrans >= 0 ; iTrans--)
		{
			QTreeWidgetItem* transItem = eventCatItem->child(iTrans);
			const DesTransition& uiTrans = getTransFromUiItem(*transItem);
			
			if (uiTrans == trans)
			{
				//remove the item from the widget
				eventCatItem->takeChild(iTrans);

				//delete the transition object associated with the UI item
				delTransFromUiItem(*transItem);

				delete transItem;
				transItem = null;
			}
		}
	}
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::onChangedCurrentState(const DesState* pNewCurrentState, const DesState* /*pOldCurrentState*/)
{
	if (pNewCurrentState)
	{
		//show the details of the current state in the State Quick Look part
		showQuickLookFor(*pNewCurrentState);
	}
	else
	{
		clearStateQuickLookWidget();
	}
}

//_________________________________________________________________________________________________

void GedStateQuickLookUiPart::onCurrentStateChanged(const DesState& /*crtState*/)
{
	updateStateQuickLookWidget();	
}

} //end of namespace DESpot
