/*	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 "AddDesToProjectDlg.h"
#include "DesInterface.h"
#include "DesSubsystem.h"
#include "InterfaceIterator.h"
#include "SubsysDependsIterator.h"
#include "MainForm.h"

namespace DESpot
{

DesLevel AddDesToProjectDlg::m_lastUsedDesLevel = eSupervisorDes;

AddDesToProjectDlg::AddDesToProjectDlg(const DesProject& project, QWidget* parent /*= null*/):
	QDialog(parent),
	m_project(project),
	m_hierLevel(eHighLevel),
        m_desLevel(m_lastUsedDesLevel),
        m_isNewDes(true)
{
	setupUi(this);

	setupButtonGroups();

	prepareDlgForProjectType();

	//Update widgets from dialog data
	updateUI();

	connect(m_desFileBrowseBtn, SIGNAL(clicked()), this,SLOT(onBrowseDesFile()));
	connect(m_newDesNameWidg, SIGNAL(textChanged(const QString&)), this, SLOT(onNewDesNameChanged(const QString&)));
	connect(m_selDesFileWidg, SIGNAL(textChanged(const QString&)), this, SLOT(onDesFileNameChanged(const QString&)));
	connect(m_levelRadioBtnGroup, SIGNAL(buttonClicked(int)), this, SLOT(onLevelChanged(int)));
}

//_________________________________________________________________________________________________

AddDesToProjectDlg::~AddDesToProjectDlg(void)
{
}

//_________________________________________________________________________________________________

//Hierarhical level - used only for adding a DES to a hierarhical project
ProjectLevel AddDesToProjectDlg::getHierLevel() const
{
	return m_hierLevel;
}

//_________________________________________________________________________________________________
void AddDesToProjectDlg::setHierLevel(ProjectLevel level)
{
	m_hierLevel = level;
	
	//if the level is interface the DES type must be set to supervisor and disabled
	//as the user cannot change it
	enableDesTypeUI(m_hierLevel != eInterfLevel);

	//clear the DES container widget and fill it again according to the new level
	fillDesContainerWidget();

	updateProjectLevelUI();
}

//_________________________________________________________________________________________________
//The name of the DES container
std::wstring AddDesToProjectDlg::getContainerName() const
{
	return m_containerName;
}

//_________________________________________________________________________________________________
void AddDesToProjectDlg::setContainerName(const std::wstring& containerName)
{
	m_containerName = containerName;

	updateDesContainerUI();
}

//_________________________________________________________________________________________________
//The type of DES to be added: Plant or Supervisor. Note that when the DES
//is to be added to the Interface level the DES type is unacessible
DesLevel AddDesToProjectDlg::getDesLevel() const
{
	return m_desLevel;
}

//_________________________________________________________________________________________________
void AddDesToProjectDlg::setDesLevel(DesLevel desLevel)
{
	if (m_hierLevel == eInterfLevel)
		throw EX("Cannot set DES level for interface level")

	m_desLevel = desLevel;
	
	updateDesLevelUI();
}

//_________________________________________________________________________________________________
//When the user adds a new DES isNewDes returns true. In this case a new DES is to be created
//If an existing DES is to be added the method will return false
bool AddDesToProjectDlg::isNewDes() const
{
	return m_isNewDes;
}

//_________________________________________________________________________________________________
//Used for new Des to return the name given by the user to the DES
std::wstring AddDesToProjectDlg::getNewDesName() const
{
	return m_newDesName;
}

//_________________________________________________________________________________________________
//Used for existing Des to return the name of file where the existing DES is saved
std::wstring AddDesToProjectDlg::getDesFileName() const
{
	return m_desFileName;
}

//_________________________________________________________________________________________________
//Shows / Hides / Initializes controls depending on the project type
void AddDesToProjectDlg::prepareDlgForProjectType()
{	
	if (m_project.getType() == eFlatProject)
	{
		//initialize dialog for adding DES to flat project
		
		//hide all controls specific to hierhical projects
		showHierProjectControls(false);

		//adjust the size of the dialog
		const QSize offset(35, 200);
		QRect crtLayoutRect = layoutWidget->geometry();
		layoutWidget->setGeometry(crtLayoutRect.x(), crtLayoutRect.y(), 
								  crtLayoutRect.width() - offset.width(), 
								  crtLayoutRect.height() - offset.height());

		QRect dlgRect = geometry();		
		dlgRect.setSize(dlgRect.size() - offset);
		//and re-center the dialog
		dlgRect.moveCenter(parentWidget()->geometry().center());

		setGeometry(dlgRect);
	}
	else
	{
		//initialize dialog for adding DES to HISC project

		//hide all controls specific to hierhical projects
		showHierProjectControls();

		//load the name of all possible containers for the DES (depending on the selected level)
		fillDesContainerWidget();
	}
}

