/*
 * Software License Agreement (BSD License)
 *
 *  Point Cloud Library (PCL) - www.pointclouds.org
 *  Copyright (c) 2010-2011, Willow Garage, Inc.
 *
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id$
 */

#ifndef PCL_OCTREE_BASE_HPP
#define PCL_OCTREE_BASE_HPP

#include <vector>

namespace pcl {
namespace octree {
//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
OctreeBase<LeafContainerT, BranchContainerT>::OctreeBase()
: root_node_(new BranchNode())
{}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
OctreeBase<LeafContainerT, BranchContainerT>::~OctreeBase()
{
  // deallocate tree structure
  deleteTree();
  delete (root_node_);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::setMaxVoxelIndex(
    uindex_t max_voxel_index_arg)
{
  uindex_t tree_depth;

  if (max_voxel_index_arg <= 0) {
    PCL_ERROR("[pcl::octree::OctreeBase::setMaxVoxelIndex] Max voxel index (%lu) must "
              "be > 0!\n",
              max_voxel_index_arg);
    return;
  }

  // tree depth == bitlength of maxVoxels
  tree_depth =
      std::min(static_cast<uindex_t>(OctreeKey::maxDepth),
               static_cast<uindex_t>(std::ceil(std::log2(max_voxel_index_arg))));

  setTreeDepth(tree_depth);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::setTreeDepth(uindex_t depth_arg)
{
  if (depth_arg <= 0) {
    PCL_ERROR("[pcl::octree::OctreeBase::setTreeDepth] Tree depth (%lu) must be > 0!\n",
              depth_arg);
    return;
  }
  if (depth_arg > OctreeKey::maxDepth) {
    PCL_ERROR("[pcl::octree::OctreeBase::setTreeDepth] Tree depth (%lu) must be <= max "
              "depth(%lu)!\n",
              depth_arg,
              OctreeKey::maxDepth);
    return;
  }

  // set octree depth
  octree_depth_ = depth_arg;

  // define depth_mask_ by setting a single bit to 1 at bit position == tree depth
  depth_mask_ = (1 << (depth_arg - 1));

  // define max_key_
  max_key_.x = max_key_.y = max_key_.z = (1 << depth_arg) - 1;
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
LeafContainerT*
OctreeBase<LeafContainerT, BranchContainerT>::findLeaf(uindex_t idx_x_arg,
                                                       uindex_t idx_y_arg,
                                                       uindex_t idx_z_arg) const
{
  // generate key
  OctreeKey key(idx_x_arg, idx_y_arg, idx_z_arg);

  // find the leaf node addressed by key
  return (findLeaf(key));
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
LeafContainerT*
OctreeBase<LeafContainerT, BranchContainerT>::createLeaf(uindex_t idx_x_arg,
                                                         uindex_t idx_y_arg,
                                                         uindex_t idx_z_arg)
{
  // generate key
  OctreeKey key(idx_x_arg, idx_y_arg, idx_z_arg);

  // create a leaf node addressed by key
  return (createLeaf(key));
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
bool
OctreeBase<LeafContainerT, BranchContainerT>::existLeaf(uindex_t idx_x_arg,
                                                        uindex_t idx_y_arg,
                                                        uindex_t idx_z_arg) const
{
  // generate key
  OctreeKey key(idx_x_arg, idx_y_arg, idx_z_arg);

  // check if key exist in octree
  return (existLeaf(key));
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::removeLeaf(uindex_t idx_x_arg,
                                                         uindex_t idx_y_arg,
                                                         uindex_t idx_z_arg)
{
  // generate key
  OctreeKey key(idx_x_arg, idx_y_arg, idx_z_arg);

  // delete the leaf node addressed by key
  deleteLeafRecursive(key, depth_mask_, root_node_);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::deleteTree()
{

  if (root_node_) {
    // reset octree
    deleteBranch(*root_node_);
    leaf_count_ = 0;
    branch_count_ = 1;
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::serializeTree(
    std::vector<char>& binary_tree_out_arg) const
{

  OctreeKey new_key;

  // clear binary vector
  binary_tree_out_arg.clear();
  binary_tree_out_arg.reserve(this->branch_count_);

  serializeTreeRecursive(root_node_, new_key, &binary_tree_out_arg, nullptr);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::serializeTree(
    std::vector<char>& binary_tree_out_arg,
    std::vector<LeafContainerT*>& leaf_container_vector_arg) const
{

  OctreeKey new_key;

  // clear output vectors
  binary_tree_out_arg.clear();
  leaf_container_vector_arg.clear();

  binary_tree_out_arg.reserve(this->branch_count_);
  leaf_container_vector_arg.reserve(this->leaf_count_);

  serializeTreeRecursive(
      root_node_, new_key, &binary_tree_out_arg, &leaf_container_vector_arg);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::serializeLeafs(
    std::vector<LeafContainerT*>& leaf_container_vector_arg)
{
  OctreeKey new_key;

  // clear output vector
  leaf_container_vector_arg.clear();

  leaf_container_vector_arg.reserve(this->leaf_count_);

  serializeTreeRecursive(root_node_, new_key, nullptr, &leaf_container_vector_arg);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::deserializeTree(
    std::vector<char>& binary_tree_out_arg)
{
  OctreeKey new_key;

  // free existing tree before tree rebuild
  deleteTree();

  // iterator for binary tree structure vector
  std::vector<char>::const_iterator binary_tree_out_it = binary_tree_out_arg.begin();
  std::vector<char>::const_iterator binary_tree_out_it_end = binary_tree_out_arg.end();

  deserializeTreeRecursive(root_node_,
                           depth_mask_,
                           new_key,
                           binary_tree_out_it,
                           binary_tree_out_it_end,
                           nullptr,
                           nullptr);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::deserializeTree(
    std::vector<char>& binary_tree_in_arg,
    std::vector<LeafContainerT*>& leaf_container_vector_arg)
{
  OctreeKey new_key;

  // set data iterator to first element
  typename std::vector<LeafContainerT*>::const_iterator leaf_vector_it =
      leaf_container_vector_arg.begin();

  // set data iterator to last element
  typename std::vector<LeafContainerT*>::const_iterator leaf_vector_it_end =
      leaf_container_vector_arg.end();

  // free existing tree before tree rebuild
  deleteTree();

  // iterator for binary tree structure vector
  std::vector<char>::const_iterator binary_tree_input_it = binary_tree_in_arg.begin();
  std::vector<char>::const_iterator binary_tree_input_it_end = binary_tree_in_arg.end();

  deserializeTreeRecursive(root_node_,
                           depth_mask_,
                           new_key,
                           binary_tree_input_it,
                           binary_tree_input_it_end,
                           &leaf_vector_it,
                           &leaf_vector_it_end);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
uindex_t
OctreeBase<LeafContainerT, BranchContainerT>::createLeafRecursive(
    const OctreeKey& key_arg,
    uindex_t depth_mask_arg,
    BranchNode* branch_arg,
    LeafNode*& return_leaf_arg,
    BranchNode*& parent_of_leaf_arg)
{
  // index to branch child
  unsigned char child_idx;

  // find branch child from key
  child_idx = key_arg.getChildIdxWithDepthMask(depth_mask_arg);

  OctreeNode* child_node = (*branch_arg)[child_idx];

  if (!child_node) {
    if ((!dynamic_depth_enabled_) && (depth_mask_arg > 1)) {
      // if required branch does not exist -> create it
      BranchNode* childBranch = createBranchChild(*branch_arg, child_idx);

      branch_count_++;

      // recursively proceed with indexed child branch
      return createLeafRecursive(key_arg,
                                 depth_mask_arg >> 1,
                                 childBranch,
                                 return_leaf_arg,
                                 parent_of_leaf_arg);
    }
    // if leaf node at child_idx does not exist
    LeafNode* leaf_node = createLeafChild(*branch_arg, child_idx);
    return_leaf_arg = leaf_node;
    parent_of_leaf_arg = branch_arg;
    this->leaf_count_++;
  }
  else {
    // Node exists already
    switch (child_node->getNodeType()) {
    case BRANCH_NODE:
      // recursively proceed with indexed child branch
      return createLeafRecursive(key_arg,
                                 depth_mask_arg >> 1,
                                 static_cast<BranchNode*>(child_node),
                                 return_leaf_arg,
                                 parent_of_leaf_arg);
      break;

    case LEAF_NODE:
      return_leaf_arg = static_cast<LeafNode*>(child_node);
      parent_of_leaf_arg = branch_arg;
      break;
    }
  }

  return (depth_mask_arg >> 1);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::findLeafRecursive(
    const OctreeKey& key_arg,
    uindex_t depth_mask_arg,
    BranchNode* branch_arg,
    LeafContainerT*& result_arg) const
{
  // index to branch child
  unsigned char child_idx;

  // find branch child from key
  child_idx = key_arg.getChildIdxWithDepthMask(depth_mask_arg);

  OctreeNode* child_node = (*branch_arg)[child_idx];

  if (child_node) {
    switch (child_node->getNodeType()) {
    case BRANCH_NODE:
      // we have not reached maximum tree depth
      BranchNode* child_branch;
      child_branch = static_cast<BranchNode*>(child_node);

      findLeafRecursive(key_arg, depth_mask_arg >> 1, child_branch, result_arg);
      break;

    case LEAF_NODE:
      // return existing leaf node
      LeafNode* child_leaf;
      child_leaf = static_cast<LeafNode*>(child_node);

      result_arg = child_leaf->getContainerPtr();
      break;
    }
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
bool
OctreeBase<LeafContainerT, BranchContainerT>::deleteLeafRecursive(
    const OctreeKey& key_arg, uindex_t depth_mask_arg, BranchNode* branch_arg)
{
  // index to branch child
  unsigned char child_idx;
  // indicates if branch has children, if so, it can't be removed
  bool b_has_children;

  // find branch child from key
  child_idx = key_arg.getChildIdxWithDepthMask(depth_mask_arg);

  OctreeNode* child_node = (*branch_arg)[child_idx];

  if (child_node) {
    switch (child_node->getNodeType()) {

    case BRANCH_NODE:
      BranchNode* child_branch;
      child_branch = static_cast<BranchNode*>(child_node);

      // recursively explore the indexed child branch
      b_has_children = deleteLeafRecursive(key_arg, depth_mask_arg >> 1, child_branch);

      if (!b_has_children) {
        // child branch does not own any sub-child nodes anymore -> delete child branch
        deleteBranchChild(*branch_arg, child_idx);
        branch_count_--;
      }
      break;

    case LEAF_NODE:
      // return existing leaf node

      // our child is a leaf node -> delete it
      deleteBranchChild(*branch_arg, child_idx);
      this->leaf_count_--;
      break;
    }
  }

  // check if current branch still owns children
  b_has_children = false;
  for (child_idx = 0; (!b_has_children) && (child_idx < 8); child_idx++) {
    b_has_children = branch_arg->hasChild(child_idx);
  }
  // return false if current branch can be deleted
  return (b_has_children);
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::serializeTreeRecursive(
    const BranchNode* branch_arg,
    OctreeKey& key_arg,
    std::vector<char>* binary_tree_out_arg,
    typename std::vector<LeafContainerT*>* leaf_container_vector_arg) const
{
  char node_bit_pattern;

  // branch occupancy bit pattern
  node_bit_pattern = getBranchBitPattern(*branch_arg);

  // write bit pattern to output vector
  if (binary_tree_out_arg)
    binary_tree_out_arg->push_back(node_bit_pattern);

  // iterate over all children
  for (unsigned char child_idx = 0; child_idx < 8; child_idx++) {

    // if child exist
    if (branch_arg->hasChild(child_idx)) {
      // add current branch voxel to key
      key_arg.pushBranch(child_idx);

      OctreeNode* childNode = branch_arg->getChildPtr(child_idx);

      switch (childNode->getNodeType()) {
      case BRANCH_NODE: {
        // recursively proceed with indexed child branch
        serializeTreeRecursive(static_cast<const BranchNode*>(childNode),
                               key_arg,
                               binary_tree_out_arg,
                               leaf_container_vector_arg);
        break;
      }
      case LEAF_NODE: {
        auto* child_leaf = static_cast<LeafNode*>(childNode);

        if (leaf_container_vector_arg)
          leaf_container_vector_arg->push_back(child_leaf->getContainerPtr());

        // we reached a leaf node -> execute serialization callback
        serializeTreeCallback(**child_leaf, key_arg);
        break;
      }
      default:
        break;
      }

      // pop current branch voxel from key
      key_arg.popBranch();
    }
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename LeafContainerT, typename BranchContainerT>
void
OctreeBase<LeafContainerT, BranchContainerT>::deserializeTreeRecursive(
    BranchNode* branch_arg,
    uindex_t depth_mask_arg,
    OctreeKey& key_arg,
    typename std::vector<char>::const_iterator& binary_tree_input_it_arg,
    typename std::vector<char>::const_iterator& binary_tree_input_it_end_arg,
    typename std::vector<LeafContainerT*>::const_iterator* leaf_container_vector_it_arg,
    typename std::vector<LeafContainerT*>::const_iterator*
        leaf_container_vector_it_end_arg)
{

  if (binary_tree_input_it_arg != binary_tree_input_it_end_arg) {
    // read branch occupancy bit pattern from input vector
    char node_bits = (*binary_tree_input_it_arg);
    binary_tree_input_it_arg++;

    // iterate over all children
    for (unsigned char child_idx = 0; child_idx < 8; child_idx++) {
      // if occupancy bit for child_idx is set..
      if (node_bits & (1 << child_idx)) {
        // add current branch voxel to key
        key_arg.pushBranch(child_idx);

        if (depth_mask_arg > 1) {
          // we have not reached maximum tree depth
          BranchNode* newBranch = createBranchChild(*branch_arg, child_idx);

          branch_count_++;

          // recursively proceed with new child branch
          deserializeTreeRecursive(newBranch,
                                   depth_mask_arg >> 1,
                                   key_arg,
                                   binary_tree_input_it_arg,
                                   binary_tree_input_it_end_arg,
                                   leaf_container_vector_it_arg,
                                   leaf_container_vector_it_end_arg);
        }
        else {
          // we reached leaf node level

          LeafNode* child_leaf = createLeafChild(*branch_arg, child_idx);

          if (leaf_container_vector_it_arg &&
              (*leaf_container_vector_it_arg != *leaf_container_vector_it_end_arg)) {
            LeafContainerT& container = **child_leaf;
            LeafContainerT* src_container_ptr = **leaf_container_vector_it_arg;
            container = *src_container_ptr;
            ++*leaf_container_vector_it_arg;
          }

          leaf_count_++;

          // execute deserialization callback
          deserializeTreeCallback(**child_leaf, key_arg);
        }

        // pop current branch voxel from key
        key_arg.popBranch();
      }
    }
  }
}

} // namespace octree
} // namespace pcl

#define PCL_INSTANTIATE_OctreeBase(T)                                                  \
  template class PCL_EXPORTS pcl::octree::OctreeBase<T>;

#endif
