#include <math.h>


#include "graphic_components.h"
#include "griddesigner.h"

#include "QtXml/QDomDocument"
#include "QtXml/QDomElement"

#define SELECTION_RECT_SIZE 3

#define COMPONENT_Z			10000
#define CONNECTION_Z		0
#define COMMENT_Z			100000

#pragma warning(disable : 4100)

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

DesignerItemMap::DesignerItemMap(const DesignerItemMap& itemMap)
: ItemMap(itemMap)
{

}

void DesignerItemMap::moveBy(qreal dx, qreal dy)
{
	DesignerItem* item;
	
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		item->moveBy(dx, dy);
	}
}

void DesignerItemMap::moveBy(const QPointF& dp)
{
	moveBy(dp.x(), dp.y());
}

void DesignerItemMap::hide()
{
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		item->hide();
	}
}

void DesignerItemMap::show()
{
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		item->show();
	}
}

void DesignerItemMap::removeFromScene()
{
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		if(item->scene())
			item->scene()->removeItem(item);
	}
}

void DesignerItemMap::addToScene(QGraphicsScene* scene)
{
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		scene->addItem(item);
	}
}

DesignerItemMap DesignerItemMap::clone(bool setFixedPoints)
{
	DesignerItemMap newList;
	DesignerItem* item, *newItem;

	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		if(item->type() >= DesignerItem::_component_item) {
			newItem = item->clone();
			newList[item->id()] = newItem;
		}
	}

	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		if(item->type() == DesignerItem::_connection_item) {
			DesignerComponent* src;
			DesignerComponent* dest;
			Connection* connection = (Connection*) item;
			iterator itSrc = newList.find(connection->source()->id());
			if(itSrc == newList.end()) {
				src = connection->source();
			}
			else
				src = (DesignerComponent*) itSrc.value();

			iterator itDest = newList.find(connection->destination()->id());
			if(itDest == newList.end()) {
				dest = connection->destination();
			}
			else
				dest = (DesignerComponent*) itDest.value();

			newItem = connection->clone(src, dest, setFixedPoints);
			newList[item->id()] = newItem;
		}
	}

	return newList;
}

void DesignerItemMap::deleteAllItems()
{
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		delete item;
	}
	clear();
}

DesignerComponent* DesignerItemMap::componentByName(const QString& itemName)
{
	DesignerItem* item;
	DesignerComponent* ret = NULL;

	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		if(item->type() >= DesignerItem::_component_item) {
			DesignerComponent* comp = (DesignerComponent*) item;
			if(comp->name() == itemName) {
				ret = comp;
				break;
			}
		}
	}
	return ret;
}

bool DesignerItemMap::fromXml(GridDesigner* designer, const QDomElement& node)
{
	QDomElement ele = node;
	while(!ele.isNull()) {
		DesignerItem* item = NULL;
		QString tagName = ele.tagName();
		if(tagName == GRID_NODE_NAME) {
			item = new GridNode(designer->scene(), designer->lastId());
			item->load(*this, ele);
		}
		else if(tagName == LINK_NAME) {
			item = new Link(designer->scene(), designer->lastId());
			item->load(*this, ele);
		}

		if(item) {
			(*this)[item->id()] = item;
		}
		ele = ele.nextSiblingElement();
	}

	ele = node;
	while(!ele.isNull()) {
		DesignerItem* item = NULL;
		QString tagName = ele.tagName();
		if(tagName == CONNETION_NAME) {
			item = new Connection(designer->scene(), designer->lastId());
			item->load(*this, ele);
		}
		
		if(item) {
			(*this)[item->id()] = item;
		}
		ele = ele.nextSiblingElement("Connection");
	}

	return true;
}

void DesignerItemMap::setMovingPoints(const ConnectionPointMap& points)
{
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		if(item->type() == DesignerItem::_connection_item) {
			Connection* conn = (Connection*) item;
			if(points.find(conn) != points.end()) {
				PointList ps = points[conn];
				conn->setMovingPoints(ps);
			}
			else
				conn->setFixedPoints(conn->points());
		}
	}
}

ConnectionPointMap DesignerItemMap::movingPoints()
{
	ConnectionPointMap connPoints;
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		if(item->type() == DesignerItem::_connection_item) {
			Connection* conn = (Connection*) item;
			PointList ps = conn->movingPoints();
			if(!ps.empty())
				connPoints[conn] = ps;
		}
	}
	return connPoints;
}

