
#ifndef GRIDDESIGNER_H
#define GRIDDESIGNER_H

#include "ui_griddesigner.h"

#include <qactiongroup.h>
#include <qgraphicssceneevent.h>
#include <qgraphicsitem.h>

#include "graphic_components.h"
#include "script_api.h"

class Command;

class DesignerItem;
class DesignerComponent;
class Connection;
class DesignerContextMenu;
class SimgridSimulation;


class NetworkPath : public DesignerComponentList {
public:
};

typedef QList<NetworkPath> NetworkPaths;


#define QtSimulationEventType QEvent::User + 5
#define QtUpdateTemplatesEventType QEvent::User + 6

class SimulationEvent : public QEvent {
public:
	typedef enum {
		_script_output,
		_script_error,
		_task_put,
		_task_exec_start,
		_task_exec_finish,
		_process_create,
		_process_start,
		_process_terminate,
		_process_suspend,
		_process_resume,
		_function_entry,
		_function_exit,
		_set_message,
		_set_red_level,
		_inc_red_level,
		_dec_red_level
	} SimulationEventType;

	SimulationEvent(SimulationEventType t, const QString &comp, const QString &msg = QString(), int level = 0) : QEvent(QEvent::Type(QtSimulationEventType))
	{
		_type = t;
		_comp = comp;
		_msg = msg;
		_mutex = NULL;
		_cond = NULL;
		_level = level;
	}
	~SimulationEvent()
	{
		//wakeAll();
	}
	void wakeOne()
	{
		_cond->wakeOne();
	}

	void setMutex(QMutex* mutex) { _mutex = mutex; }
	QMutex* mutex() { return _mutex; }

	void setWaitCondition(QWaitCondition* cond) { _cond = cond; }
	QWaitCondition* waitCondition() { return _cond; }

	QString component() { return _comp; }
	QString message() { return _msg; }
	int level() { return _level; }

	SimulationEventType simEventType() { return _type; }
private:
	QWaitCondition* _cond;
	QMutex* _mutex;
	QString _comp, _msg;
	int _level;
	SimulationEventType _type;
};

class UpdateTemplatesEvent : public QEvent {
public:
	UpdateTemplatesEvent() : QEvent(QEvent::Type(QtUpdateTemplatesEventType)) {}
};


class GridDesigner : public QMainWindow, public SimgridSimulationInterface
{
    Q_OBJECT

	friend class DesignerContextMenu;
	friend class DesignerItemMap;
	
	typedef enum _SelectionType {
		_pointer,
		_gridnode_component,
		_link_component,
		_connection_component,
		_comment_component
	} SelectionType;

	typedef QList<Command*> CommandList;
	typedef QMap<DesignerComponent*, DesignerComponentList> ComponentConnectionsMap;
	typedef QMap<QString, DesignerComponent*> DesignerComponentNameMap;

	typedef QList<DesignerItem*> DesignerItemList;

public:
    GridDesigner(QWidget *parent = 0);
	~GridDesigner();

	void executeCommand(Command *command, bool removeRedoCmds=true);

	void setActionSelection(SelectionType selection);

	/**
	Get first top-level component at p.<br>
	if onlyComponents is true it returns only DesignerItem objects that has component property true.
	*/
	DesignerItem* topLevelItem(const QPointF& p, bool onlyComponents=true, QGraphicsItem** childItem=NULL);
	static DesignerItem* topLevelItem(QGraphicsItem* item, bool onlyComponents=true);
	static DesignerItem* topLevelItem(QGraphicsScene* scene, const QPointF& p, bool onlyComponents, QGraphicsItem** childItem);
	
	/**
	Return: all top-level items in rect.<br>
	If rect is NULL, returns all top-level items.
	*/
	static DesignerItemMap topLevelItems(QGraphicsScene* scene, 
										 DesignerItem::ComponentType compType = DesignerItem::_invalid_item, 
										 const QRectF& rect=QRectF());
	DesignerItemMap topLevelItems(DesignerItem::ComponentType compType = DesignerItem::_invalid_item, 
								  const QRectF& rect=QRectF());

	DesignerComponent* simulationComponentByName(const QString &name);
	
	static GraphicsItemList items(QGraphicsScene* scene, const QPointF& p);

	QGraphicsScene* scene();

	/**
	If setSelected is true setSelected(true) is called for each selected item.
	*/
	void setSelection(DesignerItem* item, const QRectF& selRect = QRectF(), bool setSelected = true);

	/**
	If setSelected is true setSelected(true) is called for each selected item.
	*/
	void setSelection(DesignerItemMap items, const QRectF& selRect = QRectF(), bool setSelected = true);

	void addSelection(DesignerItem* item);
	void addSelection(DesignerItemMap items);
	void addSelection(DesignerItemMap items, const QRectF& selRect);

	void removeSelection(DesignerItemMap items);
	void removeSelection(DesignerItem* item);

	const DesignerItemMap& selection() { return _selItems; }

	void clearSelection();

	void setNetworkName(const QString& name);
	const QString& networkName() { return _networkName; }

	/**
	Return: a selection rect from p.
	*/
	static QRectF selectionRect(const QPointF& p);

	/**
	Add to items all the Connection items whose source or destination is in items.
	*/
	void addConnectionItems(DesignerItemMap& items);

	bool isDirty() { return _dirty; }

	///
	/// From SimgridSimulationInterface
	///
	void setComponentMessage(const char *comp, const char *message);
	void setComponentRedLevel(const char *comp, int level);
	void incComponentRedLevel(const char *comp);
	void decComponentRedLevel(const char *comp);
	void scriptStdoutMsg(const char *msg);
	void scriptStderrMsg(const char *msg);

