// Camera.cpp: implementation of the Camera class.
//
//////////////////////////////////////////////////////////////////////

#include "Camera.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Camera::Camera()
{

}

Camera::~Camera()
{

}
void Camera::roll(float angle)
{
	float cs = cos(3.14159265 / 180 * angle);
	float sn = sin(3.14159265 / 180 * angle);

	Vector3 t = u; // remember old u
	u.set(cs*t.x - sn*v.x, cs*t.y - sn*v.y, cs*t.z - sn*v.z);
	v.set(sn*t.x + cs*v.x, sn*t.y + cs*v.y, sn*t.z + cs*v.z);
	setModelViewMatrix();
}

void Camera::pitch(float angle)
{
	float cs = cos(3.14159265 / 180 * angle);
	float sn = sin(3.14159265 / 180 * angle);

	Vector3 t = v; // remember old u
	v.set(cs*t.x - sn*n.x, cs*t.y - sn*n.y, cs*t.z - sn*n.z);
	n.set(sn*t.x + cs*n.x, sn*t.y + cs*n.y, sn*t.z + cs*n.z);
	setModelViewMatrix();
}

void Camera::yaw(float angle)
{
	float cs = cos(3.14159265 / 180 * angle);
	float sn = sin(3.14159265 / 180 * angle);

	Vector3 t = u; // remember old u
	u.set(cs*t.x - sn*n.x, cs*t.y - sn*n.y, cs*t.z - sn*n.z);
	n.set(sn*t.x + cs*n.x, sn*t.y + cs*n.y, sn*t.z + cs*n.z);
	setModelViewMatrix();
}

void Camera::slide(float delU, float delV, float delN)
{
	eye.x += delU * u.x + delV * v.x + delN * n.x;
	eye.y += delU * u.y + delV * v.y + delN * n.y;
	eye.z += delU * u.z + delV * v.z + delN * n.z;
	setModelViewMatrix();
}

void Camera::set(Point3 Eye, Point3 look, Vector3 up)
{
	eye.set(Eye); // store the given eye position

	n.set(eye.x - look.x, eye.y - look.y, eye.z - look.z);	// make n
	u.set(up.cross(n)); // make u = up X n

	n.normalize();
	u.normalize();

	v.set(n.cross(u));	// make v = n X u
	setModelViewMatrix();
}

void Camera::setModelViewMatrix(void)
{
	float m[16];
	Vector3 eVec(eye.x, eye.y, eye.z); // vector of eye
	m[0] = u.x;		m[4] = u.y;		m[8] = u.z;		m[12] = -eVec.dot(u);
	m[1] = v.x;		m[5] = v.y;		m[9] = v.z;		m[13] = -eVec.dot(v);
	m[2] = n.x;		m[6] = n.y;		m[10] = n.z;	m[14] = -eVec.dot(n);
	m[3] = 0;		m[7] = 0;		m[11] = 0;		m[15] = 1.0;
	glMatrixMode(GL_MODELVIEW);
	glLoadMatrixf(m); // load OpenGL's modelview matrix
}

void Camera::setShape(float vAng, float asp, float nearD, float farD)
{
	viewAngle = vAng*3.14159/180;
	aspect = asp;
	nearDist = nearD;
	farDist = farD;

	wHeight = nearDist * tan(viewAngle/2.0);
	wWidth = wHeight * aspect;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(vAng, aspect, nearDist, farDist);

}

void Camera::raytrace(Scene& scn, int blockSize){

	int nCols = 640;
	int nRows = 480;

	Ray viewRay;
	Color3 color;
	viewRay.setStart(eye);

	//set up OpenGL for simple 2D drawing
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, nCols, 0, nRows);
	glDisable(GL_LIGHTING);	// so glColor3f() works properly

	float Wconst = 2*wWidth/(float)nCols;
	float Hconst = 2*wHeight/(float)nRows;

	//begin ray tracing
	for(int row = 0; row < nRows; row += blockSize){
       	        printf( "Doing row %3d of %3d...", row, nRows );
		for(int col = 0; col < nCols; col += blockSize){
			float x = -wWidth + col*Wconst;
			float y = -wHeight + row*Hconst;

			viewRay.setDir(	-nearDist * n.x+x * u.x+y * v.x,
							-nearDist * n.y+x * u.y+y * v.y,
							-nearDist * n.z+x * u.z+y * v.z);

			viewRay.dir.normalize();
			viewRay.curLightSpeed = scn.envLightSpeed;
			color.set(scn.shade(viewRay)); // find color

			glColor3f(color.red, color.green, color.blue);
			glRecti(col,row, col+blockSize, row+blockSize);
//			glFlush();
		}
  	        printf( "Done!\n" );
	}//end for each row
}
