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

#ifndef RW_MATH_VPU_VECTOR4_H
#define RW_MATH_VPU_VECTOR4_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/vector3.h"
#include "rw/math/fpu/vector4.h"

namespace rw {
namespace math {
namespace vpu {

/*!
    \brief Vector with four components

    It is implemented as Euclidean R4 - no hidden/implicit dividing by W.

    See http://mathworld.wolfram.com/HomogeneousCoordinates.html

    The Vector4 class is implemented using hardware efficient vector types.
    Therefore, Vector4 needs to 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 Vector4
{
public:
    enum { IsAligned = 1 };
    enum { Alignment = 16 };
    enum { NumberOfElement = 4 };

    typedef float FloatType;
    typedef VecFloat FastFloatType;

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

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

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

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

    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);

    template<VectorAxis INDEX>
    RW_MATH_FORCE_INLINE void SetW(VecFloatRef<INDEX> w);
    RW_MATH_FORCE_INLINE void SetW(float w);
    RW_MATH_FORCE_INLINE void SetW(VecFloat::InParam w);
    RW_MATH_FORCE_INLINE void SetW(VecFloatRefIndex::InParam w);

    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& GetW() const{ return mV.w; }
        RW_MATH_FORCE_INLINE       float& GetW(){ return mV.w; }
        
        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; }
        RW_MATH_FORCE_INLINE const float& W() const{ return mV.w; }
        RW_MATH_FORCE_INLINE       float& W(){ return mV.w; }
    #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 VecFloatRefW GetW() const;
        RW_MATH_FORCE_INLINE       VecFloatRefW GetW();
        
        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();
        RW_MATH_FORCE_INLINE const VecFloatRefW W() const;
        RW_MATH_FORCE_INLINE       VecFloatRefW W();
    #endif

public:

    VectorIntrinsic  mV;


    #if ! RW_MATH_ENABLE_DEPRECATED_API
        private:
        Vector4& operator = (const vpu::Vector2& v);
        Vector4& operator = (const vpu::Quaternion& v);
        Vector4& operator = (const vpu::Plane& v);
        explicit Vector4(const vpu::Vector2& v);
        explicit Vector4(const vpu::Quaternion& v);
        explicit Vector4(const vpu::Plane& v);
    #endif
};

}

using ::rw::math::vpu::Vector4;

}

/*!
 *  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::Vector4& t);

}

#ifndef RW_MATH_VPU_NO_INLINE_FUNCTIONS
    #include "rw/math/vpu/detail/vector4_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>
    Vector4Template<T>::Vector4Template(const rw::math::vpu::Vector4 &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());
        this->w = static_cast<T>(v.W().GetFloat());
    }
}}}

#endif
