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

int
neper_t (int fargc, char **fargv, int argc, char **argv)
{
  // ###################################################################
  // ### INITIALIZING ##################################################

  // Variable declaration ###
  int i, tess, level, ParentTessId;
  FILE *file = NULL;
  struct IN In;
  struct REG Reg;
  struct MTESS MTess;
  struct TESS *Tess = NULL;
  struct TESS FTess;
  struct SEEDSET *SeedSet = NULL;
  struct TESR Tesr;
  struct POINT Point;

  net_in_set_zero (&In);
  net_reg_set_zero (&Reg);
  neut_tesr_set_zero (&Tesr);
  neut_mtess_set_zero (&MTess);
  neut_tess_set_zero (&FTess);
  neut_point_set_zero (&Point);

  // Printing program header ###
  ut_print_moduleheader ("-T", fargc, fargv, argc, argv);

  // Reading input data ###
  ut_print_message (0, 1, "Reading input data...\n");
  net_input (&In, fargc, fargv, argc, argv);

  // ###################################################################
  // ### CREATING DOMAIN ###############################################

  // Creating domain ###
  neut_mtess_set_dom (&MTess, &Tess);
  if (strcmp (In.input, "tess") != 0 && strcmp (In.input, "tesr") != 0)
  {
    ut_print_message (0, 1, "Creating domain...\n");
    net_domain (In, &MTess, &(Tess[0]));
  }

  // ###################################################################
  // ### RUNNING MULTILEVEL TESSELLATION ###############################
  // ###################################################################

  if (!strcmp (In.mode, "tess"))
  {
    if (In.checktess == 1)
    {
      ut_print_message (0, 1, "Checking tessellation...\n");
      Tess = calloc (2, sizeof (struct TESS));
      file = ut_file_open (In.load, "r");
      neut_tess_fscanf_verbosity (file, &(Tess[1]), 1);
      ut_file_close (file, In.load, "r");
      return 0;
    }

    else if (In.levelqty >= 1)
    {
      MTess.LevelQty = In.levelqty;
      MTess.LevelTessQty = ut_alloc_1d_int (In.levelqty + 1);
      MTess.LevelTessQty[0] = 1;

      MTess.LevelTess = ut_alloc_2d_int (In.levelqty + 1, 2);
      MTess.LevelTess[0][1] = 0;

      ut_print_message (0, 1, "Creating tessellation%s...\n",
			(In.levelqty <= 1) ? "" : "s");

      if (In.levelqty > 1)
	ut_print_message (0, 1,
			  "  [level] Running in multiscale mode (%d scales)...\n",
			  In.levelqty);

      int cell, tessqty, level_cellqty, prevlevel_cellqty;

      prevlevel_cellqty = 1;
      for (level = 1; level <= In.levelqty; level++)
      {
	for (tess = 1; tess <= MTess.LevelTessQty[level - 1]; tess++)
	  ParentTessId = MTess.LevelTess[level - 1][tess];

	MTess.LevelTessQty[level] = 0;

	// ###############################################################
	// ### COMPUTING TESSELLATIONS ###################################

	tessqty = 0;
	level_cellqty = 0;
	for (tess = 1; tess <= MTess.LevelTessQty[level - 1]; tess++)
	{
	  ParentTessId = MTess.LevelTess[level - 1][tess];

	  for (cell = 1; cell <= Tess[ParentTessId].CellQty;
	      cell++)
	  {
	    MTess.LevelTessQty[level]++;

	    MTess.LevelTess[level]
	      =
	      ut_realloc_1d_int (MTess.LevelTess[level],
				 MTess.LevelTessQty[level] + 1);
	    MTess.LevelTess[level][MTess.LevelTessQty[level]] =
	      ++MTess.TessQty;

	    MTess.DomTess[ParentTessId][cell] = MTess.TessQty;

	    // ### CREATING SEEDS ########################################

	    if (In.morphostring[0] != '@' && In.levelqty == 1)
	      ut_print_message (0, 2, "Distributing seeds...\n");
	    else if (In.morphostring[0] == '@' && In.levelqty == 1)
	      ut_print_message (0, 2, "Reading seeds from `%s'...\n",
				In.morphostring + 1);

	    MTess.TessDom =
	      ut_realloc_2d_int_addline (MTess.TessDom, MTess.TessQty + 1, 2);
	    MTess.TessDom[MTess.TessQty][0] = ParentTessId;
	    MTess.TessDom[MTess.TessQty][1] = cell;

	    SeedSet
	      =
	      realloc (SeedSet,
		       (MTess.TessQty + 1) * sizeof (struct SEEDSET));
	    neut_seedset_set_zero (SeedSet + MTess.TessQty);

	    net_seed (In, level, Tess[ParentTessId], cell,
		      &(SeedSet[MTess.TessQty]));

	    if (In.levelqty > 1)
	    {
	      ut_print_clearline (stdout, 72);
	      ut_print_message (0, 1, "  [%5d]  %d/%d: n=%d ",
				level, ++tessqty, prevlevel_cellqty,
				SeedSet[MTess.TessQty].N);
	      if (In.centroid[level])
	      {
		printf ("centroid ");
		fflush (stdout);
	      }
	    }

	    // ### COMPUTING TESSELLATION ###############################

	    if (In.levelqty == 1)
	      ut_print_message (0, 2, "Running tessellation... ");

	    Tess = realloc (Tess, (MTess.TessQty + 1) * sizeof (struct TESS));
	    neut_tess_set_zero (Tess + MTess.TessQty);

	    if (strcmp (In.input, "tess") != 0)
	      net_tess (In, level, Tess[ParentTessId], cell,
			&(SeedSet[MTess.TessQty]), MTess.TessQty, &MTess,
			&(Tess[MTess.TessQty]));

	    MTess.DomTess
	      = ut_realloc_2d_int_addline (MTess.DomTess,
					   MTess.TessQty + 1,
					   Tess[MTess.TessQty].PolyQty + 1);

	    MTess.DomTessFaceNb
	      = ut_realloc_3d_int_addarray (MTess.DomTessFaceNb,
					    MTess.TessQty + 1,
					    Tess[MTess.TessQty].FaceQty + 1,
					    2);

	    level_cellqty += SeedSet[MTess.TessQty].N;
	  }
	}

	prevlevel_cellqty = level_cellqty;

	// Sorting cells ###
	if (level == 1 && In.sortstring)
	{
	  printf ("\n");
	  if (MTess.LevelQty > 1)
	    ut_print_message (0, 1, "[%5d] ", level);
	  else
	    ut_print_message (0, 2, "", level);
	  printf ("Sorting cells...");
	  if ((Tess[1]).CellQty > 0)
	    net_sort (In, Tess + 1, NULL);
	}

	if (In.scale != NULL && level == 1)
	{
	  ut_print_message (0, 1, "[%5d] ", level);
	  printf ("Scaling tessellation...\n");
	  neut_tess_scale (Tess + 1, In.scale[0], In.scale[1], In.scale[2]);
	}

	printf ("\n");
      }

      // #############################################################
    }

    else if (In.load != NULL)
    {
      ut_print_message (0, 1, "Importing tessellation...\n");
      file = ut_file_open (In.load, "r");
      neut_mtess_fscanf (file, &MTess, &Tess);
      ut_file_close (file, In.load, "r");

      // Sorting cells ###
      if (In.sortstring)
      {
	ut_print_message (0, 1, "Sorting cells...\n");
	net_sort (In, Tess + 1, NULL);
      }
    }

    if (In.levelqty == 2)
    {
      ut_print_message (0, 1, "Flattening 2-scale tessellation...\n");
      net_flatten (MTess, Tess, &FTess);

      if (In.dim == 2)
	net_dim (2, &FTess);
    }
    else if (In.levelqty == 1 || In.load)
      neut_tess_tess (Tess[1], &FTess);

    if (In.reg)
    {
      ut_print_message (0, 1, "Regularizing tessellation...\n");
      net_reg (In, &FTess, &Reg);
    }
  }

  // ###################################################################
  // ### COMPUTING VOXEL DATA ##########################################

  else if (!strcmp (In.mode, "tesr"))
  {
    if (strcmp (In.input, "tesr") != 0)
    {
      ut_print_message (0, 1, "Rastering domain... \n");
      SeedSet = calloc (2, sizeof (struct SEEDSET));
      net_seed (In, 1, Tess[0], 1, &(SeedSet[1]));
      net_tesr (In, Tess[0], SeedSet[1], &Tesr);

      // Sorting cells ###
      if (In.sortstring)
      {
	ut_print_message (0, 1, "Sorting cells...\n");
	ut_print_message (2, 1, "Not available yet...\n");
	/*
	   if (Tesr.PolyQty > 0)
	   net_sort (In, NULL, &Tesr);
	 */
      }
    }

    else
    {
      ut_print_message (0, 1, "Importing raster tessellation...\n");
      neut_tesr_name_fscanf (In.load, &Tesr);
      ut_print_message (0, 2, "Volume has %dx%dx%d (%g) %s and %d %s.\n",
			Tesr.size[0], Tesr.size[1], Tesr.size[2],
			(double) (Tesr.size[0] * Tesr.size[1] * Tesr.size[2]),
			(Tesr.size[0] * Tesr.size[1] * Tesr.size[2] <
			 2) ? "point" : "points", Tesr.CellQty,
			(Tesr.CellQty < 2) ? "cell" : "cells");

      int i, j, k;
      int min_id = INT_MAX, max_id = INT_MIN;
      for (k = 1; k <= Tesr.size[2]; k++)
	for (j = 1; j <= Tesr.size[1]; j++)
	  for (i = 1; i <= Tesr.size[0]; i++)
	  {
	    min_id = ut_num_min (min_id, Tesr.VoxCell[i][j][k]);
	    max_id = ut_num_max (max_id, Tesr.VoxCell[i][j][k]);
	  }

      if (min_id == 1 && max_id == Tesr.CellQty)
	ut_print_message (0, 3,
			  "The polys are numbered contiguously from 1.\n");
      else
	ut_print_message (1, 3, "The poly ids range between %d and %d.\n",
			  min_id, max_id);
    }
  }

  else if (!strcmp (In.mode, "ori"))
  {
    if (In.levelqty > 1)
      ut_print_message (2, 0, "'-format ori' only available for 1-scale tessellations.\n");

    SeedSet = malloc (2 * sizeof (struct SEEDSET));
    neut_seedset_set_zero (&(SeedSet[1]));
    net_seed (In, 1, Tess[0], 1, &(SeedSet[1]));
  }

  // Scaling ###
  if (In.scale)
    if (Tesr.CellQty > 0)
      neut_tesr_scale (&Tesr, In.scale[0], In.scale[1], In.scale[2], NULL);

  // Cropping ###
  if (In.crop)
    if (Tesr.CellQty > 0)
    {
      ut_print_message (0, 1, "Cropping tessellation...\n");
      neut_tesr_crop (&Tesr, In.crop);
    }

  // #################################################################
  // ### Rastering tessellation if necessary ########################

  if (MTess.TessQty > 0
      && (ut_string_inlist (In.format, NEUT_SEP_NODEP, "tesr") == 1
	  || ut_string_inlist (In.format, NEUT_SEP_NODEP, "raw") == 1))
  {
    ut_print_message (0, 1, "Rastering tessellation... ");
    net_tess_tesr (In, FTess, &Tesr);
  }

  // ###################################################################
  // ### IMPORTING POINTS ##############################################

  if (In.loadpoint)
  {
    ut_print_message (0, 1, "Importing points...\n");
    neut_point_name_fscanf (In.loadpoint, &Point);
  }

  // ###################################################################
  // ### WRITING RESULTS ###############################################

  if (In.filter && Tesr.Dim > 0)
  {
    ut_print_message (0, 1, "Filtering raster tessellation...\n");
    net_filter (In, &Tesr);
  }

  // ###################################################################
  // ### WRITING RESULTS ###############################################

  ut_print_message (0, 1, "Writing tessellation...\n");
  net_res (In, MTess, Tess, FTess, Tesr, SeedSet);

  // ###################################################################
  // ### WRITING STATISTICS  ###########################################

  if (In.stc || In.stv || In.ste || In.stf || In.stp || In.stg ||
      In.stpt)
  {
    ut_print_message (0, 1, "Writing statistics...\n");
    net_stat (In, FTess, Tesr, Point);
  }

  // ###################################################################
  // ### CLOSING #######################################################

  net_reg_free (Reg);
  neut_tesr_free (&Tesr);
  neut_mtess_free (MTess, Tess);
  neut_point_free (&Point);
  neut_tess_free (&FTess);

  if (SeedSet)
  {
    if (strcmp (In.mode, "tess") == 0)
      for (i = 1; i <= MTess.TessQty; i++)
	neut_seedset_free (&(SeedSet[i]));
    else if (strcmp (In.mode, "tesr") == 0)
      neut_seedset_free (&(SeedSet[1]));

    free (SeedSet);
  }
  net_in_free (In);

  return EXIT_SUCCESS;
}
