/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     alberta-grape.c                                                */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Alfred Schmidt                                               */
/*             Zentrum fuer Technomathematik                                */
/*             Fachbereich 3 Mathematik/Informatik                          */
/*             Universitaet Bremen                                          */
/*             Bibliothekstr. 2                                             */
/*             D-28359 Bremen, Germany                                      */
/*                                                                          */
/*             Kunibert G. Siebert                                          */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*             Claus-Justus Heine                                           */
/*             Abteilung fuer Angewandte Mathematik                         */
/*             Albert-Ludwigs-Universitaet Freiburg                         */
/*             Hermann-Herder-Str. 10                                       */
/*             D-79104 Freiburg im Breisgau, Germany                        */
/*                                                                          */
/*             Robert Kloefkorn                                             */
/*             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 A. Schmidt and K.G. Siebert (1996-2004)                          */
/*         C.-J. Heine and R. Kloefkorn (1998-2007)                         */
/*                                                                          */
/*     This program is free software; you can redistribute it and/or modify */
/*     it under the terms of the GNU General Public License as published by */
/*     the Free Software Foundation; either version 2 of the License, or    */
/*     any later version.                                                   */
/*                                                                          */
/*     This program is distributed in the hope that it will be useful,      */
/*     but WITHOUT ANY WARRANTY; without even the implied warranty of       */
/*     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
/*     GNU General Public License for more details.                         */
/*--------------------------------------------------------------------------*/

#include <grape.h>
#include <alberta/alberta.h>

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

#if FE_DIM == 2
/*
typedef HMESH2D GRAPE_MESH;
#define Grape_Mesh HMesh2d
*/
/* higher order visual */
typedef HPMESH2D GRAPE_MESH;
#define Grape_Mesh HPMesh2d
#else
typedef HMESH3D GRAPE_MESH;
#define Grape_Mesh HMesh3d
#endif

extern GRAPE_MESH *setup_grape_mesh(MESH *mesh, char *name);
extern void grape_ini_f_data_d(GRAPE_MESH *, DOF_REAL_VEC_D *);
extern void grape_ini_f_data(GRAPE_MESH *, DOF_REAL_VEC *);

static SCENE *first_scene = NULL;

static void sc_add(MESH *mesh, DOF_REAL_VEC *drv, DOF_REAL_VEC_D *drdv)
{
  FUNCNAME("sc_add");
  SCENE      *scene = NULL;
  GRAPE_MESH *grape_mesh = NULL;

  TEST_EXIT(mesh, "no mesh ???\n");

  TEST_EXIT(FE_DIM == mesh->dim, "Mesh dimension must be %d!\n", FE_DIM);

  if (mesh->n_dof[VERTEX] == 0) {
    /* Ensure that we have a DOF_ADMIN for vertices. */
    WARNING("Mesh without vertex DOFs, forcibly adding a vertex DOF-admin.\n");
    get_vertex_admin(mesh, ADM_PERIODIC);
  }

  if(first_scene) {
    void * obj = NULL;
    scene = first_scene;
    grape_mesh = (GRAPE_MESH *) scene->object;
    if( grape_mesh ) {
      obj = grape_mesh->user_data;

      /* if mesh and user_data of grape mesh are the same, then dont
       * create new object
       */
      if( obj != mesh ) {
        scene = NULL;
        grape_mesh = NULL;
      }
    }
  }

  /* Until we know how to do things correctly temporarily force the generation
   * of a new scene.
   */
  if(true || !scene ) {
    scene = (SCENE *)GRAPE(Scene,"new-instance")(mesh->name);
    scene->object = NULL;
    scene->next_scene = NULL;
  }

  if(!grape_mesh)
    grape_mesh = setup_grape_mesh(mesh, (char *) mesh->name);

  TEST_EXIT(grape_mesh, "grape_mesh is NULL");

  if (!first_scene) first_scene = scene;

  /* what is this doing ?
  if (last_scene) last_scene->next_scene = scene;
  last_scene = scene;
  */

  if (drdv)
    grape_ini_f_data_d(grape_mesh, drdv);
  else if (drv)
    grape_ini_f_data(grape_mesh, drv);

  if(!scene->object) scene->object = (G_SCENE_OBJECT *) grape_mesh;
  return;
}

static void sc_graph(SCENE *sc)
{
  if (sc)
  {
    MANAGER       *mgr;
#ifdef GRID_MODE
    GRAPHICDEVICE *grdev;

    grdev = (GRAPHICDEVICE *)GRAPE(GraphicDevice,"get-stddev")();
    grdev->clear();
    if (grdev && (grdev->grid_patch != G_GRID)) {
      GRAPE(grdev,"grid-patch")(G_GRID);
    }
#endif
    mgr = (MANAGER *)GRAPE(Manager,"get-stdmgr")();
    GRAPE(mgr,"handle")(sc);
  }
}

static void print_help(const char *funcName, FILE *f, int status)
{
  fprintf(f,
"Usage: %s [-p PATH] [OPTIONS]\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"
"  -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_VEC_D\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"
"  -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",   0, 0, 'h' },
  {"binary", 0, 0, 'b' },
  {"xdr",    0, 0, 'x' },
  {"path",   1, 0, 'p' },
  {"mesh",   1, 0, 'm' },
  {"scalar", 1, 0, 's' },
  {"vector", 1, 0, 'v' },
  { NULL,    0, 0, '\0' }
};

int main(int argc, char **argv)
{
  FUNCNAME("main");
  MESH           *mesh = NULL;
  const char     *mesh_path = NULL;
  DOF_REAL_VEC   *drv = NULL;
  DOF_REAL_VEC_D *drdv = NULL;
  const char     *fn, *path = NULL;
  REAL           dummy;
  int            c, option_index;

  MESH *(*rm)(const char *, REAL *,
	      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, "bhm:p:s:v:x", long_options, &option_index);
    if (c == -1)
      break;
    switch (c) {
    case 'b':
      rm = read_mesh;
      rdrv = read_dof_real_vec;
      rdrdv = read_dof_real_vec_d;
      break;
    case 'h':
      print_help(argv[0], stdout, 0);
      break;
    case 'm':
      mesh_path = optarg;
      fn = filename(path, mesh_path);
      MSG("reading mesh `%s'\n", fn);
      mesh = (*rm)(fn, &dummy, NULL, NULL);
      TEST_EXIT(mesh,"could not read mesh `%s'!\n", fn);
      break;
    case 'p':
      path = optarg;
      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 = (*rdrv)(fn, mesh, NULL);
      TEST_EXIT(drv,"could not read dof_real_vec `%s'!\n", fn);
      sc_add(mesh, drv, NULL);
      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);
      drdv = (*rdrdv)(fn, mesh, NULL);
      TEST_EXIT(drdv,"could not read dof_real_d_vec `%s'!\n", fn);
      sc_add(mesh, NULL, drdv);
      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, 1);
    }
  }

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

  if (!drv && !drdv)
  {
    sc_add(mesh, NULL, NULL);
  }

  g_project_add("uif-gm");

  if (first_scene)
    sc_graph(first_scene);

  return(0);
}
