/*
 *  Copyright (C) 1998 Marcel Bosc  
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
/*! \file   Vect3D.hpp */

#ifndef _Vect3D_hpp
#define _Vect3D_hpp

#include<stdio.h>
#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<ImLib3D/CppTools.hpp>

// ********************** Vect3D ********************
//! 3D Vector, with operators 
template<class Value,class Real=Value>
class Vect3D
{
public:
	Value x,y,z;

//	~Vect3D(){;}
	Vect3D(Value _x,Value _y,Value _z):x(_x),y(_y),z(_z){}
	Vect3D(){}

	Real Norme2()const {return(x*x+y*y+z*z);}
	Real Norm2() const {return(x*x+y*y+z*z);}
	Real Norme() const {return(sqrt(Norme2()));}
	Real Norm()  const {return(sqrt(Norme2()));}
	Real Unit();
	Real SafeUnit();

	Real Dist2(const Vect3D &p2) const {return((p2.x-x)*(p2.x-x)+(p2.y-y)*(p2.y-y)+(p2.z-z)*(p2.z-z));}
	Real Dist(const Vect3D &p2) const {return(sqrt(Dist2(p2)));}
	Real Distance2(const Vect3D &p2) const {return((p2.x-x)*(p2.x-x)+(p2.y-y)*(p2.y-y)+(p2.z-z)*(p2.z-z));}
	Real Distance(const Vect3D &p2) const {return(sqrt(Dist2(p2)));}

	void Rotate(Vect3D axis,double angle)
	{
		Vect3D &V=*this;
		axis.Unit();
		Vect3D Vn=(V*axis)*axis;
		*this=Vn+cos(angle)*(V-Vn)+sin(angle)*(axis^V);
	}

	void Show() const {Show("","");}
	void Show(const char *p) const {Show(p,"");}
	void Show(const char *p,const char *s) const 
	{
#ifdef _CppTools_hpp
  		if(IsInt<Value>())
  		{printf("%s%d %d %d%s",p,(int)x,(int)y,(int)z,s);}
  		else{printf("%s%f %f %f%s",p,(float)x,(float)y,(float)z,s);}
#else // _CppTools_hpp
		printf("%s%f %f %f%s",p,(float)x,(float)y,(float)z,s);
#endif // _CppTools_hpp
	}


	Value & operator()(int i)
	{
		switch(i)
		{case 0:return x;case 1:return y;case 2:return z;}
		return x;
	}

	Value  operator()(int i) const
	{
		switch(i)
		{case 0:return x;case 1:return y;case 2:return z;}
		return x;
	}
	Vect3D<Value,Real>& operator=(Value val)
	{
		x=val;
		y=val;
		z=val;
		return *this;
	}
	
// can't get these to compile on all compilers???
//  	friend Vect3D operator+ (const Vect3D& v1,const Vect3D& v2);
//  	friend Vect3D operator- (const Vect3D& v1,const Vect3D& v2);
//  	friend Value operator*  (const Vect3D& v1,const Vect3D& v2);
//  	friend Vect3D operator* (Value l,const Vect3D& v2);
//  	friend Vect3D operator* (int l,const Vect3D& v2);
//  	friend Vect3D operator/ (const Vect3D& v1,Value l);
//  	friend Vect3D operator^ (const Vect3D& v1,const Vect3D& v2);

	bool operator==(const Vect3D &other) const {return other.x==x && other.y==y && other.z==z;}
	bool operator!=(const Vect3D &other) const {return !((*this)==other);}
	Vect3D &operator+=(const Vect3D &v){x+=v.x;y+=v.y;z+=v.z;return(*this);}
	Vect3D &operator-=(const Vect3D &v){x-=v.x;y-=v.y;z-=v.z;return(*this);}
	Vect3D &operator/=(const Value &d) {x/=d;  y/=d;  z/=d;return(*this);}
	Vect3D &operator*=(const Value &d) {x*=d;  y*=d;  z*=d;return(*this);}
	Vect3D  operator-() const {return Vect3D(-x,-y,-z);}

	template<class V1,class R1>
	operator Vect3D<V1,R1> () const {return Vect3D<V1,R1>(x,y,z);}


#ifdef _SB_LINEAR_
	operator SbVec3f(){SbVec3f V(x,y,z);return(V);}
#endif
#ifdef misc_H
inline
	operator Color_rgb() const
	{Color_rgb cl((byte)limit_byte(x*255),
				  (byte)limit_byte(y*255),
				  (byte)limit_byte(z*255));
	return(cl);}
#endif

};

template<class Value,class Real>
inline
Real
Vect3D<Value,Real>::Unit()
{
	Real n2=Norme2();
#ifdef _CppTools_hpp
	if (n2==0) { ThrowError("Vect3D::Unit: UNIT FAILED");}
#else  // _CppTools_hpp
	fprintf(stderr,"Vect3D::Unit: UNIT FAILED\n");
#endif // _CppTools_hpp
	if(n2==1){return 1;}
	Real n=sqrt(n2);
	x/=(Value)n;
	y/=(Value)n;
	z/=(Value)n;
	return(n);
}
template<class Value,class Real>
inline
Real
Vect3D<Value,Real>::SafeUnit()
{
	Real n2=Norme2();
	if(!finite(n2) || n2==0) { *this=Vect3D(1,0,0);return sqrt(n2);}
	if(n2==1){return 1;}
	Real n=sqrt(n2);
	x/=(Value)n;
	y/=(Value)n;
	z/=(Value)n;
	return(n);
}


