/*--------------------------------------------------------------------------*/
/* 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 (1998-2008) 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>
#undef REFINE
#undef COARSE

#include "alberta/alberta.h"
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>

typedef struct info INFO;
struct info
{
  int       vector;   /*  0: REAL 1: REAL_D */
  char      *name;
  TIMESCENE *tsc;
};

#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

static int print_help(const char *funcName, FILE *f, int status)
{
  fprintf(f,
"Usage: %s START END [-p PATH] [OPTIONS]\n"
"          -m  MESH [-s DRV] [-v DRDV] [[-s DRV1] [-v DRDV1] ...]\n"
"\n"
"Example:\n"
"  %s --mesh=mymesh 0 10 -i 5 -s u_h --vector v_h\n"
"\n"
"  reads grid mesh000000 with scalar function u_h000000 and\n"
"  vector function v_h000000, then mesh000005 with u_h000005 and\n"
"  v_h000005, and finally mesh000010 with u_h000010 and v_h000010\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 not significant with the exception that the\n"
"non-option arguments START and END must come _first_.\n"
"\n"
"Non-option arguments:\n"
"  START END\n"
"            Two integers specifying the start- and end-scene. The actual\n"
"            file names of the data-files are generated by appending a six\n"
"            digit number which loops between START and END.\n"
"            See also `-i' below.\n"
"Options:\n"
"  -i, --increment=INC\n"
"            INC is an integers specifying the increment while reading in\n"
"            the time scenes. To read e.g. only every second time-scene\n"
"            use `-i 2'. INC defaults to 1\n"
"  -m, --mesh=MESH\n"
"            The file-name prefix of an ALBERTA-mesh gnereated by the ALBERTA\n"
"            library routines `write_mesh()' or `write_mesh_xdr()'\n"
"            `-x' and `-b' options below. The actual file name is generated\n"
"            by appending a six digit time-scene number to MESH, unless\n"
"            the `-f' option is also specified, see below.\n"
"            This option is mandatory and may not be omitted.\n"
"  -f, --fixed-mesh\n"
"            Use a single fixed mesh for all time-scenes (i.e. in the\n"
"            non-adaptive case). If `-f' is used `-m MESH' gives the actual\n"
"            file name of the mesh and not only the mesh-prefix. See `-m'\n"
"            above.\n"
"  -s, --scalar=DRV\n"
"            Load the data-files DRVXXXXXX which must contain DOF_REAL_VECs\n"
"            dumped to disk by `write_dof_real_vec[_xdr]()'.\n"
"            `XXXXXX' stands for the time-scene number.\n"
"            This option may be specified multiple times. The DOF_REAL_VECs\n"
"            must belong to the meshes specified with the `-m' option.\n"
"            See `-m', `-b', `-p' and `-i'.\n"
"  -v, --vector=DRDV\n"
"            Load the data-files DRDVXXXXXX which contain DOF_REAL_VEC_Ds\n"
"            dumped to disk by `write_dof_real_d_vec[_xdr]()'.\n"
"            `XXXXXX' stands for the time-scene number.\n"
"            This option may be specified multiple times. The vectors\n"
"            must belong to the meshes specified with the `-m' option.\n"
"            See `-m', `-b', `-p' and `-i'.\n"
"  -p, --path=PATH\n"
"            Specify a path prefix for all data-files. PATH is supposed to\n"
"            be the directory containing all data-files specified by the\n"
"            `-m', `-s' and `-v' options.\n"
"  -B, --Bar\n"
"            Generate a time-progress-bar when displaying the data in GRAPE.\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"
"  -h, --help\n"
"            Print this help.\n",
	  funcName, funcName);
  exit(status);

  fprintf(f, "usage: %s <i_start> <i_end> -i increment", funcName);
  fprintf(f, " [-h] [--help] [-b] [-p path] ");
  fprintf(f, "[-m mesh] [-s drv] [-v drdv] [[-s drv] [-v drdv]]\n");

  fprintf(f,
	  "%s reads a sequence of %dd-mesh[es] with dof_real(_d)_vectors\n",
	  funcName, FE_DIM);
  fprintf(f, "      and displays all data with GRAPE\n");
  fprintf(f, "%s assumes data in XDR format as default\n", funcName);
  fprintf(f, "   i.e. data has been written by write_mesh_xdr, etc.\n");
  fprintf(f, "options:\n");
  fprintf(f, "   -h or -help: display this help\n");
  fprintf(f, "   -B: display time bar\n");
  fprintf(f, "   -f: use one fixed mesh (from non adaptive simulations)\n");
  fprintf(f, "   -b: switch to machine dependent binary format\n");
  fprintf(f, "         i.e. data has been written by write_mesh, etc.\n");
  fprintf(f, "   -p path: read data from path 'path'\n");
  fprintf(f, "   -m mesh: basename of meshes is 'mesh'; default: 'mesh'\n");
  fprintf(f, "   -s drv:  read dof_real_vec with basename 'drv'\n");
  fprintf(f, "   -v file: read dof_real_d_vec with basename 'drdv'\n");
  fprintf(f, "   -t time step size: size for a single time step;\n");
  fprintf(f, "            only used with '-f' for one fixed mesh\n\n");

  fprintf(f, "Example\n");
  fprintf(f, "alberta_movi 0 10 -i 5 -s u_h -v v_h\n");
  fprintf(f,
	  "  reads grid mesh000000 with scalar function u_h000000 and\n");
  fprintf(f,
	  "  vector function v_h000000, then mesh000005 with u_h000005 and\n");
  fprintf(f,
	  "  v_h000005, and finally mesh000010 with u_h000010 and v_h000010\n");
  return status;
}

