/*
    This file is part of Msc-generator.
    Copyright (C) 2008-2024 Zoltan Turanyi
    Distributed under GNU Affero General Public License.

    Msc-generator is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Msc-generator 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Msc-generator.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file gvgraphs.h Declaration of objects to hold, manipulate and render a graphviz graph.
* @ingroup libgvgen_files */

#ifndef GVGRAPH_H
#define GVGRAPH_H


#include <list>
#include "canvas.h"
#include "gvstyle.h"
#include "chartbase.h"

#ifndef GRAPHVIZ_VER
#error Need graphviz!
#endif

/** @name Types & externs copied from cgraph.h: forward declare cgraph types, so we do not have to include the whole cgraph library here
 ** @{ */
#undef CGRAPH_API
#ifdef GVDLL //define this for Windows build (cgraph.h also needs it)
#define CGRAPH_API __declspec(dllimport)
#else
#define CGRAPH_API
#endif

typedef struct Agraph_s Agraph_t;	///<graph, subgraph (or hyperedge) (copied from cgraph.h of graphviz)
typedef struct Agnode_s Agnode_t;	///<node (atom) (copied from cgraph.h of graphviz)
typedef struct Agedge_s Agedge_t;	///<node pair (copied from cgraph.h of graphviz)
typedef struct Agsym_s Agsym_t; ///<symbol (copied from cgraph.h of graphviz)
typedef struct GVC_s GVC_t; ///<global gvc context (copied from cgraph.h of graphviz)
typedef struct obj_state_s obj_state_t; ///<generic object (node, edge, graph, etc.) (copied from cgraph.h of graphviz)
#if GRAPHVIZ_VER < 249
extern "C" CGRAPH_API void *agbindrec(void *obj, char *name, unsigned int size, int move_to_front);
extern "C" CGRAPH_API int agxset(void *obj, Agsym_t * sym, char *value);
extern "C" CGRAPH_API int agset(void *obj, char *name, char *value);
extern "C" CGRAPH_API Agsym_t *agattr(Agraph_t * g, int kind, char *name, char *value);
#else
extern "C" CGRAPH_API int agset(void *obj, char *name, const char *value);
extern "C" CGRAPH_API Agsym_t *agattr(Agraph_t * g, int kind, char *name, const char *value);
#endif
extern "C" CGRAPH_API char *agget(void *obj, char *name);
extern "C" CGRAPH_API Agraph_t *agsubg(Agraph_t * g, char *name, int cflag);
extern "C" CGRAPH_API Agnode_t *agnode(Agraph_t * g, char *name, int createflag);
extern "C" CGRAPH_API Agedge_t *agedge(Agraph_t * g, Agnode_t * t, Agnode_t * h, char *name, int createflag);
/** @} */

/** @name wrappers for passing const char* parameters to cgraph functions
 ** @{ */
#if GRAPHVIZ_VER < 249
inline void *agbindrec(void *obj, const char *name, unsigned int size, int move_to_front) { return agbindrec(obj, const_cast<char*>(name), size, move_to_front); }
inline int agxset(void *obj, Agsym_t * sym, const char *value) {return agxset(obj, sym, const_cast<char*>(value)); }
#endif
inline const char *agget(void *obj, const char *name) { return agget(obj, const_cast<char*>(name)); }
inline int agset(void *obj, const char *name, const char *value) { return agset(obj, const_cast<char*>(name), const_cast<char*>(value)); }
inline Agsym_t *agattr(Agraph_t * g, int kind, const char *name, const char *value) {return agattr(g, kind, const_cast<char*>(name), const_cast<char*>(value));}
inline Agraph_t *agsubg(Agraph_t * g, const char *name, int cflag) { return agsubg(g, const_cast<char*>(name), cflag); }
inline Agnode_t *agnode(Agraph_t * g, const char *name, int createflag) {return agnode(g, const_cast<char*>(name), createflag);}
inline Agedge_t *agedge(Agraph_t * g, Agnode_t * t, Agnode_t * h, const char *name, int createflag) {return agedge(g, t, h, const_cast<char*>(name), createflag); }
/** @} */


