
#ifndef _JL_Camera_H_
#define _JL_Camera_H_


#include <stdlib.h>
#include <iostream.h>

#include "jllib_include.h"
#include "vec4.h"
#include "glpic.h" // for typedef PixelType
#include "ray.h"


class Camera {

	private:
                Vec4	_R;             // eye position (world coordinates)
		Vec4    _D;	        // view direction (unitized)
		Vec4    _U;	        // up vector (unitized)
//		JLFilm	_film;		// image plane corners and framebuffer
		real	_dist;		// distance of image plane along _D
		real	_near_plane, _far_plane;	// clip planes; default [0, MAXFLOAT]

		Vec4	_c00;		// bottom left
		Vec4	_c10;		// bottom right
		Vec4	_c01;		// top left
		Vec4	_c11;		// top right

		int	_x_pixels;
		int	_y_pixels;

		float	_fovx, _fovy;   // field of view, in radians

	public: 
		Camera();
		Camera(const Vec4 &R, const Vec4 &D, const Vec4 &U,
		       real dist, real nearp, real farp, // focal distance; near, far clip planes 
		       int x, int y,  // viewport size
		       float fovx, float fovy);  // x, y field of view in radians
		Camera(const Camera &C) { CopyFrom(C); }
		~Camera();

		Camera& CopyFrom(const Camera&);
		Camera& operator=(const Camera &C) { return CopyFrom(C); }

		const Vec4& R() const { return _R; }
		const Vec4& D() const { return _D; }
		const Vec4& U() const { return _U; }

		void Set(const Vec4 &R, const Vec4 &D) { _R=R; _D=D; UpdateCorners(); }
		void Set(const Vec4 &R, const Vec4 &D, const Vec4 &U ) { _R=R; _D=D; _U=U; UpdateCorners(); }
		void SetR(const Vec4 &R) { _R = R; UpdateCorners(); }
		void SetD(const Vec4 &D) { _D = D; UpdateCorners(); }
		void SetU(const Vec4 &U) { _U = U; UpdateCorners(); }

		// doesn't update corners -- will break if aspect ratio changes
		void SetResolution(int x, int y) {_x_pixels = x; _y_pixels = y; }

		int x() { return _x_pixels; }
		int y() { return _y_pixels; }

		void CalcSamplePoint(float s, float t, Vec4 &v);
		void CalcPixelPoint(int x, int y, Vec4 &v);

		void CalcPixelPointWithJitter(int x, int y, Vec4 &v);

		Vec4& c00() { return _c00; }
		Vec4& c10() { return _c10; }
		Vec4& c01() { return _c01; }
		Vec4& c11() { return _c11; }

		const Vec4& c00() const { return _c00; }
		const Vec4& c10() const { return _c10; }
		const Vec4& c01() const { return _c01; }
		const Vec4& c11() const { return _c11; }

		void SetPosition(Vec4 &c00, Vec4 &c10, Vec4 &c01, Vec4 &c11) {
				_c00 = c00;
				_c10 = c10;
				_c01 = c01;
				_c11 = c11;
			}

                // assumes valid dist, near, far, fovx, fovy
		void SetPosition(Vec4 &Eye, Vec4 &LookAt, Vec4 &Up);

		// assumes ray origin (R.R()) is already set to camera origin (_R)!!!
                // XXX -- probably want to set ray origin to (_R + near * _D)
		void SetEyeRayDirection(int x, int y, Ray &R) {
				Vec4 P, D;
				CalcPixelPoint(x, y, P);
				Vec4FastSub(D, P, _R);
				D.MakeUnit();
				R.SetD(D);
			}

/*
		void SetPixel(int x, int y, PixelType p)
			{ _film.GetPicture()->Pixel(x, y, p); }

		PixelType GetPixel(int x, int y)
			{ return _film.GetPicture()->Pixel(x, y); }
*/
  
  //given an actual z coord, normalizes it to [0, 1], where
  //0 is the near plane and 1 is the far plane.
  real NormalizeZ (real z) const {
    return ((z - _near_plane) / (_far_plane - _near_plane));
  }
                real Dist() const { return _dist; }
		real Near() const { return _near_plane; }
		real Far() const { return _far_plane; }
		void SetDist(real d) { _dist = d; }
		void SetNear(real n) { _near_plane = n; }
		void SetFar(real f) { _far_plane = f; }

		friend ostream& operator<<(ostream&, const Camera&);

		void UpdateCorners();

		void Draw();
		void DrawImage(GLPic&);

                void SetFovX (float newfov) {_fovx = newfov;}
                void SetFovY (float newfov) {_fovy = newfov;}

                float FovX (void) {return _fovx;}
                float FovY (void) {return _fovy;}

                void SetOpenGLFrustum();
                void SetOpenGLLeftFrustum();
                void SetOpenGLViewMatrix();
                void OpenGLPushMatrices();
                void OpenGLPopMatrices();

};

#endif
