#include <QtGui/QtGui>
#include <QtXml/QtXml>
#include <qfiledialog.h>
#include <qfile.h>
#include <qmessagebox.h>
#include <qdialog.h>
#include <qsettings.h>


#include "griddesigner.h"
#include "command.h"
#include "context_menu.h"
#include "linkproperties.h"
#include "gridnodeproperties.h"
#include "scriptprefs.h"
//#include "addtemplate.h"
#include "removetemplate.h"
#include "settings.h"
#include "rundlg.h"
#include "templates.h"
#include "simgrid_simulation.h"
#include "properties.h"
#include "preferences.h"

#include "simulationresults.h"

#define MAX_COMMANDS 200

#define APPLICATION_NAME "Grid Matrix"

GridDesigner::GridDesigner(QWidget *parent)
    : QMainWindow(parent)
{
	_lastId = 0;
	_gridNodeNameCount = 0;
	_linkNameCount = 0;
	_running = false;
	_lastSavedCommand = NULL;
	_dirty = false;
	_simPaused = false;

    ui.setupUi(this);

	ui.actionPencil->setCheckable(true);
	ui.actionNew_Node->setCheckable(true);
	ui.actionNew_Link->setCheckable(true);
	ui.actionNew_Connection->setCheckable(true);
	ui.dockOutput->setVisible(false);

	ui.outputEdit->installEventFilter(this);
	connect(ui.outputEdit, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(outputContextMenuRequested(const QPoint &)));
	ui.menu_View->addAction(ui.dockOutput->toggleViewAction());

	_tempActions = new QActionGroup(this);
 	_newComponentActionGroup = new QActionGroup(this);
 	_newComponentActionGroup->setExclusive(true);
 	_newComponentActionGroup->addAction(ui.actionNew_Node);
 	_newComponentActionGroup->addAction(ui.actionNew_Link);
 	_newComponentActionGroup->addAction(ui.actionNew_Connection);
	_newComponentActionGroup->addAction(ui.actionPencil);

	ui.actionCopy->setVisible(false);
	ui.actionCu_t->setVisible(false);
	ui.action_Paste->setVisible(false);

	_tempMenuSeparator = new QAction(this);
	_tempMenuSeparator->setSeparator(true);

	_simgridSim = new SimgridSimulation(this, (SimgridSimulationInterface*) this);

	connect(_simgridSim, SIGNAL(simulationFinished(bool, bool, int)), this, SLOT(simulationFinished(bool, bool, int)), Qt::DirectConnection);
	//connect(_simgridSim, SIGNAL(setComponentMessage(const QString &, const QString &)), this, SLOT(setComponentMessage(const QString &, const QString &)), Qt::DirectConnection);
	//connect(_simgridSim, SIGNAL(setComponentRedLevel(const QString &, int)), this, SLOT(setComponentRedLevel(const QString &, int)), Qt::DirectConnection);
	//connect(_simgridSim, SIGNAL(incComponentRedLevel(const QString &)), this, SLOT(incComponentRedLevel(const QString &)), Qt::DirectConnection);
	//connect(_simgridSim, SIGNAL(decComponentRedLevel(const QString &)), this, SLOT(decComponentRedLevel(const QString &)), Qt::DirectConnection);

	ui.menuTemplates->insertAction(ui.actionAddTemplate, _tempMenuSeparator);

	updateTemplates();
	
	_selectedComponentType = _pointer;
	_curConnection = NULL;
	_newPointConnection = NULL;
	_selRect = NULL;

	QGraphicsScene* scene = new QGraphicsScene(ui.graphicsView);
	ui.graphicsView->setScene(scene);
	scene->setSceneRect(QRectF(0, 0, 1000, 1000));
	ui.graphicsView->setAlignment(Qt::AlignLeft | Qt::AlignTop);

	scene->installEventFilter(this);

	connect(ui.actionPencil, SIGNAL(triggered(bool)), this, SLOT(setPointer()));
	connect(ui.actionNew_Node, SIGNAL(triggered(bool)), this, SLOT(addGridNode()));
	connect(ui.actionNew_Link, SIGNAL(triggered(bool)), this, SLOT(addLink()));
	connect(ui.actionNew_Connection, SIGNAL(triggered(bool)), this, SLOT(addConnection()));
	connect(ui.actionEdit_Component, SIGNAL(triggered(bool)), this, SLOT(editComponent()));
	connect(ui.actionDelete_Components, SIGNAL(triggered(bool)), this, SLOT(deleteComponents()));
	connect(ui.actionEditUndo, SIGNAL(triggered(bool)), this, SLOT(editUndo()));
	connect(ui.actionEditRedo, SIGNAL(triggered(bool)), this, SLOT(editRedo()));
	connect(ui.actionAdd_Point, SIGNAL(triggered(bool)), this, SLOT(addConnectionPoint()));
	connect(ui.actionRemove_Point, SIGNAL(triggered(bool)), this, SLOT(removeConnectionPoint()));
	connect(ui.actionFile_Open, SIGNAL(triggered(bool)), this, SLOT(fileOpen()));
	connect(ui.action_New, SIGNAL(triggered(bool)), this, SLOT(fileNew()));
	connect(ui.actionFile_Save, SIGNAL(triggered(bool)), this, SLOT(fileSave()));
	connect(ui.actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(editSelectAll()));
	connect(ui.action_Properties, SIGNAL(triggered(bool)), this, SLOT(componentProperties()));
	connect(ui.actionAddTemplate, SIGNAL(triggered(bool)), this, SLOT(addTemplate()));
	connect(ui.actionRemoveTemplate, SIGNAL(triggered(bool)), this, SLOT(removeTemplate()));
	connect(ui.actionRun, SIGNAL(triggered(bool)), this, SLOT(run()));
	connect(ui.actionRunAgain, SIGNAL(triggered(bool)), this, SLOT(runAgain()));
	connect(ui.action_NetworkProperties, SIGNAL(triggered(bool)), this, SLOT(networkProperties()));
	connect(ui.actionSave_As, SIGNAL(triggered(bool)), this, SLOT(fileSaveAs()));
	connect(ui.action_Preferences, SIGNAL(triggered(bool)), this, SLOT(preferences()));
	connect(this, SIGNAL(dirty(bool)), this, SLOT(dirtyChanged(bool)));
	connect(ui.actionCancel, SIGNAL(triggered(bool)), this, SLOT(cancel()));
	connect(ui.actionClear, SIGNAL(triggered(bool)), this, SLOT(clearSimulation()));
	connect(ui.actionClearOutput, SIGNAL(triggered(bool)), ui.outputEdit, SLOT(clear()));
	connect(ui.actionStop, SIGNAL(triggered(bool)), this, SLOT(stopSimulation()));
	connect(ui.actionPause, SIGNAL(triggered(bool)), this, SLOT(changePauseSimulationState()));
	connect(ui.actionDisplay_Graphics, SIGNAL(triggered(bool)), this, SLOT(displayGraphics()));

	ui.actionDisplay_Graphics->setEnabled(false);

	ui.actionCancel->setShortcut(Qt::Key_Escape);	
	this->addAction(ui.actionCancel);
	
	ui.actionFile_Open->setShortcut(QKeySequence::Open);
	ui.actionFile_Save->setShortcut(QKeySequence::Save);
	ui.actionEditUndo->setShortcut(QKeySequence::Undo);
	ui.actionEditRedo->setShortcut(QKeySequence::Redo);
	ui.actionCopy->setShortcut(QKeySequence::Copy);
	ui.action_Paste->setShortcut(QKeySequence::Paste);
	ui.actionCu_t->setShortcut(QKeySequence::Cut);
	ui.actionSelect_All->setShortcut(QKeySequence::SelectAll);
	ui.actionDelete_Components->setShortcut(QKeySequence::Delete);
	ui.action_New->setShortcut(QKeySequence::New);

	ui.actionPencil->trigger();

	fileNew();

	//AppSettings::setLastDocument("examples/SimNet.net");
	QString lastDoc = AppSettings::lastDocument();
	if(!lastDoc.isEmpty()) {
		fileOpen(lastDoc);
	}

	dirtyChanged(false);

	// test
	//setNetworkName(tr("Test Network"));
	//executeCommand(new NewGridNode(this, 100, 100, lastId(), newGridNodeName()));
	//executeCommand(new NewGridNode(this, 500, 100, lastId(), newGridNodeName()));
	//executeCommand(new NewLink(this, 300, 350, lastId(), newLinkName()));
	//executeCommand(new NewConnection(this, (DesignerComponent*) topLevelItem(QPointF(100, 100)), (DesignerComponent*) topLevelItem(QPointF(300, 350)), lastId()));
}

GridDesigner::~GridDesigner()
{
	clearSimulation();
	reset();

	if(_simgridSim) {
		delete _simgridSim;
		_simgridSim = NULL;
	}
}