/** Global variable needed by DOT.
 * On windows it is initialized statically in gvraphs.cpp, on Linux in main.cpp
 * (On windows we only use statically linked (well, DLLs) DOT components, on Linux we auto-load them via gvc. */
extern GVC_t *Gvc;

/** Read a list of layout plugins from gvc. */
std::set<std::string> GetLayoutMethods();

namespace graph {

class GraphChart;
class GraphNode;
class GraphEdge;
class SubGraph;

typedef struct { gsl::owner<GraphNode*> node; std::string port; FileLineColRange file_pos, port_file_pos, compass_file_pos; } GraphNodePort; ///<A GvNode and a port name (may have internal ':' for sub-ports) (needed only during input text parsing, graph )
typedef std::list<GraphNodePort> GraphNodePortList; ///<A list of nodes and ports (needed only during input text parsing, graph )
struct GraphEdgeOneSide { GraphEdgeType type; FileLineColRange pos; GraphNodePortList nodes; }; ///<Information on <edgeop, nodelist> (needed only during input text parsing, graph );
typedef std::list<GraphEdgeOneSide> GraphEdgeList; ///<A list of edges in the graph parser. type of edgeop can be for future -> => etc. (needed only during input text parsing, graph )
typedef struct { gsl::owner<GraphEdgeList*> edgelist; gsl::owner<GraphStyle*> style; } EdgeAttrlist; ///<An edge list and potential options bundled  (needed only during input text parsing, graph )


/** The draw passes of graphs. */
enum class EGraphDrawPass {
    BACKGROUND = 0,
    CLUSTER = 1,
    NODE = 2,
    EDGE = 3,
};

/** A struct used to hold a drawing primitive emitted by graphviz*/
struct GraphDrawingPrimitive
{
    Contour contour; ///<If the drawing primitive is closed area, we hold it here
    Path path;       ///<If the drawing primitive is an open path, we hold it here
    LineAttr line = LineAttr::None();   ///<The attributes of the line to draw, if any
    FillAttr fill = FillAttr::None();   ///<The attributes of the fill to use on 'contour' if any
    ShadowAttr shadow;///<The shadow to draw to 'contour' if any
    Label label;     ///<The text to emit if the drawing primitive is a label
    double sx = 0;    ///<The left side of the label if the drawing primitive is a label
    double dx = 0;    ///<The right side of the label if the drawing primitive is a label
    double y = 0;     ///<The top side of the label if the drawing primitive is a label
    GraphDrawingPrimitive(Path &&p, const LineAttr &l) noexcept : path(std::move(p)), line(l) {}
    GraphDrawingPrimitive(Contour &&c, const LineAttr &l) noexcept : path(std::move(c)), line(l) {}
    GraphDrawingPrimitive(Contour &&c, const LineAttr &l, const FillAttr &f) noexcept :
        contour(std::move(c)), line(l), fill(f) {}
    GraphDrawingPrimitive(Label &&l, double _sx, double _dx, double _y) noexcept :
        label(std::move(l)), sx(_sx), dx(_dx), y(_y) {}
    void ShiftBy(const XY &) noexcept;
    Contour Cover(const ShapeCollection &) const; ///<Returns the cover of the drawing primitive
    void CollectIsMapElements(Canvas& canvas, Chart& chart) const { label.CollectIsMapElements(chart.ismapData, canvas, chart.Shapes, sx, dx, y); }
    void Draw(Canvas&, const ShapeCollection&) const; ///<Actually draws the drawing primitive onto 'canvas'
};

/** A list of drawing primitives */
typedef std::vector<GraphDrawingPrimitive> DrawingList;

/** A base class for all graph elements: graphs, cluster subgraphs, nodes and edges.
 * As a descendant to 'Element' it can be tracked and may have element controls.*/
class GraphElement : public Element
{
protected:
    DrawingList drawingList;  ///<The drawing elements emitted by graphviz. Used only if we rely on the graphviz rendering engine and do not draw ourselves.
public:
    GraphElement(GraphStyle::EGraphElementType t) : style(EStyleType::ELEMENT, t) {}
    std::string name;  ///<The name of the graph, cluster, node (or the key of the edge)
    StyleCoW<GraphStyle> style;  ///<The collection of attributes applicable to us.
    bool AddAttribute(const Attribute &a, Chart *chart); ///<Add an Attribute to us.
    /** Adds all our attributes to us. Also Sets label.*/
    void AddAttributeList(const GraphStyle *s, Chart *chart);
    /** Add a drawing primitive - called from the "contour" gv (pseudo)renderer. Also adds the cover to "area"*/
    void AddDrawingPrimitive(Path &&p, const LineAttr &l);
    /** Add a drawing primitive - called from the "contour" gv (pseudo)renderer. Also adds the cover to "area"*/
    void AddDrawingPrimitive(Contour &&c, const LineAttr &l, const FillAttr &f, const ShapeCollection &shapes) { drawingList.emplace_back(std::move(c), l, f); area += drawingList.back().Cover(shapes); }
    /** Add a drawing primitive - called from the "contour" gv (pseudo)renderer. Also adds the cover to "area"*/
    void AddDrawingPrimitive(Label &&l, double _sx, double _dx, double _y, const ShapeCollection &shapes) { drawingList.emplace_back(std::move(l), _sx, _dx, _y); area += drawingList.back().Cover(shapes);}
    ShadowAttr GetShadow(Chart*chart) const;
    void AddAttributesToGraph(void *obj) const;
    virtual void ShiftBy(const XY &) override;
    virtual void PostPosProcess(Canvas &, Chart *ch) override;
    /** Draw the list of drawing primitives we have received.*/
    void DrawAll(Canvas &canvas, const ShapeCollection &shapes) const { for (auto &p: drawingList) p.Draw(canvas, shapes); }
    void CollectIsMapElements(Canvas&) final { _ASSERT(0); /*Call GraphCollectIsMapElements instead */ }
    virtual void GraphCollectIsMapElements(Canvas& canvas, Chart &chart) const { for (auto& p : drawingList) p.CollectIsMapElements(canvas, chart); }
    /** Draw this graph element (parts with this pass) */
    virtual void Draw(Canvas &canvas, const ShapeCollection &shapes, EGraphDrawPass) const = 0;
};

/** Placeholder for a graph. All nodes and edges refer back to this.
 * It is able to manage descendants of SubGraph, GraphNode and GraphEdge, if they have
 * constructor of the following syntax (one for defining at top level and one
 * for adding it to a subgraph).
 * - SubGraph(Graph &graph, std::string_view name, bool is_cluster, ...);
 * - SubGraph(SubGraph *parent, std::string_view name, bool is_cluster, ...);
 * - GraphNode(Graph &graph, std::string_view name, ...);
 * - GraphNode(SubGraph &parent, std::string_view name, ...);
 * Or two versions for GraphEdge based on how to handle port string memory ownership
 * - GraphEdge(GraphNode *_1, GraphNode *_2, const std::string &p1, const std::string &p2, const FileLineColRange &pos1, const FileLineColRange &pos2, ...);
 * - GraphEdge(GraphNode *_1, GraphNode *_2, std::string &&p1, std::string &&p2, const FileLineColRange &pos1, const FileLineColRange &pos2, ...);
 * Descendants are free to add any more parameters.
 * These elements will be created by CreateSubGraph(), CreateNode() and CreateEdge() functions,
 * which ensure proper linking inside class Graph.
 *
 * One node can only be in one visible subgraph (cluster==true), but in any number of non-visible ones.
 * So when we insert a node to a visible subgraph, we do the following:
 * - If it is not present in any other visible subgraphs, we add it.
 * - If it is present only in one of our parent visible subgraphs, we move it to us.
 * - If it is present in a visible subgraph, that is not our parent, we do not add it.
 */
 class Graph : public GraphElement
{
private:
    /** During graphviz rendering, this holds the graphviz object we render. nullptr at other times.*/
    mutable Agraph_t *in_gv = nullptr;  ///<If we are established in cgraph, this is our object there.
protected:
    std::vector<std::unique_ptr<GraphNode>> storage_nodes;    ///<All Nodes of the graph
    std::vector<std::unique_ptr<SubGraph>> storage_subgraphs; ///<All subgraphs of the graph
    std::set<std::string> attr_node;  ///<All node attributes mentioned (so that we can bind them)
    std::set<std::string> attr_edge;  ///<All edge attributes mentioned (so that we can bind them)
    std::set<std::string> attr_graph; ///<All graph attributes mentioned (so that we can bind them)
public:
    NPtrList<SubGraph> subgraphs; ///<Only the subgraphs directly in us.
    NPtrList<GraphNode> nodes;    ///<Nodes appearing directly in us.
    UPtrList<GraphEdge> edges;    ///<All edges of the graph
    const bool directed;          ///<True, if a directed graph.
    const bool strict;            ///<True, if a strict graph.
    Block boundingBox;            ///<The bounding box after layout.

