#ifndef __GRAPHIC_COMPONENTS_H
#define __GRAPHIC_COMPONENTS_H

#include <QtXml/QtXml>
#include <qgraphicsscene.h>
#include <qgraphicsitem.h>


// if this property is set the QGraphicsItem is a item created to show the selection (e.g.: a QGraphicsRectItem)
#define SELECTION_PROPERTY 2321

#define GRID_NODE_NAME "GridNode"
#define LINK_NAME "Link"
#define CONNETION_NAME "Connection"
#define COMMENT_NAME "Comment"

class DesignerItem;
class DesignerComponent;
class Connection;
class GridDesigner;


typedef QList<DesignerItem*> ItemList;
typedef QMap<int, DesignerItem*> ItemMap;

typedef QList<QGraphicsItem*> GraphicsItemList;
typedef QList<QPointF> PointList;

typedef QMap<Connection*, PointList> ConnectionPointMap;

typedef QList<Connection*> ConnectionList;
typedef QList<DesignerComponent*> DesignerComponentList;


class DesignerItemMap : public ItemMap {
public:

	DesignerItemMap() {}
	DesignerItemMap(const ItemMap& itemMap);
	DesignerItemMap(const DesignerItemMap& itemMap);

	/**
	Remove items from the map and delete them.
	*/
	void deleteAllItems();

	void moveBy(qreal dx, qreal dy);
	void moveBy(const QPointF& dp);

	void hide();
	void show();

	void removeFromScene();
	void addToScene(QGraphicsScene* scene);
	
	/**
	setFixedPoints: all the points of Connection objects that are not selected are set fixed
	*/
	DesignerItemMap clone(bool setFixedPoints=true);

	bool fromXml(GridDesigner* designer, const QDomElement& node);

	DesignerComponent* componentByName(const QString& itemName);

	void setMovingPoints(const ConnectionPointMap& points);
	ConnectionPointMap movingPoints();

	void setSelectedPoints(const ConnectionPointMap& points);
	ConnectionPointMap selectedPoints();

	/**
	Return: true if any of the items in the map can be moved (e.g.: Connection without selected points cannot be moved).
	*/
	bool anyMoveItem();

protected:
	//void calcBoundingRect();
private:
	QRectF _boundRect;
	ConnectionPointMap _movingPoints;
};



/**
All top-level items in the designer must inherit this class.
*/
class DesignerItem : public QObject, public QGraphicsItem {
	Q_OBJECT
public:
	typedef enum {
		_invalid_item = 0,
		_basic_item = QGraphicsItem::UserType + 1,
		_connection_item = _basic_item + 1,
		_comment_item = _connection_item + 1,
		_component_item = QGraphicsItem::UserType + 100,
		_gridnode_item = _component_item + 1,
		_link_item = _gridnode_item + 1
	} ComponentType;

	typedef QList<QGraphicsRectItem*> RectList;

	DesignerItem(QGraphicsScene* scene, int compId) : QGraphicsItem(NULL, scene) { _compId = compId; _selected = false; }
	~DesignerItem() { setSelected(false); }

	virtual bool load(DesignerItemMap& map, const QDomElement& element) = 0;

	/**
	Set the selection property of the item.<br>
	selRect is the selection rect for partial selection. By default selRect is ignored.<br>
	When selected is false selRect is ignored.
	*/
	virtual void setSelected(bool selected, const QRectF& selRect = QRectF());
	virtual bool selected() { return _selected; }

	virtual DesignerItem* clone() = 0;
	int id() { return _compId; }
	void setId(int id) { _compId = id; }

	virtual int type() const { return _basic_item; }

	/**
	Return: true if the item is a component. Components are items that can be connected.<br>
	All components inherit DesignerComponent.
	*/
	virtual bool isComponent() { return false; }

	virtual void moveBy(qreal dx, qreal dy) { QGraphicsItem::moveBy(dx, dy); }

	static QGraphicsRectItem* newSelectionRect(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent, QGraphicsScene *scene);