void GridDesigner::reset()
{
	_lastSavedCommand = NULL;

	_selItems.clear();
	_movingItems.deleteAllItems();
	_simulationComps.clear();

	for(CommandList::iterator it=_undoCommands.begin(); it!=_undoCommands.end(); it++) {
		delete *it;
	}

	_undoCommands.clear();

	for(CommandList::iterator it=_redoCommands.begin(); it!=_redoCommands.end(); it++) {
		delete *it;
	}

	_redoCommands.clear();

	// delete all top-level items before the items are removed in scene destructor because some items remove children
	// items and if the scene delete child items before their parent item a exception is thrown.
	GraphicsItemList toRemove;
	GraphicsItemList items = scene()->items();
	for(GraphicsItemList::iterator it=items.begin(); it!=items.end(); it++) {
		QGraphicsItem* item = *it;
		if(item->parentItem() == NULL) {
			toRemove.push_back(item);
		}
	}
	for(GraphicsItemList::iterator it=toRemove.begin(); it!=toRemove.end(); it++) {
		QGraphicsItem* item = *it;
		delete item;
	}
}

void GridDesigner::setNetworkName(const QString& name)
{
	_networkName = name;
	updateWindowName();
}

void GridDesigner::updateWindowName()
{
	if(isDirty())
		setWindowTitle(_networkName + " * - " + APPLICATION_NAME);
	else
		setWindowTitle(_networkName + " - " + APPLICATION_NAME);
}

QGraphicsScene* GridDesigner::scene()
{
	return ui.graphicsView->scene();
}

void GridDesigner::fileOpen(const QString &docName)
{
	QDomDocument domDocument;
    QString fileName;

	fileName = docName;

	if(docName.isEmpty()) {
		fileName = QFileDialog::getOpenFileName(this, tr("Open Grid Network File"), QDir::currentPath(), tr("Network Files (*.net)"));
		if (fileName.isEmpty())
			return;
	}
	
	QFile file(fileName);
	if(!file.open(QFile::ReadOnly | QFile::Text)) {
		if(docName.isEmpty())
			QMessageBox::warning(this, tr("Grid Matrix"), tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString()));
		return;
	}

	if(saveDiscardCancelDlg())
		return;

	QString errorStr;
	int errorLine;
	int errorColumn;
	
	if(!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
		QMessageBox::information(window(), tr("Grid Matrix"),
			tr("Parse error at line %1, column %2:\n%3").arg(errorLine).arg(errorColumn).arg(errorStr));
		return;
	}

	_fileName = fileName;
	
	reset();

	DesignerItemMap itemMap;
	QDomElement net = domDocument.firstChildElement();
	if(!net.isNull()) {
		// default name
		setNetworkName(net.attribute("name", tr("New Network")));

		itemMap.fromXml(this, net.firstChildElement());
	}
	
	net = domDocument.firstChildElement("network");
	if(!net.isNull()) {
		setNetworkName(net.attribute("name"));
	}

	AppSettings::setLastDocument(_fileName);

	setDirty(false);
}

void GridDesigner::fileNew()
{
	if(saveDiscardCancelDlg())
		return;

	reset();
	_fileName.clear();
	setNetworkName(tr("New Network"));
	updateDirty();
	scene()->update();
}