    /** @name variables used during parsing, pseudo-rendering, layouting
     * These make Graph non-reentrant. But hey, the whole graphviz library
     * is not reentrant.
     * @{ */
    static Graph* _current;      ///<A static variable pointing to the Graph currently under parsing, layout or pseudo-rendering. Error messages and rendered drawing primitives are inserted into this.
    Canvas *_canvas = nullptr;   ///<The canvas during pseudo-rendering.
    std::string _err_msg_accum;  ///<We collect multi-segment DOT error messages in this.
    GraphChart *_chart = nullptr;///<The chart to add errors to during layout or parsing.
    static int DotError(char *msg);
    /** @} */

    Graph(bool d = true, bool s = false) : GraphElement(GraphStyle::GRAPH), directed(d), strict(s) {}
    Graph(const Graph&) = delete;  //we cannot copy, since all elements refer to us by their 'graph' member
    Graph(Graph&&) = delete;       //we cannot move, since all elements refer to us by their 'graph' member
    virtual ~Graph() override;
    void AddToGvGraph();
    Agraph_t *GetGv() const { return in_gv; } ///<If we are established in cgraph, this is our object there.
    std::set<std::string> GetAllSubGraphs() const;
    /** Create a subgraph of your choice (see notes for class Graph) and add it
     * at the top level to the graph.
     * Returns false in *created if non-null and if we have found this subgraph
     * among the top-level subgraphs by name. In this case we return the found
     * subgraph and do not allocate a new. If a subgraph of this name lives
     * elsewhere in a subgraph, we allocate a new one. (Hopefully mirroring
     * how graphviz handles this.)*/
    template <class SG, typename ...Args>
    SubGraph *CreateSubGraph(std::string_view name, bool cluster, bool *created, Args &&... args);
    /** Create a subgraph of your choice (see notes for class Graph) and add it
     * to the graph subgraph 'g'.
     * Returns false in *created if non-null and if we have found this subgraph
     * among the subgraphs of 'g' by name. In this case we return the found
     * subgraph and do not allocate a new. If a subgraph of this name lives
     * elsewhere, we allocate a new one. (Hopefully mirroring
     * how graphviz handles this.)*/
    template <class SG, typename ...Args>
    SubGraph *CreateSubGraph(SubGraph *g, std::string_view name, bool cluster, bool *created, Args &&... args);

