/*******************************************************************************
 * ALBERTA:  an Adaptive multi Level finite element toolbox using
 *           Bisectioning refinement and Error control by Residual
 *           Techniques for scientific Applications
 *
 * file:     alberta2gmv.c
 *
 * description: Converter from ALBERTA data to GMV input.
 *
 *******************************************************************************
 *
 *  authors:   Claus-Justus Heine
 *             Abteilung fuer Angewandte Mathematik
 *             Albert-Ludwigs-Universitaet Freiburg
 *             Hermann-Herder-Str. 10
 *             D-79104 Freiburg im Breisgau, Germany
 *
 *  http://www.alberta-fem.de
 *
 *  (c) by C.-J. Heine (2008)
 *
 ******************************************************************************/

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

#include <alberta/alberta.h>

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

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

static void print_help(const char *funcName, FILE *f, int status)
{
  fprintf(f,
"Usage: %s [-p PATH] [-o OUTPUT]\n"
"          -m  MESH [-s DRV] [-v DRDV] [[-m MESH1] [-s DRV1] [-v DRDV1] ...]\n"
"\n"
"Example:\n"
"  %s --mesh=mymesh -s temperature --vector velocity\n"
"    where \"mymesh\", \"temperature\" and \"velocity\" are file-names.\n"
"\n"
"If a long option shows an argument as mandatory, then it is mandatory\n"
"for the equivalent short option also.  Similarly for optional arguments.\n"
"\n"
"The order of the options _is_ significant in the following cases:\n"
"`-p PATH' alters the search path for all following data-files.\n"
"`-m MESH' specifies a new mesh for all following DRVs and DRDVs (see below)\n"
"\n"
"Options:\n"
"  -m, --mesh=MESH\n"
"            The file-name of an ALBERTA-mesh gnereated by the ALBERTA\n"
"            library routines `write_mesh()' or `write_mesh_xdr()'\n"
"            `-x' and `-b' options below.\n"
"            This option is mandatory and may not be omitted. This option\n"
"            may be specified multiple times. All following dof-vectors\n"
"            given by the `-s' and `-v' options must belong to the most\n"
"            recently specified mesh.\n"
"  -a, --ascii\n"
"            Write the GMV fiel in ASCII format. NOTE: the `--binary' option\n"
"            refers to the INPUT format. To select binary OUTPUT format\n"
"            simply do not specify `--ascii', because binary OUTPUT format\n"
"            is the default.\n" 
"  -r, --refined\n"
"            Display higher order Lagrange finite element functions on a\n"
"            refined mesh.\n"
"  -b, --binary\n"
"            Expect MESH, DRV and DRDV to contain data in host dependent\n"
"            byte-order, generated by `write_SOMETHING()' routines of the\n"
"            ALBERTA library (SOMETHING is `mesh', `dof_real_vec' etc.\n"
"  -x, --xdr\n"
"            This is the default and just mentioned here for completeness.\n"
"            Expect MESH, DRV and DRDV to contain data in network\n"
"            byte-order, generated by `write_SOMETHING_xdr()' routines\n"
"            of the ALBERTA library. Per convention this means big-endian\n"
"            byte-order.\n"
"  -s, --scalar=DRV\n"
"            Load the data-file DRV which must contain a DOF_REAL_VEC\n"
"            dumped to disk by `write_dof_real_vec[_xdr]()'.\n"
"            This option may be specified multiple times. The DOF_REAL_VECs\n"
"            must belong to the most recently specified mesh.\n"
"            See `-m' and `-b' above.\n"
"  -v, --vector=DRDV\n"
"            Load the data-file DRDV which must contain a DOF_REAL_D_VEC\n"
"            dumped to disk by `write_dof_real_d_vec[_xdr]()'.\n"
"            This option may be specified multiple times. The vector\n"
"            must belong to the most recently specified mesh.\n"
"            See `-m' and `-b' above.\n"
"  -o, --output=FILENAME\n"
"            Specify an output file-name. If this option is omitted, then the\n"
"            output file-name is \"alberta.gmv\".\n"
"  -p, --path=PATH\n"
"            Specify a path prefix for all following data-files. This option\n"
"            may be specified multiple times. PATH is supposed to be the\n"
"            directory containing all data-files specified by the following\n"
"            `-m', `-s' and `-v' options.\n"
"  -h, --help\n"
"            Print this help.\n",
	  funcName, funcName);
  exit(status);
}

static const char *filename(const char *path, const char *fn)
{
  static char  name[1024];

  if (!fn) return(NULL);

  if (path == NULL || path[0] == '\0')
  {
    sprintf(name, "./%s", fn);
  }
  else
  {
    const char *cp = path;
    while (*cp)
      cp++;
    cp--;
    if (*cp == '/')
      sprintf(name, "%s%s", path, fn);
    else
      sprintf(name, "%s/%s", path, fn);
  }
  return((const char *) name);
}

