/*	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 <QHeaderView>

#include "ProjEventSetWidget.h"
#include "DesProject.h"
#include "DesInterface.h"
#include "DesSubsystem.h"

namespace DESpot
{

const QString ProjEventSetWidget::cSrcDesColName = "Source DES";
const int  ProjEventSetWidget::cSrcDesColIdx = 4;

const QString ProjEventSetWidget::cSourceDesTooltip = "\n\nUsed in the following DES:\n";

const QString ProjEventSetWidget::cHighLevelEventsText = "High Level Events";
const QString ProjEventSetWidget::cInterfLevelEventsText = "Interface Events";
const QString ProjEventSetWidget::cAnswerEventsText = "Answer Events";
const QString ProjEventSetWidget::cRequestLevelEventsText = "Request Events";
const QString ProjEventSetWidget::cLowDataEventsText = "Low Data Events";
const QString ProjEventSetWidget::cLowLevelEventsText = "Low Level Events";

ProjEventSetWidget::ProjEventSetWidget(QWidget* parent) : EventViewerWidget(parent),
			m_interfEventRoot(null)
{
}

//_________________________________________________________________________________________________

ProjEventSetWidget::~ProjEventSetWidget(void)
{
}

//_________________________________________________________________________________________________

void ProjEventSetWidget::loadEvents(DesProject* project)
{
	//make sure there is nothing loaded in the control
	clear();
	
	//First add the root items where the event items will be placed
	addRootItems();

	DesProject::EventIteratorPtr eventIt = project->createProjEventIterator();
	for(eventIt->first(); eventIt->isDone() == false; eventIt->next())
	{
		const ProjectEvent& event = eventIt->currentItem();

    	
		QTreeWidgetItem* pUiItem = createUiItemFromEvent(event);	

		if (m_typeHierMode)
		{
			QTreeWidgetItem* rootItem = findRootFor(&event);
			rootItem->addChild(pUiItem);
		}
		else
		{
			addTopLevelItem(pUiItem);
		}
        
	}

	//update the size of the columns to match the contents
	resizeHeaders(geometry().width());
}

//_________________________________________________________________________________________________

QList<const ProjectEvent*> ProjEventSetWidget::getSelectedEvents()
{
	QList<const ProjectEvent*> selEvents;
	
	QList<QTreeWidgetItem*> selItems = selectedItems();
	for(int iItem = 0; iItem < selItems.size(); iItem++)
	{
		QTreeWidgetItem& crtItem = *selItems.at(iItem);
		if (isEventUiItem(crtItem))
		{
			selEvents.append(static_cast<const ProjectEvent*>(&getEventFromUiItem(crtItem)));
		}
	}

	return selEvents;
}

//_________________________________________________________________________________________________

//If typeHierMode = true, the events are going to be shown in a hierarchy based on their type according to 
//the following the following structure
//
//	High Level Events
//	Interface Events
//		Request Events
//			Interface 1
//			Interface 2
//			...
//		Answer Events
//			Interface 1
//			Interface 2
//			...
//		Low Data Events
//			Interface 1
//			Interface 2
//			...
//	Low Level Events
//		Low Subsys 1
//		Low Subsys 2
//	    ...
void ProjEventSetWidget::initWidget(bool typeHierMode /*= false*/)
{
	EventViewerWidget::initWidget();

	m_typeHierMode = typeHierMode;

	//set properties
	setAllowEdit(false);
	setSelectionMode(QAbstractItemView::ExtendedSelection);

}

//_________________________________________________________________________________________________

int ProjEventSetWidget::getColumnCount()
{
	//We need one more column for the list of sources for the event
	return EventViewerWidget::getColumnCount() + 1;
}

//_________________________________________________________________________________________________

void ProjEventSetWidget::getHeaders(QStringList& out_headerList)
{
	EventViewerWidget::getHeaders(out_headerList);

	//add the Source DES column
	out_headerList << cSrcDesColName; 
}

//_________________________________________________________________________________________________

void ProjEventSetWidget::fillEventUiItem(QTreeWidgetItem& eventUiItem, const DesEvent& desEvent)
{
	//fill the Source DES column
	QString eventSourceList;
	const ProjectEvent& projEvent = static_cast<const ProjectEvent&>(desEvent);
	ProjectEvent::SourceIteratorPtr srcIt = projEvent.createSourceIterator();
	for(srcIt->first(); srcIt->notDone(); srcIt->next())
	{
		const ProjectEvent::Source& crtSource = srcIt->currentItem();
		
		if (eventSourceList.isEmpty() == false)
		{
			//there is already something there; add a separator before adding another source DES
			eventSourceList += ", ";
		}
		
		eventSourceList += QString::fromStdWString(crtSource.des->getName());
	}
	if (eventSourceList != eventUiItem.text(cSrcDesColIdx))
	{
		eventUiItem.setText(cSrcDesColIdx, eventSourceList);
	}

	//check if the event is consistent and if not mark it in red
	std::wstring reason;
	bool validEvent = projEvent.isValid(&reason);
	if (!validEvent)
	{
		eventUiItem.setData(cSrcDesColIdx, Qt::UserRole, QString::fromStdWString(reason));
	}

	//fill the rest of the item; this will triger a call to fillTooltip as well
	EventViewerWidget::fillEventUiItem(eventUiItem, desEvent);

	//check if the event is consistent and if not mark it in red
	if (!validEvent )
	{
		for(int i = 0; i < getColumnCount(); i++)
		{
			eventUiItem.setTextColor(i, Qt::red);
		}
	}
}

