/**
 * @file graphicsedge.cpp
 * @brief Implements the GraphicsEdge class, which visualizes edges in the network graph.
 * @author Dimitris B. Kalamaras
 * @copyright
 *   Copyright (C) 2005-2026 by Dimitris B. Kalamaras.
 *   This file is part of SocNetV (Social Network Visualizer).
 * @license
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, version 3 or later.
 *   For more details, see <http://www.gnu.org/licenses/>.
 * @see https://socnetv.org
 */


#include "graphicsedge.h"

#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QStyleOptionGraphicsItem>
#include <QPainter>
#include <QtDebug>                  //used for qDebug messages
#include <cmath>

#include "global.h"
#include "graphicswidget.h"
#include "graphicsnode.h"
#include "graphicsedgeweight.h"
#include "graphicsedgelabel.h"
// #include <QPaintEngine>

SOCNETV_USE_NAMESPACE


GraphicsEdge::GraphicsEdge(GraphicsWidget *gw,
                           GraphicsNode *from,
                           GraphicsNode *to,
                           const qreal &weight,
                           const QString &label,
                           const QString &color,
                           const Qt::PenStyle &style,
                           const int &type,
                           const bool &drawArrows,
                           const bool &bezier,
                           const bool &weightNumbers,
                           const bool &highlighting,
                           const int &arrowSize) : graphicsWidget(gw)
{

    graphicsWidget->scene()->addItem(this);  //add edge to scene to be displayed

    from->addOutEdge( this );       // adds this new edge to sourceNode
    to->addInEdge( this );          // adds this new edge to targetNode

    source=from;                    // saves the source node
    target=to;                      // saves the target node

    m_style = style;
    m_state = EDGE_STATE_REGULAR ;

    m_color=QColor(color);                  // saves the color of the edge

    m_drawArrows=drawArrows;        // controls if edge will have arrows or not

    m_edgeDirType=type;

    m_minOffsetFromNode = 6;           // controls the minimum offset from source/target node center

    m_offsetFromSourceNode=source->size()+m_minOffsetFromNode;  // offsets edge from the centre of source node
    m_offsetFromTargetNode=target->size()+m_minOffsetFromNode;  // offsets edge from the centre of target node

    m_arrowSize=arrowSize;
    angle = 0.0;
    m_srcAngle = 0.0;

    m_weight = weight ;              // saves the weight/value of this edge

    if ( fabs(m_weight) > 1  )  {
        m_width = 1+log ( 1+ log(fabs(m_weight) )) ;
    }
    else {
        m_width = fabs(m_weight) ;
    }

    m_Bezier = bezier;               // controls if it will appear as line or curve

    m_label = label;
    m_drawLabel = !m_label.isEmpty();

    m_drawWeightNumber = weightNumbers;     // controls if weight number will be shown

//    qDebug()<< "Constructed graphics edge:"
//            << source->nodeNumber()
//            << "->"
//            << target->nodeNumber()
//            <<" = " << m_weight
//           <<" label " << m_label
//          <<" edgeType " << m_edgeDirType;

    if (m_drawWeightNumber) {
        addWeightNumber();
    }
    if (m_drawLabel)
        addLabel();

    m_hoverHighlighting = highlighting;

    setAcceptHoverEvents(true);

    setFlags(QGraphicsItem::ItemIsSelectable);

    //Edges have lower z than nodes. Nodes always appear above edges.
    setZValue(ZValueEdge);

    setBoundingRegionGranularity(0);

    // Leave the default cache option (NoCache)
    // setCacheMode(QGraphicsItem::NoCache);

    rebuildPens();
    adjust();
}



/**
 * @brief Toggles displaying edge arrow
 * @param drawArrows
 */
void GraphicsEdge::showArrows(const bool &drawArrows){
    m_drawArrows=drawArrows;
    update();
}


void GraphicsEdge::toggleBezier(const bool &toggle) {
    m_Bezier = toggle;
    adjust();
}