extern GRAPE_MESH *setup_grape_mesh(MESH *mesh, const char *name);
extern void copyHmeshes(GRAPE_MESH *orgMesh, GRAPE_MESH *self);

void tsc_init(INFO *info, int n_info, int time_bar);

void tsc_add(TIMESCENE *, REAL, MESH *, DOF_REAL_VEC *, DOF_REAL_VEC_D *);

void tsc_timebar(TIMESCENE *, REAL, REAL);
void tsc_graph(TIMESCENE *);

static struct option long_options[] = {
  {"help",       0, 0, 'h' },
  {"increment",  1, 0, 'i' },
  {"binary",     0, 0, 'b' },
  {"Bar",        0, 0, 'B' },
  {"xdr",        0, 0, 'x' },
  {"path",       1, 0, 'p' },
  {"mesh",       1, 0, 'm' },
  {"fixed-mesh", 0, 0, 'f' },
  {"scalar",     1, 0, 's' },
  {"vector",     1, 0, 'v' },
  {"time-step",  1, 0, 't' },
  { NULL,        0, 0, '\0' }
};

int main(int argc, char **argv)
{
  FUNCNAME("main");
  MESH           *mesh = NULL;
  DOF_REAL_VEC   *drv = NULL;
  DOF_REAL_VEC_D *drdv = NULL;
  const char     *fn;
  REAL           f_t_start = LARGE, t_start = LARGE, t_end = -LARGE, t_act;
  long int       i, i_start = 0, i_end = 0, ntime, n_step = 0;

  INFO           *info = MEM_ALLOC(10, INFO);
  int            n = 0, n_info = 10;

  int            i_delta = 1;
  const          char *path = NULL;
  const          char *mesh_name = "mesh";
  int            time_bar = false;
  int            fix_mesh = false;
  REAL           timestep = 1.0e-3;
  int            c, option_index;

  MESH *(*readmesh)(
    const char *name, REAL *timeptr,
    NODE_PROJECTION *(*init_node_proj)(MESH *, MACRO_EL *, int),
    MESH *master);
  DOF_REAL_VEC   *(*read_drv)(const char *name, MESH *, FE_SPACE *);
  DOF_REAL_VEC_D *(*read_drdv)(const char *name, MESH *, FE_SPACE *);

  readmesh  = read_mesh_xdr;
  read_drv  = read_dof_real_vec_xdr;
  read_drdv = read_dof_real_vec_d_xdr;

  info[0].name = "mesh";

  /* first get start and stop scene index */
  if (argc < 3) {
    return print_help(argv[0], stderr, EXIT_FAILURE);
  }
  while (1) {
    c = getopt_long(argc, argv, "-Bbfhi:m:p:s:t:v:x",
		    long_options, &option_index);
    if (c == -1) {
      break;
    }
    switch (c) {
    case 1: /* should be i_start, i_stop */
      if (optind == 2) {
	i_start = atoi(optarg);
      } else if (optind == 3) {
	i_end = atoi(optarg);
      } else {
	return print_help(argv[0], stderr, EXIT_FAILURE);
      }
      break;
    case 'B':
      time_bar = true;
      break;
    case 'b':
      readmesh  = read_mesh;
      read_drv  = read_dof_real_vec;
      read_drdv = read_dof_real_vec_d;
      break;
    case 'h':
      return print_help(argv[0], stdout, EXIT_SUCCESS);
      break;
    case 'f':
      fix_mesh = true;
      break;
    case 'i':
      i_delta = atoi(optarg);
      break;
    case 'm':
      mesh_name = optarg;
      break;
    case 'p':
      path = optarg;
      break;
    case 's':
      if (n == n_info)
	info = MEM_REALLOC(info, n_info, n_info+10, INFO);
      n_info += 10;
      info[n].vector = false;
      info[n].name = optarg;
      n++;
      break;
    case 't':
      timestep = atof(optarg);
      break;
    case 'v':
      if (n == n_info)
	info = MEM_REALLOC(info, n_info, n_info+10, INFO);
      n_info += 10;
      info[n].vector = true;
      info[n].name = optarg;
      n++;
      break;
    case 'x':
      readmesh  = read_mesh_xdr;
      read_drv  = read_dof_real_vec_xdr;
      read_drdv = read_dof_real_vec_d_xdr;
      break;
    default:
      return print_help(argv[0], stderr, EXIT_FAILURE);
    }
  }

  MSG("start %d, end %d, delta %d\n", i_start, i_end, i_delta);

  TEST_EXIT(i_end >= i_start, "end < start\n");
  TEST_EXIT(i_delta > 0, "delta <= 0\n");

  tsc_init(info, n, time_bar);

  if (fix_mesh)
  {
    char name[256];
    if (path)
      sprintf(name, "%s/%s", path, mesh_name);
    else
      sprintf(name, "%s", mesh_name);
    MSG("reading \"%s\"\n", name);
    mesh = (*readmesh)(name, &t_act, NULL, NULL);

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

    f_t_start = t_act;
  }

  for (ntime = i_start; ntime <= i_end; ntime += i_delta)
  {
    MSG("ntime %d, end %d, delta %d\n", ntime, i_end, i_delta);
    if (!fix_mesh)
    {
      fn = generate_filename(path, mesh_name, ntime);
      MSG("reading \"%s\"\n", fn);
      mesh = (*readmesh)(fn, &t_act, NULL, NULL);
      TEST_EXIT(mesh,"couldn't read mesh \"%s\"\n", fn);
      TEST_EXIT(FE_DIM==mesh->dim,"Mesh dimension must be %d!\n", FE_DIM);
    }
    else
      t_act = f_t_start+ntime*timestep;

    MSG("Aktuelle Zeit: %f (Zeitschrittweite: %f)\n",t_act,timestep);

    if (n > 0)
    {
      for (i = n-1; i >= 0; i--)
      {
	if (info[i].vector)
	{
	  fn = generate_filename(path, info[i].name, ntime);
	  MSG("reading \"%s\"\n", fn);
	  drdv = (*read_drdv)(fn, mesh, NULL);
	  TEST_EXIT(drdv,"couldn't read dof_real_d_vec \"%s\"\n", fn);

	  tsc_add(info[i].tsc, t_act, mesh, NULL, drdv);
	}
	else
	{
	  fn = generate_filename(path, info[i].name, ntime);

	  MSG("reading \"%s\"\n", fn);
	  drv = (*read_drv)(fn, mesh, NULL);
	  TEST_EXIT(drv,"couldn't read dof_real_vec \"%s\"\n", fn);

	  MSG("range of %s in [%e,%e]\n", NAME(drv), dof_min(drv),
	      dof_max(drv));
	  tsc_add(info[i].tsc, t_act, mesh, drv, NULL);
	}
      }
    }
    else
    {
      tsc_add(info[0].tsc, t_act, mesh, NULL, NULL);
    }

    if (ntime == i_start) t_start = t_end = t_act;
    t_start = MIN(t_start, t_act);
    t_end = MAX(t_end, t_act);

    if (timestep > 0) t_act += timestep*i_delta;
    n_step++;
  }

  if (time_bar)
  {
    tsc_timebar(info[0].tsc, t_start, t_end);
  }

  MSG("\n");
  MSG("step = %d\n", n_step-1);

  g_project_add("uif-gm");
  tsc_graph(info[0].tsc);

  return(0);
}