bool GridDesigner::saveDiscardCancelDlg()
{
	bool cancelled = false;

	if(isDirty()) {
		int ret = QMessageBox::warning(this, tr("Grid Matrix"),
			tr("The document has been modified.\n"
			"Do you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
			QMessageBox::Save);

		switch(ret) {
			case QMessageBox::Save:
			{
				if(!fileSave())
					cancelled = true;
				break;
			}
			case QMessageBox::Discard:
			{
				break;
			}
			case QMessageBox::Cancel:
			{
				cancelled = true;
				break;
			}
		}
	}

	return cancelled;
}

bool GridDesigner::fileSaveAs()
{
	QString fileName = QFileDialog::getSaveFileName(this, tr("Save Grid Network File"), QDir::currentPath(), 
													tr("Network Files (*.net)"));
	if (fileName.isEmpty())
		return false;
	
	_fileName = fileName;

	return fileSave(true);
}

bool GridDesigner::fileSave(bool force)
{
	if(_fileName.isNull()) {
		return fileSaveAs();
	}

	// nothing to do
	if(!force && !isDirty())
		return true;

	DesignerItemMap allItems;
	QDomDocument doc;

	_movingItems.deleteAllItems();
	clearSelection();
	scene()->update();

	
	QDomElement domElement = doc.firstChildElement("network");
	
	if(domElement.isNull()) {
		domElement = doc.createElement(tr("network"));
		doc.appendChild(domElement);
	}

	allItems = topLevelItems();
	for(DesignerItemMap::iterator it = allItems.begin(); it != allItems.end(); it++) {
		DesignerItem* item = it.value();
		item->serialize(doc, domElement);
	}
	
	QFileInfo fileInfo(_fileName);
	QFile file(_fileName);
	if (!file.open(QFile::WriteOnly | QFile::Text)) {
		QMessageBox::critical(this, tr("Grid Matrix"),
							 tr("Cannot write file %1:\n%2.").arg(_fileName).arg(file.errorString()));
		return false;
	}

	if(!_networkName.isEmpty())
		domElement.setAttribute(tr("name"), _networkName);
	else {
		setNetworkName(fileInfo.baseName());
		domElement.setAttribute(tr("name"), _networkName);
	}

	QTextStream out(&file);
    doc.save(out, 4);

	if(!_undoCommands.empty()) {
		_lastSavedCommand = _undoCommands.last();
	}
	updateDirty();

	AppSettings::setLastDocument(_fileName);

	return true;
}

void GridDesigner::networkProperties()
{
	Properties propDlg(this);
	propDlg.ui.nameEdit->setText(_networkName);
	propDlg.ui.nameEdit->selectAll();
	
	while(propDlg.exec() == QDialog::Accepted) {
		if(propDlg.ui.nameEdit->text().isEmpty()) {
			QMessageBox::critical(this, tr("Network Properties"),
								  tr("Name cannot be empty."));
		}
		else {
			setNetworkName(propDlg.ui.nameEdit->text());
			break;
		}
	}	
}

void GridDesigner::preferences()
{
	Preferences pref(this);
	pref.exec();
}

void GridDesigner::addTemplate()
{
	ScriptPrefs addTemp(this);
	if(addTemp.exec() == QDialog::Accepted) {
		postUpdateTemplates();
	}
}

void GridDesigner::removeTemplate()
{
	RemoveTemplate remTemp(this);

	SimulationTemplates temps;
	QString tempEntry;
	
	remTemp.ui.okButton->setEnabled(temps.fill(remTemp.ui.tempListCombo) != 0);

	if(remTemp.exec() == QDialog::Accepted) {
		tempEntry = remTemp.ui.tempListCombo->itemData(remTemp.ui.tempListCombo->currentIndex()).toString();
		temps.remove(tempEntry);

		QMessageBox::information(this, tr("Remove Template"),
								 tr("Template removed successfully."));
		
		postUpdateTemplates();
	}
}

void GridDesigner::templateProperties()
{
	QAction *senderAction = (QAction *) sender();
	QString tempEntry = senderAction->data().toString();
	ScriptPrefs modTemp(this, tempEntry);
	if(modTemp.exec() == QDialog::Accepted) {
		postUpdateTemplates();
	}
}

void GridDesigner::postUpdateTemplates()
{
	QApplication::postEvent(this, new UpdateTemplatesEvent());
}

void GridDesigner::updateTemplates()
{
	QList<QAction*> actions = _tempActions->actions();
	
	for(QList<QAction*>::iterator itAct = actions.begin(); itAct != actions.end(); itAct++) {
		QAction *action = *itAct;
		
		_tempActions->removeAction(action);
		delete action;
	}

	SimulationTemplates temps;
	QString tempEntry;
	QString tempName;
	QString tempScript;
	bool anyTemp = false;

	while(temps.next(tempEntry, tempName, tempScript)) {
		if(!tempEntry.isEmpty() && !tempName.isEmpty() && !tempScript.isEmpty()) {
			QAction *action = new QAction(tempName, _tempActions);
			
			action->setData(tempEntry);
			ui.menuTemplates->insertAction(_tempMenuSeparator, action);

			connect(action, SIGNAL(triggered(bool)), this, SLOT(templateProperties()));
		}
		anyTemp = true;
	}

	_tempMenuSeparator->setVisible(anyTemp);
	ui.actionRemoveTemplate->setEnabled(anyTemp);
}

void GridDesigner::setSelection(DesignerItemMap items, const QRectF& selRect, bool setSelected)
{
	for(DesignerItemMap::iterator it=_selItems.begin(); it!=_selItems.end(); it++) {
		DesignerItem* item = it.value();
		if(!items.contains(item->id())) {
			item->setSelected(false);
		}
	}

	_selItems = items;

	if(setSelected) {
		for(DesignerItemMap::iterator it=_selItems.begin(); it!=_selItems.end(); it++) {
			DesignerItem* item = it.value();
			item->setSelected(true);
		}	
	}
}

void GridDesigner::setSelection(DesignerItem* item, const QRectF& selRect, bool setSelected)
{
	DesignerItemMap items;
	items[item->id()] = item;
	setSelection(items, selRect, setSelected);
}

void GridDesigner::addSelection(DesignerItemMap items)
{
	for(DesignerItemMap::iterator it=items.begin(); it!=items.end(); it++) {
		addSelection(it.value());
	}
}

void GridDesigner::addSelection(DesignerItem* item)
{
	_selItems[item->id()] = item;
}

void GridDesigner::addSelection(DesignerItemMap items, const QRectF& selRect)
{
	for(DesignerItemMap::iterator it=items.begin(); it!=items.end(); it++) {
		DesignerItem* item = it.value();
		item->setSelected(true, selRect);
	}
	addSelection(items);
}

void GridDesigner::removeSelection(DesignerItemMap items)
{
	for(DesignerItemMap::iterator it=items.begin(); it!=items.end(); it++) {
		removeSelection(it.value());
	}
}

void GridDesigner::removeSelection(DesignerItem* item)
{
	_selItems.remove(item->id());
	item->setSelected(false);
}

void GridDesigner::clearSelection()
{
	for(DesignerItemMap::iterator it=_selItems.begin(); it!=_selItems.end(); it++) {
		DesignerItem* item = it.value();
		item->setSelected(false);
	}
	_selItems.clear();
}

void GridDesigner::processSelection(const QPointF& p, bool controlPressed)
{
	QPointF p1;
	DesignerItem* item = topLevelItem(p, false);
	if(item) {
		Connection* conn = NULL;
		if(item->type() == DesignerItem::_connection_item) {
			conn = (Connection*) item;
		}

		// if there is selection and the item is in the selection -> don't change selection because items will 
		// move it the user keep mouse button pressed
		DesignerItemMap::iterator it = _selItems.find(item->id());
		if(it == _selItems.end()) {
			if(conn) {
				if(conn->pointNear(p, p1)) {
					if(controlPressed) {
						conn->selectPoint(p1);
						addSelection(conn);
					}
					else {
						conn->selectPoint(p1);
						setSelection(conn, QRectF(), false);
					}
				}
				else {
					conn->setSelected(true);

					if(controlPressed) {
						addSelection(conn);
					}
					else {
						setSelection(conn);
					}
				}
			}
			else {
				item->setSelected(true);
				if(selection().empty() || !controlPressed) {
					setSelection(item);
				}
				// if control is hold add this item to current selection
				else {
					addSelection(item);
				}
			}
			_prevMovePosition = _startMovePosition = p;
		}
		else {
			if(conn) {
				if(controlPressed) {
					if(conn->pointNear(p, p1)) {
						if(conn->pointSelected(p1)) {
							conn->unselectPoint(p1);
							if(conn->selectedPoints().empty()) {
								removeSelection(conn);
							}
						}
						else {
							conn->selectPoint(p1);
						}
					}
					else {
						if(conn->selectedPoints().empty() && !conn->points().empty()) {
							conn->setSelected(true);
						}
						else {
							conn->setSelected(false);
							removeSelection(conn);
						}
					}
				}
				else {
					if(conn->pointNear(p, p1) && !conn->pointSelected(p1)) {
						clearSelection();
						conn->selectPoint(p1);
					}
					else if(!conn->pointNear(p, p1)) {
						clearSelection();
						conn->setSelected(true);
						setSelection(conn);
					}
				}
			}
			else {
				if(controlPressed) {
					item->setSelected(false);
					removeSelection(item);
				}
			}
			_prevMovePosition = _startMovePosition = p;
		}
	}
	else {
		QRectF r(p.x(), p.y(), 1, 1);
		
		_selRect = new QGraphicsRectItem(r, NULL, scene());
		QPen p = _selRect->pen();
		p.setStyle(Qt::DashDotLine);
		_selRect->setPen(p);
	}

	scene()->update();
}

void GridDesigner::setPointer()
{
	setActionSelection(_pointer);
}

void GridDesigner::addGridNode()
{
	setActionSelection(_gridnode_component);
}

void GridDesigner::addLink()
{
	setActionSelection(_link_component);
}

void GridDesigner::addConnection()
{
	setActionSelection(_connection_component);
}

void GridDesigner::editComponent()
{

}

void GridDesigner::deleteComponents()
{
	if(!_selItems.empty())
	{
		Command* command = new DeleteItems(this, _selItems);
		executeCommand(command);
	}
}

void GridDesigner::editUndo()
{
	if(!_undoCommands.empty()) {
		Command* command = _undoCommands.takeLast();
		command->undo();
		_redoCommands.push_back(command);
		scene()->update();

		updateDirty();
	}
}

void GridDesigner::editRedo()
{
	if(!_redoCommands.empty()) {
		Command* command = _redoCommands.takeLast();
		command->execute();
		_undoCommands.push_back(command);
		scene()->update();

		updateDirty();
	}
}

void GridDesigner::editSelectAll()
{
	DesignerItemMap allItems = topLevelItems();
	setSelection(allItems);
}

void GridDesigner::addConnectionPoint()
{
	if(_ctxMenuItems.count() > 0) {
		Connection* conn = qgraphicsitem_cast<Connection*>(_ctxMenuItems.begin().value());
		if(conn && _ctxChildItem) {
			Command* command = new AddConnectionPoint(this, conn, _ctxPoint);
			executeCommand(command);
			scene()->update();
		}
	}
}

void GridDesigner::componentProperties()
{
	DesignerItemMap::iterator it = _selItems.begin();
	if(it == _selItems.end()) {
		return;
	}

	DesignerItem* item = it.value();
	if(item) {
		Command* command = NULL;

		switch(item->type()) {
			case DesignerItem::_link_item:
			{
				Link* link = (Link*) item;
				LinkProperties linkProps(this);
				QDoubleValidator* validator1 = new QDoubleValidator(linkProps.ui.bandwidthEdit);
				QDoubleValidator* validator2 = new QDoubleValidator(linkProps.ui.latencyEdit);

				validator1->setBottom(1);
				validator2->setBottom(0);

				linkProps.ui.bandwidthEdit->setValidator(validator1);
				linkProps.ui.latencyEdit->setValidator(validator2);
				linkProps.ui.nameEdit->setText(link->name());
				linkProps.ui.nameEdit->selectAll();
				linkProps.ui.bandwidthEdit->setText(QString("%1").arg(link->bandwidth()));
				linkProps.ui.latencyEdit->setText(QString("%1").arg(link->latency()));
				while(linkProps.exec() == QDialog::Accepted) {
					if(!linkProps.ui.bandwidthEdit->hasAcceptableInput()) {
						QMessageBox::critical(this, tr("Link Properties"),
											  tr("Invalid Bandwidth."));
						continue;
					}
					if(!linkProps.ui.latencyEdit->hasAcceptableInput()) {
						QMessageBox::critical(this, tr("Link Properties"),
											  tr("Invalid Latency."));
						continue;
					}
					command = new ChangeLinkProperties(this, link, linkProps.ui.nameEdit->text(), 
													   linkProps.ui.bandwidthEdit->text().toDouble(),
													   linkProps.ui.latencyEdit->text().toDouble());
					break;
				}
				break;
			}
			case DesignerItem::_gridnode_item:
			{
				GridNode* node = (GridNode*) item;
				GridNodeProperties nodeProps(this);
				QDoubleValidator* validator = new QDoubleValidator(nodeProps.ui.powerEdit);
				
				validator->setBottom(1);
				nodeProps.ui.powerEdit->setValidator(validator);
				nodeProps.ui.nameEdit->setText(node->name());
				nodeProps.ui.powerEdit->setText(QString("%1").arg(node->power()));
				nodeProps.ui.nameEdit->selectAll();
				while(nodeProps.exec() == QDialog::Accepted) {
					if(!nodeProps.ui.powerEdit->hasAcceptableInput()) {
						QMessageBox::critical(this, tr("Grid Node Properties"),
											  tr("Invalid CPU Power."));
						continue;
					}
					command = new ChangeNodeProperties(this, node, nodeProps.ui.nameEdit->text(), 
																nodeProps.ui.powerEdit->text().toDouble());
					break;
				}
				break;
			}
		}

		if(command) {
			executeCommand(command);
		}
	}
}

void GridDesigner::removeConnectionPoint()
{
	if(_ctxMenuItems.count() > 0) {
		Connection* conn = qgraphicsitem_cast<Connection*>(_ctxMenuItems.begin().value());
		if(conn && _ctxChildItem) {
			QPointF remP;
			if(!conn->pointNear(_ctxPoint, remP)) {
				qDebug("GridDesigner::removeConnectionPoint: Cannot remove point %d %d", _ctxPoint.x(), _ctxPoint.y());
				return;
			}

			Command* command = new RemoveConnectionPoint(this, conn, remP);
			executeCommand(command);
			scene()->update();
		}
	}
}

void GridDesigner::executeCommand(Command *command, bool removeRedoCmds)
{
	if (_undoCommands.count () > MAX_COMMANDS) {
		Command *com = _undoCommands.first();
		_undoCommands.removeFirst();
		delete com;
	}

	command->execute();
	_undoCommands.push_back(command);
	
	if(removeRedoCmds) {
		if(!_redoCommands.empty()) {
			for(CommandList::iterator it=_redoCommands.begin(); it!=_redoCommands.end(); it++) {
				delete *it;
			}
			_redoCommands.clear();
		}
	}

	scene()->update();

	updateDirty();
}

void GridDesigner::setDirty(bool d)
{
	if(d != _dirty) {
		_dirty = d;
		emit( dirty(d) );
	}
}

void GridDesigner::updateDirty()
{
	if(_undoCommands.empty()) {
		setDirty(_lastSavedCommand != NULL);
	}
	else {
		Command* command = _undoCommands.last();
		setDirty(command != _lastSavedCommand);
	}
}

void GridDesigner::dirtyChanged(bool dirty)
{
	updateWindowName();
	ui.actionFile_Save->setEnabled(dirty);
}

void GridDesigner::setActionSelection(SelectionType selection)
{
	switch(selection) {
		case _pointer:
		{
			//unsetCursor();
		}
		case _gridnode_component:
		case _link_component:
		case _connection_component:
		case _comment_component:
		{
			//QCursor cursor;
			//cursor.setShape (Qt::CrossCursor);
			//setCursor(cursor);
			_selectedComponentType = selection;
			break;
		}
	}	

}

QRectF GridDesigner::selectionRect(const QPointF& p)
{
	return QRectF((p.x() <=  2 ? 0 : p.x()-2), (p.y() <=  2 ? 0 : p.y()-2), 4, 4);
}

void GridDesigner::addConnectionItems(DesignerItemMap& items)
{
	DesignerItem* item;
	Connection* conn;
	DesignerItemMap toAdd;
	for(DesignerItemMap::iterator it=items.begin(); it!=items.end(); it++) {
		item = it.value();
		if(item->type() >= DesignerItem::_component_item) {
			DesignerComponent* comp = (DesignerComponent*) item;
			QRectF rect = comp->mapToScene(comp->boundingRect()).boundingRect();

			DesignerItemMap conns = topLevelItems(DesignerItem::_connection_item, rect);

			for(DesignerItemMap::iterator itConn=conns.begin(); itConn!=conns.end(); itConn++) {
				conn = (Connection*) itConn.value();
				if(conn->source() == comp || conn->destination() == comp)
				{
					toAdd[conn->id()] = conn;
				}
			}
		}
	}
	items.unite(toAdd);
}

DesignerItem* GridDesigner::topLevelItem(QGraphicsScene* scene, const QPointF& p, bool onlyComponents, QGraphicsItem** childItem)
{
	QGraphicsItem* item = NULL;
	GraphicsItemList items = scene->items(selectionRect(p));
	if(!items.empty()) {
		item = items.first();
		// skip selection items. This items are used to show the selection to the user, they are not items of the network
		while(item->data(SELECTION_PROPERTY).isValid()) {
			items.removeFirst();
			if(items.empty()) {
				item = NULL;
				break;
			}
			item = items.first();
		}
	}
	if(childItem) {
		*childItem = item;
	}
	
	return topLevelItem(item, onlyComponents);
}

DesignerItem* GridDesigner::topLevelItem(const QPointF& p, bool onlyComponents, QGraphicsItem** childItem)
{
	QGraphicsItem* item = NULL;
	GraphicsItemList items = scene()->items(selectionRect(p));
	if(!items.empty()) {
		item = items.first();
		// skip selection items. This items are used to show the selection to the user, they are not items of the network
		while(item->data(SELECTION_PROPERTY).isValid()) {
			items.removeFirst();
			if(items.empty()) {
				item = NULL;
				break;
			}
			item = items.first();
		}
	}
	if(childItem) {
		*childItem = item;
	}

	return topLevelItem(item, onlyComponents);
}

DesignerItem* GridDesigner::topLevelItem(QGraphicsItem* item, bool onlyComponents)
{
	DesignerItem* ret = NULL;
	if(item) {
		while(item->parentItem() != NULL) {
			item = item->parentItem();
		}
		
		if(item->type() >= DesignerItem::_basic_item) {
			ret = (DesignerItem*) item;
			if(!ret->isComponent() && onlyComponents)
				ret = NULL;
		}
	}

	return ret;
}

DesignerItemMap GridDesigner::topLevelItems(QGraphicsScene* scene, DesignerItem::ComponentType compType, const QRectF& rect)
{
	DesignerItemMap ret;
	GraphicsItemList allItems;
	if(rect.isNull())
		allItems = scene->items();
	else
		allItems = scene->items(rect);

	for(GraphicsItemList::iterator it = allItems.begin(); it != allItems.end(); it++) {
		QGraphicsItem* item = *it;

		if(item->type() >= DesignerItem::_basic_item) {
			if(compType == DesignerItem::_invalid_item || item->type() == compType) {
				DesignerItem* ditem = (DesignerItem*) item;
				ret[ditem->id()] = ditem;
			}
		}
	}

	return ret;
}

DesignerItemMap GridDesigner::topLevelItems(DesignerItem::ComponentType compType, const QRectF& rect)
{
	return topLevelItems(scene(), compType, rect);
}

GraphicsItemList GridDesigner::items(QGraphicsScene* scene, const QPointF& p)
{
	return scene->items(selectionRect(p));
}

DesignerComponent* GridDesigner::simulationComponentByName(const QString &name)
{
	DesignerComponent* ret = NULL;
	DesignerComponentNameMap::iterator it = _simulationComps.find(name);
	if(it != _simulationComps.end()) {
		ret = it.value();
	}

	return ret;
}

QString GridDesigner::newGridNodeName()
{
	QString name = tr("NewGridNode%1").arg(_gridNodeNameCount++);
	return name;
}

QString GridDesigner::newLinkName()
{
	QString name = tr("NewLink%1").arg(_linkNameCount++);
	return name;
}

void GridDesigner::mouseReleaseEvent(QMouseEvent* e)
{
}

void GridDesigner::sceneMousePressEvent(QGraphicsSceneMouseEvent* e)
{
	if(e->button() == Qt::LeftButton) {
		switch(_selectedComponentType) {
			case _pointer:
			{
				processSelection(e->lastScenePos(), (e->modifiers() & Qt::ControlModifier));
				break;
			}
			case _connection_component:
			{
				DesignerComponent* comp = (DesignerComponent*) topLevelItem(e->lastScenePos(), true);
				if(comp) {
					_curConnection = new Connection(ui.graphicsView->scene(), lastId());
					_curConnection->setSource(comp);
					_curConnection->setAuxDestinationPoint(e->lastScenePos());
					scene()->update();
				}
				break;
			}
			
		}
	}

	_firstMove = true;
}

void GridDesigner::sceneMouseReleaseEvent(QGraphicsSceneMouseEvent* e)
{
	if(e->button() == Qt::LeftButton) {
		Command* command = NULL;
		
		switch(_selectedComponentType) {
			case _pointer:
			{
				if(_selRect) {
					QRectF rect = _selRect->rect();
					
					if(rect.topLeft().x() > rect.topRight().x())
						rect.setRect(rect.topRight().x(), rect.topLeft().y(), -rect.width(), rect.height());
					if(rect.topLeft().y() > rect.bottomLeft().y())
						rect.setRect(rect.topLeft().x(), rect.bottomLeft().y(), rect.width(), -rect.height());

					// with control only add items to selection
					if(!(e->modifiers() & Qt::ControlModifier)) {
						clearSelection();
					}

					GraphicsItemList items = scene()->items(rect);
					if(items.count() > 0) {
						DesignerItemMap selItems;

						// avoid repetitions using a map 
						for(GraphicsItemList::iterator it=items.begin(); it!=items.end(); it++) {
							DesignerItem* item = topLevelItem(*it, false);
							if(item) {
								selItems[item->id()] = item;
							}
						}
						addSelection(selItems, rect);
					}
					delete _selRect;
					_selRect = NULL;
					scene()->update();
				}
				else if(!_movingItems.empty()) {
					if(_newPointConnection) {
						DesignerItemMap::iterator it = _selItems.find(_newPointConnection->id());
						if(it != _selItems.end()) {							
							command = new AddConnectionPoint(this, (Connection*) it.value(), _startMovePosition, e->lastScenePos());
						}

						// this object is in the _movingItems list and is deleted
						_newPointConnection = NULL;
					}
					else {
						DesignerItemMap movItems;
						movItems = _selItems;
						movItems.setMovingPoints(_selItems.selectedPoints());
						if(movItems.anyMoveItem())
							command = new MoveItems(this, movItems, adjustToSceneRect(e->lastScenePos())-_startMovePosition);
					}
					_movingItems.deleteAllItems();
				}
				break;
			}
			case _gridnode_component:
			{
				command = new NewGridNode(this, (double) e->lastScenePos().x(), (double) e->lastScenePos().y(), lastId(), newGridNodeName());
				break;
			}
			case _link_component:
			{
				command = new NewLink(this, (double) e->lastScenePos().x(), (double) e->lastScenePos().y(), lastId(), newLinkName());
				break;
			}
			case _connection_component:
			{
				if(_curConnection) {
					DesignerComponent* comp = (DesignerComponent*) topLevelItem(e->lastScenePos(), true);
					// connect only link <-> grid or link <-> link
					if(comp && comp != _curConnection->source() && 
						(_curConnection->source()->type() == DesignerItem::_link_item || comp->type() == DesignerItem::_link_item)) {
						command = new NewConnection(this, _curConnection->source(), comp, lastId());
						
						delete _curConnection;
						_curConnection = NULL;
					}
					else {
						delete _curConnection;
						_curConnection = NULL;
						scene()->update();
					}
				}
				break;
			}
			case _comment_component:
			{
				break;
			}
		}	
		
		if(command) {
			executeCommand(command);
		}
	}

	if(!_movingItems.empty()) {
		_newPointConnection = NULL;
		_movingItems.deleteAllItems();
		scene()->update();
	}
}

void GridDesigner::sceneMouseMoveEvent(QGraphicsSceneMouseEvent* e)
{
	if(e->buttons() & Qt::LeftButton) {
		switch(_selectedComponentType) {
			case _pointer:
			{
				if(_selRect) {
					QRectF r;
					r.adjust(_selRect->rect().topLeft().x(), _selRect->rect().topLeft().y(), e->lastScenePos().x(), e->lastScenePos().y());

					_selRect->setRect(r);
				}
				else if(!_selItems.empty() && _movingItems.empty() && _firstMove) {
					if(_selItems.count() == 1) {
						DesignerItem* item = _selItems.begin().value();
						if(item->type() == DesignerItem::_connection_item) {
							Connection* conn = (Connection*) item;
							if(conn->nearSelectionRect(_startMovePosition) && !conn->pointNear(_startMovePosition)) {
								_newPointConnection = (Connection *) conn->clone(conn->source(), conn->destination(), false);

								PointList prevPoints = _newPointConnection->points();

								_newPointConnection->addPoint(_startMovePosition);
								_newPointConnection->setFixedPoints(prevPoints);
								_movingItems[_newPointConnection->id()] = _newPointConnection;
							}
						}
					}
					if(_newPointConnection == NULL) {
						_movingItems = _selItems.clone();
						_prevMovePosition = /*_startMovePosition = */e->lastScenePos();
					}
					scene()->update();
				}
				else if(!_movingItems.empty()) {
					QRectF sceneRect = scene()->sceneRect();
					QPointF currPoint = adjustToSceneRect(e->lastScenePos());
					
					QPointF dp = currPoint - _prevMovePosition;

					_movingItems.moveBy(dp);
					//qCritical("Moving items: %d %d\n", dp.x(), dp.y());
					_prevMovePosition = currPoint;
				}
				break;
			}
			case _gridnode_component:
			{
				break;
			}
			case _link_component:
			{
				break;
			}
			case _connection_component:
			{
				if(_curConnection) {
					_curConnection->setAuxDestinationPoint(e->lastScenePos());
					scene()->update();
				}
				break;
			}
			case _comment_component:
			{
				break;
			}
		}
	}

	_firstMove = false;
}

QPointF GridDesigner::adjustToSceneRect(const QPointF& p)
{
	QRectF sceneRect = scene()->sceneRect();
	QPointF newP = p;
	
	if(!sceneRect.contains(newP)) {
		if(newP.y() < sceneRect.top())
			newP.setY(sceneRect.top());
		else if(newP.y() > sceneRect.bottom())
			newP.setY(sceneRect.bottom());
		if(newP.x() < sceneRect.left())
			newP.setX(sceneRect.left());
		else if(newP.x() > sceneRect.right())
			newP.setX(sceneRect.right());
	}

	return newP;
}

void GridDesigner::sceneContextMenu(QGraphicsSceneContextMenuEvent* e)
{
	DesignerItem* item = topLevelItem(e->scenePos(), false, &_ctxChildItem);
	if(item) {
		_ctxPoint = e->scenePos();

		// if the context menu is executed over a selected item assume that the action is taken for all selected items
		DesignerItemMap::iterator it = _selItems.find(item->id());
		if(it == _selItems.end() || _selItems.count() == 1) {
			_ctxMenuItems.clear();
			_ctxMenuItems[item->id()] = item;

			if(it == _selItems.end()) {
				setSelection(item);
				scene()->update();
			}

			QPoint p = e->screenPos();
			DesignerContextMenu ctxMenu(this, item);
			ctxMenu.exec(e->screenPos(), e->scenePos());
		}
		else {
			_ctxMenuItems = _selItems;

			DesignerContextMenu ctxMenu(this, _selItems);
			ctxMenu.exec(e->screenPos(), e->scenePos());
		}
	}
}

DesignerComponentList GridDesigner::connectedComponents(DesignerComponent* comp)
{
	DesignerComponentList ret;
	ComponentConnectionsMap::iterator it = _cacheConnections.find(comp);
	if(it != _cacheConnections.end()) {
		ret = it.value();
	}
	else {
		DesignerItemMap items;
		items[comp->id()] = comp;
		addConnectionItems(items);
		items.remove(comp->id());
		
		for(DesignerItemMap::iterator itItems = items.begin(); itItems != items.end(); itItems++) {
			Connection* conn = (Connection*) itItems.value();
			Q_ASSERT(conn->type() == DesignerItem::_connection_item);
			
			if(conn->source() != comp && conn->source())
				ret.append(conn->source());
			else if(conn->destination() != comp && conn->destination())
				ret.append(conn->destination());			
		}
	}
	return ret;
}

void GridDesigner::clearSimulation()
{
	ui.dockOutput->setVisible(false);

	for(DesignerComponentNameMap::iterator it = _simulationComps.begin(); it != _simulationComps.end(); it++) {
		DesignerComponent* comp = it.value();
		comp->clearMessage();
		comp->setRedLevel(0);
	}

	_simulationComps.clear();
	clearGraphics();
}

void GridDesigner::stopSimulation()
{
	_stopSim = true;
	if(_simPaused)
		changePauseSimulationState();
}

void GridDesigner::changePauseSimulationState()
{
	if(_simPaused) {
		_simPaused = false;
		_outputMutex.lock();
		_outputWC.wakeOne();
		_outputMutex.unlock();
	}
	else {
		_pauseSim = true;
	}
}

NetworkPaths GridDesigner::networkPaths(GridNode* node)
{
	NetworkPaths paths;
	DesignerComponentList exList;
	
	paths = networkPaths(node, exList);
	
	return paths;
}

NetworkPaths GridDesigner::networkPaths(DesignerComponent* comp, DesignerComponentList exclude)
{
	NetworkPaths paths;
	DesignerComponentList connectedComp = connectedComponents(comp);

	exclude.append(comp);
	
	for(DesignerComponentList::iterator it = connectedComp.begin(); it != connectedComp.end(); it++) {
		DesignerComponent* currComp = *it;
		NetworkPath path;
		NetworkPaths currCompPaths;

		if(!exclude.contains(currComp)) {
			if(currComp->type() == DesignerItem::_gridnode_item) {
				path.prepend(currComp);
				path.prepend(comp);
				paths.append(path);
			}
			else {
				DesignerComponentList currCompExclude = exclude;

				currCompPaths = networkPaths(currComp, currCompExclude);
				for(NetworkPaths::iterator itPaths = currCompPaths.begin(); itPaths != currCompPaths.end(); itPaths++) {
					NetworkPath currCompPath = *itPaths;
					currCompPath.prepend(comp);
					paths.append(currCompPath);
				}
			}
		}
	}
	
	return paths;
}

QString GridDesigner::createPlatformFile()
{
	QDir tempDir(QDir::tempPath() + "/GridDesigner");

	if(!tempDir.exists()) {
		tempDir.setPath(QDir::tempPath());
		
		if(!tempDir.mkpath("GridDesigner")) {
			QMessageBox::critical(this, tr("Simgrid Simulation Error"),
								  tr("Cannot create directory to store Platform file."));
			return QString();
		}
		tempDir.setPath(QDir::tempPath() + "/GridDesigner");
		if(!tempDir.exists()) {
			QMessageBox::critical(this, tr("Simgrid Simulation Error"),
								  tr("Cannot create directory to store Platform file."));
			return QString();
		}
	}

	// if the file was done and the network wasn't modified -> don't do it again
	QFileInfo fileInfo(_fileName);
	QString platPath = tempDir.absoluteFilePath(fileInfo.baseName() + "_platform.xml");
	QFileInfo platFileInfo(platPath);
	
	//if(platFileInfo.exists()) {
	//	QDateTime lastMod = platFileInfo.lastModified();
	//	if(fileInfo.exists() && lastMod > fileInfo.lastModified()) {
	//		return platPath;
	//	}
	//}
	
	QDomDocument doc;

	QString xmlTemplate("<?xml version=\'1.0\'?>\n" \
						"<!DOCTYPE platform_description SYSTEM \"surfxml.dtd\">\n" \
						"<platform_description version=\"1\">\n" \
						"</platform_description>");
	_cacheConnections.clear();
	
	if(!doc.setContent(xmlTemplate)) {
		QMessageBox::critical(this, tr("Simgrid Simulation Error"),
							  tr("Cannot create Platform file."));
		return QString();
	}

	QDomElement root = doc.firstChildElement("platform_description");
	if(root.isNull()) {
		return QString();
	}

	//  <cpu name="Jupiter" power="76296000"/>
	//  <network_link name="6" bandwidth="41279125" latency="5.9904e-05"/>
	//  <route src="Tremblay" dst="Tremblay"><route_element name="loopback"/></route>
	DesignerItemMap::iterator it;
	NetworkPaths paths;
	NetworkPath path;
	GridNode* node;
	Link* link;
	QDomElement cpuElem, routeElem, routeElementElem, linkElem;
	DesignerComponent* src;
	DesignerComponent* dest;
	DesignerItemMap nodes = topLevelItems(DesignerItem::_gridnode_item);
	DesignerItemMap links = topLevelItems(DesignerItem::_link_item);
	
	for(it = nodes.begin(); it != nodes.end(); it++) {
		node = (GridNode*) it.value();
		cpuElem = doc.createElement("cpu");

		root.appendChild(cpuElem);
		cpuElem.setAttribute("name", node->name());
		cpuElem.setAttribute("power", node->power());
	}

	for(it = links.begin(); it != links.end(); it++) {
		link = (Link*) it.value();
		linkElem = doc.createElement("network_link");

		root.appendChild(linkElem);
		linkElem.setAttribute("name", link->name());
		linkElem.setAttribute("bandwidth", link->bandwidth());
		linkElem.setAttribute("latency", link->latency());
	}

	linkElem = doc.createElement("network_link");

	root.appendChild(linkElem);
	linkElem.setAttribute("name", "loopback");
	linkElem.setAttribute("bandwidth", 498000000);
	linkElem.setAttribute("latency", 0.000015);

	for(it = nodes.begin(); it != nodes.end(); it++) {
		node = (GridNode*) it.value();
		routeElem = doc.createElement("route");
		routeElementElem = doc.createElement("route_element");
		
		root.appendChild(routeElem);
		routeElem.appendChild(routeElementElem);
		routeElem.setAttribute("src", node->name());
		routeElem.setAttribute("dst", node->name());
		routeElementElem.setAttribute("name", "loopback");
	}

	for(it = nodes.begin(); it != nodes.end(); it++) {
		GridNode* node = (GridNode*) it.value();
		paths = networkPaths(node);
		
		for(NetworkPaths::iterator itPaths = paths.begin(); itPaths != paths.end(); itPaths++) {
			//  <route src="Tremblay" dst="Jupiter">
			path = *itPaths;
			routeElem = doc.createElement("route");
			src = path.first();
			dest = path.last();

			root.appendChild(routeElem);
			routeElem.setAttribute("src", src->name());
			routeElem.setAttribute("dst", dest->name());

			for(NetworkPath::iterator itPath = path.begin(); itPath != path.end(); itPath++) {
				DesignerComponent* currComp = *itPath;

				// this if is matched by all the path except first and last items
				if(currComp->type() == DesignerItem::_link_item) {
					routeElementElem = doc.createElement("route_element");
					routeElementElem.setAttribute("name", currComp->name());
					routeElem.appendChild(routeElementElem);
				}
			}
		}
	}

	QFile platFile(platPath);
	
	if (!platFile.open(QFile::WriteOnly | QFile::Text)) {
		QMessageBox::warning(this, tr("Grid Matrix"),
							 tr("Cannot write Platform file."));
		return QString();
	}

	QTextStream out(&platFile);
    doc.save(out, 4);

	return platPath;
}

void GridDesigner::setSimulationEventsState(const QString &tempEntry)
{
	SimulationTemplates temps;
	bool debugMode;

	disconnect(_simgridSim, SIGNAL(taskPut(const QString &, const QString &, const QString &, int)), this, 
		SLOT(onTaskPut(const QString &, const QString &, const QString &, int)));
	disconnect(_simgridSim, SIGNAL(taskExecuteStart(const QString &, const QString &, int, const QString &)), this, 
		SLOT(onTaskExecuteStart(const QString &, const QString &, int, const QString &)));
	disconnect(_simgridSim, SIGNAL(taskExecuteFinish(const QString &, const QString &, int, const QString &, const QString &)), this, 
		SLOT(onTaskExecuteFinish(const QString &, const QString &, int , const QString &, const QString &)));
	disconnect(_simgridSim, SIGNAL(processCreate(const QString &, const QString &, const QString &)), this, 
		SLOT(onProcessCreate(const QString &, const QString &, const QString &)));
	disconnect(_simgridSim, SIGNAL(processStart(const QString &, const QString &, const QString &, int )), this, 
		SLOT(onProcessStart(const QString &, const QString &, const QString &, int )));
	disconnect(_simgridSim, SIGNAL(processTerminate(const QString &, const QString &, const QString &, int , int )), this, 
		SLOT(onProcessTerminate(const QString &, const QString &, const QString &, int , int )));
	disconnect(_simgridSim, SIGNAL(processSuspend(const QString &, const QString &, int )), this, 
		SLOT(onProcessSuspend(const QString &, const QString &, int )));
	disconnect(_simgridSim, SIGNAL(processResume(const QString &, const QString &, int )), this, 
		SLOT(onProcessResume(const QString &, const QString &, int )));
	disconnect(_simgridSim, SIGNAL(functionEntry(const QString &)), this, 
		SLOT(onFunctionEntry(const QString &)));
	disconnect(_simgridSim, SIGNAL(functionExit(const QString &)), this, 
		SLOT(onFunctionExit(const QString &)));

	disconnect(_simgridSim, SIGNAL(scriptOutput(const QString &)), this, SLOT(scriptOutput(const QString &)));
	disconnect(_simgridSim, SIGNAL(scriptError(const QString &)), this, SLOT(scriptError(const QString &)));

	if(temps.customPrefs(tempEntry)) {
		_simgridSim->setDebugMode(temps.debugMode(tempEntry));
		_changeNodesColor = temps.changeNodeColor(tempEntry);
		_showCustomProgress = temps.showCustomSimulationProgress(tempEntry);
		_showProgressNetwork = temps.showSimulationProgressNetwork(tempEntry);
		_showProgressOutput = temps.showSimulationProgressOutputWindow(tempEntry);
		_showOutput = temps.showSimulationOutput(tempEntry);
		_showErrors = temps.showSimulationErrors(tempEntry);
		debugMode = temps.debugMode(tempEntry);
	}
	else {
		_simgridSim->setDebugMode(AppSettings::debugMode());
		_changeNodesColor = AppSettings::changeNodeColor();
		_showCustomProgress = AppSettings::showCustomSimulationProgress();
		_showProgressNetwork = AppSettings::showSimulationProgressNetwork();
		_showProgressOutput = AppSettings::showSimulationProgressOutputWindow();
		_showOutput = AppSettings::showSimulationOutput();
		_showErrors = AppSettings::showSimulationErrors();
		debugMode = AppSettings::debugMode();
	}

	if(_showProgressOutput || _showProgressNetwork || _changeNodesColor) {
		connect(_simgridSim, SIGNAL(taskPut(const QString &, const QString &, const QString &, int)), this, 
			SLOT(onTaskPut(const QString &, const QString &, const QString &, int)), Qt::DirectConnection);
		connect(_simgridSim, SIGNAL(taskExecuteStart(const QString &, const QString &, int, const QString &)), this, 
			SLOT(onTaskExecuteStart(const QString &, const QString &, int, const QString &)), Qt::DirectConnection);
		connect(_simgridSim, SIGNAL(taskExecuteFinish(const QString &, const QString &, int, const QString &, const QString &)), this, 
			SLOT(onTaskExecuteFinish(const QString &, const QString &, int , const QString &, const QString &)), Qt::DirectConnection);
		connect(_simgridSim, SIGNAL(processCreate(const QString &, const QString &, const QString &)), this, 
			SLOT(onProcessCreate(const QString &, const QString &, const QString &)), Qt::DirectConnection);
		connect(_simgridSim, SIGNAL(processStart(const QString &, const QString &, const QString &, int )), this, 
			SLOT(onProcessStart(const QString &, const QString &, const QString &, int )), Qt::DirectConnection);
		connect(_simgridSim, SIGNAL(processTerminate(const QString &, const QString &, const QString &, int , int )), this, 
			SLOT(onProcessTerminate(const QString &, const QString &, const QString &, int , int )), Qt::DirectConnection);
		connect(_simgridSim, SIGNAL(processSuspend(const QString &, const QString &, int )), this, 
			SLOT(onProcessSuspend(const QString &, const QString &, int )), Qt::DirectConnection);
		connect(_simgridSim, SIGNAL(processResume(const QString &, const QString &, int )), this, 
			SLOT(onProcessResume(const QString &, const QString &, int )), Qt::DirectConnection);
	}

	if(_showOutput) {
		connect(_simgridSim, SIGNAL(scriptOutput(const QString &)), this, SLOT(scriptOutput(const QString &)), Qt::DirectConnection);
	}

	if(_showErrors) {
		connect(_simgridSim, SIGNAL(scriptError(const QString &)), this, SLOT(scriptError(const QString &)), Qt::DirectConnection);
	}

	if(debugMode) {
		connect(_simgridSim, SIGNAL(functionEntry(const QString &)), this, 
			SLOT(onFunctionEntry(const QString &)), Qt::DirectConnection);
		connect(_simgridSim, SIGNAL(functionExit(const QString &)), this, 
			SLOT(onFunctionExit(const QString &)), Qt::DirectConnection);
	}
}

void GridDesigner::runAgain()
{
	if(_simPaused) {
		changePauseSimulationState();
		return;
	}

	SimulationTemplates temps;
	QString tempEntry = temps.lastTemplateRun();
	QString deployFile;
	QString scriptFile;

	if(!tempEntry.isEmpty()) {
		scriptFile = temps.scriptFile(tempEntry);
		deployFile = temps.lastDeployFile(tempEntry);
		run(tempEntry, scriptFile, deployFile);
	}
}

void GridDesigner::run()
{
	int ret;
	QString tempEntry, scriptFile, deployFile;

	// if the dialog lives when calling run, the dialog eats the events and the user cannot interact 
	{
		RunDlg runDlg(this);
		ret = runDlg.exec();
		if(ret == QDialog::Accepted) {
			tempEntry = runDlg.tempEntry();
			scriptFile = runDlg.scriptFile();
			deployFile = runDlg.deployFile();
		}
	}
	if(ret == QDialog::Accepted) {
		run(tempEntry, scriptFile, deployFile);
	}
}

void GridDesigner::run(const QString &tempEntry, const QString &scriptFile, const QString &deployFile)
{
	///////////////////// BEGIN TEST
	//SimulationGraphic *graph = createGraphic();
	//SimulationGraphicLine *line = graph->createLine();
	//line->setColor(QColor(Qt::green).red(), QColor(Qt::green).green(), QColor(Qt::green).blue(), 40);
	////line->setWidth(10);

	////line->addPoint(0, 0);
	////line->addPoint(10, 10);
	////line->addPoint(15, 12);
	////line->addPoint(20, 19);
	////line->addPoint(25, 12);
	////line->addPoint(40, 20);
	////line->addPoint(50, 20);
	////line->addPoint(60, 80);
	////line->addPoint(70, 60);
	////line->addPoint(80, 20);
	////line->addPoint(90, 40);
	////line->addPoint(100, 90);
	////line->addPoint(110, 30);
	////line->addPoint(120, 40);
	////line->addPoint(130, 100);
	////line->addPoint(140, 80);
	////line->addPoint(150, 25);

	//line->addPoint(0, 0);
	//line->addPoint(10, 100);
	//line->addPoint(15, 12);
	//line->addPoint(20, 19);
	//line->addPoint(25, 12);
	//line->addPoint(40, 20);
	//line->addPoint(50, 20);
	//line->addPoint(60, 80);
	//line->addPoint(70, 60);
	//line->addPoint(80, 20);
	//line->addPoint(90, 40);
	//line->addPoint(100, 100);

	//SimulationResults res(this);
	//res.setGraphics(_graphics);
	//res.exec();
	//clearGraphics();
	//
	//
	//return;
	///////////////////// END TEST


	ui.outputEdit->setFontWeight(QFont::Normal);
	clearSimulation();

	ui.actionRun->setEnabled(false);
	ui.actionRunAgain->setEnabled(false);
	ui.actionPause->setEnabled(true);
	ui.actionStop->setEnabled(true);

	setSimulationEventsState(tempEntry);

	statusBar()->showMessage(tr("Simulating ..."));

	_stopSim = false;
	_pauseSim = false;
	_simPaused = false;

	ui.actionDisplay_Graphics->setEnabled(false);

	// save components for quick look up.
	DesignerItemList items;
	DesignerItemList::iterator it;
	DesignerItemList gridNodes;

	gridNodes = topLevelItems(DesignerItem::_gridnode_item).values();
	items = gridNodes;
	items += topLevelItems(DesignerItem::_link_item).values();

	for(it = items.begin(); it != items.end(); it++) {
		DesignerComponent* comp = (DesignerComponent*) *it;

		_simulationComps[comp->name()] = comp;
	}

	if(AppSettings::openOutputWindow())
		ui.dockOutput->show();
	
	ui.outputEdit->append(tr("Saving network..."));

	if(!fileSave()) {
		return;
	}

	ui.outputEdit->append(tr("Building Platform file..."));

	QString platFile = createPlatformFile();
	
	if(platFile.isNull()) {
		ui.outputEdit->append(tr("Error creating Platform file."));
		return;
	}
	
	ui.outputEdit->append(tr("Done."));
	ui.outputEdit->append(tr("Initializing Python..."));

	if(!_simgridSim->init()) {
		QMessageBox::critical(this, tr("Simgrid Simulation Error"),
							  tr("Cannot initialize."));
		return;
	}

	ui.outputEdit->append(tr("Done."));
	ui.outputEdit->append(tr("Running..."));

	_running = true;
	_simgridSim->start(scriptFile, platFile, deployFile);

	double lastSimTime = _simgridSim->simulationTime();

	// wait until the script finishes in the other thread.
	while(_running) {
		QApplication::processEvents();
		lastSimTime = _simgridSim->simulationTime();
		statusBar()->showMessage(tr("Simulating ... %1").arg(lastSimTime));
	}

	if(!_simStopped) {
		ui.outputEdit->setFontWeight(QFont::Bold);
		ui.outputEdit->append(tr("\nSimulationTime: %1\n").arg(lastSimTime));
		ui.outputEdit->setFontWeight(QFont::Normal);
	}
	else {
		ui.outputEdit->setFontWeight(QFont::Bold);
		ui.outputEdit->append(tr("\nUser Break.\n").arg(lastSimTime));
		ui.outputEdit->setFontWeight(QFont::Normal);
	}

	if(_simError) {
		statusBar()->showMessage(tr("Simulation Finished with Errors"), 10000);
	}
	else {
		statusBar()->showMessage(tr("Simulation Finished"), 10000);
	}

	ui.actionRun->setEnabled(true);
	ui.actionRunAgain->setEnabled(true);
	ui.actionPause->setEnabled(false);
	ui.actionStop->setEnabled(false);

	if(_graphics.count() > 0 && !_simError) {
		ui.actionDisplay_Graphics->setEnabled(true);

		if(QMessageBox::information(this, tr("Simulation Finished"), tr("Do you want to display results?"), 
			QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
			//SimulationGraphic *graph = createGraphic();
			//SimulationGraphicLine *line = graph->createLine();
			//line->setColor(QColor(Qt::green).red(), QColor(Qt::green).green(), QColor(Qt::green).blue(), 40);
			////line->setWidth(10);
	
			//line->addPoint(0, 0);
			//line->addPoint(10, 10);
			//line->addPoint(15, 12);
			//line->addPoint(20, 19);
			//line->addPoint(25, 12);
			//line->addPoint(40, 20);
			//line->addPoint(50, 20);
			//line->addPoint(60, 80);
			//line->addPoint(70, 60);
			//line->addPoint(80, 20);
			//line->addPoint(90, 40);
			//line->addPoint(100, 90);
			//line->addPoint(110, 30);
			//line->addPoint(120, 40);
			//line->addPoint(130, 100);
			//line->addPoint(140, 80);
			//line->addPoint(150, 25);

			SimulationResults res(this);
			res.setGraphics(_graphics);
			res.exec();
		}
	}

}

void GridDesigner::cancel()
{
	if(_selRect) {
		delete _selRect;
		_selRect = NULL;
		scene()->update();
	}
	else if(!_movingItems.empty()) {
		_movingItems.deleteAllItems();
		scene()->update();
	}
	else if(_curConnection) {
		delete _curConnection;
		_curConnection = NULL;
		scene()->update();
	}
	else {
		clearSimulation();
	}
}

void GridDesigner::scriptStdoutMsg(const char *msg)
{
	scriptOutput(QString(msg));
}

void GridDesigner::scriptStderrMsg(const char *msg)
{
	scriptError(QString(msg));
}

void GridDesigner::scriptOutput(const QString &msg)
{
	if(!_showOutput)
		return;

	QString outMsg = msg;
	int pos = outMsg.indexOf('\n');
	while(pos != -1) {
		QString line = outMsg.mid(0, pos);
		_curScriptOutput += line;

		SimulationEvent* ev = new SimulationEvent(SimulationEvent::_script_output, "", _curScriptOutput);
		syncEvent(ev);

		//if(thread() != QThread::currentThread()) {
		//	ev->setMutex(&_outputMutex);
		//	ev->setWaitCondition(&_outputWC);

		//	QApplication::postEvent(this, ev);
		//	
		//	_outputMutex.lock();
		//	_outputWC.wait(&_outputMutex);
		//	_outputMutex.unlock();
		//	
		//}
		//else {
		//	QApplication::sendEvent(this, ev);
		//}

		_curScriptOutput = QString();
		outMsg = outMsg.mid(pos+1);
		pos = outMsg.indexOf('\n');
	}
	
	_curScriptOutput += outMsg;
}

void GridDesigner::scriptError(const QString &msg)
{
	if(!_showErrors)
		return;

	QString outMsg = msg;
	int pos = outMsg.indexOf('\n');
	while(pos != -1) {
		QString line = outMsg.mid(0, pos);
		_curScriptError += line;
		
		SimulationEvent* ev = new SimulationEvent(SimulationEvent::_script_error, "", _curScriptError);
		syncEvent(ev);

		//if(thread() != QThread::currentThread()) {

		//	ev->setMutex(&_outputMutex);
		//	ev->setWaitCondition(&_outputWC);

		//	QApplication::postEvent(this, ev);
		//	
		//	_outputMutex.lock();
		//	_outputWC.wait(&_outputMutex);
		//	_outputMutex.unlock();
		//}
		//else {
		//	QApplication::sendEvent(this, ev);
		//}

		_curScriptError = QString();
		outMsg = outMsg.mid(pos+1);
		pos = outMsg.indexOf('\n');
	}
	
	_curScriptError += outMsg;
}

void GridDesigner::closeEvent(QCloseEvent *e)
{
	if(saveDiscardCancelDlg())
		e->ignore();
	else
		e->accept();
}

bool GridDesigner::eventFilter(QObject *obj, QEvent *e)
{
	if(obj == scene()) {
		switch(e->type()) {
			case QEvent::GraphicsSceneMouseRelease:
			{
				sceneMouseReleaseEvent((QGraphicsSceneMouseEvent*) e);
				return true;
			}
			case QEvent::GraphicsSceneMousePress:
			{
				sceneMousePressEvent((QGraphicsSceneMouseEvent*) e);
				return true;
			}
			case QEvent::GraphicsSceneMouseMove:
			{
				sceneMouseMoveEvent((QGraphicsSceneMouseEvent*) e);
				return true;
			}
			case QEvent::GraphicsSceneContextMenu:
			{
				sceneContextMenu((QGraphicsSceneContextMenuEvent*) e);
			}
		}
	}

	return QObject::eventFilter(obj, e);
}

void GridDesigner::outputContextMenuRequested(const QPoint &p)
{
	QMenu *menu = ui.outputEdit->createStandardContextMenu();
	menu->addSeparator();
	menu->addAction(ui.actionClearOutput);
	menu->exec(ui.outputEdit->mapToGlobal(p));
	delete menu;
}

void GridDesigner::customEvent(QEvent* e)
{
	switch(e->type()) {
		case QtSimulationEventType:
		{
			SimulationEvent* ev = (SimulationEvent*) e;

			switch(ev->simEventType()) {
				case SimulationEvent::_function_entry:
				case SimulationEvent::_function_exit:
				case SimulationEvent::_script_output:
				{
					ui.outputEdit->setFontWeight(QFont::Normal);
					ui.outputEdit->append(ev->message());
					break;
				}
				case SimulationEvent::_script_error:
				{
					ui.outputEdit->setFontWeight(QFont::Bold);
					ui.outputEdit->append(ev->message());
					ui.outputEdit->setFontWeight(QFont::Normal);
					break;
				}
				case SimulationEvent::_task_put:
				case SimulationEvent::_task_exec_start:
				case SimulationEvent::_task_exec_finish:
				case SimulationEvent::_process_create:
				case SimulationEvent::_process_start:
				case SimulationEvent::_process_terminate:
				case SimulationEvent::_process_suspend:
				case SimulationEvent::_process_resume:
				{
					// if non is set -> do nothing
					if(_showProgressNetwork || _changeNodesColor) {
						DesignerComponent* comp = simulationComponentByName(ev->component());
						if(comp) {
							if(_changeNodesColor) {
								if(ev->simEventType() == SimulationEvent::_task_exec_start) {
									comp->setRedLevel(comp->redLevel()+5);
								}
								else if(ev->simEventType() == SimulationEvent::_task_exec_finish) {
									comp->setRedLevel(comp->redLevel()-5);
								}
							}
							if(_showProgressNetwork) {
								comp->setMessage(ev->message());
							}
							scene()->update();
						}
					}
					if(_showProgressOutput) {
						ui.outputEdit->append(tr("%1: %2").arg(ev->component()).arg(ev->message()));
					}
					break;
				}
				case SimulationEvent::_set_message:
				{
					DesignerComponent* comp = simulationComponentByName(ev->component());
					if(comp) {
						comp->setMessage(ev->message());
						scene()->update();
					}
					break;
				}
				case SimulationEvent::_set_red_level:
				{
					GridNode* node = (GridNode*) simulationComponentByName(ev->component());
					if(node) {
						node->setRedLevel(ev->level());
						scene()->update();
					}
					break;
				}
				case SimulationEvent::_inc_red_level:
				{
					GridNode* node = (GridNode*) simulationComponentByName(ev->component());
					if(node) {
						node->setRedLevel(node->redLevel()+5);
						scene()->update();
					}
					break;
				}
				case SimulationEvent::_dec_red_level:
				{
					GridNode* node = (GridNode*) simulationComponentByName(ev->component());
					if(node) {
						node->setRedLevel(node->redLevel()-5);
						scene()->update();
					}
					break;
				}
				default:
					return;
			}
			
			if(_stopSim) {
				_stopSim = false;
				_simgridSim->stopSimulation();
			}
			if(_pauseSim) {
				_pauseSim = false;
				_simPaused = true;
			}
			else {
				if(ev->mutex()) {
					ev->mutex()->lock();
					ev->wakeOne();
					ev->mutex()->unlock();
				}
			}
			break;
		}
		case QtUpdateTemplatesEventType:
		{
			updateTemplates();
			break;
		}
	}
}

void GridDesigner::simulationFinished(bool stopped, bool error, int retCode)
{
	_running = false;
	_simStopped = stopped;
	_simError = error;
}

void GridDesigner::displayGraphics()
{
	SimulationResults res(this);
	res.setGraphics(_graphics);
	res.exec();
}

void GridDesigner::syncEvent(SimulationEvent *ev)
{
	if(thread() != QThread::currentThread()) {
		_outputMutex.lock();
		ev->setMutex(&_outputMutex);
		ev->setWaitCondition(&_outputWC);

		QApplication::postEvent(this, ev);
	
		_outputWC.wait(&_outputMutex);
		_outputMutex.unlock();

	}
	else {
		QApplication::sendEvent(this, ev);
	}
}

void GridDesigner::setComponentMessage(const char *comp, const char *message)
{
	if(!_showCustomProgress)
		return;	
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_set_message, comp, message);
	syncEvent(ev);
}

void GridDesigner::setComponentRedLevel(const char *comp, int level)
{
	if(!_showCustomProgress)
		return;
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_set_red_level, comp, "", level);
	syncEvent(ev);
}