void DesignerItemMap::setSelectedPoints(const ConnectionPointMap& points)
{
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		if(item->type() == DesignerItem::_connection_item) {
			Connection* conn = (Connection*) item;
			conn->setSelectedPoints(PointList());
			if(points.find(conn) != points.end()) {
				PointList ps = points[conn];
				conn->setSelectedPoints(ps);
			}
		}
	}
}

ConnectionPointMap DesignerItemMap::selectedPoints()
{
	ConnectionPointMap connPoints;
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		if(item->type() == DesignerItem::_connection_item) {
			Connection* conn = (Connection*) item;
			PointList ps = conn->selectedPoints();
			if(!ps.empty())
				connPoints[conn] = ps;
		}
	}
	return connPoints;
}

bool DesignerItemMap::anyMoveItem()
{
	DesignerItem* item;
	for(iterator it=begin(); it!=end(); it++) {
		item = it.value();
		if(item->type() == DesignerItem::_connection_item) {
			Connection* conn = (Connection*) item;
			if(!conn->movingPoints().empty())
				return true;
		}
		else
			return true;
	}
	return false;
}


/////////////////////////////////////////////////////////////////////////////////////////////
QGraphicsRectItem* DesignerItem::newSelectionRect(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent, QGraphicsScene *scene)
{
	QGraphicsRectItem* rect = new QGraphicsRectItem(x, y, w, h, parent, scene);
	rect->setData(SELECTION_PROPERTY, true);

	return rect;
}

void DesignerItem::setSelected(bool selected, const QRectF&)
{
	if(_selected == selected)
		return;
	_selected = selected;
	paintSelected(selected);
}

/////////////////////////////////////////////////////////////////////////////////////////////
DesignerComponent::DesignerComponent(const QPixmap& pix, QGraphicsScene* scene, int compId)
: DesignerItem(scene, compId)
{
	//QImage image = pix.toImage();
	//for(int i=0; i<image.width(); i++) {
	//	for(int j=0; j<image.height(); j++) {
	//		QRgb r = image.pixel(i, j);
	//		int red = qRed(r) * 1.8;
	//		//r = qRgba(red > 0xff ? 0xff : red, qGreen(r), qBlue(r), qAlpha(r));
	//		r = qRgba(qRed(r) * 1.2, qGreen(r) * .8, qBlue(r) * .8, qAlpha(r));
	//		image.setPixel(i, j, r);
	//	}
	//}
	//QPixmap p = QPixmap::fromImage(image);

	//initialize(p, scene);
	//setZValue(COMPONENT_Z);

	initialize(pix, scene);
	setZValue(COMPONENT_Z);
}

// DesignerComponent::DesignerComponent(const QPixmap& pix, QGraphicsScene* scene, const QDomElement& element, int compId)
// : DesignerItem(scene, compId)


bool DesignerComponent::load(DesignerItemMap&, const QDomElement& element)
{
	for(QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
		if(e.nodeName() == tr("x")) {
			qreal rx = e.attribute("value").toDouble();
			setPos(rx, y());
		}
		else if(e.nodeName() == tr("y")) {
			qreal ry = e.attribute("value").toDouble();
			setPos(x(), ry);
		}
		else if(e.nodeName() == tr("name")) {
			setName(e.attribute("value"));
		}
	}

	return true;
}

void DesignerComponent::initialize(const QPixmap& pix, QGraphicsScene* scene)
{
	//QPixmap newPix = pix;
	//QImage image = pix.toImage();
	//if(!image.isNull()) {
	//	for(int i = 0; i < image.numColors(); i++) {
	//		QColor c =  image.color(i);
	//		c.setRed(0xff);
	//		c.setAlpha(0xff);
	//		image.setColor(0, c.rgb());
	//	}

	//	image.invertPixels();
	//	image.invertPixels();
	//	newPix = QPixmap::fromImage(image);
	//}

	//_pixItem = new QGraphicsPixmapItem(newPix, this, scene);
	//_textItem = new QGraphicsTextItem(this, scene);
	//_msgItem = NULL;

	_redLevel = 0;

	_pixItem = new QGraphicsPixmapItem(pix, this, scene);
	_textItem = new QGraphicsTextItem(this, scene);
	_msgItem = NULL;
}