/**
 * @brief Fills the four cached arrow-corner QPointFs.
 *
 * Uses @c angle (curve tangent direction at @c targetPoint, in the source→target
 * sense) for the destination arrowhead wings, and @c m_srcAngle (tangent
 * direction at @c sourcePoint) for the source arrowhead on undirected /
 * reciprocated edges.  For straight edges @c m_srcAngle == @c angle; for
 * bezier curves they differ because the tangent varies along the arc.
 *
 * Called by @c adjust() and by @c setArrowSize() so @c paint() performs no
 * trigonometry of its own.
 */
void GraphicsEdge::computeArrowPoints() {
    if (!m_drawArrows || source == target || line_length <= 10)
        return;
    m_destArrowP1 = targetPoint + QPointF(sin(angle - M_PI_3)          * m_arrowSize,
                                          cos(angle - M_PI_3)          * m_arrowSize);
    m_destArrowP2 = targetPoint + QPointF(sin(angle - M_PI + M_PI_3)   * m_arrowSize,
                                          cos(angle - M_PI + M_PI_3)   * m_arrowSize);
    if (m_edgeDirType == EdgeType::Undirected || m_edgeDirType == EdgeType::Reciprocated) {
        m_srcArrowP1 = sourcePoint + QPointF(sin(m_srcAngle + M_PI_3)        * m_arrowSize,
                                             cos(m_srcAngle + M_PI_3)        * m_arrowSize);
        m_srcArrowP2 = sourcePoint + QPointF(sin(m_srcAngle + M_PI - M_PI_3) * m_arrowSize,
                                             cos(m_srcAngle + M_PI - M_PI_3) * m_arrowSize);
    }
}


/**
 * @brief Sets the arrow size for this edge.
 * Recalculates only the four cached arrow-corner points (no path rebuild)
 * then schedules a repaint.
 */
void GraphicsEdge::setArrowSize(const int &size) {
    prepareGeometryChange();  // bounding rect includes m_arrowSize padding
    m_arrowSize = size;
    computeArrowPoints();
    update();
}

int GraphicsEdge::arrowSize() const {
    return static_cast<int>(m_arrowSize);
}


/**
 * @brief Removes any references to this edge in source and target nodes.
 */
void GraphicsEdge::removeRefs(){
//    qDebug() << "Removing edge refs...";
    source->removeOutEdge(this);
    target->removeInEdge(this);
}


/**
 * @brief Sets the edge color
 * @param str
 */
void GraphicsEdge::setColor( const QString &str) {
    m_color=QColor(str);
    prepareGeometryChange();
    rebuildPens();
}


/**
 * @brief Returns the edge QColor.
 * @return
 */
QColor GraphicsEdge::color() const{
    return m_color;
}


/**
 * @brief Returns the edge color in pajek-accepted format
 *
 * Called from Graph
 *
 * @return
 */
QString GraphicsEdge::colorToPajek() {
    QString m_colorStr = m_color.name();
    if (m_colorStr.startsWith("#")) {
        return  ("RGB"+m_colorStr.right( m_colorStr.size()-1 )).toUpper()  ;
    }
    return m_colorStr;
}


/**
 * @brief Changes the edge weight - Updates both the width and the weightNumber
 *
 * Called from MW when user wants to change an edge's weight.

 * @param w
 */
void GraphicsEdge::setWeight(const qreal &w) {
//    qDebug() << "Setting edge weight:" << w;
    prepareGeometryChange();
    m_weight = w;
    if ( fabs(m_weight) > 1  )  {
        m_width = 1+log ( 1+ log(fabs(m_weight) )) ;
    }
    else {
        m_width = fabs(m_weight) ;
    }
    rebuildPens();
    if (m_drawWeightNumber)
        weightNumber->setPlainText (QString::number(w));
}


/**
 * @brief Returns the weight/value of this edge
 * @return
 */
qreal GraphicsEdge::weight() const {
    return m_weight;
}


/**
 * @brief Adds a graphics edge weight to this edge
 */
