/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file: gltools.c                                                          */
/*                                                                          */
/*                                                                          */
/* description: interface to gltools-2-4                                    */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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                                    */
/*                                                                          */
/*  http://www.mathematik.uni-freiburg.de/IAM/ALBERTA                       */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2003)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

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

#include "alberta_intern.h"
#include "alberta.h"
#include "glrnd.h"
#include "glmesh.h"

/*--------------------------------------------------------------------------*/
/*  vector for storing the additional coordinates, and vector with pointers */
/*  to all these coordinates; the first three have to be set on each        */
/*  element to the element's vertices                                       */
/*--------------------------------------------------------------------------*/

#if HAVE_GLMLOOPCALLBACK_COORDS_ONLY
# define COORDS_ONLY_DECL , int coords_only
# define COORDS_ONLY_ARG  , coords_only
#else
# define COORDS_ONLY_DECL /* */
# define COORDS_ONLY_ARG  /* */
# define coords_only false
#endif

typedef struct gltools_info  GLTOOLS_INFO;
struct gltools_info
{
  const DOF_REAL_VEC   *uh;
  const DOF_REAL_VEC_D *uh_d;
  const DOF_REAL_VEC_D *disp;
  int          degree;
  int          refinement;
  int          scalar;  /* 0:vector, 1:scalar, 2:vector_norm */
  MESH         *mesh;
  REAL         (*get_est)(EL *);
  double       min, max;

  GLTOOLS_INFO *next;
};

static GLTOOLS_INFO *key_P_info = NULL;

static int glrKeyAction_P(glRenderer rnd, int mask)
{
  switch (key_P_info->degree) {
  case 0:
  case 1:
    return(GL_FALSE);
  default:
    key_P_info->refinement = 
      (key_P_info->refinement + 1) % (key_P_info->degree+2);
    glrSetDataValid(rnd, GL_FALSE);
    return(GL_TRUE);
  }
}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
typedef struct cell_info CELL_INFO;
struct cell_info {
  int           dim;
  const REAL    *coords[N_VERTICES_MAX];
  const REAL    *bary[N_VERTICES_MAX];
  REAL          f[N_VERTICES_MAX];
  int           el_type;
  const EL_INFO *el_info;
  void          (*coord_to_world)(const EL_INFO *info, const QUAD *quad,
				  int n, const REAL_B lambda[],
				  REAL_D *world);
  const BAS_FCTS      *bas_fcts;
  const BAS_FCTS      *disp_bfcts;
  const EL_REAL_VEC   *el_uh;
  const EL_REAL_VEC_D *el_uh_d;
  EL_REAL_VEC_D       *el_disp;
  GLTOOLS_INFO  *info;
};
static REAL_D     glm_cellflux = {0.0};
static int        cell_count = 0;

static const int const_cell_dofs[N_VERTICES_LIMIT]= {0};

static const int local_cell_dofs[N_VERTICES_LIMIT]= {0,1,2,3};

static const REAL_B vertex_bary[N_VERTICES_LIMIT] = {
  INIT_BARY_0D(1.0),
  INIT_BARY_1D(0.0, 1.0),
  INIT_BARY_2D(0.0, 0.0, 1.0),
  INIT_BARY_3D(0.0,0.0,0.0,1.0)
 };

static const REAL_B center_bary[DIM_LIMIT+1] = {
  INIT_BARY_0D(0.0),
  INIT_BARY_1D(0.5, 0.5),
  INIT_BARY_2D(1.0/3.0, 1.0/3.0, 1.0/3.0),
  INIT_BARY_3D(0.25,0.25,0.25,0.25)
};

#if DIM_MAX >= 2
static int child_vertex_2d[2][3] = {{2, 0, -1},{1, 2, -1}};
#endif

static REAL min_uh, max_uh;

#define STIP_WIDTH 0.2
/* plot two triangles for each 1D simplex */
static void simplex_1d_2d(glMesh m, int cell_count, REAL *f, int nf, 
			  REAL **coords, glmSimplexCallback sxf)
{
  FUNCNAME("simplex_1d_2d");
    
  static REAL coords_1d_2d[4][2]={{0.0,0.0},{1.0,0.0},
				  {0.0,STIP_WIDTH},{1.0,STIP_WIDTH}};
  static REAL *cell_coords_1d_2d[2][3]
    ={{coords_1d_2d[1],coords_1d_2d[2],coords_1d_2d[0]},
      {coords_1d_2d[2],coords_1d_2d[1],coords_1d_2d[3]}};
  static int local_cell_dofs_1d_2d[2][3]={{1,0,0},{0,1,1}};
  static int const_cell_dofs_1d_2d[3]={0,0,0};

  DEBUG_TEST_EXIT(coords, "no coords");

#if DIM_OF_WORLD == 1  
  coords_1d_2d[0][0] = coords_1d_2d[2][0] = coords[0][0];
  coords_1d_2d[1][0] = coords_1d_2d[3][0] = coords[1][0];
#else
  {
    REAL len, normal[2];

    normal[0] = coords[1][1] - coords[0][1];
    normal[1] = coords[0][0] - coords[1][0];

    len = STIP_WIDTH * 0.3 / (sqrt(SQR(normal[0])+SQR(normal[1])));
    normal[0] *= len;
    normal[1] *= len;

    coords_1d_2d[0][0] = coords[0][0];
    coords_1d_2d[0][1] = coords[0][1];
    coords_1d_2d[1][0] = coords[1][0];
    coords_1d_2d[1][1] = coords[1][1];

    coords_1d_2d[2][0] = coords[0][0] + normal[0];
    coords_1d_2d[2][1] = coords[0][1] + normal[1];
    coords_1d_2d[3][0] = coords[1][0] + normal[0];
    coords_1d_2d[3][1] = coords[1][1] + normal[1];
  }
#endif

  if (nf >= 3) 
  {
    sxf(m, cell_count, 0, f, local_cell_dofs_1d_2d[0], cell_coords_1d_2d[0]);
    sxf(m, cell_count, 0, f, local_cell_dofs_1d_2d[1], cell_coords_1d_2d[1]);
  } 
  else
  {
    sxf(m, cell_count, 0, f, const_cell_dofs_1d_2d, cell_coords_1d_2d[0]);
    sxf(m, cell_count, 0, f, const_cell_dofs_1d_2d, cell_coords_1d_2d[1]);
  }
  
  return;
}

