/****************************************************************************
**
** Copyright (C) 2006-2007 Trolltech ASA. All rights reserved.
**
** This file is part of the example classes of the Qt Toolkit.
**
** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://trolltech.com/products/qt/licenses/licensing/opensource/
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
** or contact the sales department at sales@trolltech.com.
**
** In addition, as a special exception, Trolltech gives you certain
** additional rights. These rights are described in the Trolltech GPL
** Exception version 1.0, which can be found at
** http://www.trolltech.com/products/qt/gplexception/ and in the file
** GPL_EXCEPTION.txt in this package.
**
** In addition, as a special exception, Trolltech, as the sole copyright
** holder for Qt Designer, grants users of the Qt/Eclipse Integration
** plug-in the right for the Qt/Eclipse Integration to link to
** functionality provided by Qt Designer and its related libraries.
**
** Trolltech reserves all rights not expressly granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include "graphwidget.h"
#include "edge.h"
#include "arrow.h"
#include "node.h"

#include "rsiface/rspeers.h"

#include <QDebug>
#include <QGraphicsScene>
#include <QWheelEvent>

#include <math.h>

GraphWidget::GraphWidget(QWidget *parent)
    :QGraphicsView(parent), timerId(0)
{

#if 0
    QGraphicsScene *scene = new QGraphicsScene(this);
    scene->setItemIndexMethod(QGraphicsScene::NoIndex);
    //scene->setSceneRect(-200, -200, 400, 400);
    scene->setSceneRect(-200, -200, 1000, 1000);
    setScene(scene);

    centerNode = new Node(this, 1, "You");
    scene->addItem(centerNode);
    centerNode->setPos(0, 0);

#endif
    centerNode = NULL;

    setCacheMode(CacheBackground);
    setRenderHint(QPainter::Antialiasing);
    setTransformationAnchor(AnchorUnderMouse);
    setResizeAnchor(AnchorViewCenter);
    scale(0.8, 0.8);
    //setMinimumSize(400, 400);
    //setMinimumSize(1000, 1000);
    setWindowTitle(tr("Elastic Nodes"));

    clearGraph();
}

bool GraphWidget::clearGraph()
{
    QGraphicsScene *oldscene = scene();
    Node *oldcenterNode = centerNode;

    QGraphicsScene *scene = new QGraphicsScene(this);
    scene->setItemIndexMethod(QGraphicsScene::NoIndex);
    //scene->setSceneRect(-200, -200, 400, 400);
    scene->setSceneRect(-200, -200, 1000, 1000);
    setScene(scene);

    centerNode = new Node(this, 1, rsPeers->getGPGOwnId(), "You");
    scene->addItem(centerNode);
    centerNode->setPos(0, 0);

    if (oldscene)
    {
    	delete oldscene;
    }

    nodeMap.clear();
    edgeList.clear();
    arrowList.clear();

    return true;
}

void GraphWidget::addNode(uint32_t type, std::string id, std::string name)
{
    Node *node = new Node(this, type, id, name);

    /* store node */
    nodeMap[id] = node;
    scene()->addItem(node);
    node->setPos(-50 + qrand() % 100 , -50 + qrand() % 100);
}

void GraphWidget::addEdge(std::string id1, std::string id2)
{
    std::map<std::string, Node *>::iterator it;
    Node *n1 = NULL;
    Node *n2 = NULL;

    if (id1 == "")
    {
    	n1 = centerNode;
    }
    else
    {
    	it = nodeMap.find(id1);
	if (it != nodeMap.end())
	{
		n1 = it->second;
	}
    }

    it = nodeMap.find(id2);
    if (it != nodeMap.end())
    {
	n2 = it->second;
    }

    if ((n1) && (n2))
    {
       Edge *edge = new Edge(n1, n2);
       scene()->addItem(edge);
       edgeList.push_back(edge);
    }
}

