/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     triangle2alberta.c                                             */
/*                                                                          */
/* description: Interface between the Triangle mesh generator and ALBERTA   */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Daniel Koester                                               */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*  http://www.alberta-fem.de                                               */
/*                                                                          */
/*  (c) by D. Koester (2006)                                                */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#include <alberta/alberta.h>


#define VERT_IND(i,j) ((i)*N_VERTICES_2D+(j))
#define NEIGH_IND(i,j) ((i)*N_NEIGH_2D+(j))




/*--------------------------------------------------------------------------*/
/*  read_indices()  reads 3 indices from  file  into  id[0-2],              */
/*    returns true if 3 inputs arguments could be read successfully by      */
/*    fscanf(), else false                                                  */
/*--------------------------------------------------------------------------*/

static int read_indices(FILE *file, int id[])
{
  int      i;

  for (i = 0; i <= 2; i++)
    if (fscanf(file, "%d", id+i) != 1)
      return(false);
  return(true);
}


/*--------------------------------------------------------------------------*/
/*  read_triangle_files()                                                   */
/*    read Triangle .node, .ele, .neigh files and converts the information  */
/*    into a macro_data structure                                           */
/*    called by triangle2macro_data() below.                                */
/*--------------------------------------------------------------------------*/

/* BUG: should read a .edge-file if present, attaching boundary types
 * to points is somewhat pointless. cH.
 */