static void recursive_cell(glMesh m, glmSimplexCallback sxf, int level,
			   CELL_INFO *cell_info,
			   GLTOOLS_INFO *info, int cell_count COORDS_ONLY_DECL)
{
  if (level <= 0) 
  {
    if(cell_info->dim == 1) 
      simplex_1d_2d(m, cell_count, cell_info->f, 3,
		    (REAL **)cell_info->coords, sxf);
    else
      if (info->scalar || coords_only)
	sxf(m, cell_count, 0, cell_info->f,
	    (int *)local_cell_dofs, (REAL **)cell_info->coords);
      else 
	{
	  eval_uh_dow(glm_cellflux, center_bary[cell_info->dim],
		      cell_info->el_uh_d, info->uh_d->fe_space->bas_fcts);
	  sxf(m, 0, 0, glm_cellflux, (int *)const_cell_dofs,
	      (REAL **)cell_info->coords);
	}
  }
  else
  {
    CELL_INFO  child_info;
    REAL_D     new_coords;
    REAL_B     new_bary[1];
    REAL       new_f = 0.0;
    int        i, ichild;
    
    for (i = 0; i <= cell_info->dim; i++)
      new_bary[0][i] = 0.5*(cell_info->bary[0][i] + cell_info->bary[1][i]);
    
    if (cell_info->coord_to_world) {
      cell_info->coord_to_world(cell_info->el_info, NULL, 1, 
				(const REAL_B *)new_bary, &new_coords);
    }
    else 
    {
      if (false && info->disp)
      {
	eval_uh_dow(new_coords,
		    new_bary[0], cell_info->el_disp,
		    cell_info->disp_bfcts);
      }
      else
      {
	for (i = 0; i <= cell_info->dim; i++)
	  new_coords[i] = 0.5*(cell_info->coords[0][i] 
			       + cell_info->coords[1][i]);
      }
    }
    if (!coords_only) {
      switch (info->scalar) 
      {
      case 1:
	new_f = eval_uh(new_bary[0], cell_info->el_uh, cell_info->bas_fcts);
	if (new_f > max_uh) {
	  max_uh = new_f;
	}
	if (new_f < min_uh) {
	  min_uh = new_f;
      }
	break;
      case 2:
      {
	REAL_D f_d;
	eval_uh_dow(f_d, new_bary[0], cell_info->el_uh_d, cell_info->bas_fcts);
	new_f = NORM_DOW(f_d);
	break;
      }
      default:
	new_f = 0.0;
      }
    }
    
    child_info = *cell_info;

    for (ichild = 0; ichild < 2; ++ichild) {
      switch(cell_info->dim) {
      case 1:
	child_info.coords[ichild]   = cell_info->coords[ichild];
	child_info.coords[1-ichild] = new_coords;
	child_info.bary[ichild]     = cell_info->bary[ichild];
	child_info.bary[1-ichild]   = new_bary[0];
	child_info.f[ichild]        = cell_info->f[ichild];
	child_info.f[1-ichild]      = new_f;
	break;
#if DIM_MAX >= 2
      case 2:
	for (i = 0; i < 2; i++) {
          int j = child_vertex_2d[ichild][i];
          child_info.coords[i] = cell_info->coords[j];
          child_info.bary[i]   = cell_info->bary[j];
          child_info.f[i]      = cell_info->f[j];
        }
	child_info.coords[2] = new_coords;
	child_info.bary[2]   = new_bary[0];
	child_info.f[2]      = new_f;
	break;
#endif
#if DIM_MAX >= 3
      case 3:
	for (i = 0; i < 3; i++) {
          int j = child_vertex_3d[cell_info->el_type][ichild][i];
          child_info.coords[i] = cell_info->coords[j];
          child_info.bary[i]   = cell_info->bary[j];
          child_info.f[i]      = cell_info->f[j];
        }
	child_info.coords[3] = new_coords;
	child_info.bary[3]   = new_bary[0];
	child_info.f[3]      = new_f;
	child_info.el_type   = (cell_info->el_type + 1) % 3;
	break;
#endif
      }

      recursive_cell(m, sxf, level-1,
		     &child_info, info, cell_count COORDS_ONLY_ARG);
    }
  }
  return;
}