	SimulationGraphic *createGraphic();
	void clearGraphics();

public slots:

	/**
	Clear all simulation.
	*/
	void clearSimulation();

	void stopSimulation();
	void changePauseSimulationState();

	void simulationFinished(bool stopped, bool error, int retCode);

	void onTaskPut(const QString &who, const QString &dest, const QString &task, int channel);
	void onTaskExecuteStart(const QString &host, const QString &procName, int procId, const QString &task);
	void onTaskExecuteFinish(const QString &host, const QString &procName, int procId, const QString &task, const QString &errorStr);
	void onProcessCreate(const QString &whoHost, const QString &fncName, const QString &destHost);
	void onProcessStart(const QString &fncName, const QString &host, const QString &procName, int procId);
	void onProcessTerminate(const QString &fncName, const QString &host, const QString &procName, int procId, int retCode);
	void onProcessSuspend(const QString &host, const QString &procName, int procId);
	void onProcessResume(const QString &host, const QString &procName, int procId);

	void onFunctionEntry(const QString &fncName);
	void onFunctionExit(const QString &fncName);

protected:
	int lastId() { return _lastId++; }

	void processSelection(const QPointF& p, bool controlPressed);
	void reset();

	/**
	Show Save / Discard / Cancel dialog if the document is dirty.<br>
	Return: false if the action is canceled.
	*/
	bool saveDiscardCancelDlg();

private slots:
	void setPointer();
	void addGridNode();
	void addLink();
	void addConnection();
	void removeConnectionPoint();
	void editComponent();
	void deleteComponents();
	void editUndo();
	void editRedo();
	void editSelectAll();
	void addConnectionPoint();
	void componentProperties();

	void fileOpen(const QString &docName = QString());
	void fileNew();
	bool fileSave(bool force = false);
	bool fileSaveAs();

	void setDirty(bool dirty);

	void networkProperties();
	
	void preferences();

	void addTemplate();
	void removeTemplate();

	void postUpdateTemplates();
	void updateTemplates();
	void templateProperties();

	void run();
	void run(const QString &tempEntry, const QString &scriptFile, const QString &deployFile = QString());
	void runAgain();

	void scriptOutput(const QString &msg);
	void scriptError(const QString &msg);

	void dirtyChanged(bool dirty);

	void cancel();

	void displayGraphics();

	void outputContextMenuRequested(const QPoint &);

signals:
	void dirty(bool dirty);
	

protected:
	QString createPlatformFile();
	NetworkPaths networkPaths(GridNode* node);
	NetworkPaths networkPaths(DesignerComponent* comp, DesignerComponentList exclude);

	DesignerComponentList connectedComponents(DesignerComponent* comp);
	
	QString newGridNodeName();
	QString newLinkName();

	void mouseReleaseEvent(QMouseEvent* event);
	void sceneMouseReleaseEvent(QGraphicsSceneMouseEvent* e);
	void sceneMousePressEvent(QGraphicsSceneMouseEvent* e);
	void sceneMouseMoveEvent(QGraphicsSceneMouseEvent* e);
	void sceneContextMenu(QGraphicsSceneContextMenuEvent* e);

	void updateDirty();
	void updateWindowName();

	/**
	Convert p to a point inside the scene rect. If it is inside do nothing.
	*/
	QPointF adjustToSceneRect(const QPointF& p);

	bool eventFilter(QObject *obj, QEvent *event);
	void closeEvent(QCloseEvent *e);
	void customEvent(QEvent* e);

	void syncEvent(SimulationEvent *ev);

	void setSimulationEventsState(const QString &tempEntry);

	Ui_GridDesigner* getUi() { return &ui; }

private:
	ComponentConnectionsMap _cacheConnections;

    Ui_GridDesigner ui;
	CommandList _undoCommands, _redoCommands;
	QActionGroup *_newComponentActionGroup;
	bool _dirty;
	Command* _lastSavedCommand;

	SelectionType _selectedComponentType;
	int _lastId;
	int _gridNodeNameCount;
	int _linkNameCount;

	Connection* _curConnection;
	Connection* _newPointConnection;
	QGraphicsRectItem* _selRect;
	DesignerItemMap _selItems;
	DesignerItemMap _movingItems;

	// true when the user presses a mouse button until the first move event
	bool _firstMove;

	// context menu
	DesignerItemMap _ctxMenuItems;
	QGraphicsItem* _ctxChildItem;
	QPointF _ctxPoint;

	QPointF _startMovePosition;
	QPointF _prevMovePosition;

	QString _fileName;
	QString _networkName;

	QAction *_tempMenuSeparator;
	QActionGroup *_tempActions;

	SimgridSimulation *_simgridSim;

	QString _curScriptOutput, _curScriptError;

	// simulation
	bool _running;
	DesignerComponentNameMap _simulationComps;

	bool _showProgressOutput, _showProgressNetwork;
	bool _showOutput, _showErrors;

	QMutex _outputMutex;
	QWaitCondition _outputWC;
	bool _simStopped;
	bool _simError;
	bool _stopSim;
	bool _pauseSim;
	bool _simPaused;

	bool _changeNodesColor;
	bool _showCustomProgress;
	bool _showSimProgressNet;
	double _lastSimTime;

	SimgridSimulationInterface *_innerInstance;
	SimulationGraphicList _graphics;
};

#endif