static MACRO_DATA *read_triangle_files(const char *basename)
{
  FUNCNAME("read_triangle_files");
  FILE       *node_file, *ele_file, *neigh_file = NULL;
  MACRO_DATA *macro_data = NULL;
  int        dim, nv, ne, i, j, ind[3], i_tmp, longest_edge = 0;
  int        n_node_attr, n_ele_attr, n_bound;
  REAL       d_tmp, max_norm;
  char       node_name[2048], ele_name[2048], neigh_name[2048];
  BNDRY_TYPE *bound_vec = NULL;


/*--------------------------------------------------------------------------*/
/*  Check for user errors.                                                  */
/*--------------------------------------------------------------------------*/

  TEST_EXIT(basename,"no base name specified\n");
  TEST_EXIT(strlen(basename) < (unsigned int) 2047,
    "can only handle filenames up to 2047 characters\n");

/*--------------------------------------------------------------------------*/
/*  Open the files. A .neigh file is not necessary.                         */
/*--------------------------------------------------------------------------*/

  snprintf(node_name, 2048, "%s.node", basename);
  TEST_EXIT((node_file=fopen(node_name,"r")),
	    "cannot open file '%s'\n", node_name);

  snprintf(ele_name, 2048, "%s.ele", basename);
  TEST_EXIT((ele_file=fopen(ele_name,"r")),
	    "cannot open file '%s'\n", ele_name);

  snprintf(neigh_name, 2048, "%s.neigh", basename);
  neigh_file = fopen(neigh_name, "r");

/*--------------------------------------------------------------------------*/
/*  Start reading the .node file.                                           */
/*--------------------------------------------------------------------------*/
  /*  Number of vertices: */

  TEST_EXIT(fscanf(node_file, "%d", &nv) == 1,
    "node file '%s': can not read number of vertices correctly\n", node_name);
  TEST_EXIT(nv > 0,
    "node file '%s': number of vertices = %d must be bigger than 0\n", 
	    node_name, nv);

  /*  Dimension of the triangulation, always 2 since this is Triangle :-P  */  

  TEST_EXIT(fscanf(node_file, "%d", &dim) == 1,
    "node file '%s': can not read dim correctly\n", node_name);
  TEST_EXIT(dim <= DIM_OF_WORLD,
    "node file '%s': dimension = %d > DIM_OF_WORLD = %d\n", 
	    node_name, dim, DIM_OF_WORLD);
  TEST_EXIT(dim == 2,
	    "node file '%s': weird dimension %d, should be 2\n",
	    node_name, dim);

  /*  Number of node attributes, not used at the moment: */
  TEST_EXIT(fscanf(node_file, "%d", &n_node_attr) == 1,
    "node file '%s': can not read number of node attributes correctly\n", 
	    node_name);

  /*  Number of boundary markers: */
  TEST_EXIT(fscanf(node_file, "%d", &n_bound) == 1,
    "node file '%s': can not read number of boundary markers correctly\n", 
	    node_name);

  if(n_bound)
    bound_vec = MEM_ALLOC(nv, BNDRY_TYPE);

/*--------------------------------------------------------------------------*/
/*  Start reading the .ele file.                                            */
/*--------------------------------------------------------------------------*/
  /*  Number of triangles: */

  TEST_EXIT(fscanf(ele_file, "%d", &ne) == 1,
    "ele file '%s': can not read number of triangles correctly\n", ele_name);
  TEST_EXIT(ne > 0,
    "ele file '%s': number of elements = %d must be bigger than 0\n", 
	    ele_name, ne);

  /* Number of nodes per triangle, only 3 supported: */

  TEST_EXIT(fscanf(ele_file, "%d", &i_tmp) == 1,
    "ele file '%s': can not read number of nodes per triangle\n", ele_name);
  TEST_EXIT(i_tmp == 3,
    "ele file '%s': only three nodes per triangle supported\n", ele_name);

  /*  Number of element attributes, not used at the moment: */
  TEST_EXIT(fscanf(ele_file, "%d", &n_ele_attr) == 1,
    "ele file '%s': can not read number of element attributes correctly\n", 
	    ele_name);

/*--------------------------------------------------------------------------*/
/*  Start reading the .neigh file.                                          */
/*--------------------------------------------------------------------------*/

  if(neigh_file) {
  /*  Number of triangles, just to check: */

    TEST_EXIT(fscanf(neigh_file, "%d", &i_tmp) == 1,
      "neigh file '%s': can not read number of triangles correctly\n", 
	      neigh_name);
    TEST_EXIT(ne == i_tmp,
      "neigh file '%s': number of elements = %d must be bigger than 0\n", 
	      ele_name, ne);

  /* Number of neighbors per triangle, must be 3: */

    TEST_EXIT(fscanf(neigh_file, "%d", &i_tmp) == 1,
	    "neigh file '%s': can not read number of neighbors per triangle\n",
	      neigh_name);
    TEST_EXIT(i_tmp == 3,
   "neigh file '%s': only three neighbors per triangle supported\n", 
	      neigh_name);
  }

/*--------------------------------------------------------------------------*/
/*  Allocate a MACRO_DATA structure.                                        */
/*--------------------------------------------------------------------------*/

  macro_data = alloc_macro_data(dim, nv, ne);

/*--------------------------------------------------------------------------*/
/*  Now read the vertex data.                                               */
/*--------------------------------------------------------------------------*/
	
  for (i = 0; i < nv; i++) {
    /* Take care of the node index. */
    TEST_EXIT(fscanf(node_file, "%d", &i_tmp) == 1,
	    "node file '%s': can not read node index for node %d\n",
	      node_name, i);
    TEST_EXIT(i_tmp == i+1, "node file '%s': node index must start with 1 and be consecutive, read %d for node %d\n", node_name, i_tmp, i);

    /* Now read node coordinates. Add a 0.0 for DIM_OF_WORLD==3 */

    TEST_EXIT(fscanf(node_file, "%lf", &d_tmp) == 1,
        "node file '%s': error while reading coordinate 0 of element %d\n",
	      node_name, i);
    macro_data->coords[i][0] = d_tmp;

    TEST_EXIT(fscanf(node_file, "%lf", &d_tmp) == 1,
        "node file '%s': error while reading coordinate 1 of element %d\n",
	      node_name, i);
    macro_data->coords[i][1] = d_tmp;

#if DIM_OF_WORLD == 3
    macro_data->coords[i][2] = 0.0;
#endif

    /* Skip the node attributes. */

    for(j = 0; j < n_node_attr; j++)
      TEST_EXIT(fscanf(node_file, "%lf", &d_tmp) == 1,
	"node file '%s': can not read attribute no. %d of %d on node %d\n",
		node_name, j, n_node_attr, i);

    /* Fill the boundary vector. */

    if(n_bound) {
      TEST_EXIT(fscanf(node_file, "%d", &i_tmp) == 1,
		"node file '%s': can not read boundary type for node %d\n",
		node_name, i);
      bound_vec[i] = (BNDRY_TYPE)i_tmp;
    }
  }

/*--------------------------------------------------------------------------*/
/*  Read the element data.                                                  */
/*--------------------------------------------------------------------------*/

  for (i = 0; i < ne; i++) {
    /* Take care of the element index. */
    TEST_EXIT(fscanf(ele_file, "%d", &i_tmp) == 1,
	    "ele file '%s': can not read element index for element %d\n",
	      ele_name, i);
    TEST_EXIT(i_tmp == i+1, "ele file '%s': element index must start with 1 and be consecutive, read %d for element %d\n", ele_name, i_tmp, i);

    /* Now read the vertex indices. */

    TEST_EXIT(read_indices(ele_file, ind),
	      "file %s: can not read vertex indices of element %d\n",
	      ele_name, i);
    
    for (j = 0; j < N_VERTICES_2D; j++)
      macro_data->mel_vertices[VERT_IND(i,j)] = ind[j] - 1;

    /* Skip the element attributes. */

    for(j = 0; j < n_ele_attr; j++)
      TEST_EXIT(fscanf(ele_file, "%lf", &d_tmp) == 1,
	"ele file '%s': can not read attribute no. %d of %d on element %d\n",
		ele_name, j, n_ele_attr, i);
  }

/*--------------------------------------------------------------------------*/
/*  Fill neighbor information if present.                                   */
/*--------------------------------------------------------------------------*/
  
  if(neigh_file) {
    for (i = 0; i < ne; i++) {
      /* Again, check the element index. */

      TEST_EXIT(fscanf(neigh_file, "%d", &i_tmp) == 1,
		"neigh file '%s': can not read element index for element %d\n",
		neigh_name, i);
      TEST_EXIT(i_tmp == i+1, "neigh file '%s': element index must start with 1 and be consecutive, read %d for element %d\n", neigh_name, i_tmp, i);

      /* Now read the neighbors. */

      TEST_EXIT(read_indices(neigh_file, ind),
		"neigh file %s: can not read neighbour info of element %d\n", 
		neigh_name, i);
      
      for(j = 0; j < N_NEIGH_2D; j++) {
	if(ind[j] == -1)
	  macro_data->neigh[NEIGH_IND(i,j)] = ind[j];
	else
	  macro_data->neigh[NEIGH_IND(i,j)] = ind[j] - 1;
      }
    }
  }
  else /* or compute neighbors ourselves */
    compute_neigh_fast(macro_data);

/*--------------------------------------------------------------------------*/
/*  Sort the boundary data into the macro_data->boundary array.             */
/*--------------------------------------------------------------------------*/

  /* provide a default Dirichlet boundary */
  default_boundary(macro_data, DIRICHLET, true);

  if(n_bound) {
    for (i = 0; i < ne; i++) {
      for(j = 0; j < N_NEIGH_2D; j++) {
	ind[0] = macro_data->mel_vertices[VERT_IND(i,(j+1)%3)];
	ind[1] = macro_data->mel_vertices[VERT_IND(i,(j+2)%3)];

	if(macro_data->neigh[NEIGH_IND(i,j)] == -1) {
	  macro_data->boundary[NEIGH_IND(i,j)] = 
	    MIN(bound_vec[ind[0]], bound_vec[ind[1]]);
	}
      }
    }
  }

/*--------------------------------------------------------------------------*/
/*  Adjust the local numbering of nodes, so that the longest edge will be   */
/*  the 0-1 edge.                                                           */
/*--------------------------------------------------------------------------*/

  for (i = 0; i < ne; i++) {
    max_norm = 0.0;
    for (j = 0; j < N_NEIGH_2D; j++) {
      ind[0] = macro_data->mel_vertices[VERT_IND(i,(j+0)%3)];
      ind[1] = macro_data->mel_vertices[VERT_IND(i,(j+1)%3)];

      d_tmp = DIST_DOW(macro_data->coords[ind[0]], macro_data->coords[ind[1]]);

      if(d_tmp > max_norm) {
	max_norm = d_tmp;
	longest_edge = j;
      }
    }
    
    if(longest_edge != 0) {
      for (j = 0; j < N_VERTICES_2D; j++)
	ind[j] = macro_data->mel_vertices[VERT_IND(i,j)];
      for (j = 0; j < N_VERTICES_2D; j++)
	macro_data->mel_vertices[VERT_IND(i, j)] = ind[(j+longest_edge)%3];

      for (j = 0; j < N_NEIGH_2D; j++)
	ind[j] = (int)macro_data->boundary[NEIGH_IND(i,j)];
      for (j = 0; j < N_NEIGH_2D; j++)
	macro_data->boundary[NEIGH_IND(i,j)] = (S_CHAR)ind[(j+longest_edge)%3];

      for (j = 0; j < N_NEIGH_2D; j++)
	ind[j] = macro_data->neigh[NEIGH_IND(i,j)];
      for (j = 0; j < N_NEIGH_2D; j++)
	macro_data->neigh[NEIGH_IND(i,j)] = ind[(j+longest_edge)%3];
    }
  }

/*--------------------------------------------------------------------------*/
/*  Finally, clean up.                                                      */
/*--------------------------------------------------------------------------*/
  
  if(n_bound)
    MEM_FREE(bound_vec, nv, S_CHAR);
  fclose(ele_file);
  fclose(node_file);
  if(neigh_file)
    fclose(neigh_file);

  return(macro_data);
}