void DesignerComponent::setName(const QString& text)
{
	_textItem->setPlainText(text);

	QRectF pixRect = _pixItem->boundingRect();
	QRectF textRect = _textItem->boundingRect();

	if(pixRect.width() < textRect.width()) {
		_pixItem->setPos((textRect.width() - pixRect.width()) / 2, 0);
		_textItem->setPos(0, pixRect.bottomLeft().y()+2);
	}
	else {
		_textItem->setPos((pixRect.width() - textRect.width()) / 2, pixRect.bottomLeft().y()+2);
		_pixItem->setPos(0, 0);
	}

	refreshSelection();
		
	prepareGeometryChange();
}

void DesignerComponent::refreshSelection()
{
	if(selected()) {
		paintSelected(false);
		paintSelected(true);
	}
}

void DesignerComponent::setPixmap(const QPixmap &pix)
{
	_pixItem->setPixmap(pix);
}

QString DesignerComponent::name()
{
	return _textItem->toPlainText();
}

QRectF DesignerComponent::boundingRect() const
{
	QRectF pixRect = _pixItem->boundingRect();
	QRectF textRect = _textItem->boundingRect();
	textRect.moveTo(textRect.bottomLeft().x(), pixRect.bottomLeft().y()+2);
	QRectF ret = pixRect | textRect;
	return ret;
}

void DesignerComponent::moveBy(qreal dx, qreal dy)
{
	DesignerItem::moveBy(dx, dy);
	emit(moved());
}

void DesignerComponent::paintSelected(bool selected)
{
	if(selected) {
		QRectF rect = boundingRect();
		
		_selRects.push_back(DesignerItem::newSelectionRect(rect.x(), rect.y(), SELECTION_RECT_SIZE, SELECTION_RECT_SIZE, this, scene()));
		_selRects.push_back(DesignerItem::newSelectionRect(rect.topRight().x()-SELECTION_RECT_SIZE, rect.topRight().y(), SELECTION_RECT_SIZE, SELECTION_RECT_SIZE, this, scene()));
		_selRects.push_back(DesignerItem::newSelectionRect(rect.bottomLeft().x(), rect.bottomLeft().y()-SELECTION_RECT_SIZE, SELECTION_RECT_SIZE, SELECTION_RECT_SIZE, this, scene()));
		_selRects.push_back(DesignerItem::newSelectionRect(rect.bottomRight().x()-SELECTION_RECT_SIZE, rect.bottomRight().y()-SELECTION_RECT_SIZE, SELECTION_RECT_SIZE, SELECTION_RECT_SIZE, this, scene()));
	}
	else {
		for(RectList::iterator it=_selRects.begin(); it!=_selRects.end(); it++) {
			delete *it;
		}
		_selRects.clear();
	}
}

void DesignerComponent::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	//_pixItem->paint(painter, option, widget);
	//_textItem->paint(painter, option, widget);
}

QDomElement DesignerComponent::serialize(QDomDocument& doc, QDomElement& parentElement)
{
	QDomElement elem = doc.createElement(itemName());
	parentElement.appendChild(elem);

	QDomElement curr = doc.createElement("x");
	elem.appendChild(curr);
	curr.setAttribute("value", x());
	curr = doc.createElement("y");
	elem.appendChild(curr);
	curr.setAttribute("value", y());
	curr = doc.createElement("name");
	elem.appendChild(curr);
	curr.setAttribute(tr("value"), name());

	return elem;
}

void DesignerComponent::setMessage(const QString &text)
{
	if(_msgItem == NULL) {
		_msgItem = new QGraphicsTextItem(this, scene());
	
	}

	_msgItem->setPlainText(text);
	
	//QRectF bound = boundingRect();
	//_textItem->setPlainText(text);
	_msgItem->setPos(0, -10);
}


void DesignerComponent::setRedLevel(int level)
{
	if(level <= 0) {
		_redLevel = 0;
	}
	else if(level >= 100) {
		_redLevel = level;
	}
	else {
		_redLevel = level;
	}
}

//DesignerComponentList DesignerComponent::connectedComponents() const
//{
//	DesignerComponentList compList;
//	
//	for(ConnectionList::const_iterator it = _connections.begin(); it != _connections.end(); it++) {
//		Connection *conn = *it;
//		if(conn->source() == this)
//			compList.append(conn->destination());
//		else
//			compList.append(conn->source());
//	}
//	return compList;
//}

