/*	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 <QTreeWidgetItem>
#include "DespotTreeWidget.h"
#include "ProjStructureUiPart.h"
#include "CommonDefinitions.h"
#include "DesFlatProject.h"
#include "Des.h"
#include "ui_FlatProjectEditor.h"
#include "ui_HierProjectEditor.h"

namespace DESpot
{

template void ProjStructureUiPart::initWidgets<Ui::FlatProjectEditorUI>(Ui::FlatProjectEditorUI*);
template void ProjStructureUiPart::initWidgets<Ui::HierProjectEditorUI>(Ui::HierProjectEditorUI*);

const int ProjStructureUiPart::cFlatProjStructColCount = 1;

ProjStructureUiPart::ProjStructureUiPart(DesProject* pProject):
	m_fViewRootItem(null),
        m_flatStructSupUiItem(null),
        m_flatStructPlantUiItem(null),
        m_pProject(pProject),
        m_pCrtDes(null)

{
	//subscribe to the project's notifications
	m_projListnerId = m_pProject->subscribe(this);
}

//_________________________________________________________________________________________________

ProjStructureUiPart::~ProjStructureUiPart(void)
{
	m_pProject->unsubscribe(m_projListnerId);
}

//_________________________________________________________________________________________________

//returns the DES type of the current item in the out parameter. If it is a supervisor
//DES or it is the root of all supervisor DESs then it will
//return eSupervisorDes. If it is a plant or the root of all plant DESs
//it will return ePlantDes. If the type cannot be decided the method returns false
bool ProjStructureUiPart::getCurrentDesLevel(DesLevel& out_desLevel)
{
	QTreeWidgetItem* pCrtItem = m_fViewProjStructWidg->currentItem();
	
	if ((pCrtItem == m_flatStructSupUiItem) || (pCrtItem->parent() == m_flatStructSupUiItem))
	{
		out_desLevel = eSupervisorDes;
		return true;
	}
	else if ((pCrtItem == m_flatStructPlantUiItem) || (pCrtItem->parent() == m_flatStructPlantUiItem))
	{
		out_desLevel = ePlantDes;
		return true;
	}
	else 
	{
		return false;
	}
}

//_________________________________________________________________________________________________

QMenu& ProjStructureUiPart::accessContextMenu()
{
	return m_projStructCtxtMenu;
}


//_________________________________________________________________________________________________

template<class T>
void ProjStructureUiPart::initWidgets(T* pUiContainer)
{
	m_fViewProjStructWidg = pUiContainer->m_fViewProjStructWidg;
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::setupConnections()
{
	connect(m_fViewProjStructWidg, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), 
			this,					  SLOT(onChangedCurrentFlatViewItem(QTreeWidgetItem*, QTreeWidgetItem*)));

	connect(m_fViewProjStructWidg, SIGNAL(onWidthChanged(int)), 
		    this, SLOT(resizeProjFlatStructHeaders(int)));

	connect(m_fViewProjStructWidg, SIGNAL(itemActivated(QTreeWidgetItem*, int)),
			this, SLOT(onFlatStructItemActivated(QTreeWidgetItem*, int)));

	connect(m_fViewProjStructWidg, SIGNAL(onCurrentItemDeleted()),
			this, SLOT(onFlatStructItemDeleted()));
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::setupProjectStructWidget()
{
	//setup up the columns
	m_fViewProjStructWidg->setColumnCount(cFlatProjStructColCount);
	
	//set the captions of each column
	QStringList headerCaption;
	headerCaption << STR_PROJ_STRUCT_HEADER;
	m_fViewProjStructWidg->setHeaderLabels(headerCaption);

	//insert default nodes, the root project node and the
	//the nodes for plant and supervisor DESs
	m_fViewRootItem = new QTreeWidgetItem(m_fViewProjStructWidg);
	QString projName = QString::fromStdWString(m_pProject->getName());
	m_fViewRootItem->setText(0, projName);
	m_fViewRootItem->setToolTip(0, projName);
	m_fViewProjStructWidg->expandItem(m_fViewRootItem);

	m_flatStructSupUiItem = new QTreeWidgetItem(m_fViewRootItem);
	m_flatStructSupUiItem->setText(0, STR_PROJ_STRUCT_SUP_ITEM);
	m_flatStructSupUiItem->setToolTip(0, STR_PROJ_STRUCT_SUP_ITEM_TOOLTIP(m_pProject->getSupDesCount()));
	m_fViewProjStructWidg->expandItem(m_flatStructSupUiItem);

	m_flatStructPlantUiItem = new QTreeWidgetItem(m_fViewRootItem);
	m_flatStructPlantUiItem->setText(0, STR_PROJ_STRUCT_PLANT_ITEM);
	m_flatStructPlantUiItem->setToolTip(0, STR_PROJ_STRUCT_PLANT_ITEM_TOOLTIP(m_pProject->getPlantDesCount()));
	m_fViewProjStructWidg->expandItem(m_flatStructPlantUiItem);

	//add the context menu
	m_fViewProjStructWidg->setContextMenu(m_projStructCtxtMenu);

	m_fViewProjStructWidg->setCurrentItem(m_fViewRootItem);
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::addSupDesToUiItem(QTreeWidgetItem* item, DesProject::DesIteratorPtr& desIt)
{
	//add DES items to the given container item
	for(desIt->first(); desIt->isDone() == false; desIt->next())
	{
		const Des& des = desIt->currentItem();

		//create a new item and add it to the parent
		QString itemText = QString::fromStdWString(des.getName());
		QString desFileName = QString::fromStdWString(des.getFileName());
		QString itemTooltip =  itemText + " - " + desFileName;
		
		//add the newly created item to the proper parent depending on the type (sup or plant) and
		//update the tooltip of the parent item with the number of DESs
		createFlatViewDesItem(itemText,itemTooltip, item, &des); 
	}

	item->setToolTip(0, STR_PROJ_STRUCT_SUP_ITEM_TOOLTIP(item->childCount()));
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::addPlantDesToUiItem(QTreeWidgetItem* item, DesProject::DesIteratorPtr& desIt)
{
	//add DES items to the given container item
	for(desIt->first(); desIt->isDone() == false; desIt->next())
	{
		const Des& des = desIt->currentItem();

		//create a new item and add it to the parent
		QString itemText = QString::fromStdWString(des.getName());
		QString desFileName = QString::fromStdWString(des.getFileName());
		QString itemTooltip =  itemText + " - " + desFileName;
		
		//add the newly created item to the proper parent depending on the type (sup or plant) and
		//update the tooltip of the parent item with the number of DESs
		/*QTreeWidgetItem* desItem = */createFlatViewDesItem(itemText,itemTooltip, item, &des); 					
	}

	item->setToolTip(0, STR_PROJ_STRUCT_PLANT_ITEM_TOOLTIP(item->childCount()));
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::resizeProjFlatStructHeaders(int projStructWidgWidth)
{
	m_fViewProjStructWidg->header()->resizeSection(0, projStructWidgWidth);
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::onProjectNameChanged(const std::wstring& newName, const std::wstring& /*oldName*/)
{
	m_fViewRootItem->setText(0, QString::fromStdWString(newName));
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::onDesAdded(const Des& des, DesLevel desLevel)
{
	//create a new item and add it to the parent
	QString itemText = QString::fromStdWString(des.getName());
	QString desFileName = QString::fromStdWString(des.getFileName());
	QString itemTooltip =  itemText + " - " + desFileName;
	
	//add the newly created item to the proper parent depending on the type (sup or plant) and
	//update the tooltip of the parent item with the number of DESs
	QTreeWidgetItem* desItem = null;
	switch(desLevel)
	{
		case eSupervisorDes:
		{
			desItem = createFlatViewDesItem(itemText,itemTooltip, m_flatStructSupUiItem, &des); 
			m_flatStructSupUiItem->setToolTip(0, STR_PROJ_STRUCT_SUP_ITEM_TOOLTIP(m_pProject->getSupDesCount()));
			break;
		}

		case ePlantDes:
		{
			desItem = createFlatViewDesItem(itemText,itemTooltip, m_flatStructPlantUiItem, &des); 			
			m_flatStructPlantUiItem->setToolTip(0, STR_PROJ_STRUCT_PLANT_ITEM_TOOLTIP(m_pProject->getPlantDesCount()));
			break;
		}

		default:
			assert(false);
	}	

	m_fViewProjStructWidg->setCurrentItem(desItem);
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::onRemovingDes(const Des& des, DesLevel /*desLevel*/)
{
	//find and remove the UI Item corresponding to the DES being deleted
	QTreeWidgetItem* desItem = &getFViewItemFrom(des);
	removeDesItem(des, desItem, m_fViewProjStructWidg); 

	//reset the current item if the des being deleted is the current one
	if (m_pCrtDes == &des)
	{
		assert(m_pCrtDes != &des); //the current des should have changed
		m_pCrtDes = null;
	}  

}

//_________________________________________________________________________________________________

void ProjStructureUiPart::onDesNameChanged(const Des& des, const std::wstring& /*oldName*/)
{
	QTreeWidgetItem& desItem = getFViewItemFrom(des);
	fillFlatViewDesItem(desItem, des);
}

//_________________________________________________________________________________________________

QTreeWidgetItem* ProjStructureUiPart::createFlatViewDesItem(const QString& text, const QString& tooltip, 
										  				QTreeWidgetItem* parent /*= null*/,
														const void* des /*= null*/)
{
	//create a new item and add it to the parent
	QTreeWidgetItem* desItem = new QTreeWidgetItem(parent);
		
	desItem->setText(0, text);
	desItem->setToolTip(0, tooltip);
	
	//save the DES pointer in the item itsel for easy retrieval.
	//this pointer cannot be used to modify the DES but only to read things from it
	desItem->setData(0, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(des)));

	return desItem;
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::fillFlatViewDesItem(QTreeWidgetItem& desItem, const Des& des)
{
	QString itemText = QString::fromStdWString(des.getName());
	QString desFileName = QString::fromStdWString(des.getFileName());
	QString itemTooltip =  itemText + " - " + desFileName;

	desItem.setText(0, itemText);
	desItem.setToolTip(0, itemTooltip);
	
	//save the DES pointer in the item itsel for easy retrieval.
	//this pointer cannot be used to modify the DES but only to read things from it
	desItem.setData(0, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(&des)));
}

//_________________________________________________________________________________________________

Des& ProjStructureUiPart::getDesFromFItem(QTreeWidgetItem& desUiItem)
{
	Des* pDes = reinterpret_cast<Des*>(desUiItem.data(0, Qt::UserRole).toULongLong());
	if (pDes == null)
	{
		assert(pDes != null);
		throw EX("Cannot return DES. Given UI item doesn't represent a DES")
	}
	
	return *pDes;
}

//_________________________________________________________________________________________________

//returns true if the given wiget item represents a project DES
bool ProjStructureUiPart::isDesFlatViewItem(QTreeWidgetItem& desUiItem)
{
	if ((&desUiItem == m_fViewProjStructWidg->topLevelItem(0)) ||
		(&desUiItem == m_flatStructSupUiItem) || (&desUiItem == m_flatStructPlantUiItem))
	{
		return false;
	}

	return true;
}

//_________________________________________________________________________________________________

QTreeWidgetItem& ProjStructureUiPart::getFViewItemFrom(const Des& des)
{
	QTreeWidgetItem* foundItem = null;
	
	//search for the DES in the supervisor items
	if (searchDesInFViewItem(*m_flatStructSupUiItem, des, foundItem))
	{
		return *foundItem;
	}

	if (searchDesInFViewItem(*m_flatStructPlantUiItem, des, foundItem))
	{
		return *foundItem;
	}

	//if we got here it means it was not in the list of supervior items nor in the plant ones.
	throw EX("Cannot locate given DES")
}

//_________________________________________________________________________________________________

bool ProjStructureUiPart::searchDesInFViewItem(QTreeWidgetItem& desContainerItem, const Des& des, QTreeWidgetItem*& out_foundItem)
{
	for(int iItem = 0; iItem < desContainerItem.childCount(); iItem++)
	{
		QTreeWidgetItem* crtItem = desContainerItem.child(iItem);
		Des& crtDes = getDesFromFItem(*crtItem);
		if (&crtDes == &des)
		{
			//found it
			out_foundItem = crtItem;
			return true;
		}
	}

	return false;
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::removeDesItem(const Des& /*des*/, QTreeWidgetItem* desItem, DespotTreeWidget* /*widget*/)
{
	//find and remove the UI Item corresponding to the DES being deleted
	QTreeWidgetItem* desItemParent = desItem->parent();
	int desItemIndex = desItemParent->indexOfChild(desItem);
	desItemParent->takeChild(desItemIndex);
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::onChangedCurrentFlatViewItem(QTreeWidgetItem * current, QTreeWidgetItem * previous)
{
	Des* pCrtDes = null;
	if (current)
	{
		if (isDesFlatViewItem(*current))
		{
			pCrtDes = &getDesFromFItem(*current);
		}
	}

	Des* pOldCrtDes = null;
	if (previous)
	{
		if (isDesFlatViewItem(*previous))
		{
			pOldCrtDes = &getDesFromFItem(*previous);
		}
	}
	
	if (m_pCrtDes != pCrtDes)
	{
		m_pCrtDes = pCrtDes;	
		emit onChangedCurrentDes(pCrtDes, pOldCrtDes);
	}
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::onFlatStructItemActivated(QTreeWidgetItem* item, int /*column*/)
{
	if (isDesFlatViewItem(*item))
	{
		emit onEnterCurrentDes();
	}
}

//_________________________________________________________________________________________________

void ProjStructureUiPart::onFlatStructItemDeleted()
{
	QTreeWidgetItem* pCrtItem = m_fViewProjStructWidg->currentItem();
	if (pCrtItem && isDesFlatViewItem(*pCrtItem))
	{
		emit onCurrentDesDeleted();
	}
}

} //end of namespace DESpot