//_________________________________________________________________________________________________
//Shows / hides the widgets specific to HISC projects
void AddDesToProjectDlg::showHierProjectControls(bool show /*= true*/)
{
	m_selHierDesLevelLabel->setVisible(show);
	m_desHighLevelWidg->setVisible(show);
	m_desInterfLevelWidg->setVisible(show);
	m_desLowLevelWidg->setVisible(show);
	m_hierDesTypeLine->setVisible(show);
	m_spacerLevel1->setVisible(show);
	m_spacerLevel2->setVisible(show);

	m_selDesParentLabel->setVisible(show);
	m_desParentWidg->setVisible(show);
	m_desParentLine->setVisible(show);
	m_spacerContainer1->setVisible(show);
	m_spacerContainer2->setVisible(show);
}

//_________________________________________________________________________________________________
//Fills the DES container wiget with the names of all possible containers
//depending on the selected level
void AddDesToProjectDlg::fillDesContainerWidget()
{
	//make sure the widget is empty
	m_desParentWidg->clear();

	//fill it with the right containers
	switch(m_hierLevel)
	{
		case eHighLevel:
		{
			m_desParentWidg->addItem(QString::fromStdWString(m_project.getRootSubsys().getName()));
			break;
		}

		case eInterfLevel:
		{			
			DesHierProject::InterfIteratorPtr interfIt = hierProject().createInterfIterator();
			for(interfIt->first(); interfIt->isDone() == false; interfIt->next())
			{
				const DesInterface& crtInterf = interfIt->currentItem();
				m_desParentWidg->addItem(QString::fromStdWString(crtInterf.getName()));
			}
			break;
		}

		case eLowLevel:
		{
			const DesSubsystem& rootSubsys = m_project.getRootSubsys();
			DesSubsystem::DependIteratorPtr lowSubsysIt = rootSubsys.createDependsIterator();
			for(lowSubsysIt->first(); lowSubsysIt->isDone() == false; lowSubsysIt->next())
			{
				const DesSubsystem&  lowSubsys = *(lowSubsysIt->currentItem().subsystem);
				m_desParentWidg->addItem(QString::fromStdWString(lowSubsys.getName()));
			}
			break;
		}
	}
}

