/*******************************************************************************
 * ALBERTA:  an Adaptive multi Level finite element toolbox using
 *           Bisectioning refinement and Error control by Residual
 *           Techniques for scientific Applications
 *
 * file:     bamg2alberta.c
 *
 * description: Converter from bamg (i.e. FreeFem++) *.msh format to 
 *              ALBERTA *.amc
 *
 *******************************************************************************
 *
 *  authors:   Christian Haarhaus
 *             Abteilung fuer Angewandte Mathematik
 *             Albert-Ludwigs-Universitaet Freiburg
 *             Hermann-Herder-Str. 10
 *             D-79104 Freiburg im Breisgau, Germany
 *
 *  http://www.alberta-fem.de
 *
 *  (c) C. Haarhaus (2008).
 *
 ******************************************************************************/

#include <stdlib.h>
// #include <alberta.h>
#include <stdio.h>
#include <math.h>
#include <getopt.h>
#include <unistd.h>
#include <string.h>

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

#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define SQR(x) ((x)*(x))

static
int msh2amc(char *input_name, char *output_name);

static
int print_help(const char *funcName, FILE *f, int status)
{
  fprintf(f,
"Usage: %s [INPUT] [OUTPUT]\n"
"\n"
"Converts bamg meshfiles to ALBERTA macro-trinagulations.\n"
"\n"
"Specify a path to an input file in *.msh format, without extention. The\n"
"programm finds the right file automatically (if present) and, if no name for\n"
"the output file is given, generates an output with the same file name, but\n"
".amc extention.\n"
"If the input mesh contains no boundary information, bamg2alberta sets\n"
"boundary type to '0'\n"
"\n"
"WARNING: at the moment the program assumes that no more than 2 boundary "
"edges\n"
"meet at one vertex. If you ever need more, change the variable \n"
"max_edges_at_vert to fit your needs.\n"
"\n"
"Options:\n"
"  -h, --help\n"
"            Print this help.\n",
	  funcName);
  return status;
}

static struct option long_options[] = {
  {"help",      no_argument,        0, 'h' },
  { NULL,       no_argument,        0, '\0' }
};

int main(int argc, char *argv[])
{
  int  c, option_index;
  char *input_name;
  char *output_name;
  char input[256];
  char output[256];
  int  status = 0;
  
  while((c=getopt_long(argc, argv, "h", long_options, &option_index))!=-1) {
    switch(c) {
    case 'h':
      return print_help(argv[0], stdout, EXIT_SUCCESS);
    default:
      return print_help(argv[0], stderr, EXIT_FAILURE);
    }
  }
  if(argc !=2 && argc !=3) {
    return print_help(argv[0], stdout, EXIT_FAILURE);
  }
  else if(argc == 2) {
    input_name = output_name = argv[1];
  } 
  else if (argc == 3) {
    input_name  = argv[1];
    output_name = argv[2];
  }
  strcpy(input, input_name); 
  strcat(input, ".msh");
  printf("\nreading file:\n%s\n", input);
  strcpy(output, output_name);
  strcat(output, ".amc");
  printf("\nprinting to file:\n%s\n\n", output);
  status = msh2amc(input, output);

  if (status == 1) {
    printf("done!\n");
    return EXIT_SUCCESS;
  } else{
    return EXIT_FAILURE;
  }
}