    /** Create a node of your choice (see notes for class Graph) and add it
     * to the graph subgraph 'g'.
     * Returns false in *created if non-null and if we have found this node
     * already added (by name). In this case we return the one found.*/
    template <class Node, typename ...Args>
    GraphNode *CreateNode(std::string_view name, bool *created, Args &&... args);
    /** Create a node of your choice (see notes for class Graph) and add it
     * at the top level to the graph.
     * Returns false in *created if non-null and if we have found this node
     * already added (by name). In this case we return the one found.
     * If the node is added to a visible (cluster==true) subgraph, then
     * we remove this node from all other visible subgraphs in the graph,
     * except from 'g' or subgraphs of 'g'. Hopefully this mimics what
     * graphviz is doing. (One node can be in one visible subgraph, at most.*/
    template <class Node, typename ...Args>
    GraphNode *CreateNode(SubGraph *g, std::string_view name, bool *created, Args &&... args);
    /** Create an edge of your choice (see notes for class Graph) and add it
     * to the graph.
     * If an edge already exists between these nodes, we return it if the graph is
     * strict. If the graph is not directed, then we consider an edge existing if
     * any heads and tails equal or vice versa. If the edge does not yet exist
     * or the graph is not strict, we allocate a new edge (and return true
     * in *created).*/
    template <class Edge, typename ...Args>
    GraphEdge *CreateEdge(GraphNode *_1, GraphNode *_2, const std::string &p1, const std::string &p2, const FileLineColRange &pos1, const FileLineColRange &pos2, bool *created, Args &&... args);
    /** Create an edge of your choice (see notes for class Graph) and add it
     * to the graph.
     * If an edge already exists between these nodes, we return it if the graph is
     * strict. If the graph is not directed, then we consider an edge existing if
     * any heads and tails equal or vice versa. If the edge does not yet exist
     * or the graph is not strict, we allocate a new edge (and return true
     * in *created).*/
    template <class Edge, typename ...Args>
    GraphEdge *CreateEdge(GraphNode *_1, GraphNode *_2, std::string &&p1, std::string &&p2, const FileLineColRange &pos1, const FileLineColRange &pos2, bool *created, Args &&... args);