/////////////////////////////////////////////////////////////////////////////////////////////
GridNode::GridNode(QGraphicsScene* scene, int compId)
	: DesignerComponent(QPixmap(":/GridDesigner/images/images/gridnode-comp.png"), scene, compId)
{
	_power = 0;
}

DesignerItem* GridNode::clone()
{
	DesignerItem* newItem = new GridNode(scene(), id());

	newItem->setPos(pos());
	newItem->setZValue(zValue());
	newItem->setVisible(isVisible());
	((GridNode*) newItem)->setName(name());
	
	return newItem;
}

void GridNode::setRedLevel(int level)
{
	DesignerComponent::setRedLevel(level);

	if((level/5) <= 0) {
		setPixmap(QPixmap(":/GridDesigner/images/images/gridnode-comp.png"));
	}
	else if(level >= 100) {
		setPixmap(QPixmap(":/GridDesigner/images/images/gridnode-comp-20.png"));
	}
	else {
		setPixmap(QPixmap(QString(":/GridDesigner/images/images/gridnode-comp-%1.png").arg(level/5)));
	}
}

QString GridNode::itemName() const
{
	return GRID_NODE_NAME;
}

bool GridNode::load(DesignerItemMap& map, const QDomElement& element)
{
	bool ret;

	ret = DesignerComponent::load(map, element);
	if(ret) {
		QDomElement e = element.firstChildElement(tr("power"));
		if(!e.isNull()) {
			uint pow = e.attribute("value").toUInt();
			setPower(pow);
		}
	}

	return ret;
}

QDomElement GridNode::serialize(QDomDocument& doc, QDomElement& parentElement)
{
	QDomElement elem = DesignerComponent::serialize(doc, parentElement);

	QDomElement curr = doc.createElement("power");
	elem.appendChild(curr);
	curr.setAttribute("value", power());

	return elem;
}


/////////////////////////////////////////////////////////////////////////////////////////////
Link::Link(QGraphicsScene* scene, int compId)
: DesignerComponent(QPixmap(":/GridDesigner/images/images/link-comp.png"), scene, compId)
{
	_latency = 0;
	_bandwidth = 0;
}

DesignerItem* Link::clone()
{
	DesignerItem* newItem = new Link(scene(), id());

	newItem->setPos(pos());
	newItem->setZValue(zValue());
	newItem->setVisible(isVisible());
	((Link*) newItem)->setName(name());

	return newItem;
}

QString Link::itemName() const
{
	return LINK_NAME;
}

bool Link::load(DesignerItemMap& map, const QDomElement& element)
{
	bool ret;
	
	ret = DesignerComponent::load(map, element);
	if(ret) {
		QDomElement e = element.firstChildElement(tr("latency"));
		if(!e.isNull()) {
			double lat = e.attribute("value").toDouble();
			setLatency(lat);
		}
		e = element.firstChildElement(tr("bandwidth"));
		if(!e.isNull()) {
			uint band = e.attribute("value").toUInt();
			setBandwidth(band);
		}
	}
	
	return ret;
}

QDomElement Link::serialize(QDomDocument& doc, QDomElement& parentElement)
{
	QDomElement elem = DesignerComponent::serialize(doc, parentElement);
	
	QDomElement curr = doc.createElement("latency");
	elem.appendChild(curr);
	curr.setAttribute("value", latency());
	
	curr = doc.createElement("bandwidth");
	elem.appendChild(curr);
	curr.setAttribute("value", bandwidth());	
	
	return elem;
}

/////////////////////////////////////////////////////////////////////////////////////////////
Connection::Connection(QGraphicsScene* scene, int compId)
: DesignerItem(scene, compId)
{
	_src = NULL;
	_dest = NULL;
	setZValue(CONNECTION_Z);
}

bool Connection::load(DesignerItemMap& map, const QDomElement& element)
{
	QString name;
	DesignerComponent* comp = NULL;

	QDomElement ele = element.firstChildElement("source");	
	if(!ele.isNull()) {
		name = ele.attribute("name");
		comp = map.componentByName(name);
		if(comp)
			setSource(comp);
	}
	ele = element.firstChildElement("destination");	
	if(!ele.isNull()) {
		name = ele.attribute("name");
		comp = map.componentByName(name);
		if(comp)
			setDestination(comp);
	}

	if(source() != NULL && destination() != NULL) {
		ele = element.firstChildElement("points");	
		if(!ele.isNull()) {
			QDomElement pointEle = ele.firstChildElement("point");
			while(!pointEle.isNull()) {
				int x;
				int y;

				x = pointEle.attribute("x", "0").toInt(NULL, 10);
				y = pointEle.attribute("y", "0").toInt(NULL, 10);

				QPointF p(x, y);
				QGraphicsLineItem* line = _lines.last();
				addPoint(line, p);

				pointEle = pointEle.nextSiblingElement("point");
			}
		}
	}

	return (source() != NULL && destination() != NULL);
}