static void m_cell_loop(glMesh m, void *data, glmSimplexCallback sxf COORDS_ONLY_DECL)
{
  FUNCNAME("m_cell_loop");
  CELL_INFO        cell_info[1];
  GLTOOLS_INFO     *info = (GLTOOLS_INFO *) data;
  const DOF_REAL_VEC     *u  = NULL;
  const DOF_REAL_VEC_D   *ud = NULL;
  MESH             *mesh;
  PARAMETRIC       *parametric;
  REAL_D           wc[N_VERTICES_MAX];
  int              i, j, dim;
  FLAGS            fill_flags = FILL_NOTHING;
  
  if (info->scalar==1) {
    u = info->uh;
    if (!u) {
      ERROR("no dof_real_vec\n");
      return;
    }
    mesh = u->fe_space->mesh;
    cell_info->bas_fcts = u->fe_space->bas_fcts;
  } else {
    ud = info->uh_d;
    if (!ud)
    {
      ERROR("no dof_real_d_vec\n");
      return;
    }
    mesh = ud->fe_space->mesh;
    cell_info->bas_fcts = ud->fe_space->bas_fcts;
  }
  fill_flags |= cell_info->bas_fcts->fill_flags;
  parametric = mesh->parametric;
  dim = mesh->dim;
  
  if (info->disp) {
    cell_info->disp_bfcts = info->disp->fe_space->bas_fcts;
    if (cell_info->disp_bfcts)
      INIT_OBJECT(cell_info->disp_bfcts);
    cell_info->el_disp = get_el_real_vec_d(cell_info->disp_bfcts);
    fill_flags |= cell_info->disp_bfcts->fill_flags;
  } else {
    cell_info->disp_bfcts = NULL;
  }
  cell_count=1;

  TRAVERSE_FIRST(mesh, -1, CALL_LEAF_EL|FILL_COORDS|fill_flags) {

    if (!coords_only) {
      if ((cell_info->disp_bfcts &&
	  INIT_ELEMENT(el_info, cell_info->disp_bfcts) == INIT_EL_TAG_NULL)
	  ||
	  INIT_ELEMENT(el_info, cell_info->bas_fcts) == INIT_EL_TAG_NULL) {
	continue;
      }

      cell_info->el_info = el_info;
      if (info->scalar==1) {
	cell_info->el_uh = fill_el_real_vec(NULL, el_info->el, u);
      } else {
	cell_info->el_uh_d = fill_el_real_vec_d(NULL, el_info->el, ud);
      }
    }
    
    cell_info->dim = dim;

    /* correct element coordinates for parametric elements */
    if (parametric) {
      parametric->init_element(el_info, parametric);
      cell_info->coord_to_world = parametric->coord_to_world;
      cell_info->coord_to_world(
	el_info, NULL, N_VERTICES(dim), vertex_bary, wc);
      for(i = 0; i < N_VERTICES(dim);i++)
	cell_info->coords[i] = wc[i];
    } else {
      cell_info->coord_to_world = NULL;
      if (info->disp &&
	  INIT_ELEMENT(el_info, cell_info->disp_bfcts) != INIT_EL_TAG_NULL) {
	/* add displacements */
	fill_el_real_vec_d(cell_info->el_disp, el_info->el, info->disp);
	for(i = 0; i < N_VERTICES(dim);i++) {
	  eval_uh_dow(wc[i], vertex_bary[i],
		      cell_info->el_disp, cell_info->disp_bfcts);
	  for (j=0; j<DIM_OF_WORLD; ++j)
	    wc[i][j] += el_info->coord[i][j];
	  cell_info->coords[i] = wc[i];
	}
      } else {
	cell_info->coord_to_world = NULL;
	for(i = 0; i < N_VERTICES(dim);i++)
	  cell_info->coords[i] = el_info->coord[i];
      }
    }
    for (i = 0; i < N_VERTICES(dim);i++)
	cell_info->bary[i] = vertex_bary[i];
    
    if (!coords_only) {
      if (info->scalar == 1) {
	for (i = 0; i < N_VERTICES(dim);i++) {
	  cell_info->f[i] =
	    eval_uh(vertex_bary[i], cell_info->el_uh, cell_info->bas_fcts);
	  if (cell_info->f[i] > max_uh) {
	    max_uh = cell_info->f[i];
	  }
	  if (cell_info->f[i] < min_uh) {
	    min_uh = cell_info->f[i];
	  }	
	}
      } else if (info->scalar == 2) {
	REAL_D f_d;
	for (i = 0; i < N_VERTICES(dim);i++) {
	  eval_uh_dow(f_d,
		      vertex_bary[i], cell_info->el_uh_d, cell_info->bas_fcts);
	  cell_info->f[i] = NORM_DOW(f_d);
	}
      }
    }
    
    if(dim == 3)
      cell_info->el_type = el_info->el_type;

    if (info->refinement)
      recursive_cell(m, sxf, dim*info->refinement, cell_info, info, cell_count
		     COORDS_ONLY_ARG);
    else
      recursive_cell(m, sxf, 0, cell_info, info, cell_count COORDS_ONLY_ARG);

    ++cell_count;
  } TRAVERSE_NEXT();

  if (info->disp) {
    free_el_real_vec_d(cell_info->el_disp);
  }

  return;
}


static int next_dialog = 1;

/* Next two functions meant for external add-ons, s.t. those can get
 * hold of the dialog switch.
 */
int gltools_get_next_dialog(void)
{
  return next_dialog;
}

