/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmSDKAPI.h"
#include <glmTypes.h>
#include "glmVector3.h"
#include "glmSegment3.h"
#include "glmTriangle3.h"
#include "glmMatrix4.h"
#include "glmSmartPointer.h"

namespace glm
{
	struct GLM_SDK_API GroundCellId
	{
		GroundCellId()
			: _meshId(-1)
			, _triangleId(-1)
		{}
		GroundCellId(int32_t meshId, int32_t triangleId)
			: _meshId(meshId)
			, _triangleId(triangleId)
		{}

		bool operator == (const GroundCellId& other) const
		{
			return (_meshId == other._meshId && _triangleId == other._triangleId);
		}

		void setGroundCellId(int32_t meshId, int32_t triangleId) { _meshId = meshId; _triangleId = triangleId; }
		int32_t getMeshId() const { return _meshId; }
		int32_t getTriangleId() const { return _triangleId; }

		bool isValid() const { return _meshId >= 0 && _triangleId >= 0; }

	private:
		int32_t _meshId;
		int32_t _triangleId;
	};

	/**
	* \brief Abstract class defining a group.
	*
	* \ingroup ground
	*
	* \author Golaem <dev@golaem.com>
	*/
    class GLM_SDK_API AbstractGround : public ReferenceCounter
    {
    public:
        typedef SmartPointer<AbstractGround> SP;

        struct GroundPinMode
        {
			enum Value
			{
				PinNone = 0,
				PinPosOnly,
				PinPosOri
			};
		};

		GLM_ABSTRACTCLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

		// indicates if heuristices lust be computed for this ground
		bool	_useHeuristics;

		/**
		* \name Construction/Destruction
		*/
		//@{
		AbstractGround();
		virtual ~AbstractGround();
		//@}

		/**
		* \name Projection retrieval
		*/
		//@{
		virtual bool computeProjection(const Vector3& position, const Vector3& upVector, Vector3& groundPosition, Vector3& groundNormal, float height, GroundCellId& groundCellId, int genericBoneIndex = -1, const void *userPtr = NULL) const = 0;
		//bool computeProjection(const Vector3& position, const Vector3& upVector, Vector3& groundPosition, Vector3& groundNormal, float height, int genericBoneIndex, const void *userPtr) const;
		//bool computeProjection(const Vector3& position, const Vector3& upVector, Vector3& groundPosition, Vector3& groundNormal, int genericBoneIndex, const void *userPtr) const;
		bool computeProjectionPosition(const Vector3& position, const Vector3& upVector, Vector3& groundPosition, float height, GroundCellId& groundCellId, int genericBoneIndex = -1, const void *userPtr = NULL) const;
		virtual bool computeProjectionFromLastPosition(const Vector3& previousPosition, Vector3& inOutPosition, Vector3& inOutUpVector, float height, GroundCellId& inOutGroundCellId, int genericBoneIndex = -1, const void *userPtr = NULL) const;
		//@}

		virtual Vector3 getGroundCellVertex(const GroundCellId& groundCellId, int vertexId) const;

		// barycenter operations, only for pinnable grounds (MayaGround), this should never be called
		virtual void getBarycenterFromPosition(const GroundCellId& groundCellId, const Vector3& position, Vector3& barycentricCoordinates) const;
		virtual Vector3 getBarycentricPosition(const GroundCellId& /*groundCellId*/, const Vector3& /*_groundCellBarycenter*/) const;
		virtual Vector3 getBarycentricNormal(const GroundCellId& /*groundCellId*/, const Vector3& /*_groundCellBarycenter*/) const;

		virtual void getTransformDelta(Matrix4& /*transformDelta*/, const GroundCellId& /*groundCellId*/, const Vector3& /*barycenter*/) const;
		
		/**
		* \name init/update/finish
		*/
		//@{
		virtual void init() = 0;
		virtual void update(Time dt) = 0;
		virtual void finish() = 0;
		//@}

		/**
		* \name Debug geometry
		*/
		//@{
		virtual void computeGeometry(glm::Array<Triangle3>& triangles, glm::Array<Segment3>& normals, const Matrix4& callerInverseMatrix) const = 0;
		//@}

		virtual bool isNoGround() const { return false; }

		virtual bool isTransformed() const { return false; }
		virtual bool isDeformed() const { return false; }

		void setGroundPinMode(GroundPinMode::Value pinMode) { _groundPinMode = pinMode; }
		GroundPinMode::Value getGroundPinMode() const { return _groundPinMode;  }

	protected:

		GroundPinMode::Value _groundPinMode;
    };
}

