#pragma once

namespace FDK
{
	value class Vector2;
	value class Vector4;
	value class Matrix;
	value class Quaternion;

	[System::Serializable]
	[System::Runtime::InteropServices::StructLayout( System::Runtime::InteropServices::LayoutKind::Sequential, Pack = 4 )]
	public value class Vector3 : IEquatable<Vector3>
	{
	public:
		float X;
		float Y;
		float Z;

		property float default[int]
		{
			float get( int index );
			void set( int index, float value );
		}

		Vector3( float value );
		Vector3( Vector2 value, float z );
		Vector3( float x, float y, float z );

		static property Vector3 Zero
		{
			Vector3 get() 
			{
				return Vector3(0, 0, 0);
			}
		}
		static property Vector3 UnitX 
		{
			Vector3 get()
			{
				return Vector3(1, 0, 0);
			}
		}
		static property Vector3 UnitY
		{
			Vector3 get()
			{
				return Vector3(0, 1, 0); 
			}
		}
		static property Vector3 UnitZ
		{
			Vector3 get()
			{
				return Vector3(0, 0, 1);
			}
		}
		static property int SizeInBytes
		{
			int get()
			{
				return System::Runtime::InteropServices::Marshal::SizeOf(Vector3::typeid);
			}
		}

		static Vector3 Add( Vector3 left, Vector3 right );
		static void Add( Vector3% left, Vector3% right, [Out] Vector3% result );
		static Vector3 Subtract( Vector3 left, Vector3 right );
		static void Subtract( Vector3% left, Vector3% right, [Out] Vector3% result );
		static Vector3 Multiply( Vector3 value, float scale );
		static void Multiply( Vector3% value, float scale, [Out] Vector3% result );
		static Vector3 Modulate( Vector3 left, Vector3 right );
		static void Modulate( Vector3% left, Vector3% right, [Out] Vector3% result );
		static Vector3 Divide( Vector3 value, float scale );
		static void Divide( Vector3% value, float scale, [Out] Vector3% result );
		static Vector3 Negate( Vector3 value );
		static void Negate( Vector3% value, [Out] Vector3% result );
		static Vector3 Barycentric( Vector3 value1, Vector3 value2, Vector3 value3, float amount1, float amount2 );
		static void Barycentric( Vector3% value1, Vector3% value2, Vector3% value3, float amount1, float amount2, [Out] Vector3% result );
		static Vector3 CatmullRom( Vector3 value1, Vector3 value2, Vector3 value3, Vector3 value4, float amount );
		static void CatmullRom( Vector3% value1, Vector3% value2, Vector3% value3, Vector3% value4, float amount, [Out] Vector3% result );
		static Vector3 Clamp( Vector3 value, Vector3 min, Vector3 max );
		static void Clamp( Vector3% value, Vector3% min, Vector3% max, [Out] Vector3% result );
		static Vector3 Hermite( Vector3 value1, Vector3 tangent1, Vector3 value2, Vector3 tangent2, float amount );
		static void Hermite( Vector3% value1, Vector3% tangent1, Vector3% value2, Vector3% tangent2, float amount, [Out] Vector3% result );
		static Vector3 Lerp( Vector3 start, Vector3 end, float factor );
		static void Lerp( Vector3% start, Vector3% end, float factor, [Out] Vector3% result );
		static Vector3 SmoothStep( Vector3 start, Vector3 end, float amount );
		static void SmoothStep( Vector3% start, Vector3% end, float amount, [Out] Vector3% result );
		static float Distance( Vector3 value1, Vector3 value2 );
		static float DistanceSquared( Vector3 value1, Vector3 value2 );
		static float Dot( Vector3 left, Vector3 right );
		static Vector3 Cross( Vector3 left, Vector3 right );
		static void Cross( Vector3% left, Vector3% right, [Out] Vector3% result );
		static Vector3 Reflect( Vector3 vector, Vector3 normal );
		static void Reflect( Vector3% vector, Vector3% normal, [Out] Vector3% result );
		static Vector3 Normalize( Vector3 vector );
		static void Normalize( Vector3% vector, [Out] Vector3% result );
		static Vector4 Transform( Vector3 vector, Matrix transform );
		static void Transform( Vector3% vector, Matrix% transform, [Out] Vector4% result );
		static void Transform( Vector3* vectorsIn, int inputStride, Matrix* transformation, Vector4* vectorsOut, int outputStride, int count );
		static void Transform( Vector3* vectorsIn, Matrix* transformation, Vector4* vectorsOut, int count );
		static void Transform( array<Vector3>^ vectorsIn, Matrix% transformation, array<Vector4>^ vectorsOut, int offset, int count );
		static void Transform( array<Vector3>^ vectorsIn, Matrix% transformation, array<Vector4>^ vectorsOut );
		static array<Vector4>^ Transform( array<Vector3>^ vectors, Matrix% transform );
		static Vector4 Transform( Vector3 value, Quaternion rotation );
		static void Transform( Vector3% value, Quaternion% rotation, [Out] Vector4% result );
		static array<Vector4>^ Transform( array<Vector3>^ vectors, Quaternion% rotation );
		static Vector3 TransformCoordinate( Vector3 coord, Matrix transform );
		static void TransformCoordinate( Vector3% coord, Matrix% transform, [Out] Vector3% result );
		static void TransformCoordinate( Vector3* coordsIn, int inputStride, Matrix* transformation, Vector3* coordsOut, int outputStride, int count );
		static void TransformCoordinate( Vector3* coordinatesIn, Matrix* transformation, Vector3* coordinatesOut, int count );
		static void TransformCoordinate( array<Vector3>^ coordsIn, Matrix% transformation, array<Vector3>^ coordsOut, int offset, int count );
		static void TransformCoordinate( array<Vector3>^ coordinatesIn, Matrix% transformation, array<Vector3>^ coordinatesOut );
		static array<Vector3>^ TransformCoordinate( array<Vector3>^ coords, Matrix% transform );
		static Vector3 TransformNormal( Vector3 normal, Matrix transform );
		static void TransformNormal( Vector3% normal, Matrix% transform, [Out] Vector3% result );
		static void TransformNormal( Vector3* normalsIn, int inputStride, Matrix* transformation, Vector3* normalsOut, int outputStride, int count );
		static void TransformNormal( Vector3* normalsIn, Matrix* transformation, Vector3* normalsOut, int count );
		static void TransformNormal( array<Vector3>^ normalsIn, Matrix% transformation, array<Vector3>^ normalsOut, int offset, int count );
		static void TransformNormal( array<Vector3>^ normalsIn, Matrix% transformation, array<Vector3>^ normalsOut );
		static array<Vector3>^ TransformNormal( array<Vector3>^ normals, Matrix% transform );
		static Vector3 Project( Vector3 vector, float x, float y, float width, float height, float minZ, float maxZ, Matrix worldViewProjection );
		static void Project( Vector3% vector, float x, float y, float width, float height, float minZ, float maxZ, Matrix% worldViewProjection, [Out] Vector3% result );
		static Vector3 Unproject( Vector3 vector, float x, float y, float width, float height, float minZ, float maxZ, Matrix worldViewProjection );
		static void Unproject( Vector3% vector, float x, float y, float width, float height, float minZ, float maxZ, Matrix% worldViewProjection, [Out] Vector3% result );
		static Vector3 Minimize( Vector3 left, Vector3 right );
		static void Minimize( Vector3% left, Vector3% right, [Out] Vector3% result );
		static Vector3 Maximize( Vector3 left, Vector3 right );
		static void Maximize( Vector3% left, Vector3% right, [Out] Vector3% result );
		static Vector3 operator + ( Vector3 left, Vector3 right );
		static Vector3 operator - ( Vector3 left, Vector3 right );
		static Vector3 operator - ( Vector3 value );
		static Vector3 operator * ( Vector3 value, float scale );
		static Vector3 operator * ( float scale, Vector3 vec );
		static Vector3 operator / ( Vector3 value, float scale );
		static bool operator == ( Vector3 left, Vector3 right );
		static bool operator != ( Vector3 left, Vector3 right );
		static bool Equals( Vector3% value1, Vector3% value2 );

		virtual String^ ToString() override;
		virtual int GetHashCode() override;
		virtual bool Equals( Object^ value ) override;
		virtual bool Equals( Vector3 value );

		float Length();
		float LengthSquared();
		void Normalize();

	private:
		static void CheckArrayBounds( Array^ data, int offset, int% count );
		static void CheckBounds( int lowerBound, int size, int offset, int% count );
	};
}