void gltools_set_next_dialog(int dialog)
{
  next_dialog = dialog;
}

static int glrKeyAction_Q(glRenderer rnd, int mask)
{
  int   dialog;
  glrGetDialog(rnd, &dialog);
  glrSetDialog(rnd, !dialog);
  next_dialog = 0;
  return GL_TRUE;
}

static int glrKeyAction_q(glRenderer rnd, int mask)
{
  int   dialog;
  glrGetDialog(rnd, &dialog);
  glrSetDialog(rnd, !dialog);
  next_dialog = 1;
  return GL_TRUE;
}

static int glrKeyAction_s(glRenderer rnd, int mask)
{
  glrSetDialog(rnd, 1);
  next_dialog = 1;
  return GL_TRUE;
}

static int glrKeyAction_X(glRenderer rnd, int mask)
{
  exit(0);

  return GL_TRUE;
}

static void xmin_xmax(MESH *mesh, REAL_D xmin, REAL_D xmax)
{
#if 0
  PARAMETRIC *parametric;
#endif
  EL_INFO    mel_info[1];
  MACRO_EL   *mel;
  int        m, n, i, dim = mesh->dim;

  if (!mesh)
    return;

  mel_info->fill_flag = FILL_COORDS;

#if 0
  parametric = mesh->parametric;
  if(parametric)
    mel_info->fill_flag |= FILL_PROJECTION;
#endif

  for (n = 0; n < DIM_OF_WORLD; n++)
  {
    xmin[n] = LARGE;
    xmax[n] = -LARGE;
  }

  for (m = 0; m < mesh->n_macro_el; m++) {
    mel = mesh->macro_els + m;

    fill_macro_info(mesh, mel, mel_info);

#if 0 /* cH: This just cannot work this way */
    /* correct element coordinates for parametric elements */
    if (parametric) {
      parametric->init_element(mel_info, parametric);
      parametric->coord_to_world(mel_info, NULL, N_VERTICES(dim),
				 vertex_bary, mel_info->coord);
    }
#endif
    
    for (n = 0; n < DIM_OF_WORLD; n++) {
      for (i=0; i<N_VERTICES(dim); i++) {
	xmin[n] = MIN(xmin[n], mel_info->coord[i][n]);
	xmax[n] = MAX(xmax[n], mel_info->coord[i][n]);
      }
    }
  }
  return;
}

GLTOOLS_WINDOW open_gltools_window(const char *title, const char *geometry,
				   const REAL *world, MESH *mesh, int dialog)
{
  FUNCNAME("open_gltools_window");
  REAL             xmin[3], xmax[3];
  glRenderer       rnd;
  glRendererState  state;
  int              x = 0, y = 0, w = 300, h = 300, dim = mesh->dim;
  const char       *s;

  if (dim<=0) {
    ERROR("Sorry, only implemented for dim > 0.\n");
    return(NULL);
  } 

  if (dim==1 && DIM_OF_WORLD==3) {
    ERROR("Sorry, not implemented for dim==1 && DIM_OF_WORLD==3.\n");
    return(NULL);
  } 

  INFO(0,2,"\n");
  if (geometry)
  {
    if (strchr(geometry, 'x'))
    {
      if ((s = strchr(geometry, '+')))
      {
	if (strchr(s+1, '+'))   /*  "wwxhh+xx+yy"   */
	  sscanf(geometry, "%dx%d+%d+%d", &w, &h, &x, &y);
	else                     /*  "wwxhh+xx"      */
	  sscanf(geometry, "%dx%d+%d", &w, &h, &x);
      }
      else                       /*  "wwxhh"         */
      {
	sscanf(geometry, "%dx%d", &w, &h);
      }
    }
    else
    {
      if ((s = strchr(geometry, '+')))
      {
	if (strchr(s+1, '+'))    /*  "xx+yy"         */
	  sscanf(s, "+%d+%d", &x, &y);
	else                     /*  "wwxhh+xx"      */
	  sscanf(s, "+%d", &x);
      }
    }
  }
  if (!title)
    title = "ALBERTA";
  rnd = glrCreate((char *) title, x, y, w, h);
  glrSetDialog(rnd, dialog);
  state = glrGetRendererState(rnd);
  state->sensitivity *= M_SQRT2;

  glrRegisterKeyAction(rnd, GLW_Q, glrKeyAction_Q, "Q: quit dialog");
  glrRegisterKeyAction(rnd, GLW_q, glrKeyAction_q, "q: quit");
  glrRegisterKeyAction(rnd, GLW_s, glrKeyAction_s, "s: enter dialog");
  glrRegisterKeyAction(rnd, GLW_X, glrKeyAction_X, "X: exit program");

  if (world)
  {
    xmin[0] = world[0];
    xmax[0] = world[1];
#if DIM_OF_WORLD == 2
    xmin[1] = world[2];
    xmax[1] = world[3];
#if DIM_OF_WORLD == 3
    xmin[2] = world[4];
    xmax[2] = world[5];
#endif
#endif
  }
  else if (mesh)
  {
    xmin_xmax(mesh, xmin, xmax);
  }
  else
  {
    xmin[0] = 0.0;
    xmax[0] = 1.0;
    xmin[1] = 0.0;
    xmax[1] = 1.0;
    xmin[2] = 0.0;
    xmax[2] = 1.0;
  }

  if (dim<DIM_OF_WORLD && dim==1) {
    xmin[0] -= STIP_WIDTH*0.5; 
    xmin[1] -= STIP_WIDTH*0.5; 
    xmax[0] += STIP_WIDTH*0.5; 
    xmax[1] += STIP_WIDTH*0.5; 
  }

#if DIM_OF_WORLD == 1
  glrSetAxisName(rnd, 0, "x");
  glrSetAxisName(rnd, 2, "f(x)");
  xmin[1] = 0.0;
  xmax[1] = STIP_WIDTH;
  xmin[2] = -1.0;
  xmax[2] = 1.0;
#elif DIM_OF_WORLD == 2
  glrSetAxisName(rnd, 0, "x");
  glrSetAxisName(rnd, 1, "y");
  glrSetAxisName(rnd, 2, "f(x,y)");
  xmin[2] = -0.5*MAX(xmax[0]-xmin[0], xmax[1]-xmin[1]);
  xmax[2] = -xmin[2];
#elif DIM_OF_WORLD == 3
  glrSetAxisName(rnd, 0, "x");
  glrSetAxisName(rnd, 1, "y");
  glrSetAxisName(rnd, 2, "z");
#endif

  glrSetVolume(rnd, xmin[0], xmax[0], xmin[1], xmax[1], xmin[2], xmax[2]);

  return((GLTOOLS_WINDOW)  rnd);
}

