/* 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_utils_.h"

int
net_poly_clip (struct POLY *pPoly, double *eq)
{
  int i, status;
  struct POLYMOD Polymod;

  neut_polymod_set_zero (&Polymod);

  neut_poly_polymod ((*pPoly), &Polymod);

  int *BadVer = ut_alloc_1d_int (Polymod.VerQty + 1);

  for (i = 1; i <= Polymod.VerQty; i++)
    if (Polymod.VerUse[i] == 1)
      if (ut_space_planeside (eq, Polymod.VerCoo[i] - 1) == 1)
	BadVer[++BadVer[0]] = i;

  status = 0;
  if (BadVer[0] != 0)
  {
    PolyhedronModification (-1, eq, &Polymod, BadVer);
    status = 1;
  }

  ut_free_1d_int (BadVer);

  neut_poly_free (pPoly);

  net_polymod_poly (Polymod, pPoly);

  status = (status == 0) ? 0 : (*pPoly).FaceQty;

  neut_polymod_free (&Polymod);

  return status;
}

void
CreateTesl (struct SEEDSET SeedSet, double **Size, struct TESL *pTesl)
{
  struct POLY Domain;
  struct POLY *Poly = NULL;

  net_domain_cube (Size, &Domain);
  net_polycomp (Domain, SeedSet, &Poly, 0);
  net_tesl (SeedSet, Poly, pTesl);

  return;
}

void
net_tess_poly (struct TESS Tess, int poly, struct POLY *pPoly)
{
  int i, j, ver, face, verqty, faceqty, ori;
  int *vers = NULL;
  int *faces = NULL;
  int verfaceqty;
  int *vers_inv = NULL;
  int vers_invqty;
  int *faces_inv = NULL;
  int faces_invqty;

  neut_poly_free (pPoly);

  neut_tess_poly_vers (Tess, poly, &vers, &verqty);
  (*pPoly).VerQty = verqty;

  (*pPoly).VerFace = ut_alloc_1d_pint ((*pPoly).VerQty + 1);
  (*pPoly).VerCoo = ut_alloc_2d ((*pPoly).VerQty + 1, 3);

  for (i = 1; i <= (*pPoly).VerQty; i++)
  {
    ver = vers[i - 1];

    neut_tess_ver_poly_faces (Tess, ver, poly, &((*pPoly).VerFace[i]),
			      &verfaceqty);

    if (verfaceqty != 3)
    {
      printf ("ver = %d verfaceqty = %d != 3\n", ver, verfaceqty);
      abort ();
    }

    ut_array_1d_memcpy ((*pPoly).VerCoo[i], 3, Tess.VerCoo[ver]);
  }

  (*pPoly).FaceQty = Tess.PolyFaceQty[poly];

  (*pPoly).FacePoly = ut_alloc_1d_int ((*pPoly).FaceQty + 1);
  (*pPoly).FaceEq = ut_alloc_2d ((*pPoly).FaceQty + 1, 4);
  (*pPoly).FaceVerQty = ut_alloc_1d_int ((*pPoly).FaceQty + 1);
  (*pPoly).FaceVerNb = ut_alloc_1d_pint ((*pPoly).FaceQty + 1);

  faceqty = (*pPoly).FaceQty;
  for (i = 1; i <= (*pPoly).FaceQty; i++)
  {
    face = Tess.PolyFaceNb[poly][i];

    (*pPoly).FacePoly[i] = (Tess.FacePoly[face][0] == poly) ?
      Tess.FacePoly[face][1] : Tess.FacePoly[face][0];

    (*pPoly).FaceVerQty[i] = Tess.FaceVerQty[face];
    (*pPoly).FaceVerNb[i] = ut_alloc_1d_int ((*pPoly).FaceVerQty[i] + 1);
    ut_array_1d_int_memcpy ((*pPoly).FaceVerNb[i] + 1,
			    (*pPoly).FaceVerQty[i], Tess.FaceVerNb[face] + 1);

    ut_array_1d_memcpy ((*pPoly).FaceEq[i], 4, Tess.FaceEq[face]);

    // reversing face to get it pointing outwards, if necessary
    neut_tess_poly_face_ori (Tess, poly, face, &ori);

    if (ori == -1)
    {
      ut_array_1d_scale ((*pPoly).FaceEq[i], 4, -1);
      ut_array_1d_int_reverseelts ((*pPoly).FaceVerNb[i] + 1,
				   (*pPoly).FaceVerQty[i]);
    }
  }

  // renumbering vers
  int vermin = ut_array_1d_int_min (vers, verqty);
  ut_array_1d_int_addval (vers, verqty, -vermin, vers);

  ut_array_1d_int_inv (vers, verqty, &vers_inv, &vers_invqty);

  for (i = 1; i <= (*pPoly).FaceQty; i++)
    for (j = 1; j <= (*pPoly).FaceVerQty[i]; j++)
    {
      ver = (*pPoly).FaceVerNb[i][j];
      (*pPoly).FaceVerNb[i][j] = vers_inv[ver - vermin] + 1;
    }

  // renumbering faces
  faces = ut_alloc_1d_int (faceqty);
  ut_array_1d_int_memcpy (faces, faceqty, Tess.PolyFaceNb[poly] + 1);

  int facemin = ut_array_1d_int_min (faces, faceqty);
  ut_array_1d_int_addval (faces, faceqty, -facemin, faces);

  ut_array_1d_int_inv (faces, faceqty, &faces_inv, &faces_invqty);

  for (i = 1; i <= (*pPoly).VerQty; i++)
    for (j = 0; j < 3; j++)
    {
      face = (*pPoly).VerFace[i][j];
      (*pPoly).VerFace[i][j] = faces_inv[face - facemin] + 1;
    }

  for (i = 1; i <= (*pPoly).FaceQty; i++)
    (*pPoly).FacePoly[i] = -i;

  ut_free_1d_int (faces);
  ut_free_1d_int (vers);
  ut_free_1d_int (faces_inv);
  ut_free_1d_int (vers_inv);

  return;
}

void
net_poly_tesl (struct POLY Poly, double* coo, struct TESL *pTesl)
{
  struct SEEDSET SeedSet;
  struct POLY *PolyArray = (struct POLY *) calloc (2, sizeof (struct POLY));
  PolyArray[1] = Poly;

  neut_tesl_set_zero (pTesl);

  neut_seedset_set_zero (&SeedSet);

  SeedSet.N = 1;
  SeedSet.N1d = 1;
  SeedSet.Id = 1;

  SeedSet.morpho = ut_alloc_1d_char (10);
  sprintf (SeedSet.morpho, "poly");

  SeedSet.SeedCoo = ut_alloc_2d (2, 3);
  ut_array_1d_memcpy (SeedSet.SeedCoo[1], 3, coo);

  SeedSet.NDensity = 1;

  net_tesl (SeedSet, PolyArray, pTesl);

  free (PolyArray);

  neut_seedset_free (&SeedSet);

  return;
}

void
net_poly_tess (struct POLY Poly, double* coo, struct TESS *pTess)
{
  struct TESL Tesl;
  neut_tesl_set_zero (&Tesl);

  net_poly_tesl (Poly, coo, &Tesl);
  neut_tesl_tess (Tesl, 0, 0, pTess);
  neut_tess_poly_centroid (*pTess, 1, (*pTess).SeedCoo[1]);
  (*pTess).CellTrue = ut_alloc_1d_int ((*pTess).PolyQty + 1);
  (*pTess).CellBody = ut_alloc_1d_int ((*pTess).PolyQty + 1);

  neut_tesl_free (&Tesl);

  return;
}

void
net_poly_centroid (struct POLY Poly, double *coo)
{
  int i, f, ver;
  double area, vol, totvol;
  double *p0 = ut_alloc_1d (3);
  double *p1 = NULL;
  double *p2 = NULL;
  double *tmp = ut_alloc_1d (3);
  double *tmpcoo = ut_alloc_1d (3);

  ut_array_1d_set (tmpcoo, 3, 0);

  for (i = 1; i <= Poly.VerQty; i++)
    ut_array_1d_add (tmpcoo, Poly.VerCoo[i], 3, tmpcoo);

  ut_array_1d_scale (tmpcoo, 3, 1. / Poly.VerQty);

  totvol = 0;
  ut_array_1d_set (coo, 3, 0);

  for (f = 1; f <= Poly.FaceQty; f++)
  {
    ver = Poly.FaceVerNb[f][1];
    ut_array_1d_memcpy (p0, 3, Poly.VerCoo[ver]);

    for (i = 1; i <= Poly.FaceVerQty[f]; i++)
    {
      p1 = Poly.VerCoo[Poly.FaceVerNb[f][i]];
      p2 = Poly.VerCoo[Poly.FaceVerNb[f]
		       [ut_num_rotpos (1, Poly.FaceVerQty[f], i, 1)]];

      area = ut_space_triangle_area (p0, p1, p2);

      if (area > 1e-20)
      {
	ut_space_tet_centre (tmpcoo, p0, p1, p2, tmp);
	vol = ut_space_tet_volume (tmpcoo, p0, p1, p2);
	ut_array_1d_scale (tmp, 3, vol);
	ut_array_1d_add (coo, tmp, 3, coo);
	totvol += vol;
      }
    }
  }

  ut_array_1d_scale (coo, 3, 1. / totvol);

  ut_free_1d (p0);
  ut_free_1d (tmp);

  return;
}

void
net_tess_tesr (struct IN In, struct TESS Tess, TESR * pTesr)
{
  int i, j, k, qty;
  int pid, prevpid;
  char *progress = ut_alloc_1d_char (100);
  double *coo = ut_alloc_1d (3);

  (*pTesr).Dim = In.dim;
  neut_tesr_init_tesrsize (pTesr, Tess, In.dim, In.tesrsizestring);
  neut_tesr_alloc (pTesr, Tess.Dim, (*pTesr).size, (*pTesr).vsize);
  (*pTesr).CellQty = Tess.CellQty;
  
  if (Tess.CellId)
  {
    (*pTesr).CellId = ut_alloc_1d_int (Tess.CellQty + 1);
    ut_array_1d_int_memcpy ((*pTesr).CellId + 1, Tess.CellQty,
	                    Tess.CellId + 1);
  }
  else if (Tess.CellOri)
  {
    (*pTesr).CellId = ut_alloc_1d_int (Tess.CellQty + 1);
    ut_array_1d_int_set_id ((*pTesr).CellId, Tess.CellQty + 1);
  }
  
  if (Tess.CellOri)
  {
    (*pTesr).CellOri = ut_alloc_2d (Tess.CellQty + 1, 4);
    ut_array_2d_memcpy ((*pTesr).CellOri + 1, Tess.CellQty, 4,
	                Tess.CellOri + 1);
  }

  ut_print_progress (stdout, 0,
		     (*pTesr).size[0] * (*pTesr).size[1] * (*pTesr).size[2],
		     "%3.0f%%", progress);
  prevpid = 1;
  qty = 0;
  for (k = 1; k <= (*pTesr).size[2]; k++)
  {
    coo[2] = (*pTesr).vsize[2] * ((double) k - 0.5);

    for (j = 1; j <= (*pTesr).size[1]; j++)
    {
      coo[1] = (*pTesr).vsize[1] * ((double) j - 0.5);

      for (i = 1; i <= (*pTesr).size[0]; i++)
      {
	qty++;

	coo[0] = (*pTesr).vsize[0] * ((double) i - 0.5);

	if (neut_tess_point_incell (Tess, coo, prevpid) == 1)
	  (*pTesr).VoxCell[i][j][k] = prevpid;
	else
	{
	  for (pid = 1; pid <= Tess.CellQty; pid++)
	  {
	    if (pid == prevpid)
	      continue;

	    if (neut_tess_point_incell (Tess, coo, pid) == 1)
	    {
	      (*pTesr).VoxCell[i][j][k] = pid;
	      prevpid = pid;
	      break;
	    }
	  }
	}

	ut_print_progress (stdout, qty,
			   (*pTesr).size[0] * (*pTesr).size[1] *
			   (*pTesr).size[2], "%3.0f%%", progress);
      }
    }
  }

  ut_free_1d_char (progress);
  ut_free_1d (coo);

  return;
}