/****************************************************************************/
/****************************************************************************/

static GRAPE_MESH *grape_mesh_interpol(GRAPE_MESH *mesh1, GRAPE_MESH *mesh2,
				       double factor)
{
  GRAPE_MESH *self, *org;

  self = (GRAPE_MESH *)START_METHOD(G_INSTANCE);
  ASSURE (self, "", END_METHOD (NULL));

  if (factor < 0.5)
    org = mesh1;
  else
    org = mesh2;

  copyHmeshes(org, self);

  END_METHOD(self);
}
/****************************************************************************/

void tsc_init(INFO *info, int n_info, int time_bar)
{
  int   n;
  extern void def_timebar_methods(void);

  if (time_bar)
    def_timebar_methods();

  GRAPE(Grape_Mesh,"add-method")("interpol", grape_mesh_interpol);

  for (n = 0; n < MAX(1, n_info); n++)
  {
    info[n].tsc = (TIMESCENE *)GRAPE(TimeScene,"new-instance")(info[n].name);
    info[n].tsc->sync = 1;
    if (n > 0)
      info[n-1].tsc->next_scene = (SCENE *) info[n].tsc;
  }

  return;
}

extern void grape_ini_f_data_d(GRAPE_MESH *, DOF_REAL_VEC_D *);
extern void grape_ini_f_data(GRAPE_MESH *, DOF_REAL_VEC *);

void tsc_add(TIMESCENE *tsc, REAL time, MESH *mesh,
	     DOF_REAL_VEC *drv, DOF_REAL_VEC_D *drdv)
{
  GRAPE_MESH  *grape_mesh;

  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);
  }

  grape_mesh = setup_grape_mesh(mesh, mesh->name);

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

  if (tsc->dynamic)
    tsc->dynamic = (G_SCENE_OBJECT *)GRAPE(tsc->dynamic,"put")
	                            (grape_mesh, grape_mesh, time);
  else
    tsc->dynamic = (G_SCENE_OBJECT *)GRAPE(TimeStep,"put")
                                     (grape_mesh, grape_mesh, time);
  return;
}

void tsc_timebar(TIMESCENE *tsc, REAL t_start, REAL t_end)
{
  GRAPE(tsc,"timebar-v")(t_start, t_end);
}

void tsc_graph(TIMESCENE *tsc)
{
  if (tsc)
  {
    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")(tsc);
  }
}