void GridDesigner::incComponentRedLevel(const char *comp)
{
	if(!_showCustomProgress)
		return;
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_inc_red_level, comp);
	syncEvent(ev);
}

void GridDesigner::decComponentRedLevel(const char *comp)
{
	if(!_showCustomProgress)
		return;
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_dec_red_level, comp);
	syncEvent(ev);
}

SimulationGraphic *GridDesigner::createGraphic()
{
	SimulationGraphic *graph = new SimulationGraphic;
	_graphics.append(graph);
	return graph;
}

void GridDesigner::clearGraphics()
{
	ui.actionDisplay_Graphics->setEnabled(false);

	for(SimulationGraphicList::iterator it = _graphics.begin(); it != _graphics.end(); it++) {
		delete (*it);
	}
	_graphics.clear();
}

void GridDesigner::onTaskPut(const QString &who, const QString &dest, const QString &task, int channel)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_task_put, who, tr("Put task %1 in host %2").arg(task).arg(dest));
	syncEvent(ev);
}

void GridDesigner::onTaskExecuteStart(const QString &host, const QString &procName, int procId, const QString &task)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_task_exec_start, host, tr("Executing task %1").arg(task));
	syncEvent(ev);
}

void GridDesigner::onTaskExecuteFinish(const QString &host, const QString &procName, int procId, const QString &task, const QString &errorStr)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_task_exec_finish,  host, tr("Finished task %1").arg(task));
	syncEvent(ev);
}