void GraphicsEdge::addWeightNumber (){
    // create edge weight item
    double x = -20 + ( source->x() + target->x() ) / 2.0;
    double y = -20 + ( source->y() + target->y() ) / 2.0;
    weightNumber = new  GraphicsEdgeWeight (this, 7, QString::number(m_weight) );
    weightNumber->setPos(x,y);
    weightNumber->setDefaultTextColor (m_color);
    m_drawWeightNumber = true;
}


/**
 * @brief Toggles visibility of weight numbers
 * @param toggle
 */
void GraphicsEdge::setWeightNumberVisibility (const bool &toggle) {
    if (m_drawWeightNumber) {
        if (toggle)
            weightNumber->show();
        else
            weightNumber->hide();
    }
    else{
        if (toggle)
            addWeightNumber();
    }

}




/**
 * @brief Changes the edge label.
 *
 * Called from MW when user wants to change an edge's label
 *
 * @param label
 */
void GraphicsEdge::setLabel(const QString &label) {
//    qDebug() << "Setting graphics edge label:" << label;
    prepareGeometryChange();
    m_label = label;
    if (m_drawLabel)
        edgeLabel->setPlainText (m_label);
}


/**
 * @brief Returns the edge label text
 * @return QString
 */
QString GraphicsEdge::label() const {
    return m_label;
}


/**
 * @brief Adds a graphics edge label to this edge
 */
void GraphicsEdge::addLabel (){
    // create edge label item
    double x =  5+ ( source->x() + target->x() ) / 2.0;
    double y =  5+ ( source->y() + target->y() ) / 2.0;
    edgeLabel = new  GraphicsEdgeLabel (this, 7, m_label );
    edgeLabel->setPos(x,y);
    edgeLabel->setDefaultTextColor (m_color);
    m_drawLabel = true;
}

/**
 * @brief Toggles the graphics edge label visibility
 * @param toggle
 */
void GraphicsEdge::setLabelVisibility (const bool &toggle) {
    if (m_drawLabel) {
        if (toggle)
            edgeLabel->show();
        else
            edgeLabel->hide();
    }
    else{
        if (toggle)
            addLabel();
    }

}


/**
 * @brief Returns the source node of this graphics edge
 * @return
 */
GraphicsNode *GraphicsEdge::sourceNode() const {
    return source;
}


/**
 * @brief Sets the source node of this graphics edge
 * @param node
 */
void GraphicsEdge::setSourceNode(GraphicsNode *node) {
    source = node;
    adjust();
}


/**
 * @brief Called from graphicsNode to update edge offset from source node (i.e. when node size changes)
 * @param offset
 */
void GraphicsEdge::setSourceNodeSize(const int &size){
    m_offsetFromSourceNode=size + m_minOffsetFromNode;
    adjust();
}


/**
 * Returns the source node number
 * @return int
 */
int GraphicsEdge::sourceNodeNumber () {
    return source->nodeNumber();
}


/**
 * @brief Returns the target node.
 * @return
 */
GraphicsNode *GraphicsEdge::targetNode() const {
    return target;
}


/**
 * @brief Sets the target node.s
 * @param node
 */
void GraphicsEdge::setTargetNode(GraphicsNode *node){
    target = node;
    adjust();
}

/**
 * @brief Called from graphicsNode to update edge offset from target node (i.e. when node size changes)
 * @param offset
 */
void GraphicsEdge::setTargetNodeSize(const int & size){
    m_offsetFromTargetNode=size + m_minOffsetFromNode;
    adjust();
}

/**
 * Returns the target node number
 * @return int
 */
int GraphicsEdge::targetNodeNumber() {
    return target->nodeNumber();
}


/**
* @brief Updates Minimum Offset From Node and calls adjust to update the edge
* @param offset
*/
void GraphicsEdge::setMinimumOffsetFromNode(const int &offset) {
    m_minOffsetFromNode = offset;
    m_offsetFromTargetNode = target->size() + m_minOffsetFromNode;
    adjust();
}


/**
 * @brief Returns the horizontal difference between target and source nodes.
 * @return
 */
qreal GraphicsEdge::dx() const
{
    return target->x() - source->x();
}

/**
 * @brief Returns the vertical difference between target and source nodes.
 * @return
 */