void close_gltools_window(GLTOOLS_WINDOW win)
{
  if (win)
    glrDestroy((glRenderer) win);
  return;
}

void gltools_disp_drv(GLTOOLS_WINDOW win, const DOF_REAL_VEC *u,
		      REAL min, REAL max, const DOF_REAL_VEC_D *disp, REAL time)
{
  FUNCNAME("gltools_disp");
  static GLTOOLS_INFO *first = NULL;
  GLTOOLS_INFO        *info;
  MESH                *mesh;
  int                 degree;
  int                 ndof;
  glMesh              glm;
  glRenderer          rnd;

  INFO(0,2,"\n");
  if (!u || !win)  return;
  
  min_uh = 1e8;
  max_uh = -1e8;
  
  rnd = (glRenderer) win;
  glrGetDialog(rnd, &next_dialog);

  mesh       = u->fe_space->mesh;
  degree     = u->fe_space->bas_fcts->degree;
  ndof       = u->fe_space->admin->size_used;

#if 0
  if(mesh->dim != DIM_OF_WORLD) {
    ERROR("Sorry, only implemented for dim == DIM_OF_WORLD.\n");
    return;
  } 
#endif

  for (info = first; info; info = info->next)
    if (info->uh == u)  break;

  if (!info)
  {
    info = MEM_CALLOC(1, GLTOOLS_INFO);
    info->degree      = degree;
    info->refinement  = 0;
    info->uh          = u;
    info->scalar      = 1;

    info->next = first;
    first = info;
  }
  info->disp = disp;

  glm = glmCreate(ndof,
		  mesh->n_elements,
		  MAX(2,DIM_OF_WORLD),
		  info,m_cell_loop);

  glrRegisterKeyAction(rnd, GLW_P, glrKeyAction_P, "P: use Lagrange grid");
  key_P_info = info;

  if (isfinite(time) && u->name) {
    glrSetUserInfo(rnd, "%s t=%e", u->name, time);
  } else if (isfinite(time)) {
    glrSetUserInfo(rnd, "t=%e", time);
  }

  glmSetVoffset(glm, 0);
  glrSetInfoCallback(rnd, (glrDrawCallback) glmDrawInfo);

  if (min >= max) {
#if 0
      min = dof_min(u);
      max = dof_max(u);
#else
      L8_uh_at_qp(&min, &max, NULL, u);
#endif
  }
  if (ABS(min - max) < 1.e-25) min++;
  glmSetFunction(glm, u->vec, min, max);
  glRender(rnd, (glrDrawCallback)glmDraw, glm);
#if GLTOOLS_VERSION == 25
  glRender(rnd, NULL, NULL);
#endif
  glrSetInfoCallback(rnd, NULL);
  glmDestroy(glm);

  key_P_info = NULL;
  glrRegisterKeyAction(rnd, GLW_P, NULL, NULL);
  glrSetDialog(rnd, next_dialog);

  MSG("min_uh: %e, max_uh: %e\n", min_uh, max_uh);

  return;
}

void gltools_drv(GLTOOLS_WINDOW win, const DOF_REAL_VEC *u,
		 REAL min, REAL max, REAL time)
{
  gltools_disp_drv(win, u, min, max, NULL, time);
}

