/***************************************************************************
 *   Copyright (C) 2003-2005 by David Saxton                               *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#ifndef NODE_H
#define NODE_H

//#include <canvas.h> // 2018.10.16 - not needed
#include "canvasitems.h"
#include <QPointer>

class CNItem;
class Item;
class ICNDocument;
class ICNDocument;
class Connector;
class Node;
class NodeData;
class NodeGroup;
class QTimer;

typedef QList<QPointer<Connector>> ConnectorList;
typedef QList<QPointer<Node>> NodeList;

/**
@short A standard node that can be associated with a Connector or a CNItem
@author David Saxton
*/
class Node : /* public QObject, */ public KtlQCanvasPolygon
{
    Q_OBJECT
public:
    // this shall disappear one day
    /**
     * Used for run-time identification of the node:
     * Can be electronic node (so has values of current, voltage, etc)
     * or a pic part node
     * this enum will be cleared soon
     */
    enum node_type { ec_pin, ec_junction, fp_in, fp_out, fp_junction };

    /**
     * @param dir the direction of the node; 0 degrees for left, 90 degrees for
     * up, etc in an anti-clockwise direction. An "up" node has the
     * wire-connection point at the top and the (component/flowpart)-end at the
     * bottom.
     */
    Node(ICNDocument *icnDocument, Node::node_type type, int dir, const QPoint &pos, QString *id = nullptr);
    ~Node() override;

    /**
     * Sets the node's visibility, as well as updating the visibility of the
     * attached connectors as appropriate
     */
    void setVisible(bool yes) override;
    /**
     * Returns the global id, that is unique to the node
     * amongst all the nodes on the canvas
     */
    const QString id() const
    {
        return m_id;
    }
    /**
     * Returns the id that is internal to the CNItem to which the
     * node belongs to. Returns a null QString if no parentitem
     */
    const QString childId() const
    {
        return m_childId;
    }
    /**
     * Use this function to set the child-id, that is unique to the node
     * amongst the other nodes associated with its parent CNItem
     */
    void setChildId(const QString &id)
    {
        m_childId = id;
    }
    /**
     * Sets the "level" of the node. By default, the level is 0. The level of
     * the node tells the node what CNItems it can be connected to through
     * a connector.
     * @see level
     */
    virtual void setLevel(const int level);
    /**
     * Returns the level of the nodes
     * @see setLevel
     */
    int level() const
    {
        return m_level;
    }

    /**
     * Sets the orientation of the node.
     */
    void setOrientation(int dir);
    /**
     * Changes the lenght of the node. By default, this is 8. Some node types
     * (such as junctions) do not make use of this value.
     */
    void setLength(int length);
    /**
     * Associates a CNItem with the node - ie the node belongs to the CNItem,
     * and hence gets deleted when the CNItem gets deleted.s
     */
    virtual void setParentItem(CNItem *parentItem);
    /**
     * Returns true if the node is part of a CNItem
     * (i.e. not between multiple connectors)
     */
    bool isChildNode() const
    {
        return (p_parentItem != nullptr);
    }
    /**
     * Returns a pointer to the CNItem to which the node belongs,
     * or Null if it doesn't.
     */
    CNItem *parentItem() const
    {
        return p_parentItem;
    }

    NodeData nodeData() const;

    void setNodeGroup(NodeGroup *ng)
    {
        p_nodeGroup = ng;
    }
    NodeGroup *nodeGroup() const
    {
        return p_nodeGroup;
    }

    /* interface common to ecnode and fpnode; these might be required by ItemDocumentData, ICNDocument  */

    virtual bool isConnected(Node *node, NodeList *checkedNodes = nullptr) = 0;

    virtual void removeConnector(Connector *connector) = 0;

    /**
     * Returns the total number of connections to the node. This is the number
     * of connectors and the parent
     * item connector if it exists and is requested.
     * @param includeParentItem Count the parent item as a connector if it exists
     * @param includeHiddenConnectors hidden connectors are those as e.g. part of a subcircuit
     */
    virtual int numCon(bool includeParentItem, bool includeHiddenConnectors) const = 0;

    /**
     * @return the list of all the connectors attached to the node
     */
    virtual ConnectorList getAllConnectors() const = 0;

    /**
     * For a flownode: returns the first input connector, if it exist, or the fist outptut connector, if it exists.
     * For an electric node: returns the first connector
     * If the node isn't connected to anyithing, returns null ( 0 )
     * @return pointer to the desired connector
     */
    virtual Connector *getAConnector() const = 0;

    /**
     * Removes all the NULL connectors
     */
    virtual void removeNullConnectors() = 0;

    /**
     * Draw shape. Note that this has to remain public.
     */
    void drawShape(QPainter &p) override = 0;

    void setICNDocument(ICNDocument *documentPtr);

public slots:
    void moveBy(double dx, double dy) override;
    void removeNode(Item *)
    {
        removeNode();
    }
    void removeNode();
    void setNodeSelected(bool yes);

signals:
    void moved(Node *node);
    /**
     * Emitted when the CNItem is removed. Normally, this signal is caught by associated
     * nodes, who will remove themselves as well.
     */
    void removed(Node *node);

protected:
    virtual void initPoints();
    /**
     * Moves and rotates (according to m_dir) the painter, so that our current
     * position is (0,0).
     */
    void initPainter(QPainter &p);
    /**
     * Undoes the effects of initPainter.
     */
    void deinitPainter(QPainter &p);

    /** If this node has precisely two connectors emerging from it, then this
     * function will trace the two connectors until the point where they
     * diverge; this point is returned. */
    virtual QPoint findConnectorDivergePoint(bool *found) = 0;

    /** The node's type. This member will be removed! */
    node_type m_type;

    int m_dir;
    int m_length;
    int m_level;

    ICNDocument *p_icnDocument;
    CNItem *p_parentItem;

    NodeGroup *p_nodeGroup;

    static QColor m_selectedColor;

private:
    // these fields are critical to saved circuit documents.
    QString m_id;
    QString m_childId;

    bool b_deleted;
};

#endif