    void AddAttrName(GraphStyle::EGraphElementType t, const GraphStyle &s);
    void AddAttrName(GraphStyle::EGraphElementType t, const std::string &attr_name);

    GraphNode *GetNode(std::string_view name) const;    ///<Return the node of a given name (if found)
    SubGraph *GetSubGraph(std::string_view name) const; ///<Return the subgraph of a given name (if found)
    GraphEdge *GetFirstEdge(const GraphNode *_1, const GraphNode *_2) const;
    GraphElement *GetElement(void *in_gv);              ///<Return the node, edge or subgraph from their pointer in cgraph (if found)
    SubGraph *GetNodeFromVisibleSubgraph(GraphNode *n);
    bool RemoveEmptySubGraphs();

    void ShiftBy(const XY &) override;
    void Layout(GraphChart*chart);
    void PseudoRenderFrom(GraphChart*chart);
    void PostPosProcess(Canvas &, Chart *ch) override;
    void AdjustBkArea(const Range& total_x) noexcept;
    void GraphCollectIsMapElements(Canvas& canvas, Chart& chart) const override;
    void Draw(Canvas& canvas, const ShapeCollection& shapes, EGraphDrawPass) const override;
    std::optional<BackgroundFill> GetBkFill() const noexcept;
};

/** A subgraph. May or may not be a cluster subgraph.
* (Cluster subgraphs are drawn, their name usually begins with 'cluster',
* but not all layout engines honour this convention.)*/
class SubGraph : public GraphElement
{
    friend class Graph;
    friend class GraphNode;
protected:
    mutable Agraph_t *in_gv = nullptr;  ///<If we are established in cgraph, this is our object there.
    virtual void AddToGvGraph() const; ///<Create a counterpart for us in cgraph. Relies on parent to be already there
    virtual void ClearGv() const { in_gv = nullptr; for (auto &s: subgraphs) s->ClearGv(); }
public: //XXX should be protected, but make_unique needs them.
    SubGraph(Graph &g, std::string_view n, bool c) : GraphElement(c ? GraphStyle::CLUSTER : GraphStyle::SUBGRAPH), graph(g), parent(nullptr), cluster(c) { name = n; }
    SubGraph(SubGraph* g, std::string_view n, bool c) : GraphElement(c ? GraphStyle::CLUSTER : GraphStyle::SUBGRAPH), graph(g->graph), parent(g), cluster(c) { name = n; }
public:
    Graph &graph;                 ///<The graph we belong to.
    SubGraph *const parent;       ///<Our parent subgraph, nullptr if we are directly in a graph.
    const bool cluster;           ///<True, if we are a visible subgraph
    NPtrList<GraphNode> nodes;    ///<The nodes mentioned directly in us.
    NPtrList<SubGraph> subgraphs; ///<We own only the directly included subgraphs

