// (c) Electronic Arts. All Rights Reserved.

#ifndef RW_MATH_VPU_VECTOR3_H
#define RW_MATH_VPU_VECTOR3_H

#if defined _MSC_VER
    #pragma once
#endif

#include "rw/math/detail/base.h"

#include "rw/math/types_fwd.h"
#include "rw/math/vpu/vec_float.h"
#include "rw/math/vpu/vector_intrinsic.h"
#include "rw/math/vpu/vector2.h"
#include "rw/math/fpu/vector3.h"

namespace rw {
namespace math {
namespace vpu {

/*!
    \brief Represents a vector in three-dimensional (3-D) space

    The Vector3 class is implemented using hardware efficient vector types.
    Therefore, the Vector3 should always be aligned on a 16 byte boundary.

    Vectors are considered to be row vectors, therefore we multiply row 
    vectors by matrices on the right, i.e. vector2 = vector1 * matrix.
*/
#if defined EA_PLATFORM_XENON && defined RW_MATH_DISABLE_VECTOR_COPY_CTOR_AND_ASSIGNMENT_OPERATOR
    __declspec(passinreg) 
#endif
class Vector3
{
public:
    enum { IsAligned = 1 };
    enum { Alignment = 16 };
    enum { NumberOfElement = 3 };

    typedef float FloatType;
    typedef VecFloat FastFloatType;

    #ifdef RW_MATH_VECTOR_PASS_BY_VALUE
        typedef const Vector3 InParam;
    #else
        #if defined EA_PLATFORM_XENON && defined RW_MATH_DISABLE_VECTOR_COPY_CTOR_AND_ASSIGNMENT_OPERATOR
            typedef const Vector3 InParam; 
        #else
            typedef const Vector3& InParam; 
        #endif            
    #endif 
    
    #if RW_MATH_ENABLE_DEPRECATED_API
        typedef Vector3& OutParam;
        typedef Vector3& InOutParam;
    #endif

public:

    template<class T>
    explicit RW_MATH_FORCE_INLINE Vector3(const fpu::Vector3Template<T> &v);
    RW_MATH_FORCE_INLINE Vector3();
    #ifndef RW_MATH_DISABLE_VECTOR_COPY_CTOR_AND_ASSIGNMENT_OPERATOR
        RW_MATH_FORCE_INLINE Vector3(const Vector3 &rhs);
        RW_MATH_FORCE_INLINE Vector3& operator = (Vector3::InParam v);
    #endif
    //! This constructor creates a vector with all its elements equal to \c scalar.
    RW_MATH_FORCE_INLINE explicit Vector3(VecFloat::InParam scalar);
    RW_MATH_FORCE_INLINE Vector3(float x, float y, float z);
    //! Construct a Vector3 from an array of floats.
    //!
    //! The array must be aligned to a 16byte boundary for hardware efficiency reasons.
    RW_MATH_FORCE_INLINE explicit Vector3(const float xyz[3]);
    RW_MATH_FORCE_INLINE RW_MATH_FUTURE_EXPLICIT Vector3( VectorIntrinsicInParam v );
    RW_MATH_FORCE_INLINE Vector3( VecFloat::InParam x, VecFloat::InParam y, VecFloat::InParam z );
    RW_MATH_FORCE_INLINE Vector3(VecFloatRefIndex::InParam x, VecFloatRefIndex::InParam y, VecFloatRefIndex::InParam z );
    RW_MATH_FORCE_INLINE Vector3(Vector2::InParam v2, VecFloatRefZ::InParam z);
    RW_MATH_FORCE_INLINE Vector3(Vector2::InParam v2, VecFloat::InParam z);
    template<VectorAxis XSRC_INDEX, VectorAxis YSRC_INDEX, VectorAxis ZSRC_INDEX>
    RW_MATH_FORCE_INLINE Vector3(VecFloatRef<XSRC_INDEX> x, VecFloatRef<YSRC_INDEX> y, VecFloatRef<ZSRC_INDEX> z);    
    
    #if RW_MATH_ENABLE_DEPRECATED_API
        RW_MATH_FORCE_INLINE Vector3& operator = (VectorIntrinsicInParam v);
        RW_MATH_FORCE_INLINE operator       VectorIntrinsic&();
        RW_MATH_FORCE_INLINE operator const VectorIntrinsic&() const;
    #endif

    RW_MATH_FORCE_INLINE void  SetVector(VectorIntrinsicInParam v);

    RW_MATH_FORCE_INLINE       VectorIntrinsic& GetVector();
    RW_MATH_FORCE_INLINE const VectorIntrinsic& GetVector() const;

    template<VectorAxis XSRC_INDEX, VectorAxis YSRC_INDEX, VectorAxis ZSRC_INDEX>
    RW_MATH_FORCE_INLINE void Set(VecFloatRef<XSRC_INDEX> x, VecFloatRef<YSRC_INDEX> y, VecFloatRef<ZSRC_INDEX> z);
    RW_MATH_FORCE_INLINE void Set(float x, float y, float z);
    RW_MATH_FORCE_INLINE void Set(VecFloat::InParam x, VecFloat::InParam y, VecFloat::InParam z);
    RW_MATH_FORCE_INLINE void Set(VecFloatRefIndex::InParam x, VecFloatRefIndex::InParam y, VecFloatRefIndex::InParam z);

