/*	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 <QMessageBox>
#include <QWorkspace>
#include <QDesktopWidget>

#include "CommonDefinitions.h"
#include "OpenedWndUiPart.h"
#include "DespotForm.h"
#include "ProjectEditor.h"
#include "SimConfig.h"
#include "DesEditor.h"
#include "EventPoolForm.h"
#include "MainForm.h"
#include "Des.h"
#include "DesProject.h"

#include "GedDesEditor.h"

namespace DESpot
{

const short OpenedWndUiPart::cWndListWidgColumnCount = 1;
const short OpenedWndUiPart::cWndNameColumnIdx = 0;

OpenedWndUiPart::OpenedWndUiPart(Ui::MainFormUI* pUiContainer)
{
	initUi(pUiContainer);

	setupConnections();

	initOpenedWindowsWidget();
}

//_________________________________________________________________________________________________

OpenedWndUiPart::~OpenedWndUiPart(void)
{
}

//_________________________________________________________________________________________________

bool OpenedWndUiPart::editorOpened(const QString& fileName, DesEditor*& out_desEditor)
{
	for(FormMapIt formIt = m_openedFormsMap.begin(); formIt != m_openedFormsMap.end(); formIt++)
	{
		DespotForm* crtForm = formIt->first;
		if ((crtForm->type() == DespotForm::eDesEditor) && (fileName == crtForm->getDocFileName()))
		{
			//found it
			out_desEditor = dynamic_cast<DesEditor*>(crtForm);
			return true;
		}
	}

	return false;
}

//_________________________________________________________________________________________________

bool OpenedWndUiPart::editorOpened(Des* des, DesEditor*& out_desEditor)
{
	for(FormMapIt formIt = m_openedFormsMap.begin(); formIt != m_openedFormsMap.end(); formIt++)
	{
		DespotForm* crtForm = formIt->first;
		if (crtForm->type() == DespotForm::eDesEditor)
		{
			DesEditor*   crtDesEditor = dynamic_cast<DesEditor*>(crtForm);
			if (des == crtDesEditor->des())
			{
				//found it
				out_desEditor = crtDesEditor;
				return true;
			}
		}
	}

	return false;
}

//_________________________________________________________________________________________________

bool OpenedWndUiPart::editorOpened(const QString& fileName, ProjectEditor*& out_projEditor)
{
	for(FormMapIt formIt = m_openedFormsMap.begin(); formIt != m_openedFormsMap.end(); formIt++)
	{
		DespotForm* crtForm = formIt->first;
		if (((crtForm->type() == DespotForm::eFlatProjectEditor) ||(crtForm->type() == DespotForm::eHierProjectEditor)) &&
			(fileName == crtForm->getDocFileName()))
		{
			//found it
			out_projEditor = dynamic_cast<ProjectEditor*>(crtForm);
			return true;
		}
	}

	return false;
}

//_________________________________________________________________________________________________

void OpenedWndUiPart::initUi(Ui::MainFormUI* pUiContainer)
{
	m_wndListWidg = pUiContainer->m_openedWndTreeWidg;
	m_wndListWidg->setBoxWidget(pUiContainer->m_openedWndGBox);
	m_showCrtWndBtn = pUiContainer->m_showWindowBtn;
	m_closeCrtWndBtn = pUiContainer->m_closeWindowBtn;
}

//_________________________________________________________________________________________________

void OpenedWndUiPart::setupConnections()
{
	connect(m_wndListWidg, SIGNAL(onWidthChanged(int)),
		    this, SLOT(resizeWndListWidgHeaders(int)));

	connect(m_wndListWidg, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(onShowCurrentWindow()));
	connect(m_showCrtWndBtn, SIGNAL(pressed()), this, SLOT(onShowCurrentWindow()));
	connect(m_closeCrtWndBtn, SIGNAL(pressed()), this, SLOT(onCloseCurrentWindow()));
}

//_________________________________________________________________________________________________

void OpenedWndUiPart::initOpenedWindowsWidget()
{
	//add one column: Project Name | Path
	m_wndListWidg->setColumnCount(cWndListWidgColumnCount);
	QStringList headers;
	headers << tr("Window Name");
	m_wndListWidg->setHeaderLabels(headers);
	m_wndListWidg->header()->setStretchLastSection(true);

	resizeWndListWidgHeaders(m_wndListWidg->geometry().width());
}

//_________________________________________________________________________________________________

void OpenedWndUiPart::resizeWndListWidgHeaders(int width)
{
	m_wndListWidg->header()->resizeSection(cWndNameColumnIdx, width);
}

//_________________________________________________________________________________________________

void OpenedWndUiPart::onFormOpened(DespotForm* form)
{
	switch(form->type())
	{
		case DespotForm::eMainForm:
		{
			//The main form is the root of all opened windows
			m_mainFormItem = new QTreeWidgetItem(m_wndListWidg);
			m_mainFormItem->setText(cWndNameColumnIdx, form->title());
			m_mainFormItem->setToolTip(cWndNameColumnIdx, form->title());
			m_mainFormItem->setData(cWndNameColumnIdx, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(form)));
			m_wndListWidg->expandItem(m_mainFormItem);
			m_openedFormsMap[form] = m_mainFormItem;
			break;
		}

		case DespotForm::eFlatProjectEditor:
		case DespotForm::eHierProjectEditor:
		{
			QTreeWidgetItem* projFormItem = new QTreeWidgetItem(m_mainFormItem);
			projFormItem->setText(cWndNameColumnIdx, form->title());
			projFormItem->setToolTip(cWndNameColumnIdx, form->title());
			projFormItem->setData(cWndNameColumnIdx, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(form)));
			m_wndListWidg->expandItem(projFormItem);

			//add the project item to the list so it's easier to find
			m_openedFormsMap[form] = projFormItem;
			break;
		}

		// For simulation, xma May/9/08
		case DespotForm::eSimConfig:
		{
			QTreeWidgetItem* projFormItem = new QTreeWidgetItem(m_mainFormItem);
			projFormItem->setText(cWndNameColumnIdx, form->title());
			projFormItem->setToolTip(cWndNameColumnIdx, form->title());
			projFormItem->setData(cWndNameColumnIdx, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(form)));
			m_wndListWidg->expandItem(projFormItem);

			//add the project item to the list so it's easier to find
			m_openedFormsMap[form] = projFormItem;
			break;
		}

		case DespotForm::eDesEditor:
		{
			QTreeWidgetItem* desItemParent = null;
			DesEditor* desEditor = dynamic_cast<DesEditor*>(form);
			if (desEditor->ownedByProject())
			{
				ProjectEditor* projEditor = desEditor->projEditorOwner();
				desItemParent = findItem(projEditor);
			}
			else
			{
				desItemParent = m_mainFormItem;
			}

			QTreeWidgetItem* desFormItem = new QTreeWidgetItem(desItemParent);
			desFormItem->setText(cWndNameColumnIdx, form->title());
			desFormItem->setToolTip(cWndNameColumnIdx, form->title());
			desFormItem->setData(cWndNameColumnIdx, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(form)));

			//add the project item to the list so it's easier to find
			m_openedFormsMap[form] = desFormItem;
			break;
		}

		// For GED
		case DespotForm::eGedDesEditor:
		{
			QTreeWidgetItem* desItemParent = null;
			GedDesEditor* desEditor = dynamic_cast<GedDesEditor*>(form);
			if (desEditor->ownedByProject())
			{
				ProjectEditor* projEditor = desEditor->projEditorOwner();
				desItemParent = findItem(projEditor);
			}
			else
			{
				desItemParent = m_mainFormItem;
			}

			QTreeWidgetItem* desFormItem = new QTreeWidgetItem(desItemParent);
			desFormItem->setText(cWndNameColumnIdx, form->title());
			desFormItem->setToolTip(cWndNameColumnIdx, form->title());
			desFormItem->setData(cWndNameColumnIdx, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(form)));

			//add the project item to the list so it's easier to find
			m_openedFormsMap[form] = desFormItem;
			break;
		}

		case DespotForm::eProjectEventPool:
		{
			EventPoolForm* eventPoolForm = dynamic_cast<EventPoolForm*>(form);
			ProjectEditor* projEditor = eventPoolForm->projEditorOwner();
			QTreeWidgetItem* projItem = findItem(projEditor);

			QTreeWidgetItem* evenPoolItem = new QTreeWidgetItem(projItem);
			evenPoolItem->setText(cWndNameColumnIdx, form->title());
			evenPoolItem->setToolTip(cWndNameColumnIdx, form->title());
			evenPoolItem->setData(cWndNameColumnIdx, Qt::UserRole, QVariant(reinterpret_cast<unsigned long long>(form)));
			m_openedFormsMap[form] = evenPoolItem;
			break;
		}

		default:
			assert(false);
	}

	form->window()->installEventFilter(this);
}

//_________________________________________________________________________________________________

void OpenedWndUiPart::onFormClosed(DespotForm* form, bool& okToClose)
{
	//One of the windows displayed in this UiPart has been closed
	//Find it and remove it from the list of opened windows
	okToClose = removeItem(form);
}

//_________________________________________________________________________________________________

void OpenedWndUiPart::onShowCurrentWindow()
{
	QTreeWidgetItem* crtItem = m_wndListWidg->currentItem();
	if (crtItem)
	{
		DespotForm* crtForm = getFormFromItem(crtItem);
		crtForm->open();
	}
}

//_________________________________________________________________________________________________

void OpenedWndUiPart::onCloseCurrentWindow()
{
	QTreeWidgetItem* crtItem = m_wndListWidg->currentItem();
	if (crtItem)
	{
		DespotForm* crtForm = getFormFromItem(crtItem);
		QMainWindow* crtWnd = crtForm->window();
		crtWnd->close();
	}
}

//_________________________________________________________________________________________________

QTreeWidgetItem* OpenedWndUiPart::findItem(ProjectEditor* projEditor)
{
	FormMapIt projEdIt = m_openedFormsMap.find(projEditor);
	if (projEdIt != m_openedFormsMap.end())
	{
		//found it
		return projEdIt->second;
	}

	throw EX("Cannot find project editor in the list of opened windows");
}

//_________________________________________________________________________________________________

bool OpenedWndUiPart::eventFilter(QObject* target, QEvent* event)
{
	if(event->type() == QEvent::WindowTitleChange)
	{
		//One of the windows displayed in this UiPart has a differnt title
		//thus its corresponding item must be updated
		QMainWindow* wnd = dynamic_cast<QMainWindow*>(target);
		QTreeWidgetItem* item = null;
		if (findItemFromWindow(wnd, item))
		{
			item->setText(0, wnd->windowTitle());
		}
	}

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

//_________________________________________________________________________________________________

bool OpenedWndUiPart::removeItem(DespotForm* closingForm)
{
	FormMapIt formIt = m_openedFormsMap.find(closingForm);
	if (formIt != m_openedFormsMap.end())
	{
		QTreeWidgetItem* itemToRemove = formIt->second;

		//ensure that all owned windows are closed first so they don't remained orphaned
		if (closeOwnedForms(closingForm, itemToRemove))
		{
			if (itemToRemove == m_mainFormItem)
			{
				m_wndListWidg->takeTopLevelItem(0);
			}
			else
			{
				QTreeWidgetItem* parentItem = itemToRemove->parent();
				int indexToRemove = parentItem->indexOfChild(itemToRemove);
				parentItem->takeChild(indexToRemove);
				removeItemFromAccessList(itemToRemove);
			}

			delete itemToRemove;
			itemToRemove = null;

			return true;
		}
		else
		{
			//the window that is closing still has owned windows that did not close
			//thus the item was not removed
			return false;
		}
	}
	else
	{
		assert(false);
		return true;
	}
}

//_________________________________________________________________________________________________

bool OpenedWndUiPart::closeOwnedForms(DespotForm* /*closingForm*/, QTreeWidgetItem* formItem)
{
	bool allFormsClosed = true;
	for(int iChild = formItem->childCount() - 1; iChild >= 0 ; iChild--)
	{
		QTreeWidgetItem* childItem = formItem->child(iChild);

		//make sure all the windows the child owns close as well using a recursive call
		DespotForm* childItemForm = getFormFromItem(childItem);
		QMainWindow* childItemWnd = childItemForm->window();

		if (childItemForm->type() == DespotForm::eProjectEventPool)
		{
			//the project is being deleted so we must delete the event pool to
			childItemWnd->setAttribute(Qt::WA_DeleteOnClose);
		}

		//Attempt to close the owned window that corresponds to the child item in the tree
		//Note that this will cause a recursive call as the form will indeed attempt to
		//remove itself from the list of opened windows and thus removeItem will be called
		//which in turn will call closeOwnedForms for the owned windows of this "child form"
		//This ensures that the entire tree is closed in a depth-first manner and no orphaned
		//windows remain.
		allFormsClosed  &= childItemForm->window()->close();
	}

	return allFormsClosed;
}

//_________________________________________________________________________________________________

void OpenedWndUiPart::removeItemFromAccessList(QTreeWidgetItem* item)
{
	for(FormMapIt formIt = m_openedFormsMap.begin(); formIt != m_openedFormsMap.end(); formIt++)
	{
		QTreeWidgetItem* crtItem = formIt->second;
		if (item == crtItem)
		{
			m_openedFormsMap.erase(formIt);
			return;
		}
	}
}

//_________________________________________________________________________________________________

DespotForm* OpenedWndUiPart::getFormFromItem(QTreeWidgetItem* item)
{
	return reinterpret_cast<DespotForm*>(item->data(0, Qt::UserRole).toULongLong());
}

//_________________________________________________________________________________________________

bool OpenedWndUiPart::findItemFromWindow(QMainWindow* wnd, QTreeWidgetItem*& out_foundItem)
{
	for(FormMapIt formIt = m_openedFormsMap.begin(); formIt != m_openedFormsMap.end(); formIt++)
	{
		DespotForm* crtForm = formIt->first;
		if (crtForm->window() == wnd)
		{
			out_foundItem = formIt->second;
			return true;
		}
	}

	return false;
}

} //end of namespace DESpot