void gltools_disp_drv_d(GLTOOLS_WINDOW win, const DOF_REAL_VEC_D *u, 
			REAL min, REAL max, const DOF_REAL_VEC_D *disp,
			REAL time)
{
  FUNCNAME("gltools_disp_drv_d");
  static GLTOOLS_INFO *first = NULL;
  GLTOOLS_INFO        *info;
  MESH                *mesh;
  int                 degree;
  int                 ndof;
  glMesh              glm;
  glRenderer          rnd;

  INFO(0,2,"\n");
  if (!u || !win)  return;

  rnd = (glRenderer) win;
  glrGetDialog(rnd, &next_dialog);

  mesh       = u->fe_space->mesh;
  degree     = u->fe_space->bas_fcts->degree;
  ndof       = u->fe_space->admin->size_used;

  if(mesh->dim != DIM_OF_WORLD) {
    ERROR("Sorry, only implemented for dim == DIM_OF_WORLD.\n");
    return;
  } 

  for (info = first; info; info = info->next)
    if (info->uh_d == u)  break;

  if (!info)
  {
    info = MEM_CALLOC(1, GLTOOLS_INFO);
    info->degree      = degree;
    info->refinement  = 0;
    info->uh_d        = u;
    info->scalar      = 2;

    info->next = first;
    first = info;
  }
  info->disp = disp;

  glm = glmCreate(ndof,
		  mesh->n_elements,
		  MAX(2,DIM_OF_WORLD),
		  info, m_cell_loop);

  glrRegisterKeyAction(rnd, GLW_P, glrKeyAction_P, "P: use Lagrange grid");
  key_P_info = info;

  if (isfinite(time) && u->name) {
    glrSetUserInfo(rnd, "%s t=%e", u->name, time);
  } else if (isfinite(time)) {
    glrSetUserInfo(rnd, "t=%e", time);
  }

  glmSetVoffset(glm, 0);
  glrSetInfoCallback(rnd, (glrDrawCallback) glmDrawInfo);

  if (min >= max) {
#if 0
      min = dof_min_dow(u);
      max = dof_max_dow(u);
#else
      L8_uh_at_qp_dow(&min, &max, NULL, u);
#endif
  }
  glmSetFunction(glm, (REAL *)u->vec, min, max);
  glRender(rnd, (glrDrawCallback)glmDraw, glm);
#if GLTOOLS_VERSION == 25
  glRender(rnd, NULL, NULL);
#endif
  glrSetInfoCallback(rnd, NULL);
  glmDestroy(glm);

  key_P_info = NULL;
  glrRegisterKeyAction(rnd, GLW_P, NULL, NULL);
  glrSetDialog(rnd, next_dialog);

  MSG("min_uh: %e, max_uh: %e\n", min, max);

  return;
}

void gltools_drv_d(GLTOOLS_WINDOW win, const DOF_REAL_VEC_D *u, 
		   REAL min, REAL max, REAL time)
{
  gltools_disp_drv_d(win, u, min, max, NULL, time);
}

void gltools_disp_vec(GLTOOLS_WINDOW win, const DOF_REAL_VEC_D *u, 
		      REAL min, REAL max, const DOF_REAL_VEC_D *disp,
		      REAL time)
{
  FUNCNAME("gltools_disp_vec");
  GLTOOLS_INFO        info[1] = {{0}};
  MESH                *mesh;
  int                 degree;
  glMesh              glm;
  glRenderer          rnd;

  INFO(0,2,"\n");
  if (!u || !win)  return;

  rnd = (glRenderer) win;
  glrGetDialog(rnd, &next_dialog);

  mesh       = u->fe_space->mesh;
  degree     = u->fe_space->bas_fcts->degree;

  if(mesh->dim != DIM_OF_WORLD) {
    ERROR("Sorry, only implemented for dim == DIM_OF_WORLD.\n");
    return;
  } 

  info->degree      = degree;
  info->refinement  = 0;
  info->uh_d        = u;
  info->scalar      = 0;
  info->disp = disp;

  glm = glmCreate(1,
		  1,
		  MAX(2,DIM_OF_WORLD),
		  info, m_cell_loop);

  glrRegisterKeyAction(rnd, GLW_P, glrKeyAction_P, "P: use Lagrange grid");
  key_P_info = info;

  if (isfinite(time) && u->name) {
    glrSetUserInfo(rnd, "%s t=%e", u->name, time);
  } else if (isfinite(time)) {
    glrSetUserInfo(rnd, "t=%e", time);
  }

  glmSetVoffset(glm, 0);
  glrSetInfoCallback(rnd, (glrDrawCallback) glmDrawInfo);

  if (min >= max) {
#if 0
      min = dof_min_dow(u);
      max = dof_max_dow(u);
#else
      L8_uh_at_qp_dow(&min, &max, NULL, u);
#endif
  }
/*   glmSetFunction(glm, (REAL *)u->vec, min, max); */
  glmSetCellFlux(glm, glm_cellflux, min, max);
  glRender(rnd, (glrDrawCallback)glmDraw, glm);
#if GLTOOLS_VERSION == 25
  glRender(rnd, NULL, NULL);
#endif
  glrSetInfoCallback(rnd, NULL);
  glmDestroy(glm);

  key_P_info = NULL;
  glrRegisterKeyAction(rnd, GLW_P, NULL, NULL);
  glrSetDialog(rnd, next_dialog);

  return;
}

void gltools_vec(GLTOOLS_WINDOW win, const DOF_REAL_VEC_D *u, 
		 REAL min, REAL max, REAL time)
{
  gltools_disp_vec(win, u, min, max, NULL, time);
}