Connection::~Connection()
{
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end(); it++) {
		QGraphicsLineItem* line = *it;
		delete line;
	}
	_lines.clear();
}

void Connection::setSelected(bool selected, const QRectF& selRect)
{
	if(selected) {
		PointList ps = points();
		for(PointList::iterator it=ps.begin(); it!=ps.end(); it++) {
			if(selRect.contains(*it) || selRect.isNull())
				selectPoint(*it);
		}
	}
	DesignerItem::setSelected(selected, selRect);
}

void Connection::setSelectedPoints(const PointList& selPoints)
{
	for(PointList::const_iterator it=selPoints.begin(); it!=selPoints.end(); it++) {
		selectPoint(*it);
	}
	DesignerItem::setSelected(!selPoints.empty());
}

QGraphicsLineItem* Connection::newLine()
{
	QGraphicsLineItem* line = new QGraphicsLineItem(this, scene());
	QPen pen = line->pen();
	pen.setCapStyle(Qt::RoundCap);
	pen.setColor(Qt::gray);
	line->setPen(pen);

	return line;
}

DesignerItem* Connection::clone()
{
	DesignerComponent* src = (DesignerComponent*) source()->clone();
	DesignerComponent* dest = (DesignerComponent*) destination()->clone();

	return clone(src, dest, true);
}

DesignerItem* Connection::clone(DesignerComponent* src, DesignerComponent* dest, bool setFixedPoint)
{
	Connection* newItem = new Connection(scene(), id());
	
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end(); it++) {
		QGraphicsLineItem* line = newLine();
		line->setLine((*it)->line());
		line->setVisible((*it)->isVisible());
		newItem->_lines.append(line);
	}
	
	newItem->setZValue(zValue());
	newItem->setSource(src);
	newItem->setDestination(dest);
	
	if(setFixedPoint)
		newItem->setFixedPoints(unselectedPoints());

	newItem->setVisible(isVisible());
	
	return newItem;
}

void Connection::setSource(DesignerComponent* src)
{
	if(src != NULL) {
		QGraphicsLineItem* line;

		//if(_src != NULL)
		//	_src->removeConnection(this);

		_src = src;

		connect(_src, SIGNAL(moved()), this, SLOT(sourceMoved()));

		if(_lines.count() > 0) {
			line = _lines.first();
		}
		else {
			line = newLine();
			_lines.append(line);
		}

		//_src->addConnection(this);

		updateSourcePos();
	}
}

void Connection::setDestination(DesignerComponent* dest)
{
	if(dest != NULL) {
		QGraphicsLineItem* line;
		
		//if(_dest != NULL)
		//	_dest->removeConnection(this);
		
		_dest = dest;

		connect(_dest, SIGNAL(moved()), this, SLOT(destinationMoved()));
		
		if(_lines.count() > 0) {
			updateDestinationPos();
		}
		else {
			line = newLine();
			_lines.append(line);
		}

		//_dest->addConnection(this);
	}
}

void Connection::updateSourcePos()
{
	if(_lines.count() > 0) {
		QGraphicsLineItem* line = _lines.first();
		//line->setLine(_src->x() + _src->boundingRect().width()/2, _src->y() + _src->boundingRect().height()/2, 
		//	line->line().p2().x(), line->line().p2().y());
		line->setLine(_src->x() + _src->imageWidth()/2, _src->y() + _src->imageHeight()/2, 
			line->line().p2().x(), line->line().p2().y());
		updatePath();
	}
	paintSelected(selected());
}

void Connection::updateDestinationPos()
{
	if(_lines.count() > 0) {
		QGraphicsLineItem* line = _lines.last();
		//line->setLine(line->line().p1().x(), line->line().p1().y(),
		//	_dest->x() + _dest->boundingRect().width()/2, _dest->y() + _dest->boundingRect().height()/2);
		line->setLine(line->line().p1().x(), line->line().p1().y(),
			_dest->x() + _dest->imageWidth()/2, _dest->y() + _dest->imageHeight()/2);
		updatePath();
	}
	paintSelected(selected());
}