    virtual ~SubGraph() = default;
    Agraph_t *GetGv() const { return in_gv; } ///<If we are established in cgraph, this is our object there.
    bool IsMyParent(SubGraph *s);
    bool Remove(SubGraph*s);
    void Remove(GraphNode *n);
    SubGraph* GetNodeFromVisibleSubgraph(GraphNode *n);
    bool RemoveEmptySubGraphs();
    GraphNode *GetNode(std::string_view name) const; ///<Return the node of a given name (if found)
    SubGraph *GetSubGraph(std::string_view name);    ///<Return the subgraph of a given name (if found)
    SubGraph *GetSubGraph(Agraph_t *);     ///<Return the subgraph of a given pointer in cgraph (if found)
    GraphElement *GetElement(void *in_gv); ///<Return the node, edge or subgraph from their pointer in cgraph (if found)
    NPtrList<GraphNode> GetAllNodes() const;
    virtual void ShiftBy(const XY &) override;
    std::unique_ptr<GraphNodePortList> CopyOfNodes() const;
    virtual void PostPosProcess(Canvas &, Chart *ch) override;
    virtual void RegisterCover(Chart *);
    /** Draw all contained subgraphs (and us too), but no nodes or edges*/
    void Draw(Canvas &canvas, const ShapeCollection &shapes, EGraphDrawPass p) const  override;
};



/** (Usable) Base class for nodes. */
class GraphNode : public GraphElement
{
    friend class Graph;
    friend class SubGraph;
    friend class GraphEdge;
protected:
    mutable Agnode_t *in_gv = nullptr;  ///<If we are established in cgraph, this is our object there.
    virtual void AddToGvGraph(const SubGraph *parent) const;  ///<Create a counterpart for us in cgraph. Relies on parent to be already there. If parent is nullptr, we are in the graph itself
    virtual void ClearGv() const { in_gv = nullptr; }
    void SetLabelFromName();
public: //XXX These should be protected, but make_unique needs them.
    GraphNode(Graph &g, std::string_view n) : GraphElement(GraphStyle::NODE), graph(g) { name = n; SetLabelFromName(); }
    GraphNode(SubGraph &g, std::string_view n) : GraphElement(GraphStyle::NODE), graph(g.graph) { name = n; SetLabelFromName();}
public:
    Graph &graph;               ///<The graph we belong to.

    virtual ~GraphNode() = default;
    Agnode_t *GetGv() const { return in_gv; }  ///<If we are established in cgraph, this is our object there.
    void Draw(Canvas &canvas, const ShapeCollection &shapes, EGraphDrawPass p) const override;
};

/** (Usable) Base class for edges. */
class GraphEdge : public GraphElement
{
    friend Graph;
protected:
    mutable Agedge_t *in_gv = nullptr;  ///<If we are established in cgraph, this is our object there.
    virtual void AddToGvGraph() const; ///<Create a counterpart for us in cgraph. Relies on parent to be already there
    virtual void ClearGv() const { in_gv = nullptr; }
public: //XXX should be protected, but make_unique needs them.
    GraphEdge(GraphNode *_1, GraphNode *_2, const std::string &p1, const std::string &p2, const FileLineColRange &pos1, const FileLineColRange &pos2) :
        GraphElement(GraphStyle::EDGE), graph(_1->graph), n1(_1), n2(_2), port1(p1), port2(p2), port_file_pos1(pos1), port_file_pos2(pos2) {}
    GraphEdge(GraphNode *_1, GraphNode *_2, std::string &&p1, std::string &&p2, const FileLineColRange &pos1, const FileLineColRange &pos2) :
        GraphElement(GraphStyle::EDGE), graph(_1->graph), n1(_1), n2(_2), port1(p1), port2(p2), port_file_pos1(pos1), port_file_pos2(pos2) {}
public:
    Graph &graph;             ///<The graph we belong to.
    GraphNode *n1;        ///<The first nodes we are between (tail)
    GraphNode *n2;        ///<The second nodes we are between (head)
    const std::string port1;  ///<The port on n1 (if any). Also contains compass point (if any), like "port:ne"
    const std::string port2;  ///<The port on n2 (if any). Also contains compass point (if any), like "port:ne"
    const FileLineColRange port_file_pos1; ///<The location of port1 in the input file (if any)
    const FileLineColRange port_file_pos2; ///<The location of port1 in the input file (if any)
    GraphEdgeType type = {GraphEdgeType::SOLID, GraphEdgeType::NO_ARROW};  ///<The type of the edge specified in the input file.

