/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2023, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */
#pragma once

#include <mrpt/containers/yaml_frwd.h>
#include <mrpt/math/TPoint3D.h>
#include <mrpt/math/TPoseOrPoint.h>

#include <vector>

namespace mrpt::math
{
/** 3D polygon, inheriting from std::vector<TPoint3D>
 * \sa TPolygon2D,TSegment3D,TLine3D,TPlane,TPoint3D
 * \ingroup geometry_grp
 */
class TPolygon3D : public std::vector<TPoint3D>
{
   public:
	/** Default constructor. Creates a polygon with no vertices.  */
	TPolygon3D() : std::vector<TPoint3D>() {}

	/** Constructor for a given size. Creates a polygon with a fixed number of
	 * vertices (uninitialized) */
	explicit TPolygon3D(size_t N) : std::vector<TPoint3D>(N) {}

	/** Implicit constructor from a 3D points vector. */
	TPolygon3D(const std::vector<TPoint3D>& v) : std::vector<TPoint3D>(v) {}

	/** Constructor from a 2D object. Zeroes the z. */
	explicit TPolygon3D(const TPolygon2D& p);

	/** Builds a polygon from a YAML sequence (vertices) of sequences (`[x y z]`
	 * coordinates).
	 * \sa asYAML
	 * \note User must include `#include <mrpt/containers/yaml.h>` if using this
	 *       method, only a forward declaration is defined here to speed up
	 *       compilation \note (New in MRPT 2.4.1)
	 */
	static TPolygon3D FromYAML(const mrpt::containers::yaml& c);

	/** Static method to create a regular polygon, given its size and radius.
	 * \throw std::logic_error if number of edges is less than three, or radius
	 * is near zero. */

	/** Constructor from list of vertices data, for example:
	 * \code
	 * const mrpt::math::TPolygon3D p = {
	 *  {-6.0, 0.5, 0.0}, {8.0, 2.0, 1.0}, {10.0, 4.0, 2.0}
	 *  };
	 * \endcode
	 */
	TPolygon3D(const std::initializer_list<TPoint3D>&& vertices)
		: std::vector<TPoint3D>(
			  std::forward<const std::initializer_list<TPoint3D>&>(vertices))
	{
	}

	/** Returns a YAML representation of the polygon as a sequence (vertices) of
	 * sequences (`[x y z]` coordinates).
	 *
	 * \sa FromYAML
	 *
	 * \note User must include `#include <mrpt/containers/yaml.h>` if using this
	 *       method, only a forward declaration is defined here to speed up
	 *       compilation \note (New in MRPT 2.4.1)
	 */
	mrpt::containers::yaml asYAML() const;

	/** Absolute distance to point */
	double distance(const TPoint3D& point) const;

	/** Check whether a point is inside (or within geometryEpsilon of a polygon
	 * edge). This works for concave or convex polygons. */
	bool contains(const TPoint3D& point) const;

	/** Gets as set of segments, instead of set of points. */
	void getAsSegmentList(std::vector<TSegment3D>& v) const;
	/** Gets a plane which contains the polygon. Returns false if the polygon is
	 * skew and cannot be fit inside a plane. */
	bool getPlane(TPlane& p) const;
	/** Gets the best fitting plane, disregarding whether the polygon actually
	 * fits inside or not. \sa getBestFittingPlane */
	void getBestFittingPlane(TPlane& p) const;
	/** Projects into a 2D space, discarding the z. \sa getPlane,isSkew */
	void generate2DObject(TPolygon2D& p) const;
	/** Get polygon's central point. */
	void getCenter(TPoint3D& p) const;
	/** Check whether the polygon is skew. Returns true if there doesn't exist a
	 * plane in which the polygon can fit. \sa getBestFittingPlane */
	bool isSkew() const;
	/** Remove polygon's repeated vertices. */
	void removeRepeatedVertices();
	/** Erase every redundant vertex, thus saving space. */
	void removeRedundantVertices();
	static void createRegularPolygon(
		size_t numEdges, double radius, TPolygon3D& poly);
	/** Static method to create a regular polygon, given its size and radius.
	 * The center will be located on the given pose.
	 * \throw std::logic_error if number of edges is less than three, or radius
	 * is near zero.
	 */
	static void createRegularPolygon(
		size_t numEdges, double radius, TPolygon3D& poly,
		const mrpt::math::TPose3D& pose);
};

/** Text streaming function */
std::ostream& operator<<(std::ostream& o, const TPolygon3D& p);

}  // namespace mrpt::math

namespace mrpt::typemeta
{
// Specialization must occur in the same namespace
MRPT_DECLARE_TTYPENAME_NO_NAMESPACE(TPolygon3D, mrpt::math)

}  // namespace mrpt::typemeta