qreal GraphicsEdge::dy() const
{
    return target->y() - source->y();
}


/**
 * @brief Returns the euclidean length of the edge
 * @return
 */
qreal GraphicsEdge::length() const
{ return sqrt ( dx() * dx()  + dy() * dy() ) ; }




/**
 * @brief Recomputes all geometry for this edge.
 *
 * Called whenever a connected node moves or any geometric property changes
 * (node size, offset, bezier mode, etc.).  Executes four steps in order:
 *
 * 1. Snapshots @c line_dx / @c line_dy / @c line_length from current node centres.
 * 2. Sets @c sourcePoint / @c targetPoint — offset from node centres along the
 *    chord direction (straight mode) or along the curve tangent at each endpoint
 *    (bezier mode), so arrowheads land on the node boundary in the correct direction.
 * 3. Rebuilds @c m_path:
 *    - straight edge:        @c lineTo()
 *    - quadratic bezier arc: @c quadTo() with curvature capped at
 *                            min(40 px, line_length × 0.3) to avoid extreme
 *                            bowing on short edges
 *    - self-loop:            @c cubicTo() with fixed ±30 px control offsets
 * 4. Caches @c angle (tangent at @c targetPoint) and @c m_srcAngle (tangent at
 *    @c sourcePoint), then calls @c computeArrowPoints() to fill the four cached
 *    arrow-corner QPointFs.
 *
 * Arrow polygons are intentionally @b not added to @c m_path so that changing
 * @c m_arrowSize only requires recomputing four QPointFs and a repaint —
 * not a full path rebuild.
 */
void GraphicsEdge::adjust(){
    // qDebug() << "GraphicsEdge::adjust()";
    if (!source || !target) {
        return;
    }

    line_length = length();
    line_dx = dx();
    line_dy = dy();

    // Curvature for bezier mode: capped so short edges don't bow excessively.
    // Computed once here and reused for both endpoint offsetting and path ctrl.
    const qreal curvature = (m_Bezier && line_length > 0)
                            ? qMin(40.0, line_length * 0.3) : 0.0;

    prepareGeometryChange();

    if (source != target && line_length > 0) {
        if (!m_Bezier) {
            // Straight line: offset both endpoints along the chord direction.
            edgeOffset = QPointF((line_dx * m_offsetFromTargetNode) / line_length,
                                 (line_dy * m_offsetFromTargetNode) / line_length);
            sourcePoint = source->pos() + edgeOffset;
            targetPoint = target->pos() - edgeOffset;
        } else {
            // Bezier: offset endpoints along the curve's tangent at each end
            // so the arrowhead sits on the node boundary in the arrival direction.
            // Use an approximate ctrl from node centers to derive tangent vectors.
            QPointF srcPos  = source->pos();
            QPointF tgtPos  = target->pos();
            QPointF approxCtrl = (srcPos + tgtPos) / 2.0
                                 + QPointF(-line_dy, line_dx) * (curvature / line_length);
            QPointF sTan = approxCtrl - srcPos;
            qreal   sTLen = qSqrt(sTan.x()*sTan.x() + sTan.y()*sTan.y());
            sourcePoint = (sTLen > 0) ? srcPos + sTan * (m_offsetFromSourceNode / sTLen)
                                      : srcPos;
            QPointF tTan = tgtPos - approxCtrl;
            qreal   tTLen = qSqrt(tTan.x()*tTan.x() + tTan.y()*tTan.y());
            targetPoint = (tTLen > 0) ? tgtPos - tTan * (m_offsetFromTargetNode / tTLen)
                                      : tgtPos;
        }
    } else {
        edgeOffset = QPointF(0, 0);
        sourcePoint = source->pos();
        targetPoint = target->pos();
    }

    if (m_drawWeightNumber) {
        weightNumber->setPos(
                    -20 + (source->x()+target->x())/2.0,
                    -20 + (source->y()+target->y())/2.0 );
    }
    if (m_drawLabel) {
        edgeLabel->setPos(
                    5 + (source->x()+target->x())/2.0,
                    5 + (source->y()+target->y())/2.0 );
    }

    QPainterPath path(sourcePoint);

    if (source != target) {
        if (!m_Bezier) {
            path.lineTo(targetPoint);
            if (line_length > 0) {
                angle = ::acos( line_dx / line_length );
                if (line_dy >= 0)
                    angle = M_PI_X_2 - angle;
            }
            m_srcAngle = angle; // chord is uniform — same angle at both ends
        }
        else {
            // Control point: midpoint of the offset endpoints shifted
            // curvature pixels perpendicular to the chord direction.
            QPointF mid  = (sourcePoint + targetPoint) / 2.0;
            QPointF ctrl = mid + QPointF(-line_dy, line_dx) * (curvature / line_length);
            path.quadTo(ctrl, targetPoint);

            // Dest arrow: tangent at targetPoint = direction ctrl → target.
            qreal tdx = targetPoint.x() - ctrl.x();
            qreal tdy = targetPoint.y() - ctrl.y();
            qreal tlen = qSqrt(tdx * tdx + tdy * tdy);
            if (tlen > 0) {
                angle = ::acos( tdx / tlen );
                if (tdy >= 0)
                    angle = M_PI_X_2 - angle;
            }
            // Src arrow: tangent at sourcePoint = direction source → ctrl.
            qreal sdx = ctrl.x() - sourcePoint.x();
            qreal sdy = ctrl.y() - sourcePoint.y();
            qreal slen = qSqrt(sdx * sdx + sdy * sdy);
            if (slen > 0) {
                m_srcAngle = ::acos( sdx / slen );
                if (sdy >= 0)
                    m_srcAngle = M_PI_X_2 - m_srcAngle;
            }
        }
    }
    else { // self-loop
        QPointF c1 = QPointF( targetPoint.x() -30,  targetPoint.y() -30 );
        QPointF c2 = QPointF( targetPoint.x() +30,  targetPoint.y() -30 );
        path.cubicTo( c1, c2, targetPoint);
    }

    m_path = path;

    computeArrowPoints();
}



