/* author: Claus-Justus Heine
 *
 * Institut f"ur Angewandte Mathematik
 * Albert-Ludwigs-Universit"at Freiburg
 * Hermann-Herder-Str. 10
 * 79104 Freiburg
 * Germany
 *
 * (C) 1998-2005 C.-J. Heine <claus@mathematik.uni-freiburg.de>,
 *     2005      Carsten Eilks <carsten@mathematik.uni-freiburg.de>.
 */

/* convert a mesh + coordinate vector to oogl format */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
 
#include <alberta/alberta.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>

#if DIM_OF_WORLD != 3
# error Only for parametric surfaces in 2d
#endif
 
#define FE_DIM 2

typedef struct 
{
  FILE *of;
  const DOF_ADMIN *admin;
  DOF_INT_VEC *map[8];
  DOF_REAL_VEC *con;
  REAL con_max;
  DOF_REAL_D_VEC *coords;
  unsigned int flags;
  int v_ind; /* global vertex index */
  int n_elem;
  REAL val_min, val_max, val_fac;
} DUMP_EL_DATA;

static int verbose;
static REAL opt_val_min = HUGE_VAL;
static REAL opt_val_max = HUGE_VAL;



/* count vertices and create dof->vertex number mapping */
static void count_vertices(const EL_INFO *elinfo, DUMP_EL_DATA *data)
{
  int n0 = data->admin->n0_dof[VERTEX];
  DOF dof;
  int i;
  
  data->n_elem ++;

  /* if (data->flags & ERROR_COLORS) {
    value = get_el_est(elinfo->el);
    data->val_min = MIN(data->val_min, value);
    data->val_max = MAX(data->val_max, value);
  }
  */
  for (i = 0; i < N_VERTICES(FE_DIM); i++) {
    dof = elinfo->el->dof[i][n0];
    if (data->map[0]->vec[dof] == -1) {
      data->v_ind ++;
    }
  }

 

  for (i = 0; i < N_VERTICES(FE_DIM); i++) {
    dof = elinfo->el->dof[i][n0];
    if (data->map[0]->vec[dof] == -1) {
      data->map[0]->vec[dof] = 1;
    }
  }
}
 
/* count vertices and create dof->vertex number mapping */
static void dump_vertices(const EL_INFO *elinfo, DUMP_EL_DATA *data)
{
  int n0 = data->admin->n0_dof[VERTEX];
  DOF dof;
  int i;
 
  for (i = 0; i < N_VERTICES(FE_DIM); i++) {
    dof = elinfo->el->dof[i][n0];
    if (data->map[0]->vec[dof] == -1) {
      data->map[0]->vec[dof] = data->v_ind ++;
      fprintf(data->of, "%e %e %e\n",
	      data->coords->vec[dof][0],
	      data->coords->vec[dof][1],
	      data->coords->vec[dof][2]);
    }
  }
 
  
}



static REAL
get_con_value(const EL_INFO *el_info, DOF_REAL_VEC *con, REAL con_max)
{
  const DOF_ADMIN *admin = con->fe_space->admin;
  int n0 = admin->n0_dof[VERTEX];
  DOF **dof = el_info->el->dof;
  return (con->vec[dof[0][n0]]+con->vec[dof[1][n0]]+con->vec[dof[2][n0]]+3*con_max)/(6*con_max);
}
static void dump_elements(const EL_INFO *elinfo, DUMP_EL_DATA *data)
{
 
  int n0 = data->admin->n0_dof[VERTEX];
  DOF **dof = elinfo->el->dof;
  REAL vv;
 

  
  fprintf(data->of, "3 %d %d %d",
	  data->map[0]->vec[dof[0][n0]],
	  data->map[0]->vec[dof[1][n0]],
	  data->map[0]->vec[dof[2][n0]]);
  if(data->con)
    {
      vv=get_con_value(elinfo,data->con,data->con_max);
      fprintf(data->of, "%f %f %f", vv, 4.0*vv*(1.0 -vv), 1.0 - vv);
    }
  fprintf(data->of, "\n");
 
 

}
 
