/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004                                                \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* 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 (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/
/****************************************************************************
  History
$Log: not supported by cvs2svn $
Revision 1.18  2005/12/12 16:53:43  callieri
corrected UnProject, it's necessary also a ZDepth value to perform inverse projection

Revision 1.17  2005/12/07 10:57:52  callieri
added commodity function ProjectWorldtoViewport() to obtain directly pixel indices without calling two separate function of two different objects

Revision 1.16  2005/12/02 16:14:35  callieri
in Shot<S>::Axis changed Row3  to  GetRow3 . row3 was the old method name of Matrix44

Revision 1.15  2005/12/01 01:03:37  cignoni
Removed excess ';' from end of template functions, for gcc compiling

Revision 1.14  2005/11/23 14:18:35  ganovelli
added access to similarity (just for symmetry with Camera() )

Revision 1.13  2005/11/23 11:58:52  ganovelli
Empty constructor added, untemplated class Shotf and Shotd added
usage: Shotf myShot;
corrected member access rights

Revision 1.12  2005/07/11 13:12:35  cignoni
small gcc-related compiling issues (typenames,ending cr, initialization order)

Revision 1.11  2005/01/22 11:20:20  ponchio
<...Point3.h> -> <...point3.h>

Revision 1.10  2005/01/05 13:26:15  ganovelli
corretto cambiamento di sistema di rif.

Revision 1.9  2004/12/15 18:45:50  tommyfranken
*** empty log message ***

Revision 1.4  2004/10/07 14:41:31  fasano
Little fix on ViewPoint() method

Revision 1.3  2004/10/07 14:24:53  ganovelli
added LookAt,LookToward

Revision 1.2  2004/10/05 19:04:25  ganovelli
version 5-10-2004 in progress

Revision 1.1  2004/09/15 22:58:05  ganovelli
re-creation

Revision 1.2  2004/09/06 21:41:30  ganovelli
*** empty log message ***

Revision 1.1  2004/09/03 13:01:51  ganovelli
creation

****************************************************************************/


#ifndef __VCGLIB_SHOT
#define __VCGLIB_SHOT

// #include <vector>
// #include <vcg/Matrix44.h>
// #include <vcg/Box3.h>
#include <vcg/space/point2.h>
#include <vcg/space/point3.h>
#include <vcg/math/similarity.h>
#include <vcg/math/camera.h>

namespace vcg{

template <class S>
class Shot {
public:
	typedef Camera<S> CameraType;
	typedef S ScalarType;

protected:	

	enum {
		NOTVALID_BIT = 0x1,
		CREATED_EMPTY= 0x2
	};
	char flags;
	Camera<S> & camera;																	// the camera that shot
	vcg::Similarity<S,vcg::Matrix44<S> > similarity;		// the position from where it did it

public:

	Shot( Camera<S> & c):camera(c){similarity.SetIdentity();flags=0;}
	Shot():camera(*new vcg::Camera<S>()){similarity.SetIdentity();flags|=CREATED_EMPTY;}
	~Shot(){if(flags&CREATED_EMPTY) delete &camera;}

	bool IsValid(){ return (flags& NOTVALID_BIT)==0;}
	void SetValid(bool v){ if(!v) flags|=NOTVALID_BIT; else flags&=~NOTVALID_BIT;}

	Camera<S> & Camera(){return camera;};

	/// access to similarity
	vcg::Similarity<S,vcg::Matrix44<S> > & Similarity(){return similarity;}

	/// take the i-th axis of the coordinate system of the camera
	vcg::Point3<S> Axis(const int & i)const;

	/// take the viewpoint
	 const vcg::Point3<S> ViewPoint()const;

	/// set the viewpoint
	void SetViewPoint(const vcg::Point3<S> & viewpoint);

	/// look at
	void LookAt(const vcg::Point3<S> & z_dir,const vcg::Point3<S> & up);

	/// look at (same as opengl)
	void LookAt(const S & eye_x,const S & eye_y,const S & eye_z,const S & at_x,const S & at_y,const S & at_z,
							const S & up_x,const S & up_y,const S & up_z);

	/// look towards
	void LookTowards(const vcg::Point3<S> & z_dir,const vcg::Point3<S> & up);

	/// convert a 3d point in camera coordinates
	vcg::Point3<S>  ConvertToCameraCoordinates(const vcg::Point3<S> & p) const;

	/// convert a 3d point in camera coordinates
	vcg::Point3<S>  ConvertToWorldCoordinates(const vcg::Point3<S> & p) const;

	/// project onto the camera plane
	vcg::Point2<S> Project(const vcg::Point3<S> & p) const;

	/// inverse projection from camera plane + Zdepth to original 3D
	vcg::Point3<S> UnProject(const vcg::Point2<S> & p, const S & d) const;

	/// take the distance from the point p and the plane parallel to the camera plane and passing through the view
	/// point. The would be z depth 
	S Depth(const vcg::Point3<S> & p)const;

	/// project a 3d point from 3D WORLD to 2D VIEWPORT
	vcg::Point2<S> ProjectWorldtoViewport(const vcg::Point3<S> & p) const;


}; // end class definition

template <class S>
const vcg::Point3<S> Shot<S>::ViewPoint() const {
	//Matrix44<S> m = similarity.Matrix();
	//return Point3<S>(m[0][3],m[1][3],m[2][3]);
	return -similarity.tra;
}

template <class S>
	vcg::Point3<S>  Shot<S>::Axis(const int & i) const {	
			vcg::Matrix44<S> m; 
			similarity.rot.ToMatrix(m); 
			vcg::Point3<S> aa = m.GetRow3(i);
			return aa;
	}

template <class S>
void Shot<S>::SetViewPoint(const vcg::Point3<S> & viewpoint){
	similarity.tra = -viewpoint;
}

template <class S>
void Shot<S>::LookAt(const vcg::Point3<S> & z_dir,const vcg::Point3<S> & up){
	  LookTowards(z_dir-ViewPoint(),up);
}

template <class S>
void Shot<S>::LookAt(const S & eye_x,const S & eye_y,const S & eye_z,const S & at_x,const S & at_y,const S & at_z,
										 const S & up_x,const S & up_y,const S & up_z){
	SetViewPoint(Point3<S>(eye_x,eye_y,eye_z));
	LookAt(Point3<S>(at_x,at_y,at_z),Point3<S>(up_x,up_y,up_z));
}


template <class S>
void Shot<S>::LookTowards(const vcg::Point3<S> & z_dir,const vcg::Point3<S> & up){
	vcg::Point3<S> x_dir = up ^-z_dir ;
	vcg::Point3<S> y_dir = -z_dir ^x_dir ;
	
	Matrix44<S> m;
	m.SetIdentity();
	*(vcg::Point3<S> *)&m[0][0] = x_dir/x_dir.Norm();
	*(vcg::Point3<S> *)&m[1][0] = y_dir/y_dir.Norm();
	*(vcg::Point3<S> *)&m[2][0] = -z_dir/z_dir.Norm();

	similarity.rot.FromMatrix(m);
}

template <class S>
vcg::Point3<S> Shot<S>::ConvertToCameraCoordinates(const vcg::Point3<S> & p) const{
	Matrix44<S> rotM;
	similarity.rot.ToMatrix(rotM);
	vcg::Point3<S> cp = rotM * (p+similarity.tra);
	// note: the camera reference system is right handed
	cp[2]=-cp[2];
	return cp;
	}
template <class S>
vcg::Point3<S> Shot<S>::ConvertToWorldCoordinates(const vcg::Point3<S> & p) const{
	Matrix44<S> rotM;
	vcg::Point3<S> cp = p;
	// note: the World reference system is left handed
	cp[2]=-cp[2];
	similarity.rot.ToMatrix(rotM);
	cp = Inverse(rotM) * cp-similarity.tra;
	return cp;
}
template <class S>
vcg::Point2<S> Shot<S>::Project(const vcg::Point3<S> & p) const{
		return camera.Project(ConvertToCameraCoordinates(p));
	}
template <class S>
vcg::Point3<S> Shot<S>::UnProject(const vcg::Point2<S> & p, const S & d) const{
	Point2<S> tp = camera.ViewportToLocal(p);
	vcg::Point3<S> q = camera.UnProject(tp, d);
	return ConvertToWorldCoordinates(q);
}
template <class S>
S Shot<S>::Depth(const vcg::Point3<S> & p)const {
	return ConvertToCameraCoordinates(p).Z();
}

template <class S>
vcg::Point2<S> Shot<S>::ProjectWorldtoViewport(const vcg::Point3<S> & p)const 
{
 return camera.LocalToViewport( camera.Project( ConvertToCameraCoordinates(p) ) );
}

class Shotf: public Shot<float>{};
class Shotd: public Shot<double>{};

} // end name space

#endif