static
int msh2amc(char *input_name, char *output_name)
{
//  FUNCNAME("msh2amc");

  FILE *input;
  FILE *output;

  double **list_of_verts;
  int    **list_of_elems;
  int    **boundary_list_msh;
  int    **boundary_list_alberta;
  int      nv, ne, nb, DIM, DIM_OF_WORLD, verts_per_elem, discard;
  int      i, j, k;
  int      boundary_info, verts_per_sidesimplex;
  double   length, length_max;
  int     *global_index;

/* dimension specific parameters **********************************************/
  DIM = DIM_OF_WORLD    = 2;
  verts_per_elem        = 3;
  verts_per_sidesimplex = 2;
  boundary_info         = 1;

  input  = fopen(input_name,  "r");
  if(input==NULL) {
    printf("ERROR: can not open input file\n");
    return -1;
  }
  output = fopen(output_name, "w+");
  if (output==NULL) {
    printf("ERROR: can not open output file\n");
    return -1;
  }    

  fscanf(input, "%d %d %d", &nv, &ne, &nb);
  
  printf("allocating memory...");
  list_of_verts = malloc(DIM_OF_WORLD*sizeof(double*));
  for(i=0; i<DIM_OF_WORLD; i++) {
    list_of_verts[i] = malloc(nv*sizeof(double));
  }

  list_of_elems = malloc(verts_per_elem*sizeof(int*));
  for(i=0; i<verts_per_elem; i++) {
	  list_of_elems[i] = malloc(ne*sizeof(int));
  }

  global_index = malloc(verts_per_elem*sizeof(int));

  boundary_list_msh = malloc((verts_per_sidesimplex+1)*sizeof(int*));
  for(i=0; i<verts_per_sidesimplex+1; i++) {
	  boundary_list_msh[i] = malloc(nb*sizeof(int));
  }

  boundary_list_alberta = malloc(verts_per_elem*sizeof(int*));
  for(i=0; i<verts_per_elem; i++) {
	  boundary_list_alberta[i] = malloc(ne*sizeof(int));
  }
  printf("\t\t\t\tcomplete.\n");

  fprintf(output, "DIM: %d\nDIM_OF_WORLD: %d \n\n", DIM, DIM_OF_WORLD);
  fprintf(output, "number of vertices: %d\nnumber of elements: %d\n", nv, ne);

/*-- scan/print vertices -----------------------------------------------------*/
  printf("scanning/printing vertex coordinate list...");
  fprintf(output, "\nvertex coordinates:\n");
  for(i=0; i<nv; i++) {
    fscanf(input, "%lf %lf %d", &list_of_verts[0][i], &list_of_verts[1][i],
	   &discard);
    fprintf(output, "%.16e %.16e\n", list_of_verts[0][i], list_of_verts[1][i]);
  }
  printf("\tcomplete.\n");

/*-- scan/print elements -----------------------------------------------------*/
  printf("scanning/printing element list...");
  fprintf(output, "\nelement vertices:\n");
  for(i=0; i<ne; i++) {
    fscanf(input, "%d %d %d %d", &list_of_elems[0][i], &list_of_elems[1][i],
	   &list_of_elems[2][i], &discard);

//! alberta starts from zero, freefem/bamg from 1
    list_of_elems[0][i] -= 1; 
    list_of_elems[1][i] -= 1;
    list_of_elems[2][i] -= 1;

/* Determine the longest edge, and change the numbering
 * accordingly. That is necessary because ALBERTA will always bisect
 * the edge with vertices 0 and 1 in the local numbering */

    length = length_max = 0.;
    global_index[0] = global_index[1] = global_index[2] = 0;
    for(j=0; j<verts_per_elem; j++) {
      length =  SQR( list_of_verts[0][list_of_elems[j][i]]
                    -list_of_verts[0][list_of_elems[(j+1)%3][i]])
              + SQR( list_of_verts[1][list_of_elems[j][i]]
	            -list_of_verts[1][list_of_elems[(j+1)%3][i]]);
      if (length>length_max) {
	length_max = length;
	global_index[0] = list_of_elems[j][i];
	global_index[1] = list_of_elems[(j+1)%3][i];
	global_index[2] = list_of_elems[(j+2)%3][i];
      }
    }
    for(j=0; j<verts_per_elem; j++) {
      list_of_elems[j][i] = global_index[j];
    }

    fprintf(output, "%d %d %d\n", list_of_elems[0][i], list_of_elems[1][i],
	    list_of_elems[2][i]);
  }
  printf("\t\tcomplete.\n");

/*-- boundary tags: boundary_list_msh[0] and [1] contain the indices of ------*/
/*-- boundary side-simplices, [2] contains an integer used as tag       ------*/
  if(boundary_info==1) {
    int *val;
    int *col;
    int **row;
    int col_tmp, row_tmp, tmp1, tmp2, bt_tmp;
    int index_loc, vert_index_j, vert_index_i, final_index, exit2next_edge;
    int max_edges_at_vert = 2; /* NEVER go below 2... */

    val=malloc(nb*sizeof(int));
    col=malloc(nb*sizeof(int));
    
    row=malloc(ne*sizeof(int *));
    for(i=0; i<nv; i++){
      row[i]=malloc(max_edges_at_vert*sizeof(int));
    }
    for(i=0; i<nv; i++){
      for(j=0; j<max_edges_at_vert; j++){
	row[i][j]=-1;
      }
    }
    
    printf("initializing .amc boundary list...");
    for(i=0; i<ne; i++) {
      boundary_list_alberta[0][i]=boundary_list_alberta[1][i]
	=boundary_list_alberta[2][i]=0;
    }
    printf("\t\tcomplete.\n");
    printf("scanning .msh boundary list...");
    fprintf(output,"element boundaries:\n");

/* store bndry info in crs: matrix indices = vertex indices,
 * matrix entry at ij = boundarytype of edge ij
 * the matrix is symmetric, we store only an upper triangular matrix
 * (col_index > row_index)
 * we assume that we have not more than 4 edges with different boundary
 * types meeting in one singl vertex. in an reasonable trinagulation that
 * should pose no greater problem...
 *                      _____                                                   
 * don't make it worse  \   /                                                   
 * than that:            \ /                                                    
 *                        X                                                     
 *                       / \                                                    
 *                      /___\                                                   
 */ 
    for(i=0; i<nb; i++) {
      fscanf(input, "%d %d %d", &tmp1, &tmp2, &bt_tmp);
      if (tmp1>tmp2) {
	col_tmp = tmp1-1; //shift indices to fit alberta standard
	row_tmp = tmp2-1;
      } else if (tmp1<tmp2) {
	col_tmp = tmp2-1;
	row_tmp = tmp1-1;
      } else if (tmp1==tmp2) {
	printf("\nERROR: bad boundary info.\nabort...\n");
	return 0;
      }

      val[i] = bt_tmp;
      col[i] = col_tmp;
      for (k=0; k<max_edges_at_vert; k++) {
	if(row[row_tmp][k]==-1){
	  row[row_tmp][k] = i;
	  break;
	} else if (k == max_edges_at_vert-1 ){
	  printf("\nERROR: too many boundary edges at vertex.\nabort...\n");
	  return 0;
	}}
    }
    printf("\t\t\tcomplete.\n");
    printf("converting boundary info...");
    
    for(i=0; i<ne;i++) {
      for(index_loc=0; index_loc<3; index_loc++) {
	vert_index_j = MAX(list_of_elems[index_loc][i],
			  list_of_elems[(index_loc+1)%3][i]);
	vert_index_i = MIN(list_of_elems[index_loc][i],
			  list_of_elems[(index_loc+1)%3][i]);
	for(k=0; k<max_edges_at_vert; k++) {
	  if(row[vert_index_i][k]==-1) {
	    exit2next_edge = 1;
	    break; // do nothing, no entry in matrix => no boundary info
	  } else {
	    final_index = row[vert_index_i][k];
	    if(col[final_index]==vert_index_j) {
	      boundary_list_alberta[3-index_loc-(index_loc+1)%3][i] = 
		val[final_index]; 
	    }
	  }
	}
	if (exit2next_edge == 1) continue;
      }
    }

    printf("\t\t\tcomplete.\n");
    
    printf("printing boundary info...");
    for(i=0; i<ne; i++) {
      fprintf(output, "%d %d %d\n", boundary_list_alberta[0][i],
	      boundary_list_alberta[1][i], boundary_list_alberta[2][i]);
    }
    printf("\t\t\tcomplete.\n");
  }

  fclose(input);
  fclose(output);
  return 1;
}

