/*
 * $Id: geometry.h 4506 2017-04-28 08:26:03Z antonin.misek-skoda-auto.cz $
 *
 * Description  : One line description of file.
 * Author       : Vaclav Kyba <mail/Jabber: vaseo1@gmail.com>  <ICQ: 98576293>
 * Edit			: Kry�tof Valenta valenkry@fel.cvut.cz - Point geometry support added (19.5.2011)
 *
 * Purpose      :
 *     Long description of what the file is for.
 */


#ifndef __GEOMETRY__H__
#define __GEOMETRY__H__

#include <vector>

#include "countable.h"
#include "moddefs.h"
#include "bvolumes.h"
#include "VrutApi.h"
#include "smartptr.h"


namespace VRUT
{
	///Structure with pick data
	struct RayIntersectionInfo
	{
		RayIntersectionInfo() : nodeID(NODE_ID_NONE), dist(FLT_MAX), geometryID((size_t)-1), primitiveIndex((size_t)-1), index((size_t)-1), u(0), v(0) {}
		///Picked scene node with geometry
		NODE_ID nodeID;
		///Distance from camera to intersection point
		float dist;
		///Intersection point
		Vector3 intersection;
		///Normal at intersection point
		Vector3 normal;
		///local mesh id
		size_t geometryID;
		///Intersected primitive index (index into the triDescList vector)
		size_t primitiveIndex;
		///Intersected index (index into the indices vector)
		size_t index;
		///local parameterization over the surface
		float u,v;
		/// Get string with RayIntersectionInfo properties
		wxString ToString() const
		{
			wxString res;
			res += wxString::Format(wxT("\nnodeID: %lu"), (unsigned long)nodeID);
			res += wxT("\ndist: ")+TextWriter::WriteToWxString(dist);
			res += wxT("\nintersection: ")+intersection.ToString();
			res += wxT("\nnormal: ")+normal.ToString();
			res += wxT("\ngeometryID: ")+TextWriter::WriteToWxStringSize_t(geometryID);
			res += wxT("\nprimitiveIndex: ")+TextWriter::WriteToWxStringSize_t(primitiveIndex);
			res += wxT("\nindex: ")+TextWriter::WriteToWxStringSize_t(index);
			res += wxT("\nu, v: ")+TextWriter::WriteToWxString(u)+wxT(", ")+TextWriter::WriteToWxString(v);
			return res;
		}
	};

	///Base class for geometry representation - abstract
	class VRUT_CORE_API Geometry : public Countable
	{
	private:
		///GID
		GEOMETRY_ID gid;
	protected:
		///Model space axis aligned bounding box
		AABB aabb;

	public:
		///Type of representation
		enum GEOMETRY_TYPE
		{
			GEOMETRY_UNKNOWN,    ///<Unknown geometry type
			GEOMETRY_TRIANGLE,   ///<Triangulated geometry
			GEOMETRY_POINT,		 ///<Raw 3D points with normal and color information
			GEOMETRY_TEXT		 ///<Geometry with characters instead of triangles
		} const type;		// Must not be changed once it is set, it corresponds to the type of derived classes

		///Class constructor
		Geometry(const GEOMETRY_ID & _gid, GEOMETRY_TYPE type) : gid(_gid), type(type)   {}
		///Copy constructor
		Geometry(const Geometry & g) : gid(g.gid), aabb(g.aabb), type(g.type)   {}

		///Class destructor
		virtual ~Geometry() {}

		///Get ID
		GEOMETRY_ID GetID() const
		{
			return gid;
		}

		///Get AABB
		const AABB & GetAABB() const
		{
			return aabb;
		}
		///Build AABB
		virtual void BuildAABB() = 0;
		///Check if ray intersects geometry, get intersection point distance from ray origin if it does
		///NOTE: everything must be in one space (model space by default), \c dist will be in that space too!
		virtual bool Intersects(const Ray & ray, float * dist = (float *)NULL) const = 0;

		///Cast ray towards the geometry, get intersection info if it does
		///NOTE: everything must be in one space (model space by default), \c intersection info will be in that space too!
		virtual bool CastRay(const Ray &/*ray*/, RayIntersectionInfo &/*info*/) const {
			// not pure virtual for now !
			return false;
		}

		///Get geometry as list of triangles
		virtual std::vector<Triangle> Triangulate() const = 0;

		///Get geometry as list of triangles with triangles data
		virtual std::vector<TriangleData> TriangulateData() const = 0;

		///Add Box to geometry
		virtual void AddBox(const Vector3 &position, const Vector3 &size) = 0;

		///Add content of another geometry
		virtual bool AddGeometry(const Geometry *geom) = 0;

		///Add content of another geometries
		virtual bool AddGeometries(const std::vector<Geometry *> &geoms) = 0;

		///Transform geometry vertices and normals (flush matrix etc.)
		///Some node types cannot be transformed, so is must return false in such cases
		virtual bool Transform(const Matrix *matrix) = 0;

		///Divide by discontinuity
		virtual void Divide(std::vector<Geometry*> &segments) const = 0;

		///Divide to primitives
		virtual void DivideToPrimitives(std::vector<Geometry*> &segments) const = 0;

		///Flip normals
		virtual void FlipNormals() = 0;

		///planar texture mapping
		virtual void TextureMappingPlanar(const Matrix &texMatrix) = 0;

		///triplanar texture mapping
		virtual void TextureMappingTriplanar(const Matrix &texMatrix) = 0;

		///spherical texture mapping
		virtual void TextureMappingSpherical(const Matrix &texMatrix) = 0;

		///Clear
		virtual void Clear() = 0;

		///Is same as
		virtual bool IsSameAs(const smart_pointer<Geometry> & geo) const = 0;

		///Get string with geometry properties
		virtual wxString ToString() const
		{
			return wxString::Format(wxT("Geometry ID:%lu, type: %i\n"),	(unsigned long)gid, (unsigned int)type);
		}

		///Get copy of instance
		virtual Geometry * Clone() const = 0;

		///Get geometry type
		GEOMETRY_TYPE GetType() const
		{
			return type;
		}

		///Is geometry of given type?
		bool IsOfType(GEOMETRY_TYPE _type) const
		{
			return type == _type;
		}

		/// Returns true if the geometry can be type-casted into specified type, based on its 'type' variable,
		/// using the class for the check. The implementation and its specialization is places after this class.
		template <class T> inline bool IsOfClass() const;

		/// Enhancing the class method, works with nullptr. Nullptr can be casted into anything, so it
		/// returns true in such situation.
		template <class T> static inline bool IsOfClass(const Geometry *geometry)
		{
			return !geometry || geometry->IsOfClass<T>();		// Nullptr is of any type.
		}

		friend class Scene;
	};

	template <class T> inline bool Geometry::IsOfClass() const
	{
		return IsOfType(T::ClassType);
	}
	template <> inline bool Geometry::IsOfClass<Geometry>() const
	{
		return true;			// Geometry can be casted into a Geometry
	}

}

#endif
