/*	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 <QMessageBox>
#include <QFileDialog>
#include <qevent.h>
#include <QInputDialog>
#include <QDesktopServices>
#include <QUrl>

#include "MainForm.h"
#include "MainWorkspace.h"
#include "FlatProjectEditor.h"
#include "HelpBrowser.h"
#include "HierProjectEditor.h"
#include "CommonDefinitions.h"
#include "DesEditor.h"
#include "Des.h"
#include "NewDesDialog.h"
#include "ProjectSerializer.h"
#include "RecentDocListUiPart.h"
#include "OpenedWndUiPart.h"
#include "AboutDESpotDlg.h"
#include "WaitCursor.h"
#include "GedDesEditor.h"

namespace DESpot
{

const QString MainForm::cConfigFolder = "DESpot";	
const QString MainForm::cConfigFileName = "DESpot.config.xml";
const QString MainForm::cConfigRootTag = "DESpot";
const QString MainForm::cConfigVerAttr = "ver";
const QString MainForm::cDespotVersion = "1.0.0";
const QString MainForm::cRecentProjTag = "Recent-Projects";
const QString MainForm::cRecentDesTag = "Recent-Des";

const QString MainForm::cMainFormName = "Main Form";
QString MainForm::m_lastUsedFileDir;

MainForm::MainForm(void) : DespotForm(this)
{
	//load the configuration file as the UI parts the workspace is made of need the configuration
	//information (the list of recently used projects and des files)
	loadConfig();

	//create the workspace shown in form
	m_pWorkspace = new MainWorkspace(this, m_configDoc);

	//set form attributes
	setAttribute(Qt::WA_DeleteOnClose);
}

//_________________________________________________________________________________________________

MainForm::~MainForm(void)
{
	try
	{
		if (m_pWorkspace)
		{
			delete m_pWorkspace;
			m_pWorkspace = null;
		}
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

bool MainForm::editorOpened(const QString& fileName, DesEditor*& out_desEditor)
{
	return m_pWorkspace->wndListUiPart()->editorOpened(fileName, out_desEditor);
}

//_________________________________________________________________________________________________

bool MainForm::editorOpened(Des* des, DesEditor*& out_desEditor)
{
	return m_pWorkspace->wndListUiPart()->editorOpened(des, out_desEditor);
}

//_________________________________________________________________________________________________

bool MainForm::editorOpened(const QString& fileName, ProjectEditor*& out_desEditor)
{
	return m_pWorkspace->wndListUiPart()->editorOpened(fileName, out_desEditor);
}

//_________________________________________________________________________________________________

void MainForm::loadConfig()
{
	try
	{
		//Determine the path of the configuration file. This resides in the Home Directory
		QString configFileFullPath = getConfigFileFullPath();
		QFile   configFile(configFileFullPath);

		if (configFile.exists() == false)
		{
			createConfigFile(configFile);
		}

		//have the DOM parser read the XML document
		QString loadError;
		int errorLine;
		int errorCol;

		if (m_configDoc.setContent(&configFile,true, &loadError, &errorLine, &errorCol) == false)
		{
			QString message = tr("An error occurred while loading the application configuration file: \"%1\". Line %2, Column %3. If the error cannot be fixed please delete the configuration file and restart the application").
								 arg(loadError, QVariant(errorLine).toString(), QVariant(errorCol).toString());
			QMessageBox::critical(this, STR_DESPOT_ERROR, message);
			return;
		}
	}
	catch_display_xml_ex()
	catch_display_ex()
}

//_________________________________________________________________________________________________
//Returns the path of the configuration file. Depending on the operating system and the existance of 
//environment variables
QString MainForm::getConfigFileFullPath()
{
	//Try to create a configuration folder under the user's home folder where the configuration file
	//will be created. If that fails... create it in the application folder
	QDir homeDir = QDir(QDir::homePath());
	if (homeDir.exists())
	{
		//check to see if the DESpot configuration folder exists. If it doesn't it must be created
		if (homeDir.exists(cConfigFolder))
		{			
			return homeDir.absolutePath() + "/" + cConfigFolder + "/" + cConfigFileName;
		}
		else
		{
			if (homeDir.mkdir(cConfigFolder))
			{
				return homeDir.absolutePath() + "/" + cConfigFolder + "/" + cConfigFileName;
			}
		}
	}

	//creating the configuration file in the user's home folder failed so create it in the appliation folder
	return qApp->applicationDirPath() + "/" + cConfigFileName;	
}

//_________________________________________________________________________________________________

void MainForm::createConfigFile(QFile& configFile)
{
	QDomDocument configDoc;

	QDomNode xmlProcInstr = configDoc.createProcessingInstruction("xml", "version='1.0' encoding='utf-8'");
	configDoc.appendChild(xmlProcInstr);

	QDomElement despotRoot = configDoc.createElement(cConfigRootTag);
	despotRoot.setAttribute(cConfigVerAttr, cDespotVersion);
	configDoc.appendChild(despotRoot);

	QDomElement recentProjElem = configDoc.createElement(cRecentProjTag);
	despotRoot.appendChild(recentProjElem);

	QDomElement recentDesElem = configDoc.createElement(cRecentDesTag);
	despotRoot.appendChild(recentDesElem);

	//write the XML document
	{
		if (!configFile.open(QIODevice::WriteOnly|QIODevice::Text))
		{
			QString error("Cannot create configuration file on disk.");
			throw error.toStdWString();
		}

		const int indent = 4;
		QTextStream out(&configFile);
		configDoc.save(out, indent);
		configFile.close();
	}
}

//_________________________________________________________________________________________________

void MainForm::saveConfig()
{
	//open the file
	QString		configFileFullPath = getConfigFileFullPath();
	QFile		configFile(configFileFullPath);
	if (!configFile.open(QFile::WriteOnly | QFile::Text)) {
         QMessageBox::warning(this, STR_DESPOT_ERROR,
                              tr("Cannot write file %1:\n%2.")
                              .arg(configFileFullPath)
                              .arg(configFile.errorString()));
         return;
     }

	//ask the UI parts to update their configuration
	m_pWorkspace->projListUiPart()->saveConfig(m_configDoc);
	m_pWorkspace->desListUiPart()->saveConfig(m_configDoc);

	//save the new configuration to the file
	QTextStream out(&configFile);
	m_configDoc.save(out, 4);
}

//_________________________________________________________________________________________________

void MainForm::closeEvent(QCloseEvent *event)
{
	try
	{
		//save the configuration information
		saveConfig();

		//if the main windows is closed then the whole application is going down
		//TODO: go through all the open windows and close them: calling qApp->closeAllWindows
		//doesn't work as it seems to create a loop
		if (DespotForm::onCloseForm() == false)
		{
			event->ignore();
		}
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

//Internal slot called whenever a despot form is opened by the form itself
//This slot is used to fire specific signals which in turn should populate
//the recently used projects, des and opened windows
void MainForm::onOpenForm(DespotForm* form)
{
	emit onFormOpened(form);

	if ((form->type() == eDesEditor) || (form->type() == eFlatProjectEditor) || (form->type() == eHierProjectEditor))
	{
		emit onDocumentFormOpened(form);
	}
}

//_________________________________________________________________________________________________

bool MainForm::onCloseForm(DespotForm* form)
{
	bool okToClose = true;
	emit onFormClosed(form, okToClose);
	return okToClose;
}

//_________________________________________________________________________________________________

bool MainForm::getOpenFileName(QWidget * parent, const QString & caption, const QString & filter, QString& out_fileName)
{
	QString fileName = QFileDialog::getOpenFileName(parent, caption, m_lastUsedFileDir, filter);
	if (fileName.size() > 0)
	{
		out_fileName = fileName;
		m_lastUsedFileDir = QFileInfo(fileName).dir().absolutePath();
		return true;
	}
	else
	{
		//the user canceled teh dialog
		return false;
	}
}

//_________________________________________________________________________________________________

bool MainForm::getSaveFileName(QWidget * parent, const QString & caption, const QString & filter, QString& io_fileName)
{
	static QString lastUsedFileDir;

	// If there is no previous last dir, initialize to location of the application
	if (m_lastUsedFileDir.isEmpty()) {
		m_lastUsedFileDir = qApp->applicationDirPath();
	}

	QString defaultName;
	if (io_fileName.isEmpty())
	{
		//the client did not request anything default so select the last used folder to start with
		defaultName = m_lastUsedFileDir;
	}
	else
	{
		QFileInfo fileNameInfo(io_fileName);
		if (fileNameInfo.isAbsolute())
		{
			//the client requested a specific path and file name to be the default
			defaultName = io_fileName;
		}
		else
		{
			//the client just requested a file name so use the file name together with the last used folder
			defaultName = m_lastUsedFileDir + "/" + fileNameInfo.fileName();
		}

	}
	QString fileName = QFileDialog::getSaveFileName(parent, caption, defaultName, filter);
	if (fileName.size() > 0)
	{
		// Auto append DES or project extension
		QString ext = (STR_SAVE_DES_AS_DLG_TITLE == caption) ? ".des" : ".desp";
		if (!fileName.endsWith(ext)) {
			fileName.append(ext);
		}
		io_fileName = fileName;
		m_lastUsedFileDir = QFileInfo(fileName).dir().absolutePath();
		return true;
	}
	else
	{
		//the user canceled the dialog
		return false;
	}
}

//_________________________________________________________________________________________________

void MainForm::onNewFlatProject()
{
	try
	{
		QString projName = QInputDialog::getText(this, STR_NEW_FLAT_PROJ_DLG_TITLE,
						STR_NEW_FLAT_PROJ_DLG_LABEL, QLineEdit::Normal);

		//empty DES names are not allowed. If the user has left the name empty put the default name
		if (projName.isEmpty() == false)
		{
			FlatProjectEditor* pFlatProjectEditor = new FlatProjectEditor(projName, this);
			pFlatProjectEditor->open();
		}
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onNewHiscProject()
{
	try
	{
		QString projName = QInputDialog::getText(this, STR_NEW_HIER_PROJ_DLG_TITLE,
						STR_NEW_HIER_PROJ_DLG_LABEL, QLineEdit::Normal);

		//empty DES names are not allowed. If the user has left the name empty put the default name
		if (projName.isEmpty() == false)
		{
			HierProjectEditor* pHierProjectEditor = new HierProjectEditor(projName, this);
			pHierProjectEditor->open();
		}
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onOpenProject()
{
	try
	{
		WaitCursor wait(this);

		//obtain a file name for the DES to be opened
		QString fileName;
		if (getOpenFileName(this, STR_OPEN_PROJ_DLG_TITLE, STR_PROJ_FILE_DLG_FILTER, fileName))
		{
			onOpenProject(fileName);
		}
	}
	catch_display_xml_ex()
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onOpenProject(const QString& fileName)
{
	try
	{
		WaitCursor wait(this);

		DesProject* project = DesProject::load(fileName.toStdWString());
		ProjectEditor* pProjectEditor = null;
		if (editorOpened(fileName, pProjectEditor) == false)
		{
			switch(project->getType())
			{
				case eFlatProject:
					pProjectEditor = new FlatProjectEditor(project, this);
					break;

				case eHierProject:
					pProjectEditor = new HierProjectEditor(project, this);
					break;

				default:
					assert(false);
					return;
			}
		}

		pProjectEditor->open();

			
		//save the configuration as it was updated with this opened project
		saveConfig();
	}
	catch_display_xml_ex()
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onNewDes()
{
	try
	{
		NewDesDialog newDesDlg(this);
		if (newDesDlg.exec() == QDialog::Accepted)
		{
			if (m_pWorkspace->useGraphEditor())
			{
				GedDesEditor* pDesEditor = new GedDesEditor(
						newDesDlg.getDesName(), newDesDlg.getDesType(), this);
				pDesEditor->open();
			}
			else
			{
				DesEditor* pDesEditor = new DesEditor(newDesDlg.getDesName(),
						newDesDlg.getDesType(), this);
				pDesEditor->open();
			}
		}
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onOpenDes()
{
	try
	{
		WaitCursor wait(this);

		//obtain a file name for the DES to be opened
		QString fileName;
		if (getOpenFileName(this, STR_OPEN_DES_AS_DLG_TITLE, STR_DES_FILE_DLG_FILTER, fileName))
		{
			onOpenDes(fileName);
		}
	}
	catch_display_xml_ex()
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onOpenDes(const QString& fileName)
{
	try
	{
		WaitCursor wait(this);

		if (m_pWorkspace->useGraphEditor())
		{
			GedDesEditor* pDesEditor = null;

			//if (mainForm()->editorOpened(m_pCrtDes, pDesEditor) == false)
			//{
				//create an editor that will load the DES from the selected file
				pDesEditor = new GedDesEditor(fileName, this);
			//}

			//display the DES to the user
			pDesEditor->open();
		}
		else
		{

			//ensure the editor is not opened already for the project at this location
			DesEditor* pDesEditor = null;
			if (editorOpened(fileName, pDesEditor) == false)
			{
				//create an editor that will load the DES from the selected file
				pDesEditor = new DesEditor(fileName, this);
			}

			//display the DES to the user
			pDesEditor->open();
		}

		//save the configuration as it was updated with this opened DES
		saveConfig();
	}
	catch_display_xml_ex()
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onOpenHelpFile()
{
	try
	{
	  HelpBrowser::start();
	}
	catch_display_ex()
}

//________________________________________________________________________________________________

void MainForm::onAboutDespot()
{
	try
	{
		AboutDESpotDlg aboutDespot(this);
		aboutDespot.exec();
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onExitApplication()
{
	try
	{
		qApp->closeAllWindows();
	}
	catch_display_ex()
}


#ifdef __ENABLE_NEXT_VERSION__

//_________________________________________________________________________________________________

void MainForm::onOpenWorkspace()
{
	try
	{
		QMessageBox::information(this, "DESpot","Feature not implemented yet.");
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onSaveWorkspace()
{
	try
	{
		QMessageBox::information(this, "DESpot","Feature not implemented yet.");
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onOpenRecentWorkspace()
{
	try
	{
		QMessageBox::information(this, "DESpot","Feature not implemented yet.");
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onCascadeSelWindows()
{
	try
	{
		QMessageBox::information(this, "DESpot","Feature not implemented yet.");
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onTileHorizSelWindows()
{
	try
	{
		QMessageBox::information(this, "DESpot","Feature not implemented yet.");
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onTileVertSelWindows()
{
	try
	{
		QMessageBox::information(this, "DESpot","Feature not implemented yet.");
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onCascadeAllWindows()
{
	try
	{
		QMessageBox::information(this, "DESpot","Feature not implemented yet.");
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onTileHorizAllWindows()
{
	try
	{
		QMessageBox::information(this, "DESpot","Feature not implemented yet.");
	}
	catch_display_ex()
}

//_________________________________________________________________________________________________

void MainForm::onTileVertAllWindows()
{
	try
	{
		QMessageBox::information(this, "DESpot","Feature not implemented yet.");
	}
	catch_display_ex()
}

#endif

} //end of namespace DESpot