template<class Value,class Real>
inline Vect3D<Value,Real>
operator+ (const Vect3D<Value,Real>& v1,const Vect3D<Value,Real>& v2)
{
    Vect3D<Value,Real> opres;
    opres.x=v1.x+v2.x;
    opres.y=v1.y+v2.y;
    opres.z=v1.z+v2.z;
    return opres;
}


template<class Value,class Real>
inline Vect3D<Value,Real>
operator- (const Vect3D<Value,Real>& v1,const Vect3D<Value,Real>& v2)
{
    Vect3D<Value,Real> opres;
    opres.x=v1.x-v2.x;
    opres.y=v1.y-v2.y;
    opres.z=v1.z-v2.z;
    return opres;
}

template<class Value,class Real>
inline Value 
operator* (const Vect3D<Value,Real>& v1,const Vect3D<Value,Real>& v2)
{
    return(v1.x*v2.x+v1.y*v2.y+v1.z*v2.z);
}

template<class Value,class Real>
inline Vect3D<Value,Real>
operator/(const Vect3D<Value,Real>& v1,Value l)
{
    Vect3D<Value,Real> opres;
    opres.x=v1.x/l;
    opres.y=v1.y/l;
    opres.z=v1.z/l;
    return opres;
}


template<class Value,class Real>
inline Vect3D<Value,Real>
operator* (float l,const Vect3D<Value,Real>& v1)
{
    Vect3D<Value,Real> opres;
    opres.x=v1.x*l;
    opres.y=v1.y*l;
    opres.z=v1.z*l;
    return opres;
}
template<class Value,class Real>
inline Vect3D<Value,Real>
operator* (double l,const Vect3D<Value,Real>& v1)
{
    Vect3D<Value,Real> opres;
    opres.x=(Value)(v1.x*l);
    opres.y=(Value)(v1.y*l);
    opres.z=(Value)(v1.z*l);
    return opres;
}

template<class Value,class Real>
inline Vect3D<Value,Real>
operator* (int l,const Vect3D<Value,Real>& v1)
{
    Vect3D<Value,Real> opres;
    opres.x=v1.x*l;
    opres.y=v1.y*l;
    opres.z=v1.z*l;
    return opres;
}

template<class Value,class Real>
inline Vect3D<Value,Real>
operator^ (const Vect3D<Value,Real>& v1,const Vect3D<Value,Real>& v2)
{
    Vect3D<Value,Real> opres;
    opres.x=v1.y*v2.z-v1.z*v2.y;
    opres.y=v1.z*v2.x-v1.x*v2.z;
    opres.z=v1.x*v2.y-v1.y*v2.x;
    return opres;
}

template<class Value,class Real>
inline Vect3D<Value,Real>
operator% (const Vect3D<Value,Real>& v1,const Vect3D<Value,Real>& v2)
{
    Vect3D<Value,Real> opres;
    opres.x=v1.x*v2.x;
    opres.y=v1.y*v2.y;
    opres.z=v1.z*v2.z;
    return opres;
}

template<class Value, class Real>
inline ostream& operator<< (ostream& s, const Vect3D<Value, Real>& vect)
{
	s << vect.x << " " << vect.y << " " << vect.z << " ";
//  	s << "(" << vect.x << ", "<< vect.y << ", " << vect.z << ")" << endl;
	return s;
}

//! There are two formats defined either(, can be replaced by ;): (x, y, z) or x y z 
template<class Value, class Real>
inline istream& operator>> (istream& s, Vect3D<Value, Real>& vect)
{
	char c;
	s >> c;
	if (c=='(')
	{
		s >> vect.x >> c;
		if (c==',' || c==';')
		{
			s >> vect.y >> c;
			if (c==',' || c==';')
				s >> vect.z >> c;
#ifdef _CppTools_hpp
			if (c!=')') ThrowError("istream& operator>> (istream& s, Vect3D<Value, Real>& vect) bad format");
#endif // _CppTools_hpp
		}
	}
	else
	{
		s.putback(c);
		s >> vect.x >> vect.y >> vect.z;
	}
	return s;
}

typedef Vect3D<float,float>   Vect3Df;
typedef Vect3D<double,double> Vect3Dd;
typedef Vect3D<int,float>     Vect3Di;


template<class Value,class Real>
Vect3D<Value,Real> max(const Vect3D<Value,Real> &v1,const Vect3D<Value,Real> &v2)
{
	return Vect3D<Value,Real>(max(v1.x,v2.x),max(v1.y,v2.y),max(v1.z,v2.z));
}
template<class Value,class Real>
Vect3D<Value,Real> min(const Vect3D<Value,Real> &v1,const Vect3D<Value,Real> &v2)
{
	return Vect3D<Value,Real>(min(v1.x,v2.x),min(v1.y,v2.y),min(v1.z,v2.z));
}

inline Vect3Di floor(const Vect3Df &p){return Vect3Di((int)floor(p.x),(int)floor(p.y),(int)floor(p.z));}
inline Vect3Di ceil (const Vect3Df &p){return Vect3Di((int)ceil (p.x),(int)ceil (p.y),(int)ceil (p.z));}
inline Vect3Di rint (const Vect3Df &p){return Vect3Di((int)rint (p.x),(int)rint (p.y),(int)rint (p.z));}

//! runtime type determination
template<class T> inline bool IsVect3Df() {return false;}

#ifdef _CppTools_hpp
//! runtime type determination
template<>        inline bool IsVect3Df<Vect3Df>() {return true ;}
template<>  inline string TypeName<Vect3Df>(){return "Vect3Df";}
#endif // _CppTools_hpp


#endif // _Vect3D_hpp