void GridDesigner::onProcessCreate(const QString &whoHost, const QString &fncName, const QString &destHost)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_process_create, whoHost, tr("Process created in host %1").arg(destHost));
	syncEvent(ev);
}

void GridDesigner::onProcessStart(const QString &fncName, const QString &host, const QString &procName, int procId)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_process_start, host, tr("Process started"));
	syncEvent(ev);
}

void GridDesigner::onProcessTerminate(const QString &fncName, const QString &host, const QString &procName, int procId, int retCode)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_process_terminate, host, tr("Process terminated"));
	syncEvent(ev);
}

void GridDesigner::onProcessSuspend(const QString &host, const QString &procName, int procId)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_process_suspend, host, tr("Process suspended"));
	syncEvent(ev);
}

void GridDesigner::onProcessResume(const QString &host, const QString &procName, int procId)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_process_resume, host, tr("Process resumed"));
	syncEvent(ev);
}

void GridDesigner::onFunctionEntry(const QString &fncName)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_function_entry, "", tr("Function Entry: %1").arg(fncName));
	syncEvent(ev);
}

void GridDesigner::onFunctionExit(const QString &fncName)
{
	SimulationEvent* ev = new SimulationEvent(SimulationEvent::_function_exit, "", tr("Function Exit: %1").arg(fncName));
	syncEvent(ev);
}