    template<VectorAxis INDEX>
    RW_MATH_FORCE_INLINE void SetComponent(int i, VecFloatRef<INDEX> value);
    RW_MATH_FORCE_INLINE void SetComponent(int i, float value);
    RW_MATH_FORCE_INLINE void SetComponent(int i, VecFloat::InParam value);
    RW_MATH_FORCE_INLINE void SetComponent(int i, VecFloatRefIndex::InParam value);
    
    template<VectorAxis INDEX> 
    RW_MATH_FORCE_INLINE void SetX(VecFloatRef<INDEX> x);
    RW_MATH_FORCE_INLINE void SetX(float x);
    RW_MATH_FORCE_INLINE void SetX(VecFloat::InParam x);
    RW_MATH_FORCE_INLINE void SetX(VecFloatRefIndex::InParam x);
    
    template<VectorAxis INDEX>
    RW_MATH_FORCE_INLINE void SetY(VecFloatRef<INDEX> y);
    RW_MATH_FORCE_INLINE void SetY(float y);
    RW_MATH_FORCE_INLINE void SetY(VecFloat::InParam y);
    RW_MATH_FORCE_INLINE void SetY(VecFloatRefIndex::InParam y);
    
    template<VectorAxis INDEX>
    RW_MATH_FORCE_INLINE void SetZ(VecFloatRef<INDEX> z);
    RW_MATH_FORCE_INLINE void SetZ(float z);
    RW_MATH_FORCE_INLINE void SetZ(VecFloat::InParam z);
    RW_MATH_FORCE_INLINE void SetZ(VecFloatRefIndex::InParam z);

    RW_MATH_FORCE_INLINE void SetZero();
    
    RW_MATH_FORCE_INLINE       VecFloatRefIndex GetComponent(int i);
    RW_MATH_FORCE_INLINE const VecFloatRefIndex GetComponent(int i) const;
    RW_MATH_FORCE_INLINE       VecFloatRefIndex operator [] (int i);
    RW_MATH_FORCE_INLINE const VecFloatRefIndex operator [] (int i) const;

    #ifdef RW_MATH_RETURN_FLOAT
        RW_MATH_FORCE_INLINE const float& GetX() const{ return mV.x; }
        RW_MATH_FORCE_INLINE       float& GetX(){ return mV.x; }
        RW_MATH_FORCE_INLINE const float& GetY() const{ return mV.y; }
        RW_MATH_FORCE_INLINE       float& GetY(){ return mV.y; }
        RW_MATH_FORCE_INLINE const float& GetZ() const{ return mV.z; }
        RW_MATH_FORCE_INLINE       float& GetZ(){ return mV.z; }
        
        RW_MATH_FORCE_INLINE const float& X() const{ return mV.x; }
        RW_MATH_FORCE_INLINE       float& X(){ return mV.x; }
        RW_MATH_FORCE_INLINE const float& Y() const{ return mV.y; }
        RW_MATH_FORCE_INLINE       float& Y(){ return mV.y; }
        RW_MATH_FORCE_INLINE const float& Z() const{ return mV.z; }
        RW_MATH_FORCE_INLINE       float& Z(){ return mV.z; }        
    #else
        RW_MATH_FORCE_INLINE const VecFloatRefX GetX() const;
        RW_MATH_FORCE_INLINE       VecFloatRefX GetX();
        RW_MATH_FORCE_INLINE const VecFloatRefY GetY() const;
        RW_MATH_FORCE_INLINE       VecFloatRefY GetY();
        RW_MATH_FORCE_INLINE const VecFloatRefZ GetZ() const;
        RW_MATH_FORCE_INLINE       VecFloatRefZ GetZ();
        
        RW_MATH_FORCE_INLINE const VecFloatRefX X() const;
        RW_MATH_FORCE_INLINE       VecFloatRefX X();
        RW_MATH_FORCE_INLINE const VecFloatRefY Y() const;
        RW_MATH_FORCE_INLINE       VecFloatRefY Y();
        RW_MATH_FORCE_INLINE const VecFloatRefZ Z() const;
        RW_MATH_FORCE_INLINE       VecFloatRefZ Z();
    #endif

    #if ! RW_MATH_ENABLE_DEPRECATED_API
        private:    
        Vector3&      operator = (const vpu::Vector2& v);
        explicit Vector3(const vpu::Vector2& v);
    #endif

public:

    VectorIntrinsic  mV;
};

}

using ::rw::math::vpu::Vector3;

}

/*!
 *  Warning: This function \e conditionally endian swaps the class's data.
 *
 *  Normally this function is a no-op.  See \ref p_endian_swapping for details
 *  regarding when endian swapping will actually occur.
 */
RW_MATH_FORCE_INLINE void EndianSwap(rw::math::vpu::Vector3& t);

}

#ifndef RW_MATH_VPU_NO_INLINE_FUNCTIONS
    #include "rw/math/vpu/detail/vector3_type_inline.h"
#endif

namespace rw {
namespace math {
namespace fpu {
    // Note; defining this constructor here vs. fpu/quatpos.h avoids ps3 compiler error.
    // This also allow us to remove the dependency of fpu header on vpu header
    template<class T>
    Vector3Template<T>::Vector3Template(const rw::math::vpu::Vector3 &v)
    {
        this->x = static_cast<T>(v.X().GetFloat());
        this->y = static_cast<T>(v.Y().GetFloat());
        this->z = static_cast<T>(v.Z().GetFloat());
    }
}}}

#endif
