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

#pragma once

//golaem motion includes
#include "glmMotionDefinitions.h"
#include "glmSDKAPI.h"
#include "glmSmartPointer.h"
#include <glmTypes.h>
#include <glmVector3.h>
#include <glmGolaemMotionDeclarations.h> // GolaemMotion declarations

namespace glm
{
    class GroundManager;
    class BlendTreePosture;

    /**
	* \brief Type of a target
	*/
    class IKTargetMode
    {
    public:
        enum Value
        {
            ABSOLUTEPOSITION = 0,
            ENTITYRELATIVEPOSITION,
            ENTITYGROUNDRELATIVEPOSITION,
            END
        };
    };

    /**
	 *
	 * \brief Describe a target for the inverse kinematic constraint
	 *
	 * \author dev@golaem.com
	 *
	 */
    class GLM_SDK_API IKTarget : public ReferenceCounter
    {
    public:
        GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

        /// definition of a smart pointer on an instance of this class.
        typedef SmartPointer<IKTarget> SP;

    protected:
        /**
		 * \brief default constructor
		 */
        IKTarget(Vector3 position = Vector3::getNullVector(), IKTargetMode::Value targetmode = IKTargetMode::ABSOLUTEPOSITION, const GroundManager* groundManager = NULL);

    public:
        /**
		 * \brief Comparison operator
		 * \param otherInstance
		 * \return true if constraint sets are identical
		 */
        bool operator==(const IKTarget& otherInstance) const;

        /**
		 * \brief Comparison operator
		 * \param otherInstance
		 * \return true if constraint sets are identical
		 */
        bool operator!=(const IKTarget& otherInstance) const
        {
            return !((*this) == otherInstance);
        }

        /**
		 * \brief Instance Creator
		 */
        static IKTarget* create(Vector3 position = Vector3::getNullVector(), IKTargetMode::Value targetmode = IKTargetMode::ABSOLUTEPOSITION, const GroundManager* groundManager = NULL);
        //@}

        //************************************************************
        //! @name IKTarget type accessors
        //************************************************************
        //@{
        /**
		 * \brief get target type
		 */
        inline const IKTargetMode::Value& getType() const;
        /**
		 * \brief set target type
		 */
        inline void setType(const IKTargetMode::Value& type);
        //@}

        //************************************************************
        //! @name Multiple attributes accessors
        //************************************************************
        //@{

        /**
		 * \brief Set a position to be used in ABSOLUTEPOSITION mode
		 * \param worldPos
		 * \note equivalent to:
		 *	setType(IKTargetMode::ABSOLUTEPOSITION);
		 *	setTransformation(worldPos);
		 */
        inline void setWorldPosition(const Vector3& worldPos);

        /**
		 * \brief Set a position to be used in ENTITYRELATIVEPOSITION mode
		 * \param worldPos
		 * \note equivalent to:
		 *	setType(IKTargetMode::ENTITYRELATIVEPOSITION);
		 *	setTransformation(offset);
		 */
        inline void setRelativePosition(const Vector3& offset);
        //@}

        //************************************************************
        //! @name Validation and computation accessors
        //************************************************************
        //@{
        /**
		* \brief tell if the currently setted target is valid (e.g: target type is either ABSOLUTEPOSITION or ENTITYRELATIVEPOSITION)
		* \return true if target is valid
		* \note if false is returned, a warning trace message is written, so check the log
		*/
        inline bool isValid() const;

        /**
		* \brief compute the position of the target in the given posture
		* \param post posture on which computing the target's position
		* \return the computed target's position
		*/
        virtual Vector3 computeTargetPos(int64_t entityIndex, const Vector3& upVector, const BlendTreePosture& posture);
        //@}

    protected:
        Vector3 computeTargetPosInternal(const Quaternion& inPosTransform, const Vector3& inPos, const Vector3& upVector, const BlendTreePosture& posture);
        Vector3 computeTargetPosInternal(const Quaternion& inPosTransform, const Array<Vector3>& inPos, const Vector3& upVector, const BlendTreePosture& posture);

    protected:
        IKTargetMode::Value _targetMode;     //!< see IKTargetMode for available values
        Vector3 _position;                   //!< if _targetMode is ABSOLUTEPOSITION, this represent the absolute position to be reached
        const GroundManager* _groundManager; //!< Ground manager (for ground access)
    };

    //-------------------------------------------------------------------------
    inline const IKTargetMode::Value&
    IKTarget::getType() const
    {
        return _targetMode;
    }

    //-------------------------------------------------------------------------
    inline void
    IKTarget::setType(const IKTargetMode::Value& type)
    {
        _targetMode = type;
    }

    //------------------------------------------------------------
    inline void
    IKTarget::setWorldPosition(const Vector3& worldPos)
    {
        setType(IKTargetMode::ABSOLUTEPOSITION);
        _position = worldPos;
    }

    //------------------------------------------------------------
    inline void
    IKTarget::setRelativePosition(const Vector3& offset)
    {
        setType(IKTargetMode::ENTITYRELATIVEPOSITION);
        _position = offset;
    }

    //------------------------------------------------------------
    inline bool
    IKTarget::isValid() const
    {
        return _targetMode < IKTargetMode::END;
    }
} // namespace glm
