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

#include "aabb_tree.h"
#include "aabb_inl.h"
#include "aabb_node_inl.h"
#include "../Model/Group.h"
#include "../base/LogManagement.h"

BEGIN_TEG_NAMESPACE();

AABBTree::AABBTree() {}

AABBTree::~AABBTree() {
  roots_.clear();
}

void AABBTree::Release() {
  // Free the resources used by the AABBTree.
  roots_.clear();
}

void AABBTree::Init(const std::vector<Group>& groups) {
  // Initialize and build the AABBTree with each group.
  const int kNumberOfGroups = static_cast<int> (groups.size());
  roots_.resize(kNumberOfGroups);
  for (int g = 0; g < kNumberOfGroups; ++g) {
    roots_[g] = new AABBNode(groups[g]);
    BuildTree(groups[g], roots_[g], 0);
  }
}

// Method to construct the AABBTree related to a group.
void AABBTree::BuildTree(const Group& group,
                         SmartPointer<AABBNode> root_,
                         int level) {
  if (root_) {
    const std::vector<int>& faces = root_->faces();
    const int kNumberOfFaces = root_->number_of_faces();
    Vec3f bounding_box_size = root_->bounding_box().GetSize();
    Vec3f bounding_box_center = root_->bounding_box().GetCenter();

	// Sort the axis by size.
	Vec3i axis(0, 1, 2);
    for (int i = 0; i < 3; ++i) {
      for (int j = i + 1; j < 3; ++j) {
        if (bounding_box_size.values[i] < bounding_box_size.values[j]) {
          std::swap(bounding_box_size.values[i], bounding_box_size.values[j]);
          std::swap(axis.values[i], axis.values[j]);
        }
      }
    }

    std::vector<int> left, right;
    AABB leftBB, rightBB;

    int i;
    int c1, c2, c3;
    const int kNumberOfAxis = 3;
    const int kNumberOfSplitTries = 5;
    const float kSplitTries[5] = {0.5f, 0.25f, 0.75f, 0.1f, 0.9f};
    for (i = 0; i < kNumberOfAxis; ++i) {
      for(int j = 0; j < kNumberOfSplitTries; ++j) {
				
        c1 = 0, c2 = 0, c3 = 0;
        left.clear(); right.clear();
        root_->bounding_box().Split(axis[i], leftBB, rightBB, kSplitTries[j]);

        //
        for (int f = 0; f < kNumberOfFaces; ++f) {
          const int kFacesInLeftNode = 
              leftBB.CountVertex(group, group.faceList[faces[f]]);

          switch(kFacesInLeftNode) {
            case 3: left.push_back(faces[f]); ++c1; break;	// Left.
            case 0: right.push_back(faces[f]); ++c2; break;	// Right.
            default: left.push_back(faces[f]); ++c1; break;	// Shared.
          }
        }
        if (c1 && c2) break;
      }
      if (c1 && c2) break;
    }

    if (i == 3) {
      // Any axis works...
      //PRINT_LOG(TYPE_WARNING, "Can't be splited...");
      return;
    }
		
    const int kNumberOfFacesInLeftNode = static_cast<int>(left.size());
	const int kNumberOfFacesInRightNode = static_cast<int>(right.size());
    if (kNumberOfFacesInLeftNode == 0 ||
        kNumberOfFacesInRightNode == 0 ||
        level >= kAABBTreeMaxLevel) {
      root_->set_left(NULL);
      root_->set_right(NULL);
      return;
    }
    else {
      root_->set_left(new AABBNode(group, left));
      root_->set_right(new AABBNode(group, right));
    }

    if (root_->left()) BuildTree(group, root_->left(), level + 1);
    if (root_->right()) BuildTree(group, root_->right(), level + 1);

	root_->ClearAllFaces();
  }
}

void AABBTree::Render() const {
  // Display all AABBNodes of an AABBTree.
  const int kNumberOfRoots = static_cast<int>(roots_.size());
  for (int i = 0; i < kNumberOfRoots; ++i)
    roots_[i]->Render();
}