	virtual QString itemName() const = 0;
	virtual QDomElement serialize(QDomDocument& doc, QDomElement& parentElement) = 0;

signals:
	void moved();

protected:
	/**
	Implement this function to paint the object selected.
	*/
	virtual void paintSelected(bool) {}

	/**
	Only set the property
	*/
	void setSelectedProperty(bool selected) { _selected = selected; }

private:
	int _compId;
	bool _selected;
};


/**
A item that implements a QGraphicsPixmapItem with a text. This class encapsulates all the 'big' components of a network<br>
such as GridNode and Link.
*/
class DesignerComponent : public DesignerItem {
	Q_OBJECT

public:
	DesignerComponent(const QPixmap& pix, QGraphicsScene* scene, int compId);

	int type() const { return _component_item; }

	virtual bool isComponent() { return true; }

	void setName(const QString& text);
	QString name();

	/**
	From DesignerItem.
	*/
	bool load(DesignerItemMap& map, const QDomElement& element);

	/**
	Return: width of the image part of the component. We cannot use the image width because the text can be wider than<br>
	the image so get the widest using boundingRect.
	*/
	int imageWidth() { return boundingRect().width(); }

	/**
	Return: height of the image part of the component.
	*/
	int imageHeight() { return _pixItem->pixmap().height(); }

	/**
	From QGraphicsItem.
	*/
	QRectF boundingRect () const;

	void moveBy(qreal dx, qreal dy);

	QDomElement serialize(QDomDocument& doc, QDomElement& parentElement);

	void refreshSelection();

	/**
	Change pixmap of the component.
	*/
	void setPixmap(const QPixmap &pix);

	//void addConnection(Connection* conn);
	//void removeConnection(Connection* conn);

	/**
	Return: the list of components connected directly to this.
	*/
	//DesignerComponentList connectedComponents() const;

	/**
	Set a message that will appear at the top of the item.
	*/
	void setMessage(const QString &text);
	void clearMessage() { setMessage(QString()); }

	/**
	Param level (%): Determines that the component shows a level of red to show that the node is busy or other during a simulation.
	*/
	virtual void setRedLevel(int level);

	virtual int redLevel() { return _redLevel; }

signals:
	void moved();

protected:
	void initialize(const QPixmap& pix, QGraphicsScene* scene);

	/**
	From QGraphicsItem.
	*/
	void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);

	/**
	From DesignerItem
	*/
	void paintSelected(bool selected);

private:
	QGraphicsPixmapItem* _pixItem;
	QGraphicsTextItem* _textItem;
	QGraphicsTextItem* _msgItem;
	RectList _selRects;
	int _redLevel;
};

class GridNode : public DesignerComponent {
//	Q_OBJECT

public:
	GridNode(QGraphicsScene* scene, int compId);

	int type() const { return _gridnode_item; }
	
	virtual DesignerItem* clone();

	QString itemName() const;

	void setPower(double power) { _power = power; }
	double power() { return _power; }

	/**
	From DesignerItem.
	*/
	bool load(DesignerItemMap& map, const QDomElement& element);

	/**
	Param level (%): Determines that the component shows a level of red to show that the node is busy or other during a simulation.
	*/
	void setRedLevel(int level);

	QDomElement serialize(QDomDocument& doc, QDomElement& parentElement);

private:
	double _power;
};

class Link : public DesignerComponent {
//	Q_OBJECT

public:
	Link(QGraphicsScene* scene, int compId);
	Link(QGraphicsScene* scene, const QDomElement& xml, int compId);

	int type() const { return _link_item; }
	
	virtual DesignerItem* clone();

	QString itemName() const;

	void setLatency(double lat) { _latency = lat; }
	double latency() { return _latency; }

	void setBandwidth(double band) { _bandwidth = band; }
	double bandwidth() { return _bandwidth; }

	/**
	From DesignerItem.
	*/
	bool load(DesignerItemMap& map, const QDomElement& element);

	QDomElement serialize(QDomDocument& doc, QDomElement& parentElement);

private:
	double _latency;
	double _bandwidth;
};

class Connection : public DesignerItem {
	Q_OBJECT

