/* Copyright 2019 Greg Tucker
//
// This file is part of brille.
//
// brille is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// brille is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with brille. If not, see <https://www.gnu.org/licenses/>.            */

template<class T, class S>
void Nest<T,S>::construct(const Polyhedron& poly, const size_t max_branchings, const double max_volume){
  SimpleTet root_tet(poly);
  double exponent;
  exponent = std::log(root_tet.maximum_volume()/max_volume)/std::log(static_cast<double>(max_branchings));
  // we want more than one tetrahedra at the root node
  if (root_tet.number_of_tetrahedra()<2 && max_branchings > 0){
    root_tet = SimpleTet(poly, max_volume*std::pow(static_cast<double>(max_branchings)-1, exponent));
    exponent = std::log(root_tet.maximum_volume()/max_volume)/std::log(static_cast<double>(max_branchings));
  }
  verbose_update("Largest root tetrahedron ",root_tet.maximum_volume()," and maximum leaf tetrahedron ",max_volume," give exponent ",exponent);
  // copy-over the vertices of the root node
  vertices_ = root_tet.get_vertices();
  // we need to make a guess about how many vertices we'll need:
  size_t number_density = static_cast<size_t>(poly.get_volume()/max_volume); // shockingly, this is about right for total number of vertices!
  size_t nVerts = vertices_.size();
  vertices_.resize(number_density + nVerts);
  // copy over the per-tetrahedron vertex indices to the root's branches
  const ArrayVector<size_t>& tvi{root_tet.get_vertices_per_tetrahedron()};
  for (size_t i=0; i<tvi.size(); ++i){
    std::array<size_t,4> single;
    for (size_t j=0; j<4u; ++j) single[j] = tvi.getvalue(i,j); // no need to adjust indices at this stage
    // create a branch for this tetrahedron
    NestNode branch(single, root_tet.circumsphere_info(i), root_tet.volume(i));
    // and subdivide it if necessary
    if (max_branchings > 0 && branch.volume() > max_volume)
      this->subdivide(branch, 1u, max_branchings, max_volume, exponent, nVerts);
    // storing the resulting branch/leaf at this root
    root_.branches().push_back(branch);
  }
  // ensure that we only keep actual vertices:
  if (vertices_.size() > nVerts) vertices_.resize(nVerts);
}

template<class T,class S>
void Nest<T,S>::subdivide(
  NestNode& node, const size_t nBr, const size_t maxBr,
  const double max_volume, const double exp, size_t& nVerts
){
  if (!node.is_leaf()) return; // return node; // we can only branch un-branched nodes
  Polyhedron poly(vertices_.extract(node.boundary().vertices()));
  double mult = (maxBr > nBr) ? std::pow(static_cast<double>(maxBr-nBr),exp) : 1.0;
  SimpleTet node_tet(poly, max_volume*mult);
  // add any new vertices to the object's array, keep a mapping for all:
  std::vector<size_t> map;
  const ArrayVector<double>& ntv{node_tet.get_vertices()};
  for (size_t i=0; i<ntv.size(); ++i){
    const ArrayVector<double> vi{ntv.extract(i)};
    // If we're clever we can possibly simplify this
    std::vector<size_t> idx = find(norm(vertices_.first(nVerts)-vi).is_approx(Comp::eq,0.));
    if (idx.size()>1)
      throw std::runtime_error("Multiple matching vertices?!");
    if (idx.size()==1){
      map.push_back(idx[0]);
    } else {
      // new vertex. make sure we have room to store it:
      if (vertices_.size() < nVerts+1) vertices_.resize(nVerts*2);
      vertices_.set(nVerts, vi);
      map.push_back(nVerts++);
    }
  }
  // copy over the per-tetrahedron vertex indices, applying the mapping as we go
  const ArrayVector<size_t>& tvi{node_tet.get_vertices_per_tetrahedron()};
  for (size_t i=0; i<tvi.size(); ++i){
    std::array<size_t,4> single;
    for (size_t j=0; j<4u; ++j) single[j] = map[tvi.getvalue(i,j)];
    // create a branch for this tetrahedron
    NestNode branch(single, node_tet.circumsphere_info(i), node_tet.volume(i));
    // and subdivide it if necessary
    if (nBr < maxBr && branch.volume() > max_volume)
      this->subdivide(branch, nBr+1u, maxBr, max_volume, exp, nVerts);
    // storing the resulting branch/leaf at this node
    node.branches().push_back(branch);
  }
}
