#pragma once

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

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

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

		static property Vector4 Zero
		{
			Vector4 get()
			{
				return Vector4( 0, 0, 0, 0 ); 
			}
		}
		static property Vector4 UnitX
		{
			Vector4 get()
			{
				return Vector4( 1, 0, 0, 0 ); 
			}
		}
		static property Vector4 UnitY
		{
			Vector4 get()
			{
				return Vector4( 0, 1, 0, 0 );
			}
		}
		static property Vector4 UnitZ
		{
			Vector4 get()
			{
				return Vector4( 0, 0, 1, 0 );
			}
		}
		static property Vector4 UnitW
		{
			Vector4 get()
			{
				return Vector4( 0, 0, 0, 1 );
			}
		}
		static property int SizeInBytes 
		{
			int get() 
			{ 
				return Marshal::SizeOf( Vector4::typeid );
			}
		}

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

		static Vector4 Add( Vector4 left, Vector4 right );
		static void Add( Vector4% left, Vector4% right, [Out] Vector4% result );
		static Vector4 Subtract( Vector4 left, Vector4 right );
		static void Subtract( Vector4% left, Vector4% right, [Out] Vector4% result );
		static Vector4 Multiply( Vector4 value, float scale );
		static void Multiply( Vector4% value, float scale, [Out] Vector4% result );
		static Vector4 Modulate( Vector4 left, Vector4 right );
		static void Modulate( Vector4% left, Vector4% right, [Out] Vector4% result );
		static Vector4 Divide( Vector4 value, float scale );
		static void Divide( Vector4% value, float scale, [Out] Vector4% result );
		static Vector4 Negate( Vector4 value );
		static void Negate( Vector4% value, [Out] Vector4% result );
		static Vector4 Barycentric( Vector4 value1, Vector4 value2, Vector4 value3, float amount1, float amount2 );
		static void Barycentric( Vector4% value1, Vector4% value2, Vector4% value3, float amount1, float amount2, [Out] Vector4% result );
		static Vector4 CatmullRom( Vector4 value1, Vector4 value2, Vector4 value3, Vector4 value4, float amount );
		static void CatmullRom( Vector4% value1, Vector4% value2, Vector4% value3, Vector4% value4, float amount, [Out] Vector4% result );
		static Vector4 Clamp( Vector4 value, Vector4 min, Vector4 max );
		static void Clamp( Vector4% value, Vector4% min, Vector4% max, [Out] Vector4% result );
		static Vector4 Hermite( Vector4 value1, Vector4 tangent1, Vector4 value2, Vector4 tangent2, float amount );
		static void Hermite( Vector4% value1, Vector4% tangent1, Vector4% value2, Vector4% tangent2, float amount, [Out] Vector4% result );
		static Vector4 Lerp( Vector4 start, Vector4 end, float factor );
		static void Lerp( Vector4% start, Vector4% end, float factor, [Out] Vector4% result );
		static Vector4 SmoothStep( Vector4 start, Vector4 end, float amount );
		static void SmoothStep( Vector4% start, Vector4% end, float amount, [Out] Vector4% result );
		static float Distance( Vector4 value1, Vector4 value2 );
		static float DistanceSquared( Vector4 value1, Vector4 value2 );
		static float Dot( Vector4 left, Vector4 right );
		static Vector4 Normalize( Vector4 vector );
		static void Normalize( Vector4% vector, [Out] Vector4% result );
		static Vector4 Transform( Vector4 vector, Matrix transform );
		static void Transform( Vector4% vector, Matrix% transform, [Out] Vector4% result );
		static array<Vector4>^ Transform( array<Vector4>^ vectors, Matrix% transform );
		static Vector4 Transform( Vector4 value, Quaternion rotation );
		static void Transform( Vector4% value, Quaternion% rotation, [Out] Vector4% result );
		static array<Vector4>^ Transform( array<Vector4>^ vectors, Quaternion% rotation );
		static Vector4 Minimize( Vector4 left, Vector4 right );
		static void Minimize( Vector4% left, Vector4% right, [Out] Vector4% result );
		static Vector4 Maximize( Vector4 left, Vector4 right );
		static void Maximize( Vector4% left, Vector4% right, [Out] Vector4% result );
		static Vector4 operator + ( Vector4 left, Vector4 right );
		static Vector4 operator - ( Vector4 left, Vector4 right );
		static Vector4 operator - ( Vector4 value );
		static Vector4 operator * ( Vector4 value, float scale );
		static Vector4 operator * ( float scale, Vector4 vec );
		static Vector4 operator / ( Vector4 value, float scale );
		static bool operator == ( Vector4 left, Vector4 right );
		static bool operator != ( Vector4 left, Vector4 right );
		static bool Equals( Vector4% value1, Vector4% value2 );

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

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