void AABBTree::Render(int level) const {
  // Display the AABBNodes at one level of each group.
  const int kNumberOfRoots = static_cast<int>(roots_.size());
  for (int i = 0; i < kNumberOfRoots; ++i)
    roots_[i]->Render(level);
}

bool AABBTree::Load(const std::tstring& path) {
  // Try to open the file for read.
  FILE* f = fopen(path.c_str(), "rb");
  if (f == NULL) {
    PRINT_LOG(TYPE_WARNING, "Can't open the file %s for read!", path.c_str());
    return false;
  }

  // Load the head of the file.
  _TCHAR buffer[10];
  int number_of_bytes_read;
  memset(buffer, 0, sizeof(buffer));
  number_of_bytes_read = fread(buffer, sizeof(_TCHAR), 8, f);
  if (strcmp(buffer, _T("AABBTREE")) != 0) return false;

  // Load the number of groups and the data of each group.
  int number_of_groups;
  number_of_bytes_read = fread(&number_of_groups, sizeof(int), 1, f);
  roots_.resize(number_of_groups, NULL);
  for (int i = 0; i < number_of_groups; ++i) {
    roots_[i] = new AABBNode();
    roots_[i]->Load(f);
  }
  fclose(f);
  return true;
}

bool AABBTree::Save(const std::tstring& path) const {
  // Try to open the file for write.
  FILE* f = fopen(path.c_str(), "wb");
  if (f == NULL) {
    PRINT_LOG(TYPE_WARNING, "Can't open or create the file %s for write!", path.c_str());
    return false;
  }

  // Save the head of the file.
  fwrite(_T("AABBTREE"), sizeof(_TCHAR), 8, f);

  // Save the number of groups and the data of each group.
  const int kNumberOfGroups = static_cast<int> (roots_.size());
  fwrite(&kNumberOfGroups, sizeof(int), 1, f);
  for (int i = 0; i < kNumberOfGroups; ++i)
    roots_[i]->Save(f);
  fclose(f);
  return true;
}

int AABBTree::GetWorst() const {
  // Caculate the node with more faces in the AABBTree.
  int max_number_of_faces_in_a_node = 0;
  const int kNumberOfRoots = static_cast<int>(roots_.size());
  for (int i = 0; i < kNumberOfRoots; ++i)
    max_number_of_faces_in_a_node = max(max_number_of_faces_in_a_node,
                                        GetWorst(roots_[i]));
  return max_number_of_faces_in_a_node;
}

int AABBTree::GetTotal() const {
  // Return the total number of faces in the AABBTree.
  int number_of_faces = 0;
  const int kNumberOfRoots = static_cast<int>(roots_.size());
  for (int i = 0; i < kNumberOfRoots; ++i)
    number_of_faces += GetTotal(roots_[i]);
  return number_of_faces;
}

int AABBTree::GetAverage() const {
  return GetTotal() / CountLeaves();
}

int AABBTree::CountLeaves() const {
  // Count the leaves nodes in each group in the AABBTree.
  int number_of_leaves_node = 0;
  const int kNumberOfRoots = static_cast<int>(roots_.size());
  for (int i = 0; i < kNumberOfRoots; ++i)
    number_of_leaves_node += CountLeaves(roots_[i]);
  return number_of_leaves_node;
}

int AABBTree::GetWorst(const SmartPointer<AABBNode>& node) const {
  if (!node) return 0;
  if (node->IsLeaf()) return node->number_of_faces();
  return max(GetWorst(node->left()), GetWorst(node->right()));
}

int AABBTree::GetTotal(const SmartPointer<AABBNode>& node) const {
  if (!node) return 0;
  if (node->IsLeaf()) return node->number_of_faces();
  return GetTotal(node->left()) + GetTotal(node->right());
}

int AABBTree::CountLeaves(const SmartPointer<AABBNode>& node) const {
  if (!node) return 0;
  if (node->IsLeaf()) return 1;
  return CountLeaves(node->left()) + CountLeaves(node->right());
}

END_TEG_NAMESPACE();
