// Author: Hector Navarro (hnavarro@ciens.ucv.ve),
//         Juan Carlos De Abreu (jcabreur@gmail.com)

#include "aabb.h"
#include "aabb_inl.h"
#include "../Model/Group.h"
#include "../Includes/OpenGL.h"

BEGIN_TEG_NAMESPACE();

AABB::AABB() {}

AABB::AABB(const Vec3f& min_vertex, const Vec3f& max_vertex)
    : min_vertex_(min_vertex), max_vertex_(max_vertex) {}

AABB::AABB(const Group& group) {
  // Initialize and then calculate the bounding box of the vertex set.
  min_vertex_ = group.vertexList[0];
  max_vertex_ = group.vertexList[0];

  const int kNumberOfVertex = static_cast<int> (group.vertexList.size());
  for (int i = 0; i < kNumberOfVertex; ++i) {
    const Vec3f& vertex_i = group.vertexList[i];
    min_vertex_.x = min(min_vertex_.x, vertex_i.x);
    min_vertex_.y = min(min_vertex_.y, vertex_i.y);
    min_vertex_.z = min(min_vertex_.z, vertex_i.z);
    max_vertex_.x = max(max_vertex_.x, vertex_i.x);
    max_vertex_.y = max(max_vertex_.y, vertex_i.y);
    max_vertex_.z = max(max_vertex_.z, vertex_i.z);
  }
}

AABB::AABB(const Group& group, const std::vector<int>& faces) {
  // Initialize and then calculate the bounding box of the set of faces.
  min_vertex_ = group.vertexList[group.faceList[faces[0]].vertex[0]];
  max_vertex_ = group.vertexList[group.faceList[faces[0]].vertex[0]];

  const int kNumberOfFaces = static_cast<int> (faces.size());
  for (int i = 0; i < kNumberOfFaces; ++i) {
    for (int j = 0; j < 3; ++j) {
      const Vec3f& vertex_j = 
          group.vertexList[group.faceList[faces[i]].vertex[j]];
      min_vertex_.x = min(min_vertex_.x, vertex_j.x);
      min_vertex_.y = min(min_vertex_.y, vertex_j.y);
      min_vertex_.z = min(min_vertex_.z, vertex_j.z);
      max_vertex_.x = max(max_vertex_.x, vertex_j.x);
      max_vertex_.y = max(max_vertex_.y, vertex_j.y);
      max_vertex_.z = max(max_vertex_.z, vertex_j.z);
    }
  }
}

AABB::~AABB() {}

bool AABB::IsInside(const Group& group, const Face& face) const {
  // Check if the face is inside the bounding box.
  return IsInside(group.vertexList[face.vertex[0]]) && 
         IsInside(group.vertexList[face.vertex[1]]) &&
         IsInside(group.vertexList[face.vertex[2]]);
}

int AABB::CountVertex(const Group& group, const Face& face) {
  // Count how many vertex from the face are inside the bounding box.
  int number_of_vertex_inside = 0;
  for (int i = 0; i < 3; ++i)
    if (IsInside(group.vertexList[face.vertex[i]]))
      ++number_of_vertex_inside;
  return number_of_vertex_inside;
}

void AABB::Split(int axis, AABB& left, AABB& right, float t) const {
  // Calculate the cut place and update the two bounding boxes.
  const float cut = min_vertex_[axis] + 
                    (max_vertex_[axis] - min_vertex_[axis]) * t;

  left.max_vertex_ = max_vertex_;
  left.min_vertex_ = min_vertex_;
  right.max_vertex_ = max_vertex_;
  right.min_vertex_ = min_vertex_;

  left.max_vertex_.values[axis] = cut;
  right.min_vertex_.values[axis] = cut;
}

void AABB::Render(void) const {
  // Display the bounding box with the active API.
  glBegin(GL_LINE_LOOP);
    glVertex3f(min_vertex_.x, min_vertex_.y, min_vertex_.z);
    glVertex3f(min_vertex_.x, max_vertex_.y, min_vertex_.z);
    glVertex3f(max_vertex_.x, max_vertex_.y, min_vertex_.z);
    glVertex3f(max_vertex_.x, min_vertex_.y, min_vertex_.z);
  glEnd();

  glBegin(GL_LINE_LOOP);
    glVertex3f(min_vertex_.x, min_vertex_.y, max_vertex_.z);
    glVertex3f(min_vertex_.x, max_vertex_.y, max_vertex_.z);
    glVertex3f(max_vertex_.x, max_vertex_.y, max_vertex_.z);
    glVertex3f(max_vertex_.x, min_vertex_.y, max_vertex_.z);
  glEnd();

  glBegin(GL_LINES);
    glVertex3f(min_vertex_.x, min_vertex_.y, min_vertex_.z);
    glVertex3f(min_vertex_.x, min_vertex_.y, max_vertex_.z);
    glVertex3f(min_vertex_.x, max_vertex_.y, min_vertex_.z);
    glVertex3f(min_vertex_.x, max_vertex_.y, max_vertex_.z);
    glVertex3f(max_vertex_.x, min_vertex_.y, min_vertex_.z);
    glVertex3f(max_vertex_.x, min_vertex_.y, max_vertex_.z);
    glVertex3f(max_vertex_.x, max_vertex_.y, min_vertex_.z);
    glVertex3f(max_vertex_.x, max_vertex_.y, max_vertex_.z);
  glEnd();
}

void AABB::Load(FILE* f) {
  // Try to load the min and max vertex of the bounding box and check if the
  // load was done.
  int bytes_read;
  bytes_read = fread(min_vertex_.values, sizeof(float), 3, f);
  assert(bytes_read == 3);
  bytes_read = fread(max_vertex_.values, sizeof(float), 3, f);
  assert(bytes_read == 3);
}

void AABB::Save(FILE* f) const {
  // Save the min and max vertex of the bounding box.
  fwrite(min_vertex_.values, sizeof(float), 3, f);
  fwrite(max_vertex_.values, sizeof(float), 3, f);
}

END_TEG_NAMESPACE();
