#include "spline_winding_number.h"
#include "cubic_winding_number.h"
#include "../parallel_for.h"
#include "../eytzinger_aabb.h"
#include "../eytzinger_aabb.h"
#include "../eytzinger_aabb_winding_number_tree.h"
#include "../eytzinger_aabb_winding_number.h"
#include "../placeholders.h"

#include <cassert>

template <
  typename DerivedP, 
  typename DerivedC, 
  typename DerivedB,
  typename Derivedleaf,
  typename DerivedQ,
  typename DerivedW>
IGL_INLINE void igl::predicates::spline_winding_number(
  const Eigen::MatrixBase<DerivedP>& P,
  const Eigen::MatrixBase<DerivedC>& C,
  const Eigen::MatrixBase<DerivedB>& B1,
  const Eigen::MatrixBase<DerivedB>& B2,
  const Eigen::MatrixBase<Derivedleaf>& leaf,
  const Eigen::MatrixBase<DerivedQ> & Q,
  Eigen::PlainObjectBase<DerivedW>& W)
{
  using Scalar = typename DerivedP::Scalar;
  Eigen::Matrix<typename DerivedC::Scalar,DerivedC::RowsAtCompileTime,2,Eigen::RowMajor> E(C.rows(),2);
  E << C.col(0), C.col(3);
  Eigen::VectorXi tI,tC;
  eytzinger_aabb_winding_number_tree( E, leaf, tI, tC);

  W.resize(Q.rows(),1);
  //for(int i = 0;i<Q.rows();i++)
  igl::parallel_for(Q.rows(),[&](const int i)
  {
    const auto qi = Q.row(i).eval();
    const auto primitive = [&](const int c)
    {
      typedef Eigen::Matrix<Scalar,4,DerivedP::ColsAtCompileTime,Eigen::RowMajor> Mat4;
      // point_spline_squared_distance is caching this slice.
      const Mat4 Cc = P(C.row(c),igl::placeholders::all);
      return igl::predicates::cubic_winding_number(Cc, qi);
    };
    igl::eytzinger_aabb_winding_number(qi,P,primitive,B1,B2,leaf,tI,tC,W(i));
  },1000);
}


#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::predicates::spline_winding_number<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<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -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<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::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1>>&);
template void igl::predicates::spline_winding_number<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 2, 1, -1, 2>, 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::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, 2, 1, -1, 2>> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 2, 1, -1, 2>> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1>> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1>> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1>>&);
#endif