/**
 * @brief  Returns the shape of this edge as a QPainterPath in local coordinates.
 * The shape is used for many things, including collision detection, hit tests,
 * and for the QGraphicsScene::items() functions.
 * The default implementation calls boundingRect() to return a simple rectangular shape,
 * but we reimplement it to return a more accurate shape for non-rectangular items.
 * @return QPainterPath
 */
QPainterPath GraphicsEdge::shape () const {
    //qDebug()<<"GraphicsEdge::shape()";		//too many debug messages...
    //    QPainterPath m_path_shape = m_path;
    //    m_path_shape.addPath(m_path.translated(1,1));
    //    m_path_shape.addPath(m_path.translated(-1,-1));
    //    return m_path_shape;
    return m_path;
} 


/**
 * @brief Defines the outer bounds of the edge as a rectangle;
 * All painting will be restricted to inside the edge's bounding rect.
 * Qt uses this bounding rect to determine whether the edge requires redrawing.
 * @return
 */
QRectF GraphicsEdge::boundingRect() const {
    if (!source || !target)
        return QRectF();
    // Pad by arrowSize so arrows drawn in paint() are never clipped.
    const qreal pad = m_arrowSize + m_width / 2.0 + 1.0;
    return m_path.controlPointRect().adjusted(-pad, -pad, pad, pad);
}


/**
 * @brief Changes the direction type of edge A->B
  */
void GraphicsEdge::setDirectionType(const int &dirType){
//    qDebug()<< "Edge"
//            << source->nodeNumber()
//            << "->"
//            << target->nodeNumber()
//            << "new direction type"
//            << dirType;
    prepareGeometryChange();
    m_edgeDirType = dirType;
    m_drawArrows = true;
    if (m_edgeDirType==EdgeType::Undirected) {
        m_drawArrows = false;
    }
    adjust();
}


/**
 * @brief returns the direction type of this edge
 * @return
 */
int GraphicsEdge::directionType() {
    return m_edgeDirType ;
}



/**
 * @brief Sets the PenStyle of this edge
 * @param style
 */
