#ifndef _Point_h
#define _Point_h

#include <cmath>
#include <iostream>

class Point {
public:
	Point( ) { }
	Point( float x, float y, float z ) {
		p[ 0 ] = x; p[ 1 ] = y; p[ 2 ] = z;
	}
	Point( const Point & rhs ) {
		this->p[ 0 ] = rhs.p[ 0 ]; this->p[ 1 ] = rhs.p[ 1 ]; this->p[ 2 ] = rhs.p[ 2 ];
	}
	
	inline friend float DotProduct( const Point & p1, const Point & p2 ) {
		return p1.p[ 0 ] * p2.p[ 0 ] + p1.p[ 1 ] * p2.p[ 1 ] + p1.p[ 2 ] * p2.p[ 2 ];
	}
	inline friend Point CrossProduct( const Point & p1, const Point & p2 ) {
		return Point( 	p1.p[ 1 ] * p2.p[ 2 ] - p1.p[ 2 ] * p2.p[ 1 ],
						p1.p[ 2 ] * p2.p[ 0 ] - p1.p[ 0 ] * p2.p[ 2 ],
						p1.p[ 0 ] * p2.p[ 1 ] - p1.p[ 1 ] * p2.p[ 0 ] );
	}

	inline friend float Distance( const Point & p1, const Point & p2 ) {
		return sqrt(	( p2.p[ 0 ] - p1.p[ 0 ] ) * ( p2.p[ 0 ] - p1.p[ 0 ] ) +
						( p2.p[ 1 ] - p1.p[ 1 ] ) * ( p2.p[ 1 ] - p1.p[ 1 ] ) +
						( p2.p[ 2 ] - p1.p[ 2 ] ) * ( p2.p[ 2 ] - p1.p[ 2 ] ) );
	}
	inline float Length( ) const {
		return sqrt( DotProduct( *this, *this ) );
	}
	inline float InverseLength( ) const {
		return 1.0f / this->Length( );
	}
	
	inline friend Point operator+( const Point & p1, const Point & p2 ) {
		return Point( p1.p[ 0 ] + p2.p[ 0 ], p1.p[ 1 ] + p2.p[ 1 ], p1.p[ 2 ] + p2.p[ 2 ] );
	}
	inline friend Point operator-( const Point & p1, const Point & p2 ) {
		return Point( p1.p[ 0 ] - p2.p[ 0 ], p1.p[ 1 ] - p2.p[ 1 ], p1.p[ 2 ] - p2.p[ 2 ] );
	}
	inline friend Point operator-( const Point & p1 ) {
		return Point( -p1.p[ 0 ], -p1.p[ 1 ], -p1.p[ 2 ] );
	}
	inline friend Point operator*( const Point & p, float x ) {
		return Point( p.p[ 0 ] * x, p.p[ 1 ] * x, p.p[ 2 ] * x );
	}
	inline friend Point operator*( float x, const Point & p ) {
		return Point( p.p[ 0 ] * x, p.p[ 1 ] * x, p.p[ 2 ] * x );
	}
	inline Point & operator+=( const Point & rhs ) {
		this->p[ 0 ] += rhs.p[ 0 ]; this->p[ 1 ] += rhs.p[ 1 ]; this->p[ 2 ] += rhs.p[ 2 ];
		return *this;
	}
	inline Point & operator*=( float x ) {
		this->p[ 0 ] *= x; this->p[ 1 ] *= x; this->p[ 2 ] *= x;
		return *this;
	}
	inline Point & Normalize( ) {
		return *this *= this->InverseLength( );
	}
	inline friend Point Normalize( const Point & p ) {
		float invlen = p.InverseLength( );
		return Point( p.p[ 0 ] * invlen, p.p[ 1 ] * invlen, p.p[ 2 ] * invlen );
	}
	inline float Get( int i ) const {
		return p[ i ];
	}
	inline float & Get( int i ) {
		return p[ i ];
	}
	
	inline friend bool operator==( const Point & p1, const Point & p2 ) {
		return p1.p[ 0 ] == p2.p[ 0 ] && p1.p[ 1 ] == p2.p[ 1 ] && p1.p[ 2 ] == p2.p[ 2 ];
	}
	
	inline friend std::ostream& operator<<( std::ostream & o, const Point & p ) {
		return o << "(" << p.p[ 0 ] << ", " << p.p[ 1 ] << ", " << p.p[ 2 ] << ")";
	}
	inline friend std::istream& operator>>( std::istream & i, Point & p ) {
		return i >> p.p[ 0 ] >> p.p[ 1 ] >> p.p[ 2 ];
	}
private:
	float p[ 3 ];
};

struct LineSegment {
	LineSegment( const Point & _a, const Point & _b ) : a( _a ), b( _b ) { }
	Point a, b;
};

struct Ray {
	Ray( const Point & _origin, const Point & _direction ) : origin( _origin ), direction( _direction ) { }
	Point origin, direction;
};




#endif