void GraphWidget::addArrow(std::string id1, std::string id2)
{
    std::map<std::string, Node *>::iterator it;
    Node *n1 = NULL;
    Node *n2 = NULL;

    if (id1 == "")
    {
    	n1 = centerNode;
    }
    else
    {
    	it = nodeMap.find(id1);
	if (it != nodeMap.end())
	{
		n1 = it->second;
	}
    }

    it = nodeMap.find(id2);
    if (it != nodeMap.end())
    {
	n2 = it->second;
    }

    if ((n1) && (n2))
    {
       Arrow *arrow = new Arrow(n1, n2);
       scene()->addItem(arrow);
       arrowList.push_back(arrow);
    }
}

void GraphWidget::itemMoved()
{
    if (!timerId)
        timerId = startTimer(1000 / 1);
}

void GraphWidget::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) {
    case Qt::Key_Up:
        centerNode->moveBy(0, -20);
        break;
    case Qt::Key_Down:
        centerNode->moveBy(0, 20);
        break;
    case Qt::Key_Left:
        centerNode->moveBy(-20, 0);
        break;
    case Qt::Key_Right:
        centerNode->moveBy(20, 0);
        break;
    case Qt::Key_Plus:
        scaleView(1.2);
        break;
    case Qt::Key_Minus:
        scaleView(1 / 1.2);
        break;
    case Qt::Key_Space:
    case Qt::Key_Enter:
        foreach (QGraphicsItem *item, scene()->items()) {
            if (qgraphicsitem_cast<Node *>(item))
                item->setPos(-150 + qrand() % 300, -150 + qrand() % 300);
        }
        break;
    default:
        QGraphicsView::keyPressEvent(event);
    }
}

void GraphWidget::timerEvent(QTimerEvent *event)
{
	if(!isVisible())
		return;

    QList<Node *> nodes;
    foreach (QGraphicsItem *item, scene()->items()) {
        if (Node *node = qgraphicsitem_cast<Node *>(item))
            nodes << node;
    }

    foreach (Node *node, nodes)
        node->calculateForces();

    bool itemsMoved = false;
    foreach (Node *node, nodes) {
        if (node->advance())
            itemsMoved = true;
    }

    if (!itemsMoved) {
        killTimer(timerId);
        timerId = 0;
    }
}

void GraphWidget::wheelEvent(QWheelEvent *event)
{
    scaleView(pow((double)2, -event->delta() / 240.0));
}

void GraphWidget::drawBackground(QPainter *painter, const QRectF &rect)
{
    Q_UNUSED(rect);

    // Shadow
    QRectF sceneRect = this->sceneRect();
    QRectF rightShadow(sceneRect.right(), sceneRect.top() + 5, 5, sceneRect.height());
    QRectF bottomShadow(sceneRect.left() + 5, sceneRect.bottom(), sceneRect.width(), 5);
    if (rightShadow.intersects(rect) || rightShadow.contains(rect))
	painter->fillRect(rightShadow, Qt::darkGray);
    if (bottomShadow.intersects(rect) || bottomShadow.contains(rect))
	painter->fillRect(bottomShadow, Qt::darkGray);

    // Fill
    QLinearGradient gradient(sceneRect.topLeft(), sceneRect.bottomRight());
    gradient.setColorAt(0, Qt::white);
    gradient.setColorAt(1, Qt::lightGray);
    painter->fillRect(rect.intersect(sceneRect), gradient);
    painter->setBrush(Qt::NoBrush);
    painter->drawRect(sceneRect);

    // Text
//    QRectF textRect(sceneRect.left() + 4, sceneRect.top() + 4,
//                    sceneRect.width() - 4, sceneRect.height() - 4);
//    QString message(tr("Click and drag the nodes around, and zoom with the mouse "
//                       "wheel or the '+' and '-' keys"));
//    
//    QFont font = painter->font();
//    font.setBold(true);
//    font.setPointSize(14);
//    painter->setFont(font);
//    painter->setPen(Qt::lightGray);
//    painter->drawText(textRect.translated(2, 2), message);
//    painter->setPen(Qt::black);
//    painter->drawText(textRect, message);
}

void GraphWidget::scaleView(qreal scaleFactor)
{
    qreal factor = matrix().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
    if (factor < 0.07 || factor > 100)
        return;

    scale(scaleFactor, scaleFactor);
}