static void est_loop(glMesh m,
		     void *data, glmSimplexCallback sxf COORDS_ONLY_DECL)
{
  GLTOOLS_INFO     *info = (GLTOOLS_INFO *)data;
  MESH             *mesh = info->mesh;
  PARAMETRIC       *parametric = mesh->parametric;
  int              i, j, ic = 0, dim = mesh->dim;
  REAL             *coord[N_VERTICES_MAX], el_val;
  REAL_D           wc[N_VERTICES_MAX];
  int              dof0[N_VERTICES_MAX]={0};
  const EL_REAL_VEC_D *cell_disp;
  const BAS_FCTS   *disp_bfcts;
  FLAGS            fill_flags = FILL_NOTHING;
  
  disp_bfcts = info->disp ? info->disp->fe_space->bas_fcts : NULL;
  if (disp_bfcts) {
    fill_flags |= disp_bfcts->fill_flags;
  }

  TRAVERSE_FIRST(mesh, -1, CALL_LEAF_EL|FILL_COORDS|fill_flags) {

    /* correct element coordinates for parametric elements */
    if (parametric) {
      parametric->init_element(el_info, parametric);
      parametric->coord_to_world(el_info, NULL, N_VERTICES(dim),
				 vertex_bary, wc);
    } else {
      if (disp_bfcts && INIT_ELEMENT(el_info, disp_bfcts) != INIT_EL_TAG_NULL) {
	/* add displacements */
	cell_disp = fill_el_real_vec_d(NULL, el_info->el, info->disp);
	for(i = 0; i < N_VERTICES(dim);i++)
	{
	  eval_uh_dow(wc[i],
		    vertex_bary[i], cell_disp, disp_bfcts);
	  for (j=0; j<DIM_OF_WORLD; ++j)
	    wc[i][j] += el_info->coord[i][j];
	}
      } else {
	for (i = 0; i < N_VERTICES(dim); i++)
	  COPY_DOW(el_info->coord[i], wc[i]);
      }
    }

    for (i = 0; i < N_VERTICES(dim); i++)
      coord[i] = wc[i];

    if (!coords_only) {
      el_val = info->get_est(el_info->el);
    }

    if(dim==1)
      simplex_1d_2d(m, ic+1, &el_val, 1, coord, sxf);
    else
      sxf(m, ic+1, 0, &el_val, dof0, coord);

    ic++;
  } TRAVERSE_NEXT();
}

void gltools_disp_est(GLTOOLS_WINDOW win, MESH *mesh, REAL (*get_est)(EL *),
		      REAL min, REAL max, const DOF_REAL_VEC_D *disp,
		      REAL time)
{
  FUNCNAME("gltools_est");
  static GLTOOLS_INFO gltools_info ={0};
  glMesh          glm;
  glRenderer      rnd;
  static REAL     val=0.0;
  const EL_INFO   *el_info;
  TRAVERSE_STACK  *stack;

  INFO(0,2,"\n");

  if (!mesh || !win || !get_est)  return;

  rnd = (glRenderer) win;
  glrGetDialog(rnd, &next_dialog);

  gltools_info.mesh = mesh;
  gltools_info.get_est = get_est;
  gltools_info.disp = disp;

  if(mesh->dim != DIM_OF_WORLD) {
    ERROR("Sorry, only implemented for dim == DIM_OF_WORLD.\n");
    return;
  } 

  if (isfinite(time)) {
    glrSetUserInfo(rnd, "t=%e", time);
  }

  glm=glmCreate(mesh->n_elements, mesh->n_elements, MAX(2,DIM_OF_WORLD),
		&gltools_info, est_loop);
  glmSetVoffset(glm,0);
  glrSetInfoCallback(rnd,(glrDrawCallback)glmDrawInfo);

  if (min >= max) 
  {
    min = LARGE;
    max = -LARGE;
    stack = get_traverse_stack();
    el_info = traverse_first(stack, mesh, -1, CALL_LEAF_EL);
    while (el_info)
    {
      REAL  est = get_est(el_info->el);
      min = MIN(min, est);
      max = MAX(max, est);
      el_info = traverse_next(stack,el_info);
    }
    free_traverse_stack(stack);
  }

  if (min > max) min = max = 0.0;

  gltools_info.min = min;
  gltools_info.max = max;
  glmSetFunction(glm, &val, min, max);
  glRender(rnd, (glrDrawCallback)glmDraw, glm);
#if GLTOOLS_VERSION == 25
  glRender(rnd, NULL, NULL);
#endif
  glrSetInfoCallback(rnd, NULL);
  glmDestroy(glm);
  glrSetDialog(rnd, next_dialog);

  return;
}


void gltools_est(GLTOOLS_WINDOW win, MESH *mesh, REAL (*get_est)(EL *),
		 REAL min, REAL max, REAL time)
{
  /* FUNCNAME("gltools_est"); */
  gltools_disp_est(win, mesh, get_est, min, max, NULL, time);
}


struct mesh_loop_data
{
    MESH  *mesh;
    int   mark;
    const DOF_REAL_VEC_D *disp;
};

