// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "tetrahedralize.h"
#include "mesh_to_tetgenio.h"
#include "tetgenio_to_tetmesh.h"

// IGL includes
#include "../../matrix_to_list.h"
#include "../../list_to_matrix.h"
#include "../../boundary_facets.h"

// STL includes
#include <cassert>
#include <iostream>

template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedH,
  typename DerivedVM,
  typename DerivedFM,
  typename DerivedR,
  typename DerivedTV,
  typename DerivedTT,
  typename DerivedTF,
  typename DerivedTM,
  typename DerivedTR,
  typename DerivedTN,
  typename DerivedPT,
  typename DerivedFT>
IGL_INLINE int igl::copyleft::tetgen::tetrahedralize(
  const Eigen::MatrixBase<DerivedV>& V,
  const Eigen::MatrixBase<DerivedF>& F,
  const Eigen::MatrixBase<DerivedH>& H,
  const Eigen::MatrixBase<DerivedVM>& VM,
  const Eigen::MatrixBase<DerivedFM>& FM,
  const Eigen::MatrixBase<DerivedR>& R,
  const std::string switches,
  Eigen::PlainObjectBase<DerivedTV>& TV,
  Eigen::PlainObjectBase<DerivedTT>& TT,
  Eigen::PlainObjectBase<DerivedTF>& TF,
  Eigen::PlainObjectBase<DerivedTM>& TM,
  Eigen::PlainObjectBase<DerivedTR>& TR, 
  Eigen::PlainObjectBase<DerivedTN>& TN, 
  Eigen::PlainObjectBase<DerivedPT>& PT, 
  Eigen::PlainObjectBase<DerivedFT>& FT, 
  int & num_regions)
{
  // REAL is what tetgen is using as floating point precision
  // Prepare input
  tetgenio in,out;
  mesh_to_tetgenio(V,F,H,VM,FM,R,in);
  try
  {
    char * cswitches = new char[switches.size() + 1];
    std::strcpy(cswitches,switches.c_str());
    ::tetrahedralize(cswitches,&in, &out);
    delete[] cswitches;
  }catch(int e)
  {
    std::cerr<<"^"<<__FUNCTION__<<": TETGEN CRASHED... KABOOOM!!!"<<std::endl;
    return 1;
  }
  if(out.numberoftetrahedra == 0)
  {
    std::cerr<<"^"<<__FUNCTION__<<": Tetgen failed to create tets"<<std::endl;
    return 2;
  }

  // Prepare output
  bool ret = tetgenio_to_tetmesh(out,TV,TT,TF,TM,TR,TN,PT,FT,num_regions);
  if(!ret)
  {
    return -1;
  }

  if(TF.size() == 0)
  {
    boundary_facets(TT,TF);
  }

  return 0;
}

template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedTV,
  typename DerivedTT,
  typename DerivedTF>
IGL_INLINE int igl::copyleft::tetgen::tetrahedralize(
  const Eigen::MatrixBase<DerivedV>& V,
  const Eigen::MatrixBase<DerivedF>& F,
  const std::string switches,
  Eigen::PlainObjectBase<DerivedTV>& TV,
  Eigen::PlainObjectBase<DerivedTT>& TT,
  Eigen::PlainObjectBase<DerivedTF>& TF)
{
  Eigen::VectorXi VM,FM;
  Eigen::MatrixXd H,R;
  Eigen::VectorXi TM,TR,PT;
  Eigen::MatrixXi FT,TN;
  int numRegions;
  return tetrahedralize(V,F,H,VM,FM,R,switches,TV,TT,TF,TM,TR,TN,PT,FT,numRegions);
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template int igl::copyleft::tetgen::tetrahedralize<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>>(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char>>, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&);
// generated by autoexplicit.sh
template int igl::copyleft::tetgen::tetrahedralize<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>>(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char>>, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>>&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1>>&, int&);
#endif