#if 0 // old code for boundary info
    for(i=0; i<nb; i++) {
      fscanf(input, "%d %d %d", &boundary_list_msh[0][i],
	     &boundary_list_msh[1][i], &boundary_list_msh[2][i]);
      boundary_list_msh[0][i] -= 1;
      boundary_list_msh[1][i] -= 1;
    }
    printf("\tcomplete.\n");
    
    printf("initializing .amc boundary list...");
    for(i=0; i<ne; i++) {
      boundary_list_alberta[0][i]=boundary_list_alberta[1][i]
	=boundary_list_alberta[2][i]=0;
    }
    printf("\tcomplete.\n");
    
    printf("converting boundary info...");
    for(i=0; i<nb; i++) {
      for(j=0; j<ne; j++) {
	m = 0;
	for(k=0; k<3; k++) {
	    for(l=0; l<2; l++) {
	      if(list_of_elems[k][j]==boundary_list_msh[l][i]) {
		vert_index[m]=k; // markiere fundstelle
		m += 1;
	      }
	      if(m==2) break;
	    }
	    if(m==2) break;
	}
	if(m==2) { // zwei ecken gefunden
	  for(k=0; k<3; k++) {
//	    if((k!=vert_index[0]) && (k!=vert_index[1])) {
	    if( k == 3 - vert_index[0] - vert_index[1]) {
	      boundary_list_alberta[k][j]=boundary_list_msh[2][i];
	    }
	  }
	  break; // don't search the whole rest
	}
      }
    }
#endif