void GraphicsEdge::setStyle( const Qt::PenStyle  &style ) {
    m_style = style;
    rebuildPens();
}


/**
 * @brief Rebuilds the three cached QPens and the cached QBrush.
 *
 * Called from the constructor and from any setter that changes a pen-relevant
 * field (color, weight/width, style).  This keeps paint() free of per-repaint
 * object construction.
 */
void GraphicsEdge::rebuildPens() {
    // Negative-weight edges are always drawn dashed regardless of m_style.
    const Qt::PenStyle regStyle = (m_weight < 0) ? Qt::DashLine : m_style;
    m_penRegular   = QPen(m_color,          m_width,     regStyle, Qt::RoundCap, Qt::RoundJoin);
    m_penHighlight = QPen(QColor("red"),    m_width,     m_style,  Qt::RoundCap, Qt::RoundJoin);
    m_penHover     = QPen(QColor("red"),    m_width + 1, m_style,  Qt::RoundCap, Qt::RoundJoin);
    m_brush        = QBrush(m_color);
}


/**
 * @brief Returns the PenStyle of this edge
 * @return
 */
Qt::PenStyle GraphicsEdge::style() const{
    return m_style;

}

/**
 * @brief Returns the QPen for this edge -- the pen changes when the edge state changes/
 * @return
 */
QPen GraphicsEdge::pen() const {
    //qDebug() << "GraphicsEdge::pen() - returning pen "  ;
    switch (m_state) {
    case EDGE_STATE_REGULAR:
        //qDebug() << "GraphicsEdge::pen() - returning pen for state REGULAR"  ;
        if (m_weight < 0 ){
            return  QPen(m_color, m_width, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin);
        }
        return QPen(m_color, m_width, m_style, Qt::RoundCap, Qt::RoundJoin);
        break;
    case EDGE_STATE_HIGHLIGHT: // selected
        //qDebug() << "GraphicsEdge::pen() - returning pen for state HIGHLIGHTED"  ;
        return QPen( QColor("red"), m_width, m_style, Qt::RoundCap, Qt::RoundJoin);
    case EDGE_STATE_HOVER: // hover
        //qDebug() << "GraphicsEdge::pen() - returning pen for state HOVER"  ;
        return QPen(QColor("red"), m_width+1, m_style, Qt::RoundCap, Qt::RoundJoin);
    default:
        //qDebug() << "GraphicsEdge::pen() - returning pen for state DEFAULT"  ;
        return QPen(m_color, m_width, m_style, Qt::RoundCap, Qt::RoundJoin);
    }

}


/**
 * @brief Sets the edge state
 * @param state
 */
void GraphicsEdge::setState(const int &state) {
    //NOTE: DO NOT USE HERE: prepareGeometryChange()
    m_state=state;
}


/**
 * @brief Paints the edge onto the canvas.
 *
 * Draws the pre-built line/self-loop @c m_path, then draws the arrow
 * polygons from the cached corner points (@c m_destArrowP1/P2 and, for
 * undirected/reciprocated edges, @c m_srcArrowP1/P2).  No trigonometry
 * is performed here — all geometry is pre-computed by @c adjust() and
 * @c computeArrowPoints(), so this method is as cheap as possible for
 * the common case of a static network.
 *
 * @param painter  The QPainter provided by the scene.
 * @param option   Style options (selection, hover state).
 */
void GraphicsEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *){
    if (!source || !target)
        return;

    //qDebug() <<"@@@ GraphicsEdge::paint() on" << painter->paintEngine()->type();
    //painter->setClipRect();

    //if the edge is being dragged around, darken it!
    if (option->state & QStyle::State_Selected) {
        //setZValue(ZValueEdgeHighlighted);
        setState(EDGE_STATE_HOVER);
    }
    else if (option->state & QStyle::State_MouseOver) {
        if (m_hoverHighlighting) {
            setZValue(ZValueEdgeHighlighted);
            setState(EDGE_STATE_HOVER);
        }
    }
    else if (m_state==EDGE_STATE_HIGHLIGHT){
        if (m_hoverHighlighting) {
            setZValue(ZValueEdgeHighlighted);
            setState(EDGE_STATE_HIGHLIGHT);
        }
    }
    else {
        setZValue(ZValueEdge);
        setState(EDGE_STATE_REGULAR);
    }
    // Select cached pen for current state — no QPen construction per repaint.
    switch (m_state) {
    case EDGE_STATE_HIGHLIGHT: painter->setPen(m_penHighlight); break;
    case EDGE_STATE_HOVER:     painter->setPen(m_penHover);     break;
    default:                   painter->setPen(m_penRegular);   break;
    }

    painter->setBrush(Qt::NoBrush);
    painter->drawPath(m_path);

    if (m_drawArrows && source != target && line_length > 10) {
        painter->setBrush(m_brush);
        painter->drawPolygon(QPolygonF() << targetPoint << m_destArrowP1 << m_destArrowP2);
        if (m_edgeDirType == EdgeType::Undirected || m_edgeDirType == EdgeType::Reciprocated)
            painter->drawPolygon(QPolygonF() << sourcePoint << m_srcArrowP1 << m_srcArrowP2);
    }
}


/**
 * @brief Called when the edge changes, i.e. moves, becomes disabled or changes its visibility
 * @param change
 * @param value
 * @return
 */
QVariant GraphicsEdge::itemChange(GraphicsItemChange change, const QVariant &value){
    switch (change) {
    case ItemPositionHasChanged: {
        break;
    }
    case ItemEnabledHasChanged: {
        break;
    }
    case ItemSelectedHasChanged: {
        if (value.toBool()) {
            setZValue(ZValueEdgeHighlighted);
            setHighlighted(true);
            //source->setSelected(true);
            //target->setSelected(true);
        }
        else{
            setZValue(ZValueEdge);
            setHighlighted(false);
            //source->setSelected(false);
            //target->setSelected(false);
        }
        break;
    }
    case ItemVisibleHasChanged: {
        break;
    }
    default: {
        break;
    }
    };
    return QGraphicsItem::itemChange(change, value);
}



/**
 * @brief Returns the width of the edge as a function of edge weight
 * @return
 */
qreal GraphicsEdge::width() const{
    return m_width;
}



/**
 * @brief Toggles the highlighted state of the the edge, if highlighting is allowed.
 *
 * Called from GraphicsNode when the user hovers over the node.
 *
 * @param flag
 */
void GraphicsEdge::setHighlighted(const bool &flag) {
    //qDebug()<< "GraphicsEdge::setHighlighted() - " << flag;
    if (flag && m_hoverHighlighting) {
        prepareGeometryChange();
        setState(EDGE_STATE_HIGHLIGHT);
    }
    else {
        prepareGeometryChange();
        setState(EDGE_STATE_REGULAR);
    }
}


/**
 * @brief Toggles edge highlighting on or off
 *
 * If enabled, the edge can be highlighted.
 *
 * @param toggle
 */
void GraphicsEdge::setHighlighting(const bool &toggle) {
    m_hoverHighlighting = toggle;

}




///**
// * @brief handles the events of a click on an edge
// * @param event
// */
//void GraphicsEdge::mousePressEvent(QGraphicsSceneMouseEvent *e) {
//    qDebug() << "GraphicsEdge::mousePressEvent() - click on an edge ";
//    //setClicked();
//    QGraphicsItem::mousePressEvent(e);
//}




GraphicsEdge::~GraphicsEdge(){
//    qDebug() << "self-destructing edge:"
//             << sourceNodeNumber()<< "->" << targetNodeNumber()
//             << "will remove refs first...";

    removeRefs();

//    qDebug() << "removing edge weight number, if any...";
    if (m_drawWeightNumber)
        graphicsWidget->removeItem(weightNumber);

//    qDebug() << "removing edge label, if any...";
    if (m_drawLabel)
        graphicsWidget->removeItem(edgeLabel);

    this->hide();

//    qDebug() << "calling GW removeItem for this edge";
    graphicsWidget->removeItem(this);


}

