#include "frustrum.h"


//calculate near and far plane distance and dimension
void frustrum::set_perspective(float angle, float ratio, float nearD, float farD){
	// store the information
	this->ratio = ratio;
	this->angle = angle;
	this->nearD = nearD;
	this->farD = farD;

	// compute width and height of the near and far plane sections
	float tang = (float)tan(ANG2RAD * angle * 0.5) ;
	near_dim.y = nearD * tang;
	near_dim.x = near_dim.y * ratio; 
	far_dim.y = farD  * tang;
	far_dim.x = far_dim.y * ratio;

}

//calculate planes from camera info
void frustrum::set_camera(const glm::vec3 eye, const glm::vec3 center, const glm::vec3 up) {
	glm::vec3 X, Y, Z, dir, nc, fc;

	// compute the Z axis of camera
	// this axis points in the opposite direction from 
	// the looking direction
	Z = glm::normalize(eye - center);

	// X axis of camera with given "up" vector and Z axis
	X = glm::normalize(glm::cross(up, Z));

	// the real "up" vector is the cross product of Z and X
	Y = glm::cross(Z, X);

	// compute the centers of the near and far planes
	nc = eye - Z * nearD;
	fc = eye - Z * farD;

	//set near and far plane values
	plane_normal[NEARP] = -Z;
	plane_point[NEARP] = nc;
	
	plane_normal[FARP] = Z;
	plane_point[FARP] = fc;

	//set top, bottom, left, right values
	glm::vec3 aux, point, normal;

	//TODO: use half or full plane dimensions here?
	//TOP
	point = nc + Y * near_dim.y;
	aux = glm::normalize( point - eye);
	normal = glm::cross(aux, X);
	plane_normal[TOP] = normal;
	plane_point[TOP] = point;

	//BOTTOM
	point = nc - Y * near_dim.y;
	aux = glm::normalize( point - eye);
	normal = glm::cross(aux, X);
	plane_normal[BOTTOM] = -normal;
	plane_point[BOTTOM] = point;

	//LEFT
	point = nc - X * near_dim.x;
	aux = glm::normalize( point - eye);
	normal = glm::cross(aux, Y);
	plane_normal[LEFT] = normal;
	plane_point[LEFT] = point;

	//RIGHT
	point = nc + X * near_dim.x;
	aux = glm::normalize( point - eye);
	normal = glm::cross(aux, Y);
	plane_normal[RIGHT] = -normal;
	plane_point[RIGHT] = point;


}

//culling functions
bool frustrum::point_in_frustrum(const glm::vec3& point) {
	current_point = point;
	int i = NEARP;
	for (i=0; i<6; i++) {
		if (plane_point_distance(plane_normal[i], plane_point[i], current_point) < 0) {
			return false;
		}
	}

	return true;
}

//TODO: is this correct?
bool frustrum::sphere_in_frustrum(const glm::vec3& center, const float radius) const {
	int i = NEARP;
	for (int i=0; i<6; i++) {
		if ((plane_point_distance(plane_normal[i], plane_point[i], center) + radius) < 0) {
			return false;
		}
	}

	return true;
}


GLfloat frustrum::plane_point_distance(const glm::vec3& plane_norm, const glm::vec3& plane_pt, const glm::vec3& point) const {
	//we can assume that plane_normal is normalized, so we dont need to divide by its length
	GLfloat result = (glm::dot(plane_norm, point) - glm::dot(plane_norm, plane_pt));
	return result;
}


void frustrum::debug() const {
	std::cout << "frustrum debug:\n";
	printf("nearp normal: %2.3f, %2.3f, %2.3f\n", plane_normal[NEARP].x, plane_normal[NEARP].y, plane_normal[NEARP].z);
	printf("nearp  point: %2.3f, %2.3f, %2.3f\n", plane_point[NEARP].x, plane_point[NEARP].y, plane_point[NEARP].z);
}