void Connection::setAuxDestinationPoint(const QPointF& p)
{
	// this function is used ONLY when a new line is created and it has a source and no destination
	if(_src == NULL && _dest != NULL) {
		return;
	}
	
	QGraphicsLineItem* line = _lines.first();
	
	line->setLine(_src->x() + _src->imageWidth()/2, _src->y() + _src->imageHeight()/2, p.x(), p.y());
	updatePath();
	line->show();
//	drawArrow();
}

QRectF Connection::boundingRect() const
{
//	QRectF rect;
//
// 	for(QGraphicsLineItemList::const_iterator it=_lines.begin(); it!=_lines.end(); it++) {
// 		QGraphicsLineItem* line = *it;
// 		rect = rect.united(line->boundingRect());
// 	}
//	return rect;

	return _path.boundingRect();
}

QPainterPath Connection::shape() const
{
	return _path;
}

PointList Connection::unselectedPoints()
{
	PointList unselPoints;
	PointList selPoints = selectedPoints();

	int i = 0;
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end() && i<_lines.count()-1; it++, i++) {
		QGraphicsLineItem* line = *it;
		QPointF p = line->line().p2();
		if(!selPoints.contains(p))
			unselPoints.push_back(p);
	}
	
	return unselPoints;
}

PointList Connection::selectedPoints()
{
	PointList selPoints;
	for(SelectionMap::iterator it=_selMap.begin(); it!=_selMap.end(); it++) {
		QGraphicsLineItem* line = it.key();
		selPoints.push_back(line->line().p2());
	}
	return selPoints;
}

void Connection::setFixedPoints(const PointList& points)
{
	_fixedLines.clear();

	if(points.empty())
		return;

	int i=0;
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end() && i < _lines.count()-1; it++, i++) {
		QGraphicsLineItem* line = *it;
		for(PointList::const_iterator it2=points.begin(); it2!=points.end(); it2++) {
			QPointF p = *it2;
			if(p == line->line().p2())
				_fixedLines.push_back(line);
		}
	}
}

PointList Connection::fixedPoints()
{
	PointList ret;
	int i=0;
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end() && i < _lines.count()-1; it++, i++) {
		QGraphicsLineItem* line = *it;
		if(_fixedLines.contains(line)) {
			ret.push_back(line->line().p2());
		}
	}
	return ret;
}

void Connection::setMovingPoints(const PointList& movPoints)
{
	PointList ps = points();
	
	for(PointList::iterator it = ps.begin(); it != ps.end();) {
		QPointF p = *it;
		QPointF p3 = movPoints.first();
		if(movPoints.contains(p)) {
			PointList::iterator it2 = it;
			bool last = false;

			it2++;
			if(it2 == ps.end())
				last = true;
			ps.erase(it);
			it = it2;
			if(last)
				break;
		}
		else
			it++;
	}
	setFixedPoints(ps);
}

PointList Connection::movingPoints()
{
	PointList ret;
	int i=0;
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end() && i < _lines.count()-1; it++, i++) {
		QGraphicsLineItem* line = *it;
		if(!_fixedLines.contains(line)) {
			ret.push_back(line->line().p2());
		}
	}
	return ret;
}

void Connection::refreshSelection()
{
	if(selected())
	{
		paintSelected(false);
		paintSelected(true);
	}
}

void Connection::moveBy(qreal dx, qreal dy)
{
	if(_lines.count() == 1) {
		refreshSelection();
		//for(SelectionMap::iterator it2=_selMap.begin(); it2!=_selMap.end(); it2++) {
		//	(it2.value())->moveBy(dx, dy);
		//}
	}
	else {
		int i=0;
		bool prevMoved = false;
		// move p2 of lines that are not in the fixed list and p1 of lines whose previus line wasn't in fixed list.
		for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end(); it++, i++) {
			QGraphicsLineItem* line = *it;
			if(prevMoved)
				line->setLine(line->line().p1().x()+dx, line->line().p1().y()+dy, line->line().p2().x(), line->line().p2().y());
			
			if(i == _lines.count()-1)
				break;
			
			if(!_fixedLines.contains(line)) {
				line->setLine(line->line().p1().x(), line->line().p1().y(), line->line().p2().x()+dx, line->line().p2().y()+dy);
				prevMoved = true;
				if(_selMap.contains(line)) {
					QGraphicsRectItem* rect = _selMap[line];
					rect->moveBy(dx, dy);
				}
			}
			else
				prevMoved = false;
		}
	}
}