static struct option long_options[] = {
  {"help",    no_argument,       0, 'h' },
  {"ascii",   no_argument,       0, 'a' },
  {"binary",  no_argument,       0, 'b' },
  {"xdr",     no_argument,       0, 'x' },
  {"output",  required_argument, 0, 'o' },
  {"path",    required_argument, 0, 'p' },
  {"mesh",    required_argument, 0, 'm' },
  {"refined", no_argument,       0, 'r' },
  {"scalar",  required_argument, 0, 's' },
  {"vector",  required_argument, 0, 'v' },
  { NULL,     no_argument,       0, '\0' }
};

int main(int argc, char **argv)
{
  FUNCNAME("main");
  const char     *outname = "alberta.gmv";
  MESH           *mesh = NULL;
  const char     *mesh_path = NULL;
  DOF_REAL_VEC   **drv = NULL;
  DOF_REAL_VEC_D **drdv = NULL;
  int            n_drv = 0, n_drdv = 0;
  DOF_REAL_VEC_D *velocity = NULL;
  const char     *fn, *path = NULL;
  REAL           time;
  bool           ascii = false, refined = false;
  int            c, option_index;

  MESH *(*rm)(const char *filename, REAL *timep,
	      NODE_PROJECTION *(*)(MESH *, MACRO_EL *, int),
	      MESH *master);

  DOF_REAL_VEC   *(*rdrv)(const char *, MESH *, FE_SPACE *);
  DOF_REAL_VEC_D *(*rdrdv)(const char *, MESH *, FE_SPACE *);

  rm = read_mesh_xdr;
  rdrv = read_dof_real_vec_xdr;
  rdrdv = read_dof_real_vec_d_xdr;

  while (1) {
    c = getopt_long(argc, argv, "abhm:o:p:rs:v:x", long_options, &option_index);
    if (c == -1)
      break;
    switch (c) {
    case 'a':
      ascii = true;
      break;
    case 'b':
      rm = read_mesh;
      rdrv = read_dof_real_vec;
      rdrdv = read_dof_real_vec_d;
      break;
    case 'h':
      print_help(argv[0], stdout, EXIT_SUCCESS);
      break;
    case 'm':
      mesh_path = optarg;
      fn = filename(path, mesh_path);
      MSG("reading mesh `%s'\n", fn);
      mesh = (*rm)(fn, &time, NULL, NULL);
      TEST_EXIT(mesh,"could not read mesh `%s'!\n", fn);
      break;
    case 'o':
      outname = optarg;
      break;
    case 'p':
      path = optarg;
      break;
    case 'r':
      refined = true;
      break;
    case 's':
      if (mesh_path == NULL) {
	ERROR("a mesh has to be given _FIRST_!\n");
	print_help(argv[0], stderr, 1);
      }
      fn = filename(path, optarg);
      MSG("reading dof_real_vec `%s'\n", fn);
      drv = MEM_REALLOC(drv, n_drv, n_drv+1, DOF_REAL_VEC *);
      drv[n_drv++] = (*rdrv)(fn, mesh, NULL);
      TEST_EXIT(drv[n_drv-1],"could not read dof_real_vec `%s'!\n", fn);
      break;
    case 'v':
      if (mesh_path == NULL) {
	ERROR("a mesh has to be given _FIRST_!\n");
	print_help(argv[0], stderr, 1);
      }
      fn = filename(path, optarg);
      MSG("reading dof_real_d_vec `%s'\n", fn);
      if (velocity == NULL) {
	velocity = (*rdrdv)(fn, mesh, NULL);	
	TEST_EXIT(velocity, "could not read dof_real_d_vec `%s'!\n", fn);
      } else {
	drdv = MEM_REALLOC(drdv, n_drdv, n_drdv+1, DOF_REAL_VEC_D *);
	drdv[n_drdv++] = (*rdrdv)(fn, mesh, NULL);
	TEST_EXIT(drdv[n_drdv-1],"could not read dof_real_d_vec `%s'!\n", fn);
      }
      break;
    case 'x':
      rm = read_mesh_xdr;
      rdrv = read_dof_real_vec_xdr;
      rdrdv = read_dof_real_vec_d_xdr;
      break;
    default:
      print_help(argv[0], stderr, EXIT_FAILURE);
    }
  }

  if (mesh_path == NULL) {
    ERROR("a mesh has to be given!\n\n");
    print_help(argv[0], stderr, 1);
  }

  printf("Attempting to write file \"%s\"\n", outname);

  write_mesh_gmv(mesh, outname, ascii, refined,
		 n_drv, drv, n_drdv, drdv, velocity,
		 time);

  return EXIT_SUCCESS;
}
