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

#include"net_seed_lam_.h"

// A possible improvement is to center the lamella in the grain, to
// avoid getting tiny lamella at one end (supposing that the random
// value is very close to 0 or 1).
// Another improvement is to slightly modify the width to get full-width
// lamella at both ends.
void
net_seed_lam (char *width, struct TESS Dom, int cell, struct SEEDSET *pSSet)
{
  int i, j, wqty;
  double distmin, distmax, sum = -1, theta;
  double *dist = ut_alloc_1d (Dom.VerQty + 1);
  char **parts = NULL;
  double *w = NULL;
  gsl_rng *r;
  char **vars = NULL, **vals = NULL;
  int varqty;
  FILE *file = NULL;
  int v_set, w_set;

  r = gsl_rng_alloc (gsl_rng_ranlxd2);
  gsl_rng_set (r, (*pSSet).Random);

  ut_string_function_separate (width, NULL, &vars, &vals, &varqty);

  (*pSSet).LamEq = ut_alloc_2d (2, 4);
  (*pSSet).LamWidthId = ut_alloc_1d_int (2);
  (*pSSet).LamWidth = ut_alloc_1d (2);

  v_set = 0;
  w_set = 0;
  for (i = 0; i < varqty; i++)
    if (!strcmp (vars[i], "w"))
    {
      w_set = 1;
      if (vals[i][0] == '@')
      {
	file = ut_file_open (vals[i] + 1, "R");
	ut_file_skip (file, cell - 1);
	vals[i] = ut_realloc_1d_char (vals[i], 10000);
	if (fscanf (file, "%s", vals[i]) != 1)
	  abort ();
	ut_file_close (file, vals[i] + 1, "R");
      }

      ut_string_separate (vals[i], NEUT_SEP_DEP, &parts, &wqty);
      w = ut_alloc_1d (wqty);
      for (j = 0; j < wqty; j++)
	if (sscanf (parts[j], "%lf", w + j) != 1)
	  ut_error_reportbug ();
      sum = ut_array_1d_sum (w, wqty);

      ut_free_2d_char (parts, wqty);
      parts = NULL;
    }
    else if (!strcmp (vars[i], "v"))
    {
      v_set = 1;
      if (vals[i][0] == '@')
      {
	file = ut_file_open (vals[i] + 1, "R");
	ut_file_skip (file, 3 * (cell - 1));
	vals[i] = ut_realloc_1d_char (vals[i], 10000);
	ut_array_1d_fscanf (file, (*pSSet).LamEq[1] + 1, 3);
	ut_array_1d_scale ((*pSSet).LamEq[1] + 1, 3,
			   1. / ut_array_1d_norm ((*pSSet).LamEq[1] + 1, 3));
	ut_file_close (file, vals[i] + 1, "R");
      }
      else
	ut_print_message (2, 2,
			  "'v' requires an argument of type @file_name.\n");
    }

  if (!w_set || sum < 0)
  {
    ut_print_message (2, 2, "missing lamella width!\n");
    abort ();
  }

  if (!v_set)
  {
    // Setting normal (constant is 0)
    if ((*pSSet).Dim == 3)
      ol_nb_r (gsl_rng_uniform (r), gsl_rng_uniform (r),
	       (*pSSet).LamEq[1] + 1);
    else if ((*pSSet).Dim == 2)
    {
      theta = 2 * M_PI * gsl_rng_uniform (r);
      (*pSSet).LamEq[1][1] = cos (theta);
      (*pSSet).LamEq[1][2] = sin (theta);
    }
    else
      abort ();
  }

  // Shifting the plane to the further down vertex
  for (i = 1; i <= Dom.VerQty; i++)
    ut_space_point_plane_dist_signed (Dom.VerCoo[i], (*pSSet).LamEq[1],
				      &(dist[i]));

  distmin = ut_array_1d_min (dist + 1, Dom.VerQty);
  distmax = ut_array_1d_max (dist + 1, Dom.VerQty);
  (*pSSet).LamEq[1][0] = distmin;

  int LamBeg = -1;
  (*pSSet).LamEq[1][0] -= gsl_rng_uniform (r) * sum;
  for (i = 0; i < wqty; i++)
  {
    (*pSSet).LamEq[1][0] += w[i];
    if ((*pSSet).LamEq[1][0] > distmin)
    {
      LamBeg = i;
      break;
    }
  }
  if (LamBeg == -1)
    ut_error_reportbug ();

  (*pSSet).N = 1;
  (*pSSet).LamWidth[1] = w[LamBeg];

  double coo = (*pSSet).LamEq[1][0];

  int id = LamBeg;
  while (coo < distmax)
  {
    id = ut_num_rotpos (0, wqty - 1, id, 1);
    coo += w[id];
    (*pSSet).N++;

    (*pSSet).LamEq =
      ut_realloc_2d_addline ((*pSSet).LamEq, (*pSSet).N + 1, 4);
    ut_array_1d_memcpy ((*pSSet).LamEq[(*pSSet).N], 4, (*pSSet).LamEq[1]);
    (*pSSet).LamEq[(*pSSet).N][0] = coo;

    (*pSSet).LamWidth = ut_realloc_1d ((*pSSet).LamWidth, (*pSSet).N + 1);
    (*pSSet).LamWidth[(*pSSet).N] = w[id];
    (*pSSet).LamWidthId =
      ut_realloc_1d_int ((*pSSet).LamWidthId, (*pSSet).N + 1);
    (*pSSet).LamWidthId[(*pSSet).N] = id + 1;
  }

  // Allocating SeedCoo and Size
  (*pSSet).SeedCoo = ut_alloc_2d ((*pSSet).N + 1, 3);
  (*pSSet).SeedWeight = ut_alloc_1d ((*pSSet).N + 1);
  (*pSSet).Size = ut_alloc_2d (3, 2);

  gsl_rng_free (r);
  ut_free_1d (dist);
  ut_free_2d_char (parts, wqty);
  ut_free_2d_char (vars, varqty);
  ut_free_2d_char (vals, varqty);
  
  return;
}