static void mesh_loop(glMesh m, void *ud,
		      glmSimplexCallback sxf COORDS_ONLY_DECL)
{
  struct mesh_loop_data *data = (struct mesh_loop_data *)ud;
  PARAMETRIC       *parametric = data->mesh->parametric;
  int              i, j, ic = 1, dim = data->mesh->dim;
  REAL             *coord[N_VERTICES_MAX];
  REAL             val = 0.0;
  REAL_D           wc[N_VERTICES_MAX];
  const EL_REAL_VEC_D *el_disp;
  const BAS_FCTS   *disp_bas_fcts=NULL;
  static int       dof[N_VERTICES_MAX]={0};
  FLAGS            fill_flags = FILL_NOTHING;
  
  if (data->disp) {
      disp_bas_fcts = data->disp->fe_space->bas_fcts;
      fill_flags |= disp_bas_fcts->fill_flags;
  }

  TRAVERSE_FIRST(data->mesh, -1, CALL_LEAF_EL|FILL_COORDS|fill_flags) {

    if (disp_bas_fcts &&
	INIT_ELEMENT(el_info, disp_bas_fcts) == INIT_EL_TAG_NULL)
      continue;

    if (data->mark)
    {
      if (el_info->el->mark > 0)        val =  1.0;
      else if (el_info->el->mark < 0)   val = -1.0;
      else                              val =  0.0;
    }

    /* correct element coordinates for parametric elements */
    if (parametric) {
      parametric->init_element(el_info, parametric);
      parametric->coord_to_world(el_info, NULL, N_VERTICES(dim),
				 vertex_bary, wc);
    }
    else
    {
	if (data->disp) {
	  el_disp = fill_el_real_vec_d(NULL, el_info->el, data->disp);
	  for(i = 0; i < N_VERTICES(dim);i++) {
	    eval_uh_dow(wc[i], vertex_bary[i], el_disp, disp_bas_fcts);
	    for (j=0; j<DIM_OF_WORLD; ++j)
	      wc[i][j] += el_info->coord[i][j];
	    }
	} else {
	    for (i = 0; i < N_VERTICES(dim); i++)
		COPY_DOW(el_info->coord[i], wc[i]);
	}
    }

    for(i = 0; i < N_VERTICES(dim); i++)
      coord[i] = wc[i];

    if(dim==1)
      simplex_1d_2d(m, ic++, &val, 1, coord, sxf);
    else
      sxf(m, ic++, 0, &val, dof, coord);

  } TRAVERSE_NEXT();
}

void gltools_mesh(GLTOOLS_WINDOW win, MESH *mesh, bool mark, REAL time)
{
  FUNCNAME("gltools_mesh");
  struct mesh_loop_data data[1];
  glMesh          glm;
  glRenderer      rnd;
  static REAL     val=0.0;

  INFO(0,2,"\n");

  if (!(data->mesh = mesh) || !win)  return;

  if(mesh->dim != DIM_OF_WORLD) {
    ERROR("Sorry, only implemented for dim == DIM_OF_WORLD.\n");
    return;
  } 

  data->disp = NULL;

  rnd = (glRenderer) win;
  glrGetDialog(rnd, &next_dialog);

  if (isfinite(time)) {
    glrSetUserInfo(rnd, "t=%e", time);
  }

  if ((data->mark = mark))
  {
    glm=glmCreate(1, mesh->n_elements, MAX(2,DIM_OF_WORLD), data, mesh_loop);
    glmSetVoffset(glm,0);
    glrSetInfoCallback(rnd,(glrDrawCallback)glmDrawInfo);
    glmSetFunction(glm, &val, -1.0, 1.0);
  }
  else
  {
    glm=glmCreate(1, mesh->n_elements, MAX(2,DIM_OF_WORLD), data, mesh_loop);
    glmSetVoffset(glm,0);
    glrSetInfoCallback(rnd,(glrDrawCallback)glmDrawInfo);
    glmSetFunction(glm, &val, 0.0, 1.0);
  }
  glRender(rnd, (glrDrawCallback)glmDraw, glm);
#if GLTOOLS_VERSION == 25
  glRender(rnd, NULL, NULL);
#endif
  glrSetInfoCallback(rnd, NULL);
  glmDestroy(glm);
  glrSetDialog(rnd, next_dialog);

  return;
}

void gltools_disp_mesh(GLTOOLS_WINDOW win, MESH *mesh, bool mark,
		       const DOF_REAL_VEC_D *disp, REAL time)
{
  FUNCNAME("gltools_disp_mesh");
  struct mesh_loop_data data[1];
  glMesh          glm;
  glRenderer      rnd;
  static REAL     val=0.0;

  INFO(0,2,"\n");

  if (!(data->mesh = mesh) || !win)  return;

  if(mesh->dim != DIM_OF_WORLD) {
    ERROR("Sorry, only implemented for dim == DIM_OF_WORLD.\n");
    return;
  } 

  data->disp = disp;

  rnd = (glRenderer) win;
  glrGetDialog(rnd, &next_dialog);

  if (isfinite(time)) {
    glrSetUserInfo(rnd, "t=%e", time);
  }

  if ((data->mark = mark))
  {
    glm=glmCreate(1, mesh->n_elements, MAX(2,DIM_OF_WORLD), data, mesh_loop);
    glmSetVoffset(glm,0);
    glrSetInfoCallback(rnd,(glrDrawCallback)glmDrawInfo);
    glmSetFunction(glm, &val, -1.0, 1.0);
  }
  else
  {
    glm=glmCreate(1, mesh->n_elements, MAX(2,DIM_OF_WORLD), data, mesh_loop);
    glmSetVoffset(glm,0);
    glrSetInfoCallback(rnd,(glrDrawCallback)glmDrawInfo);
    glmSetFunction(glm, &val, 0.0, 1.0);
  }
  glRender(rnd, (glrDrawCallback)glmDraw, glm);
#if GLTOOLS_VERSION == 25
  glRender(rnd, NULL, NULL);
#endif
  glrSetInfoCallback(rnd, NULL);
  glmDestroy(glm);
  glrSetDialog(rnd, next_dialog);

  return;
}