bool Connection::lineOwned(QGraphicsLineItem* line)
{
	bool ret = false;
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end(); it++) {
		QGraphicsLineItem* l = *it;
		
		if(l == line) {
			ret = true;
			break;
		}
	}

	return ret;
}

void Connection::addPoint(const QPointF& p, const QPointF& newPoint)
{
	QGraphicsLineItem* line = NULL;
	QList<QGraphicsItem*> items = GridDesigner::items(scene(), p);
	
	for(QList<QGraphicsItem*>::iterator it=items.begin(); it!=items.end(); it++) {
		line = qgraphicsitem_cast<QGraphicsLineItem*>(*it);
		if(line && lineOwned(line)) {
			break;
		}
		line = NULL;
	}

	Q_ASSERT(line != NULL);

	addPoint(line, p);

	if(!newPoint.isNull())
	{
		PointList ps;
		QPointF movePos;
		QGraphicsLineItemList oldFixed = _fixedLines;
		
		ps.append(p);
		setMovingPoints(ps);
		movePos = newPoint - p;
		moveBy(movePos.x(), movePos.y());

		_fixedLines = oldFixed;
	}
}

void Connection::addPoint(QGraphicsLineItem* childLine, const QPointF& p)
{
	if(childLine) {
		QGraphicsLineItemList newLines;
		for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end(); it++) {
			QGraphicsLineItem* line = *it;
			
			newLines.append(line);
			if(line == childLine) {
				QPointF p2 = childLine->line().p2();
				line->setLine(line->line().p1().x(), line->line().p1().y(), p.x(), p.y());
				
				QGraphicsLineItem* newline = newLine();
				
				newline->setLine(p.x(), p.y(), p2.x(), p2.y());
				newline->setVisible(isVisible());
				newLines.append(newline);

				//_points.append(new ConnectionPoint(scene(), compId, this));
			}
		}
		
		_lines = newLines;
		updatePath();
	}
}

bool Connection::removePoint(const QPointF& p, QPointF* newP)
{
	QPointF p2;
	return testPointRemove(p, p2, true, newP);
}

void Connection::updatePath()
{
	QPainterPath newPath;

	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end(); it++) {
		QGraphicsLineItem* line = *it;
	
		newPath.moveTo(line->line().p1());
		newPath.lineTo(line->line().p2());
	}

	_path = newPath;
}

void Connection::selectPoint(const QPointF& p)
{
	setSelectedProperty(true);

	int i=0;
	for(QGraphicsLineItemList::iterator it=_lines.begin(); i < _lines.count()-1; it++, i++) {
		QGraphicsLineItem* line = *it;
		QPointF p2 = line->line().p2();
		
		// verify the line where this points belong
		if(line->line().p2() == p) {
			// already selected
			if(!_selMap.contains(line)) {
				_selMap[line] = NULL;
			}
			paintSelected(true);
			break;
		}
	}
}

void Connection::unselectPoint(const QPointF& p)
{
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end(); it++) {
		QGraphicsLineItem* line = *it;
		
		// verify the line where this points belong
		if(line->line().p2() == p) {
			// already selected
			if(_selMap.contains(line)) {
				_selMap.remove(line);
				if(_selMap.count() == 0) {
					setSelected(false);
				}
				else
					paintSelected(true);
			}
			break;
		}
	}
}

PointList Connection::points()
{
	PointList ret;
	int i=0;
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end() && i < _lines.count()-1; it++, i++) {
		QGraphicsLineItem* line = *it;
		ret.push_back(line->line().p2());
	}
	return ret;
}

bool Connection::nearSelectionRect(const QPointF& p)
{
	bool ret = false;
	for(SelectionMap::iterator it=_selMap.begin(); it!=_selMap.end(); it++) {
		QGraphicsRectItem* rect = it.value();
		if(rect->rect().contains(p)) {
			ret = true;
			break;
		}
	}

	return ret;
}

QGraphicsLineItem* Connection::lineNear(const QPointF& p)
{
	QRectF selRect;
	QGraphicsLineItem* retLine = NULL;

	selRect = GridDesigner::selectionRect(p);
	
	// don't test last line
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end(); it++) {
		QGraphicsLineItem* line = *it;
		QRectF lineRect;
		
		lineRect.setRect(line->line().p1().x(), line->line().p1().y(), line->line().p2().x(), line->line().p2().y());
		
		if(selRect.intersects(lineRect)) {
			retLine = line;
		}
	}
	
	return retLine;
}

