/* This file is part of the Neper software package. */
/* Copyright (C) 2003-2014, Romain Quey. */
/* See the COPYING file in the top-level directory. */

#include"net_polycomp_.h"
#include<ANN/ANN.h>

/* PolyhedronCalculation searches out the polyhedron associated
* to the considered seed.
*/
void
PolyhedronCalculation (struct POLY Domain, struct SEEDSET SeedSet,
		       ANNkd_tree* kdTree, int PolyId, struct POLY
		       *pPoly,
		       int* pneighqty)
{
  int i;
  int *NeiId = ut_alloc_1d_int (SeedSet.N + 1);
  struct POLYMOD Polymod;
  double* dist = ut_alloc_1d (SeedSet.N + 1);
  int NeiQty;
  double wmax = 0;

  // in input, *pneighqty is taken as the average of the number of
  // neighbours used in previous computations. If 0, a default value of
  // 100 is taken instead. In output, the number of neighbours used is
  // returned.

  if (*pneighqty <= 0)
    NeiQty = 100;
  else
    NeiQty = 2 * (*pneighqty);

  wmax = ut_array_1d_max (SeedSet.SeedWeight + 1, SeedSet.N);

  neut_polymod_set_zero (&Polymod);

  neut_poly_polymod (Domain, &Polymod);

  // Standard Voronoi tessellation
  if (!SeedSet.LamEq)
  {
    kdTree->annkSearch (SeedSet.SeedCoo[PolyId],
	                ut_num_min_int (SeedSet.N, NeiQty),
			NeiId + 1, dist + 1, 0);

    ut_array_1d_int_addval (NeiId + 1, SeedSet.N, 1, NeiId + 1);

    for (i = 2; i <= SeedSet.N; i++)
    {
      // The number of recorded neighbours is exceeded.  Recomputing a
      // larger number of neighbours.
      if (i > NeiQty)
      {
	NeiQty *= 2;
	kdTree->annkSearch (SeedSet.SeedCoo[PolyId],
			    ut_num_min_int (SeedSet.N, NeiQty),
			    NeiId + 1, dist + 1, 0);
	ut_array_1d_int_addval (NeiId + 1, SeedSet.N, 1, NeiId + 1);
      }

      // CurrentPolyTest checks if the seed is close enough to be able to
      // modify Polymod
      if (CurrentPolyTest (Polymod,
			   SeedSet.SeedCoo[PolyId],
			   SeedSet.SeedWeight[PolyId],
			   SeedSet.SeedCoo[NeiId[i]],
			   SeedSet.SeedWeight[NeiId[i]]) == 1)
      {
	// NewPolyhedron can modify Polymod using the new seed
	NewPolyhedron (SeedSet, PolyId, NeiId[i], &Polymod);
      }
      
      else
      {
	// Testing is given wmax, Polymod still can be modified
	if (CurrentPolyTest (Polymod,
			     SeedSet.SeedCoo[PolyId],
			     SeedSet.SeedWeight[PolyId],
			     SeedSet.SeedCoo[NeiId[i]],
			     wmax) == 0)
	  break;
      }
    }

    (*pneighqty) = i - 1;

    net_polymod_poly (Polymod, pPoly);
  }

  // Lamellae tessellation
  else
  {
    // Initializing cut plane
    double *eq = ut_alloc_1d (4);

    ut_array_1d_memcpy (eq, 4, SeedSet.LamEq);

    if (PolyId > 1)
    {
      eq[0] = SeedSet.LamEq[0] + (PolyId - 2) * SeedSet.LamWidth;
      NewPolyhedron_plane (PolyId - 1, 1, eq, &Polymod);
    }

    // up plane
    if (PolyId < SeedSet.N)
    {
      eq[0] = SeedSet.LamEq[0] + (PolyId - 1) * SeedSet.LamWidth;
      NewPolyhedron_plane (PolyId + 1, -1, eq, &Polymod);
    }

    net_polymod_poly (Polymod, pPoly);
    net_poly_centroid (*pPoly, SeedSet.SeedCoo[PolyId]);

    ut_free_1d (eq);
  }

  neut_polymod_free (&Polymod);

  ut_free_1d_int (NeiId);
  ut_free_1d (dist);

  return;
}