    virtual ~GraphEdge() = default;
    Agedge_t *GetGv() const { return in_gv; }  ///<If we are established in cgraph, this is our object there.
    void SetType(GraphEdgeType t, const StyleSet<GraphStyle> &styles);
    void Draw(Canvas &canvas, const ShapeCollection &shapes, EGraphDrawPass p) const override { if (p==EGraphDrawPass::EDGE) DrawAll(canvas, shapes); }
};


/** Adds the attribute to the list of attribute names to bind.
 * graphviz requires the registration (binding) of all attribute
 * names (globally) before used for a given kind of object
 * (graph, edge or node).
 * @param t [in] Specifies what kind of object the attribute is for.
 * @param attr_name [in] The attribute name to add.*/
inline void Graph::AddAttrName(GraphStyle::EGraphElementType t, const std::string &attr_name)
{
    if (t==GraphStyle::EGraphElementType::ANY) {
        attr_node.insert(attr_name);
        attr_edge.insert(attr_name);
        //fallthrough: graph goes below
    }
    std::set<std::string> &attrs = t==GraphStyle::EGraphElementType::NODE ? attr_node :
        t==GraphStyle::EGraphElementType::EDGE ? attr_edge : attr_graph;
    attrs.insert(attr_name);
}


template <class SG, typename ...Args>
SubGraph *Graph::CreateSubGraph(std::string_view name, bool cluster, bool *created, Args &&... args)
{
    //Do nothing when a user-re-defines a subgraph - we re-use the same one if it exists among our sisters
    //For unnamed subgraphs: always create a new one
    auto i = name.empty()
        ? this->subgraphs.end()
        : std::find_if(this->subgraphs.begin(), this->subgraphs.end(), [name](auto p) {return p->name==name; });
    SubGraph *p = i==this->subgraphs.end() ? nullptr : *i;
    if (created) *created = !p;
    if (!p) {
        std::unique_ptr<SG> up = std::make_unique<SG>(*this, name, cluster, std::forward<Args>(args)...);
        p = up.get();
        this->storage_subgraphs.push_back(std::move(up));
        this->subgraphs.Append(p);
    }
    return p;  // cppcheck-suppress returnDanglingLifetime
}

template <class SG, typename ...Args>
SubGraph *Graph::CreateSubGraph(SubGraph *g, std::string_view name, bool cluster, bool *created, Args&&... args)
{
    _ASSERT(g);
    if (g==nullptr) return this->CreateSubGraph<SubGraph>(name, cluster, created, std::forward<Args>(args)...);
    //Do nothing when a user-re-defines a subgraph - we re-use the same one if it exists among our sisters
    //For unnamed subgraphs: always create a new one
    auto i = name.empty()
        ? g->subgraphs.end()
        : std::find_if(g->subgraphs.begin(), g->subgraphs.end(), [name](auto p) {return p->name==name; });
    SubGraph *p = i==g->subgraphs.end() ? nullptr : *i;
    if (created) *created = !p;
    if (!p) {
        std::unique_ptr<SG> up = std::make_unique<SG>(g, name, cluster, std::forward<Args>(args)...);
        p = up.get();
        this->storage_subgraphs.push_back(std::move(up));
        g->subgraphs.Append(p);
    }
    return p;  // cppcheck-suppress returnDanglingLifetime
}

template <class Node, typename ...Args>
GraphNode *Graph::CreateNode(std::string_view name, bool *created, Args &&... args)
{
    GraphNode *p = this->GetNode(name);
    if (created) *created = !p;
    if (!p) {
        std::unique_ptr<Node> up = std::make_unique<Node>(*this, name, std::forward<Args>(args)...);
        p = up.get();
        this->storage_nodes.push_back(std::move(up));
        this->nodes.Append(p);
        return p;  // cppcheck-suppress returnDanglingLifetime
    } else if (!GetNodeFromVisibleSubgraph(p)) {
        //Insert only if not already in a visible subgraph
        if (this->nodes.end() == std::find(this->nodes.begin(), this->nodes.end(), p))
            this->nodes.Append(p);
    }
    return p;
}

template <class Node, typename ...Args>
GraphNode *Graph::CreateNode(SubGraph *g, std::string_view name, bool *created, Args &&... args)
{
    if (g==nullptr) return CreateNode<Node>(name, created, std::forward<Args>(args)...);
    GraphNode *p = this->GetNode(name);
    if (created) *created = !p;
    if (!p) {
        std::unique_ptr<Node> up = std::make_unique<Node>(*this, name, std::forward<Args>(args)...);
        p = up.get();
        this->storage_nodes.emplace_back(std::move(up));
        g->nodes.Append(p);
        return p;  // cppcheck-suppress returnDanglingLifetime
    } else {
        if (g->cluster) {
            SubGraph *host = this->GetNodeFromVisibleSubgraph(p);
            if (host==nullptr)
                nodes.remove(p); //we may have it at top level
            else if (g->IsMyParent(host))
                host->Remove(p); //If the node is in one of g's ancestors, we move the node to 'g'
            else //we leave it at 'host'
                goto leave;
        } //else: we are not cluster, we freely add, if not yet added
        if (std::find(g->nodes.begin(), g->nodes.end(), p)==g->nodes.end())
            g->nodes.Append(p);
    }
leave:
    return p;
}

template <class Edge, typename ...Args>
GraphEdge *Graph::CreateEdge(GraphNode *_1, GraphNode *_2,
                             const std::string &p1, const std::string &p2,
                             const FileLineColRange &pos1, const FileLineColRange &pos2,
                             bool *created, Args &&... args)
{
    _ASSERT(_1 && _2);
    _ASSERT(&_1->graph==this && &_2->graph==this);
    if (created) *created = false;
    if (this->strict)
        for (auto &p : this->edges) {
            if (p->n1 == _1 && p->n2 == _2)
                return p.get();
            if (!this->directed && p->n2 == _1 && p->n1 == _2)
                return p.get();
        }
    if (created) *created = true;
    this->edges.Append(std::make_unique<Edge>(_1, _2, p1, p2, pos1, pos2, std::forward<Args>(args)...));
    return this->edges.back().get();
}

template <class Edge, typename ...Args>
GraphEdge *Graph::CreateEdge(GraphNode *_1, GraphNode *_2,
                             std::string && p1, std::string && p2,
                             const FileLineColRange &pos1, const FileLineColRange &pos2,
                             bool *created, Args &&... args)
{
    _ASSERT(_1 && _2);
    _ASSERT(&_1->graph==this && &_2->graph==this);
    if (created) *created = false;
    if (this->strict)
        for (auto &p : this->edges) {
            if (p->n1 == _1 && p->n2 == _2)
                return p.get();
            if (!this->directed && p->n2 == _1 && p->n1 == _2)
                return p.get();
        }
    if (created) *created = true;
    this->edges.Append(std::make_unique<Edge>(_1, _2, std::move(p1), std::move(p2), pos1, pos2, std::forward<Args>(args)...));
    return this->edges.back().get();
}



} //namespace graph

#undef CGRAPH_API

#endif