	typedef QList<QGraphicsLineItem*> QGraphicsLineItemList;
	typedef QMap<QGraphicsLineItem*, QGraphicsRectItem*> SelectionMap;
public:
	Connection(QGraphicsScene* scene, int compId);
	~Connection();

	int type() const { return _connection_item; }

	DesignerItem* clone();
	DesignerItem* clone(DesignerComponent* src, DesignerComponent* dest, bool setFixedPoint);

	/**
	From DesignerItem.
	*/
	bool load(DesignerItemMap& map, const QDomElement& element);

	void setSelected(bool selected, const QRectF& selRect = QRectF());
	void setSelectedPoints(const PointList& points);

	PointList selectedPoints();
	PointList unselectedPoints();

	void setFixedPoints(const PointList& points);
	PointList fixedPoints();

	void setMovingPoints(const PointList& points);
	PointList movingPoints();

	/**
	Points that are inside the moveRect will be moved by the moveBy function. The others will remain in their position.
	*/
	//void setMoveRect(const QRectF& moveRect);


	void setStartPoint(const QPointF& startPoint);
	void setEndPoint(const QPointF& endPoint);

	void setSource(DesignerComponent* src);
	DesignerComponent* source() { return _src; }

	void setDestination(DesignerComponent* dest);
	DesignerComponent* destination() { return _dest; }

	/**
	This function is used when the user is creating a line and the destination component is not fixed yet.
	*/
	void setAuxDestinationPoint(const QPointF& p);

	QRectF boundingRect () const;

	QPainterPath shape() const;

	void moveBy(qreal dx, qreal dy);

	bool lineOwned(QGraphicsLineItem* line);

	/**
	Add a point in the cutPoint position (this point must be near of a line) and move the new point to newPoint.
	*/
	void addPoint(const QPointF& cutPoint, const QPointF& newPoint=QPointF());

	/**
	Remove a point from Connection.<br>
	newP: returns the projection of p in the resultant Connection. This is useful to undo <br>
	the operation using: addPoint(
	*/
	bool removePoint(const QPointF& p, QPointF* newP = NULL);

	void selectPoint(const QPointF& p);

	void unselectPoint(const QPointF& p);

	PointList points();

	bool nearSelectionRect(const QPointF& p);

	/**
	Return: true if the point is selected.
	*/
	bool pointSelected(const QPointF& p);

	/**
	Return: true if a point of this Connection is near p.
	*/
	bool pointNear(const QPointF& p);

	/**
	Return: true if a point of this Connection is near p.<br>
	p2: contains the point that is near.
	*/
	bool pointNear(const QPointF& p, QPointF& p2);

	QGraphicsLineItem* lineNear(const QPointF& p);

	QString itemName() const;
	QDomElement serialize(QDomDocument& doc, QDomElement& parentElement);

protected slots:
	void sourceMoved();
	void destinationMoved();

protected:

	void addPoint(QGraphicsLineItem* childItem, const QPointF& p);
	void updateSourcePos();
	void updateDestinationPos();

	void updatePath();

	void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget=0);

	QGraphicsLineItem* newLine();

	/**
	From DesignerItem
	*/
	void paintSelected(bool selected);

	/**
	Return: true if a point of this Connection is near p and remove it if removePoint is true<br>
	p2: contains the point that is near or that was removed.<br>
	newP: returns a point in the line that was joined as a result of the remove point. Ignored if removePoint is false.
	*/
	bool testPointRemove(const QPointF& p, QPointF& p2, bool removePoint=false, QPointF* newP=NULL);

	void refreshSelection();

private:
	//ConnectionPointList _points;
	QGraphicsLineItemList _lines;
	DesignerComponent* _src, *_dest;
	//RectList _selRects;
	QRectF _selRect;
	//PointList _points;
	//QRect _moveRect;
	SelectionMap _selMap;
	// lines that don't move in the moveBy. Fixed points of this line are the p2.
	QGraphicsLineItemList _fixedLines;
	QPainterPath _path;
};

class Comment : DesignerItem {
//	Q_OBJECT

public:
	int type() { return _comment_item; }
	
	virtual DesignerItem* clone() { return NULL; }

	QString itemName() const;
};


#endif // __GRAPHIC_COMPONENTS_H
