/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2010 Sandro Santilli <strk@kbt.io>
 * Copyright (C) 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: operation/polygonize/Polygonizer.java 0b3c7e3eb0d3e
 *
 **********************************************************************/

#pragma once

#include <geos/export.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/GeometryComponentFilter.h> // for LineStringAdder inheritance
#include <geos/operation/polygonize/PolygonizeGraph.h>

#include <memory>
#include <vector>

#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 Geometry;

class LineString;

class Polygon;
}
namespace operation {
namespace polygonize {
class EdgeRing;
}
}
}

namespace geos {
namespace operation { // geos::operation
namespace polygonize { // geos::operation::polygonize

/** \brief
 * Polygonizes a set of Geometrys which contain linework that
 * represents the edges of a planar graph.
 *
 * All types of Geometry are accepted as input; the constituent linework is extracted
 * as the edges to be polygonized.
 * The edges must be correctly noded; that is, they must only meet
 * at their endpoints. Polygonization will accept incorrectly noded input but will
 * not form polygons from non-noded edges, and reports them as errors.
 *
 * The Polygonizer reports the follow kinds of errors:
 *
 * - <b>Dangles</b> - edges which have one or both ends which are
 *   not incident on another edge endpoint
 * - <b>Cut Edges</b> - edges which are connected at both ends but
 *   which do not form part of a polygon
 * - <b>Invalid Ring Lines</b> - edges which form rings which are invalid
 *   (e.g. the component lines contain a self-intersection)
 *
 *   The Polygonizer constructor allows extracting only polygons which form a
 *   valid polygonal result.
 *   The set of extracted polygons is guaranteed to be edge-disjoint.
 *   This is useful when it is known that the input lines form a valid
 *   polygonal geometry (which may include holes or nested polygons).
 *
 */
class GEOS_DLL Polygonizer {
private:
    /**
     * Add every linear element in a geometry into the polygonizer graph.
     */
    class GEOS_DLL LineStringAdder: public geom::GeometryComponentFilter {
    public:
        Polygonizer* pol;
        explicit LineStringAdder(Polygonizer* p);
        //void filter_rw(geom::Geometry *g);
        void filter_ro(const geom::Geometry* g) override;
    };

    // default factory
    LineStringAdder lineStringAdder;

    /**
     * Add a linestring to the graph of polygon edges.
     *
     * @param line the {@link LineString} to add
     */
    void add(const geom::LineString* line);

    /**
     * Perform the polygonization, if it has not already been carried out.
     */
    void polygonize();

    static void findValidRings(const std::vector<EdgeRing*>& edgeRingList,
                               std::vector<EdgeRing*>& validEdgeRingList,
                               std::vector<EdgeRing*>& invalidRingList);

    /**
     * Extracts unique lines for invalid rings,
     * discarding rings which correspond to outer rings and hence contain
     * duplicate linework.
     */
    std::vector<std::unique_ptr<geom::LineString>> extractInvalidLines(
            std::vector<EdgeRing*>& invalidRings);

    /**
     * Tests if a invalid ring should be included in
     * the list of reported invalid rings.
     *
     * Rings are included only if they contain
     * linework which is not already in a valid ring,
     * or in an already-included ring.
     *
     * Because the invalid rings list is sorted by extent area,
     * this results in outer rings being discarded,
     * since all their linework is reported in the rings they contain.
     *
     * @param invalidRing the ring to test
     * @return true if the ring should be included
     */
    bool isIncludedInvalid(EdgeRing* invalidRing);

    void findShellsAndHoles(const std::vector<EdgeRing*>& edgeRingList);

    void findDisjointShells();

    static void findOuterShells(std::vector<EdgeRing*>& shellList);

    static std::vector<std::unique_ptr<geom::Polygon>> extractPolygons(std::vector<EdgeRing*> & shellList, bool includeAll);

    bool extractOnlyPolygonal;
    bool computed;

protected:

    std::unique_ptr<PolygonizeGraph> graph;

    // initialize with empty collections, in case nothing is computed
    std::vector<const geom::LineString*> dangles;
    std::vector<const geom::LineString*> cutEdges;
    std::vector<std::unique_ptr<geom::LineString>> invalidRingLines;

    std::vector<EdgeRing*> holeList;
    std::vector<EdgeRing*> shellList;
    std::vector<std::unique_ptr<geom::Polygon>> polyList;

public:

    /** \brief
     * Create a Polygonizer with the same GeometryFactory
     * as the input Geometrys.
     *
     * @param onlyPolygonal true if only polygons which form a valid polygonal geometry should be extracted
     */
    explicit Polygonizer(bool onlyPolygonal = false);

    ~Polygonizer() = default;

    /** \brief
     * Add a collection of geometries to be polygonized.
     * May be called multiple times.
     * Any dimension of Geometry may be added;
     * the constituent linework will be extracted and used
     *
     * @param geomList a list of Geometry with linework to be polygonized
     */
    void add(std::vector<geom::Geometry*>* geomList);

    /** \brief
     * Add a collection of geometries to be polygonized.
     * May be called multiple times.
     * Any dimension of Geometry may be added;
     * the constituent linework will be extracted and used
     *
     * @param geomList a list of Geometry with linework to be polygonized
     */
    void add(std::vector<const geom::Geometry*>* geomList);

    /**
     * Add a geometry to the linework to be polygonized.
     * May be called multiple times.
     * Any dimension of Geometry may be added;
     * the constituent linework will be extracted and used
     *
     * @param g a Geometry with linework to be polygonized
     */
    void add(const geom::Geometry* g);

    /** \brief
     * Gets the list of polygons formed by the polygonization.
     *
     * Ownership of vector is transferred to caller, subsequent
     * calls will return NULL.
     * @return a collection of Polygons
     */
    std::vector<std::unique_ptr<geom::Polygon>> getPolygons();

    /** \brief
     * Get the list of dangling lines found during polygonization.
     *
     * @return a (possibly empty) collection of pointers to
     *         the input LineStrings which are dangles.
     *
     */
    const std::vector<const geom::LineString*>& getDangles();

    bool hasDangles();

    /** \brief
     * Get the list of cut edges found during polygonization.
     *
     * @return a (possibly empty) collection of pointers to
     *         the input LineStrings which are cut edges.
     *
     */
    const std::vector<const geom::LineString*>& getCutEdges();

    bool hasCutEdges();

    /** \brief
     * Get the list of lines forming invalid rings found during
     * polygonization.
     *
     * @return a (possibly empty) collection of pointers to
     *         the input LineStrings which form invalid rings
     *
     */
    const std::vector<std::unique_ptr<geom::LineString>>& getInvalidRingLines();

    bool hasInvalidRingLines();

    bool allInputsFormPolygons();

// This seems to be needed by    GCC 2.95.4
    friend class Polygonizer::LineStringAdder;
};

} // namespace geos::operation::polygonize
} // namespace geos::operation
} // namespace geos

#ifdef _MSC_VER
#pragma warning(pop)
#endif
