/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/


#ifndef GRAPHDISPLAYWIDGET_H
#define GRAPHDISPLAYWIDGET_H

#include "CamiTKAPI.h"

// Qt Stuff
#include <QObject>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QColor>
#include <QPen>
#include <QVector>
#include <QMap>

namespace camitk {

/**
 * GraphDisplayWidget is used to manage a Widget to display a
 * graph with nodes and links onto a 2D canvas. It's inherits from QGraphicsView.
 *
 * It is used by TransformationExplorer to display the graph of FrameOfReference and Transformation
 * but is not aware of the object that are managed graphically (all managed using const void*)
 */
class CAMITK_API GraphDisplayWidget : public QGraphicsView {
    Q_OBJECT

public:
    /// constructor (build the scene and connect signals/slots)
    GraphDisplayWidget(QWidget* parent = nullptr);

    /// destructor
    virtual ~GraphDisplayWidget();

    /**
     * Empty all nodes and links
     */
    void clear();
    /**
     * Add a node to the graph.
     *
     * @param ptr the Object referred to by the node
     * @param tooltip The tooltip to be displayed when the mouse hovers over the node
     * @param color The color of the disc representing the node
     * @param lineColor The color of the circle around the disc
     * @param internalText Text to display inside the node (can be any UTF-8 character)
     */
    void addNode(const void* ptr, QString tooltip, QColor color, QColor lineColor, QString internalText = "");
    /**
     * Add a link in the graph.
     *
     * @param ptr a pointer to the object that is represented by this link.
     * @param from a pointer to the object represented as a node (and added before by addNode) that this links from
     * @param to a pointer to the object represented as a node (and added before by addNode) that this links to
     * @param tooltip The tooltip to be displayed when the mouse hovers over the link
     * @param pen The pen used to draw the line (color, brush, width...)
     * @param arrowHead if true adds an arrow pointing to the to object
     */
    void addLink(const void* ptr, const void* from, const void* to, QString tooltip, const QPen& pen, bool arrowHead);

    /**
     * Refresh the 2D canvas display
     */
    void refresh();

    /**
     * Resize the content of the canvas to the size of the widget
     */
    void autoScale();

    /**
     * Set the diameter of nodes' graphical representation
     */
    void setNodeDiameter(float d);

signals:
    /**
     * Signal emitted when a node was selected in the 2D canvas
     * It sends the pointer to the object represented by the node
     */
    void nodeSelectionChanged(const void* node);

    /**
     * Signal emitted when a link was selected in the 2D canvas
     *  It sends the pointer to the object represented by the link
     */
    void linkSelectionChanged(const void* link);

    /// node was double clicked
    void nodeDoubleClicked(const void* node);

    /// link was double clicked
    void linkDoubleClicked(const void* node);

public slots:
    /**
     * Specify to the view that a node was selected
     * @param node a pointer to the object represented by the node
     */
    void setSelectedNode(const void* node);
    /**
     * Specify to the view that a link was selected
     * @param link a pointer to the object represented by the link
     */
    void setSelectedLink(const void* link);

    /// Get the selection of nodes and links from the 2D canvas
    void updateView();

protected:
    /// process double clicked nodes and link
    void mouseDoubleClickEvent(QMouseEvent*) override;

    /// make sure the scene fit the new size entirely
    void resizeEvent(QResizeEvent*) override;

    // make sure the scene fits all the potentially moved nodes
    void mouseReleaseEvent(QMouseEvent* e) override;

private:
    /// Diameter of a node representation
    double diameter;

    /// selection pen half width used to shrink selected nodes
    double halfPenWidth;

    /// Finding graphical items from objects and links, finding objects and links from graphical items
    /// node2item[nodeObjectPtr] = graphicalItem
    QMap<const void*, QGraphicsEllipseItem*> node2item;
    // same but for internal text decoration
    QMap<const void*, QGraphicsSimpleTextItem*> node2textItem;
    /// item2node[graphicalItem] = nodeObjectPtr
    QMap<QGraphicsEllipseItem*, const void*> item2node;
    /// textItem2node[graphicalItem] = nodeObjectPtr
    QMap<QGraphicsSimpleTextItem*, const void*> textItem2node;

    /// link2item[linkObjectPtr] = graphicalItem
    QMap<const void*, QGraphicsLineItem*> link2item;
    // same but for the arrow heads
    QMap<const void*, QGraphicsPolygonItem*> link2arrowHead;
    /// item2link[graphicalItem] = linkObjectPtr
    QMap<QGraphicsLineItem*, const void*> item2link;

    // Selections
    const void* currentSelectedNode;
    const void* currentSelectedLink;

    // Pens to manage to selected/unselected
    QPen selectedPen;
    QMap<const void*, QPen> unselectedPen;

    // For each link, from and to nodes
    QMap<const void*, std::pair<const void*, const void*>> links;
    // nodeArity[nodeObjectPtr] is the number of links from and to the node
    QMap<const void*, int> nodeArity;

    // Move the graphical representation of a node in the canvas
    void moveNode(const void* ptr, float posX, float posY);
    // Move the graphical representation of a link in the canvas
    void moveLink(const void* ptr, float posX1, float posY1, float posX2, float posY2);



    // update link position using the node (new) center, called when nodes are moved
    void updateLinkPosition();

    // currently being update, n
};

} // namespace camitk
#endif // GRAPHDISPLAYWIDGET_H