//_________________________________________________________________________________________________
//Update widgets from dialog data
void AddDesToProjectDlg::updateUI()
{
	if (m_project.getType() == eHierProject)
	{
		//update the widgets specific to HISC projects
		updateProjectLevelUI();
		updateDesContainerUI();
	}

	updateDesLevelUI();
	updateAddMethodUI();
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::updateProjectLevelUI()
{
	//update the level radio buttons
	switch(m_hierLevel)
	{
		case eHighLevel:
			m_desHighLevelWidg->setChecked(true);
			break;

		case eInterfLevel:
			m_desInterfLevelWidg->setChecked(true);
			
			//disable the DES type if the interface level is selected
			//while disabled the supervisor type is selected
			enableDesTypeUI(false);
			break;

		case eLowLevel:
			m_desLowLevelWidg->setChecked(true);
			break;

		default:
			assert(false);
	}
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::updateDesLevelUI()
{
	//update the DES type
	switch(m_desLevel)
	{
		case eSupervisorDes:
			m_supDesLevelWidg->setChecked(true);
			break;

		case ePlantDes:
			m_plantDesLevelWidg->setChecked(true);
			break;

		default:
			assert(false);
	}
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::updateDesContainerUI()
{
	//update the DES container if given
	if (m_containerName.empty() != true)
	{
		int idx = m_desParentWidg->findText(QString::fromStdWString(m_containerName));
		m_desParentWidg->setCurrentIndex(((idx == -1) ? 0 : idx));
	}
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::updateAddMethodUI()
{
	//update the add method (new or existing)
	if (m_isNewDes)
	{
		m_newDesWidg->setChecked(true);
	}
	else
	{
		m_existDesWidg->setChecked(true);
	}
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::enableDesTypeUI(bool bEnable /*= true*/)
{
	m_supDesLevelWidg->setEnabled(bEnable);
	m_plantDesLevelWidg->setEnabled(bEnable);
	
	if (bEnable == false)
	{
		m_supDesLevelWidg->setChecked(true);	
	}
	else
	{
		updateDesLevelUI();
	}
}

//_________________________________________________________________________________________________
//Groups the radio buttons to function properly
void AddDesToProjectDlg::setupButtonGroups()
{
	m_levelRadioBtnGroup = new QButtonGroup(this);
	m_levelRadioBtnGroup->addButton(m_desHighLevelWidg);
	m_levelRadioBtnGroup->setId(m_desHighLevelWidg, eHighLevel);
	m_levelRadioBtnGroup->addButton(m_desInterfLevelWidg);
	m_levelRadioBtnGroup->setId(m_desInterfLevelWidg, eInterfLevel);
	m_levelRadioBtnGroup->addButton(m_desLowLevelWidg);
	m_levelRadioBtnGroup->setId(m_desLowLevelWidg, eLowLevel);

	m_desLevelRadioBtnGroup = new QButtonGroup(this);
	m_desLevelRadioBtnGroup->addButton(m_supDesLevelWidg);
	m_desLevelRadioBtnGroup->addButton(m_plantDesLevelWidg);

	m_addMethodRadioBtnGroup = new QButtonGroup(this);
	m_addMethodRadioBtnGroup->addButton(m_newDesWidg);
	m_addMethodRadioBtnGroup->addButton(m_existDesWidg);
}

//_________________________________________________________________________________________________

const DesHierProject& AddDesToProjectDlg::hierProject()
{
	const DesHierProject& hierProject = dynamic_cast<const DesHierProject&>(m_project);
	return hierProject;
}

//_________________________________________________________________________________________________

bool AddDesToProjectDlg::updateData()
{
	bool bUpdateOk = true;
	if (m_project.getType() == eHierProject)
	{
		//update the widgets specific to HISC projects
		bUpdateOk &= updateLevel();
		bUpdateOk &= updateDesContainer();
	}

	bUpdateOk &= updateDesType();
	bUpdateOk &= updateAddMethod();

	return bUpdateOk;
}

//_________________________________________________________________________________________________

bool AddDesToProjectDlg::updateLevel()
{
	if (m_desHighLevelWidg->isChecked())
	{
		m_hierLevel = eHighLevel;
	}
	else if (m_desInterfLevelWidg->isChecked())
	{
		m_hierLevel = eInterfLevel;
	}
	else if (m_desLowLevelWidg->isChecked())
	{
		m_hierLevel = eLowLevel;
	}
	else
	{
		//unknown control level, cannot update
		assert(false);
		return false;
	}

	return true;
}

//_________________________________________________________________________________________________

bool AddDesToProjectDlg::updateDesType()
{
	if (m_supDesLevelWidg->isChecked())
	{
		m_desLevel = eSupervisorDes;
	}
	else if (m_plantDesLevelWidg->isChecked())
	{
		m_desLevel = ePlantDes;	
	}
	else
	{
		//unknown widget
		assert(false);
		return false;
	}

	//remember the last used DES type so that next time the dialog opens it shows the same by default
	m_lastUsedDesLevel = m_desLevel;

	return true;
}

//_________________________________________________________________________________________________

bool AddDesToProjectDlg::updateDesContainer()
{
	m_containerName = m_desParentWidg->currentText().toStdWString();
	return true;
}

//_________________________________________________________________________________________________

bool AddDesToProjectDlg::updateAddMethod()
{
	m_isNewDes = m_newDesWidg->isChecked();

	if (m_isNewDes)
	{
		m_newDesName = m_newDesNameWidg->text().toStdWString();
		if (m_newDesName.empty())
		{
			QMessageBox::critical(this, STR_DESPOT_ERROR, STR_DES_NAME_EMPTY);
			return false;
		}
		else
		{
			//verify the DES name is unique in the project
			if (m_project.findName(m_newDesName))
			{
				QMessageBox::critical(this, STR_DESPOT_ERROR, STR_PROJ_NAME_ALREADY_EXISTS);
				return false;
			}
		}
	}
	else
	{
		QString desFileName = m_selDesFileWidg->text();
		if (desFileName.isEmpty())
		{
			QMessageBox::critical(this, STR_DESPOT_ERROR, STR_DES_FILE_NAME_EMPTY);
			return false;
		}

		//make sure it is an actualy file
		if (QFile::exists(desFileName) == false)
		{
			QMessageBox::critical(this, STR_DESPOT_ERROR, STR_DES_FILE_DOES_NOT_EXIST(desFileName));
			return false;
		}

		m_desFileName = desFileName.toStdWString();
	}

	return true;
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::onBrowseDesFile()
{
	QString fileName;
	if (MainForm::getOpenFileName(this, STR_OPEN_DES_AS_DLG_TITLE, STR_DES_FILE_DLG_FILTER, fileName))
	{
		m_selDesFileWidg->setText(fileName);
	}
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::onNewDesNameChanged(const QString& /*text*/)
{
	//the user is changing the new DES name so make sure the "add-method" is correct
	m_newDesWidg->setChecked(true);

}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::onDesFileNameChanged(const QString& /*text*/)
{
	//the user is changing the new DES name so make sure the "add-method" is correct
	m_existDesWidg->setChecked(true);
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::onLevelChanged(int crtLevel)
{
	m_hierLevel = ProjectLevel(crtLevel);
	
	//if the level is interface the DES type must be set to supervisor and disabled
	//as the user cannot change it
	enableDesTypeUI(m_hierLevel != eInterfLevel);

	//clear the DES container widget and fill it again according to the new level
	fillDesContainerWidget();
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::accept()
{
	if (updateData())
	{	
		//the data was updated sucessfully so we can close the dialog
		QDialog::accept();
	}
}

//_________________________________________________________________________________________________

void AddDesToProjectDlg::reject()
{
	QDialog::reject();
}

} //end of namespace DESpot