bool Connection::pointSelected(const QPointF& p)
{
	return selectedPoints().contains(p);
}

bool Connection::pointNear(const QPointF& p)
{
	QPointF p2;
	return testPointRemove(p, p2, false);
}

bool Connection::pointNear(const QPointF& p, QPointF& p2)
{
	return testPointRemove(p, p2, false);
}

bool Connection::testPointRemove(const QPointF& p, QPointF& p2, bool removePoint, QPointF* newP)
{
	QGraphicsLineItemList newLines;
	int i=0;
	bool ret = false;

	// don't test last line
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end() && i < _lines.count()-1; it++, i++) {
		QGraphicsLineItem* line = *it;
		
		QPointF tp = line->line().p2();

		// if distance to the point <= 4
		if(sqrt(pow(line->line().p2().x() - p.x(), 2) + pow(line->line().p2().y() - p.y(), 2)) <= 4) {
			ret = true;
			p2 = line->line().p2();
			if(removePoint) {
				QGraphicsLineItem* remLine;
				QGraphicsLineItemList::iterator it2;

				it++;
				remLine = *it;

				// skip to remove line
				it2 = it;
				it++;
				
				_lines.erase(it2);

				line->setLine(line->line().p1().x(), line->line().p1().y(), remLine->line().p2().x(), remLine->line().p2().y());

				if(newP)
					*newP = line->line().pointAt(0.5);
				
				delete remLine;
			}
			break;
		}
	}

	return ret;
}

void Connection::sourceMoved()
{
	updateSourcePos();
}

void Connection::destinationMoved()
{
	updateDestinationPos();
}

void Connection::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	for(QGraphicsLineItemList::iterator it=_lines.begin(); it!=_lines.end(); it++) {
		QGraphicsLineItem* line = *it;
		line->paint(painter, option, widget);
	}
}

void Connection::paintSelected(bool selected)
{
	if(selected) {
		QLineF line;
		if(_lines.count() == 1) {
			QGraphicsLineItem* gline = _lines.first();
			if(!_selMap.contains(gline) || _selMap[gline] == NULL) {
				line = _lines.first()->line();
				_selMap[_lines.first()] = DesignerItem::newSelectionRect((line.x1()+line.x2()-SELECTION_RECT_SIZE)/2, (line.y1()+line.y2()-SELECTION_RECT_SIZE)/2, SELECTION_RECT_SIZE, SELECTION_RECT_SIZE, this, scene());
			}
		}
		else {
			for(SelectionMap::iterator it=_selMap.begin(); it!=_selMap.end(); it++) {
				line = it.key()->line();
				QGraphicsRectItem* &rect = it.value();
				if(rect == NULL) {
					rect = DesignerItem::newSelectionRect(line.x2()-SELECTION_RECT_SIZE/2, line.y2()-SELECTION_RECT_SIZE/2, SELECTION_RECT_SIZE, SELECTION_RECT_SIZE, this, scene());
				}
			}
		}
	}
	else {
		// remove all 
		for(SelectionMap::iterator it=_selMap.begin(); it!=_selMap.end(); it++) {
			delete (it.value());
		}
		_selMap.clear();
	}
}

QString Connection::itemName() const
{
	return CONNETION_NAME;
}

QDomElement Connection::serialize(QDomDocument& doc, QDomElement& parentElement)
{
	QDomElement elem = doc.createElement(itemName());
	parentElement.appendChild(elem);

	QDomElement curr = doc.createElement(tr("source"));
	elem.appendChild(curr);
	curr.setAttribute(tr("name"), _src->name());
	curr = doc.createElement(tr("destination"));
	elem.appendChild(curr);
	curr.setAttribute(tr("name"), _dest->name());

	PointList ps = points();
	if(!ps.empty()) {
		QDomElement psElement = doc.createElement(tr("points"));
		elem.appendChild(psElement);
		for(PointList::iterator it = ps.begin(); it != ps.end(); it++) {
			QPointF p = *it;
			curr = doc.createElement(tr("point"));
			psElement.appendChild(curr);
			curr.setAttribute(tr("x"), p.x());
			curr.setAttribute(tr("y"), p.y());
		}
	}

	return elem;
}