void mesh2oogl(FILE *of, MESH *mesh,
	       DOF_REAL_D_VEC *coords,
	       DOF_REAL_VEC *con,
	       FLAGS flags)
{
  DUMP_EL_DATA data;
  unsigned int i;
  TRAVERSE_STACK *stack = get_traverse_stack();
  const EL_INFO *el_info;

  for (i = 0; i < 8; i++) {
    if ((flags & (1 << i)) == (1 << i)) {
      data.map[i] = get_dof_int_vec("index map", coords->fe_space);
      FOR_ALL_DOFS(coords->fe_space->admin, data.map[i]->vec[dof] = -1);
    }
  }
 
  data.of = of;
  data.admin = coords->fe_space->admin;
  data.coords = coords;
  data.con=con;
  data.con_max=0.0;
  FOR_ALL_DOFS(con->fe_space->admin,
	       data.con_max = MAX(data.con_max, fabs(con->vec[dof])))
  data.flags = flags;
  data.v_ind = 0;
  data.n_elem = 0;
  if (isfinite(opt_val_min) && isfinite(opt_val_max)) {
    data.val_max = opt_val_max;
    data.val_min = opt_val_min;
  } else {
    data.val_max = -(data.val_min = 1.0E20);
  }

  el_info = traverse_first(stack,mesh, -1,CALL_LEAF_EL|FILL_BOUND);
  while (el_info)
   {
     count_vertices(el_info,&data);
     el_info=traverse_next(stack,el_info);  
   } 
  FOR_ALL_DOFS(coords->fe_space->admin, data.map[0]->vec[dof] = -1);

  fprintf(of, "OFF\n");

  fprintf(of, "%d %d %d\n", data.v_ind, data.n_elem, -1);

  data.v_ind = 0;
 el_info = traverse_first(stack,mesh, -1,CALL_LEAF_EL|FILL_BOUND);
  while (el_info)
   {
     dump_vertices(el_info,&data);
     el_info=traverse_next(stack,el_info);  
   } 
  /* dump the elements. Only vertex dofs are used */
  el_info = traverse_first(stack,mesh, -1,CALL_LEAF_EL|FILL_BOUND|FILL_COORDS);
  while (el_info)
    { 
      dump_elements(el_info,&data);
      el_info=traverse_next(stack,el_info);  
    } 
  free_traverse_stack(stack);
}

void usage(void)
{
  fprintf(stderr,
"albert2oogl "
"--mesh|-m MESH --coords|-c COORDS --real_vec|-s REAL_VEC\n"
" [--out-file|-o OUTFILE] [--function-bounds|b MIN:MAX\n"
" [--verbose|-v]\n");
}

static const struct option long_options[] = {
 
  { "coords", 1, 0, 'c' },
  { "mesh", 1, 0, 'm' },
  { "real_vec", 1, 0, 's'},
  { "out-file", 1, 0, 'o' },
  { "function-bounds", 1, 0, 'b' },
  { "verbose", 0, 0, 'v' },
  { NULL, 0, 0, 0 }
};

const char *short_options = "b:c:sm:o:v";

extern void *xmalloc(size_t size);

int main(int argc, char *argv[])
{
  const char *mesh_file = NULL;
  const char *coords_file = NULL;
  const char *kon_file = NULL;
  const char *out_file = NULL;
  MESH *mesh = NULL;
  DOF_REAL_D_VEC *coords = NULL;
  DOF_REAL_VEC *con=NULL;
  FILE *of = NULL;
  FLAGS flags = 0x0;

  /* sigfpe_init(); */

  while (1) {
    int c = getopt_long(argc, argv, short_options, long_options, NULL);
    if (c == -1) {
      break;
    }
    switch (c) {
    case 'b':
      sscanf(optarg, "%lf:%lf", &opt_val_min, &opt_val_max);
      break;
    case 'v':
      verbose ++;
      break;
    case 'o':
      out_file = optarg;
      break;
    case 'm':
      mesh_file = optarg;
      break;
    case 'c':
      coords_file = optarg;
      break;
    case 's':
      kon_file = optarg;
      break;
    default:
      usage();
      exit(1);
      break;
    }
  }
  if (out_file != NULL) {
    if (!(of = fopen(out_file, "w"))) {
      ERROR_EXIT("Couldn't open info file %s because of %s",
		 out_file, strerror(errno));
    }
  } else {
    of = stdout;
  }

  if (mesh_file == NULL || coords_file == NULL) {
    usage();
    exit(1);
  }

  mesh = read_mesh(mesh_file, NULL, NULL, NULL);
  coords = read_dof_real_d_vec(coords_file, mesh, NULL);
  if(kon_file) {
    con=read_dof_real_vec(kon_file,mesh,NULL);
  }
  mesh2oogl(of, mesh, coords,con, flags);

  if (out_file != NULL) {
    if (fclose(of) != 0) {
      ERROR_EXIT("Couldn't close info file %s because of %s",
		 out_file, strerror(errno));
    }
  }

  return 0;
}
 