//_________________________________________________________________________________________________

QString ProjEventSetWidget::composeTooltip(QTreeWidgetItem& eventUiItem)
{
	QString tooltip = EventViewerWidget::composeTooltip(eventUiItem);
	tooltip += cSourceDesTooltip;

	QStringList sourceList = eventUiItem.text(cSrcDesColIdx).split(", ", QString::SkipEmptyParts);
	for(int iSrc = 0; iSrc < sourceList.size(); iSrc++)
	{
		tooltip += "      " + sourceList[iSrc] + "\n";
	}

	QString invalidReason = eventUiItem.data(cSrcDesColIdx, Qt::UserRole).toString();
	if (invalidReason.isEmpty() == false)
	{
		tooltip += "\n" + invalidReason;
	}
	return tooltip;
}

//_________________________________________________________________________________________________

void ProjEventSetWidget::resizeHeaders(int /*eventViewWidgWidth*/)
{
	for(int iCol = 0; iCol < getColumnCount() - 1; iCol++)
	{
		resizeColumnToContents(iCol);
	}

	header()->resizeSection(cSrcDesColIdx, header()->sectionSizeHint(cSrcDesColIdx));
}


//_________________________________________________________________________________________________

void ProjEventSetWidget::addRootItems()
{
	if (m_typeHierMode)
	{
		//create the type root items
		m_typeEventItemList[eHighLevelEvent] = createRootItem(cHighLevelEventsText);
		
		m_interfEventRoot = createRootItem(cInterfLevelEventsText);
		m_typeEventItemList[eAnswerEvent] = createRootItem(cAnswerEventsText, m_interfEventRoot);
		m_typeEventItemList[eRequestEvent] = createRootItem(cRequestLevelEventsText, m_interfEventRoot);
		m_typeEventItemList[eLDataEvent] = createRootItem(cLowDataEventsText, m_interfEventRoot);
		
		m_typeEventItemList[eLowLevelEvent] = createRootItem(cLowLevelEventsText);
	}
}

//_________________________________________________________________________________________________

QTreeWidgetItem* ProjEventSetWidget::createRootItem(const QString& text, QTreeWidgetItem* parent)
{
	QTreeWidgetItem* item = new QTreeWidgetItem();
	
	item->setText(cEventNameColIdx, text);
	
	//make it bold
	QFont font = item->font(cEventNameColIdx);
	font.setBold(true);
	item->setFont(cEventNameColIdx, font);


	//add the item to the widget
	if (parent)
	{
		parent->addChild(item);
	}
	else
	{
		addTopLevelItem(item);
	}

	expandItem(item);

	return item;
}

//_________________________________________________________________________________________________

QTreeWidgetItem* ProjEventSetWidget::findRootFor(const ProjectEvent* event)
{
	switch(event->getType())
	{
		case eAnswerEvent:
		case eRequestEvent:
		case eLDataEvent:
			return findRootFor(static_cast<const InterfaceEvent*>(event));

		case eHighLevelEvent:
		case eLowLevelEvent:
			return findRootFor(static_cast<const SubsystemEvent*>(event));

		default:
			assert(false);
			throw EX("Invalid event type. Cannot set type of event.")
	}
}

//_________________________________________________________________________________________________

QTreeWidgetItem* ProjEventSetWidget::findRootFor(const InterfaceEvent* interfEvent)
{
	if (interfEvent->hasOwner())
	{
		const DesInterface& interf = interfEvent->owner();
		
		for(int iItem = 0; iItem < m_typeEventItemList[interfEvent->getType()]->childCount(); iItem++)
		{
			QTreeWidgetItem* crtItem = m_typeEventItemList[interfEvent->getType()]->child(iItem);
			if (crtItem->text(cEventNameColIdx).toStdWString() == interf.getName())
			{
				//found the root item corresponding to the interface that owns this event
				return crtItem;
			}
		}

		//if we got here it means there is not root for this interface event which means
		//this is the first event from this owner. Thus we need to add an item for this interface
		return createRootItem(QString::fromStdWString(interf.getName()), m_typeEventItemList[interfEvent->getType()]);
	}
	else
	{
		//This is an orphaned interface event. This means that it is used in subsystems but not defined
		//in any interface in the project. The orphaned events are displayed at the root of all interface
		//events signifying that they do not belong to any interface
		return m_interfEventRoot;
	}
}

//_________________________________________________________________________________________________

QTreeWidgetItem* ProjEventSetWidget::findRootFor(const SubsystemEvent* subsysEvent)
{
	const DesSubsystem& subsys = subsysEvent->owner();

	for(int iItem = 0; iItem < m_typeEventItemList[subsysEvent->getType()]->childCount(); iItem++)
	{
		QTreeWidgetItem* crtItem = m_typeEventItemList[subsysEvent->getType()]->child(iItem);
		if (crtItem->text(cEventNameColIdx).toStdWString() == subsys.getName())
		{
			//found the root item corresponding to the interface that owns this event
			return crtItem;
		}
	}

	//if we got here it means there is not root for this interface event which means
	//this is the first event from this owner. Thus we need to add an item for this interface
	return createRootItem(QString::fromStdWString(subsys.getName()), m_typeEventItemList[subsysEvent->getType()]);
}

} //end of namespace DESpot
