#include "GraphicEdge.hpp"
#include "../logic/DagStructure.hpp"
#include <QStringList>

//////////////////////////////////////////////////////////////////////////////
// CONSTRUCTOR & DESTRUCTOR

GraphicEdge::GraphicEdge(Agedge_t* ep, QGraphicsItem * parent) :
    QGraphicsItem(parent),
    edgeColor(Qt::black),
    edgePen(edgeColor),
    edgeBrush(Qt::NoBrush),
    arrowHeadBrush(edgeColor),
    pathItem(0),
    headArrowHeadItem(0),
    boundingBox(0, 0, 0, 0)
{

  int splinesCount;
  int bezierCount;
  int pointsCount;
  splines* splinesList;
  bezier* bezierList;
  // Graphviz names changes in newer version: point (older versions) -> pointf (v2.26.3+)
  pointf* pointsList;

  // Create Head arrow head
  headArrowHeadItem = createArrowHead(DagStructure::_agget(ep, "_hdraw_"));

  /*
   * An edge is divided into 1 or more splines.
   * A spline is divided into 1 or more bezier curves.
   * A bezier is defined by N control points (at least 4).
   */

  // Splines list and count
  splinesList = ep->u.spl;
  splinesCount = splinesList->size;

  // --- Splines loop ---
  for (int i = 0; i < splinesCount; i++) {

    // Bezier list and count
    bezierList = splinesList[i].list;
    bezierCount = splinesList[i].size;

    // --- Bezier loop ---
    for (int j = 0; j < bezierCount; j++) {

      // Control points list and count
      pointsList = bezierList[j].list;
      pointsCount = bezierList[j].size;

      // Control points loop
      QList<QPointF> controlPoints;
      for (int k = 0; k < pointsCount; k++) {

        // Invert Y axis
        controlPoints.append(QPointF(pointsList[k].x, - pointsList[k].y));

      } // end j

      // Build cubic bezier path
      QPainterPath path(controlPoints[0]);
      path.cubicTo(controlPoints[1], controlPoints[2], controlPoints[3]);

      // Build path item
      pathItem = new QGraphicsPathItem(path, this);
      pathItem->setPen(edgePen);
      pathItem->setBrush(edgeBrush);

    } // end j

  } // end i


  // Init bounding box
  boxf bb = ep->u.spl->bb;
  // width = uper_right.x - lower_left.x
  qreal width = bb.UR.x - bb.LL.x;
  // height = uper_right.y - lower_left.y
  qreal height = bb.UR.y - bb.LL.y;
  boundingBox.setRect(bb.LL.x, - bb.UR.y, width, height);

}


/**
 * Destructor
 */
GraphicEdge::~GraphicEdge() {

  if (pathItem != 0)
    delete pathItem;

  if (headArrowHeadItem != 0)
    delete headArrowHeadItem;

}


//////////////////////////////////////////////////////////////////////////////
// METHODS

/**
 * @reimp
 */
QRectF GraphicEdge::boundingRect() const {

  return boundingBox;

}


/**
 * @reimp
 */
void GraphicEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget) {

  Q_UNUSED(painter);
  Q_UNUSED(option);
  Q_UNUSED(widget);

}


/**
 * Builds the arrow head polygon for this edge object. The arrow head drawing
 * operations are described in the attr string, as defined in "Drawing with
 * Graphviz" documentation.
 *
 * @param polygon
 * @param attr
 */
QGraphicsPolygonItem* GraphicEdge::createArrowHead(QString attr) {

  // String example : "S 5 -solid c 9 -#000000ff C 9 -#000000ff P 3 316 45 322 36 312 39"
  // We need the last part : "P n x1 y1 ... xn yn"
  // wich defines a filled polygon drawing operation with n points.

  // Split attr and retrieve values
  int pIndex = attr.indexOf('P');
  QStringList stringList = attr.mid(pIndex).split(' ');

  // Read number of points
  int i = 1;
  QString token = stringList.at(i);
  int numberOfPoints = token.toInt();
  ++i; // Move to next token

  // Points loop
  int x, y;
  QPolygonF polygon;
  for (int j = 0; j < numberOfPoints; j++) {

    token = stringList.at(i);
    x = token.toInt();
    ++i; // Move to next token

    token = stringList.at(i);
    y = token.toInt();
    ++i; // Move to next token

    // Append new point to the polygon
    polygon << QPointF(x, - y);

  }

  // Build arrow head polygon
  QGraphicsPolygonItem* arrowHeadPolygon = new QGraphicsPolygonItem(polygon, this);
  arrowHeadPolygon->setPen(edgePen);
  arrowHeadPolygon->setBrush(arrowHeadBrush);

  return arrowHeadPolygon;

}
