/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2011 Sandro Santilli <strk@kbt.io>
 * Copyright (C) 2005-2006 Refractions Research Inc.
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 *
 * Last port: geomgraph/Node.java r411 (JTS-1.12+)
 *
 **********************************************************************/


#pragma once

#include <geos/export.h>
#include <geos/geomgraph/GraphComponent.h> // for inheritance
#include <geos/geom/Coordinate.h> // for member

#ifndef NDEBUG
#include <geos/geomgraph/EdgeEndStar.h> // for testInvariant
#include <geos/geomgraph/EdgeEnd.h> // for testInvariant
#endif // ndef NDEBUG

#include <cassert>
#include <string>

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class
#endif

// Forward declarations
namespace geos {
namespace geom {
class IntersectionMatrix;
}
namespace geomgraph {
class Node;
class EdgeEndStar;
class EdgeEnd;
class Label;
class NodeFactory;
}
}

namespace geos {
namespace geomgraph { // geos.geomgraph

/** \brief The node component of a geometry graph. */
class GEOS_DLL Node /* non-final */: public GraphComponent {
    using GraphComponent::setLabel;

public:

    friend std::ostream& operator<< (std::ostream& os, const Node& node);

    Node(const geom::Coordinate& newCoord, EdgeEndStar* newEdges);

    ~Node() override;

    const geom::Coordinate& getCoordinate() const;

    EdgeEndStar* getEdges();

    bool isIsolated() const override;

    /** \brief
     * Add the edge to the list of edges at this node
     */
    void add(EdgeEnd* e);

    void mergeLabel(const Node& n);

    /** \brief
     * To merge labels for two nodes,
     * the merged location for each LabelElement is computed.
     *
     * The location for the corresponding node LabelElement is set
     * to the result, as long as the location is non-null.
     */
    void mergeLabel(const Label& label2);

    void setLabel(uint8_t argIndex, geom::Location onLocation);

    /** \brief
     * Updates the label of a node to BOUNDARY,
     * obeying the mod-2 boundaryDetermination rule.
     */
    void setLabelBoundary(uint8_t argIndex);

    /**
     * The location for a given eltIndex for a node will be one
     * of { null, INTERIOR, BOUNDARY }.
     * A node may be on both the boundary and the interior of a geometry;
     * in this case, the rule is that the node is considered to be
     * in the boundary.
     * The merged location is the maximum of the two input values.
     */
    geom::Location computeMergedLocation(const Label& label2, uint8_t eltIndex);

    std::string print() const;

    const std::vector<double>& getZ() const;

    void addZ(double);

    /** \brief
     * Tests whether any incident edge is flagged as
     * being in the result.
     *
     * This test can be used to determine if the node is in the result,
     * since if any incident edge is in the result, the node must be in
     * the result as well.
     *
     * @return <code>true</code> if any indicident edge in the in
     *         the result
     */
    bool isIncidentEdgeInResult() const;

protected:

    void testInvariant() const;

    geom::Coordinate coord;

    EdgeEndStar* edges;

    /** \brief
     * Basic nodes do not compute IMs
     */
    void
    computeIM(geom::IntersectionMatrix& /*im*/) override {}

private:

    std::vector<double> zvals;

    double ztot;

};

std::ostream& operator<< (std::ostream& os, const Node& node);

inline void
Node::testInvariant() const
{
#ifndef NDEBUG
    if(edges) {
        // Each EdgeEnd in the star has this Node's
        // coordinate as first coordinate
        for(EdgeEndStar::iterator
                it = edges->begin(), itEnd = edges->end();
                it != itEnd; it++) {
            EdgeEnd* e = *it;
            assert(e);
            assert(e->getCoordinate().equals2D(coord));
        }
    }

#if 0
    // We can't rely on numerical stability with FP computations
    // ztot is the sum of doubnle sin zvals vector
    double ztot_check = 0.0;
    for(std::vector<double>::const_iterator
            i = zvals.begin(), e = zvals.end();
            i != e;
            i++) {
        ztot_check += *i;
    }
    assert(ztot_check == ztot);
#endif // 0

#endif
}


} // namespace geos.geomgraph
} // namespace geos

#ifdef _MSC_VER
#pragma warning(pop)
#endif