/*--------------------------------------------------------------------------*/
/* triangle2macro_data(): Read an ALBERTA macro triangulation from          */
/* Triangle files.                                                          */
/*--------------------------------------------------------------------------*/

extern MACRO_DATA *triangle2macro_data(const char *basename)
{
  MACRO_DATA *macro_data = NULL;
  char       filenew[1024];

  macro_data = read_triangle_files(basename);

  strncpy(filenew, basename, 1023);
  filenew[1023] = '\0';
  strncat(filenew, ".new", 1023);
  filenew[1023] = '\0';
  macro_test(macro_data, filenew);

  return macro_data;
}

#if DO_MAIN
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <unistd.h>
#include <getopt.h>

#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif

static void print_help(const char *name, FILE *f, int status) 
{
  fprintf(f,
	  "Usage: %s [-h][--help] IN_BASENAME OUT_BASENAME\n"
	  "\n"
	  "Convert { IN_BASENAME.ele, IN_BASENAME.node } to OUT_BASENAME.mac\n"
	  "The input-files are assumed to be in triangle format, the output\n"
	  "format is ALBERTA's ASCII macro-file format.\n", name);
  exit(status);
}

int main(int argc, char *argv[])
{
  static struct option long_options[] = {
    { "help", no_argument, 0, 'h' },
    { NULL, no_argument, 0, '\0' }
  };
  int c, option_index;
  MACRO_DATA *macro_data;
  char filename[PATH_MAX];
  
  while (true) {
    c = getopt_long(argc, argv, "h", long_options, &option_index);
    if (c == -1) {
      break;
    }
    switch (c) {
    case 'h':
      print_help(argv[0], stdout, EXIT_SUCCESS);
      break;
    default:
      print_help(argv[0], stderr, EXIT_FAILURE);
      break;
    }
  }

  if (argc - optind != 2) {
    print_help(argv[0], stderr, EXIT_FAILURE);
    return EXIT_FAILURE;
  }
  
  macro_data = triangle2macro_data(argv[optind]);
  sprintf(filename, "%s.amc", argv[optind+1]);
  write_macro_data(macro_data, filename);
  free_macro_data(macro_data);

  return EXIT_SUCCESS;
}
#endif
