/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     traverse_r.c                                                   */
/*                                                                          */
/*                                                                          */
/* description:                                                             */
/*           recursive mesh traversal routines - common ?d part             */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Alfred Schmidt                                               */
/*             Zentrum fuer Technomathematik                                */
/*             Fachbereich 3 Mathematik/Informatik                          */
/*             Univesitaet 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)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#include "alberta_intern.h" /* to keep stuff consistent */
#include "alberta.h"

/* traverse hierarchical mesh,  call el_fct() for each/some element */

typedef struct traverse_info TRAVERSE_INFO;

struct traverse_info
{
  MESH  *mesh;
  FLAGS trav_flags;
  FLAGS fill_flag;
  int   level;
  void  (*el_fct)(const EL_INFO *, void *);
  void  *data;
};

#include "traverse_r_1d.c"
#if DIM_MAX > 1
#include "traverse_r_2d.c"
#if DIM_MAX > 2
#include "traverse_r_3d.c"
#endif
#endif


/*--------------------------------------------------------------------------*/
/* common traversal routines for all dimensions                             */
/*--------------------------------------------------------------------------*/

void fill_macro_info(MESH *mesh, const MACRO_EL *mel, EL_INFO *el_info)
{
  FUNCNAME("fill_macro_info");

  TEST_EXIT(mesh, "No mesh specified!\n");

  switch(mesh->dim) {
  case 0:
    el_info->mesh     = mesh;
    el_info->macro_el = mel;
    el_info->el       = mel->el;
    el_info->parent   = NULL;
    el_info->level    = 0;

    if (el_info->fill_flag & FILL_COORDS) {
      TEST_EXIT(mel->coord[0], "no mel->coord[0]\n");
      COPY_DOW(*mel->coord[0], el_info->coord[0]);
    }
    if (el_info->fill_flag & FILL_MASTER_INFO) {
      /* Simply copy-over the macro-element info. */
      MACRO_EL *mst_mel = mel->master.macro_el;
      int      mst_ov   = mel->master.opp_vertex;

      el_info->master.el         = mst_mel->el;
      el_info->master.opp_vertex = mst_ov;
      if (el_info->fill_flag & FILL_COORDS) {
	COPY_DOW(*mst_mel->coord[mst_ov], el_info->master.opp_coord);
      }
      if ((el_info->fill_flag & FILL_MASTER_NEIGH) &&
	  mst_mel->neigh[mst_ov] &&
	  (!mesh->is_periodic ||
	   !(el_info->fill_flag & FILL_NON_PERIODIC) ||
	   mst_mel->neigh_vertices[mst_ov][0] < 0)) {
	MACRO_EL *mstn_mel = mst_mel->neigh[mst_ov];
	int      mstn_ov  = mst_mel->opp_vertex[mst_ov];
	
	el_info->mst_neigh.el         = mstn_mel->el;
	el_info->mst_neigh.opp_vertex = mstn_ov;
	if (el_info->fill_flag & FILL_COORDS) {
	  COPY_DOW(*mstn_mel->coord[mstn_ov], el_info->mst_neigh.opp_coord);
	}
      }
    }
    break;
  case 1:
    fill_macro_info_1d(mesh, mel, el_info);
    break;
#if DIM_MAX > 1
  case 2:
    fill_macro_info_2d(mesh, mel, el_info);
    break;
#if DIM_MAX > 2
  case 3:
    fill_macro_info_3d(mesh, mel, el_info);
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim == %d!\n", mesh->dim);
  }
}


void fill_elinfo(int ichild, FLAGS mask, const EL_INFO *parent_info, EL_INFO *el_info)
{
  FUNCNAME("fill_elinfo");
  int dim = parent_info->mesh->dim;

  switch(dim) {
  case 1:
    fill_elinfo_1d(ichild, mask, parent_info, el_info);
    break;
#if DIM_MAX > 1
  case 2:
    fill_elinfo_2d(ichild, mask, parent_info, el_info);
    break;
#if DIM_MAX > 2
  case 3:
    fill_elinfo_3d(ichild, mask, parent_info, el_info);
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim == %d!\n", dim);
  }
}

/*--------------------------------------------------------------------------*/
/*   recursive_traverse:                                		    */
/*   -------------------                                		    */
/*   recursively traverse the mesh hierarchy tree       		    */
/*   call the routine traverse_info->el_fct(el_info) with    		    */
/*    - all tree leaves                                 		    */
/*    - all tree leaves at a special level              		    */
/*    - all tree elements in pre-/in-/post-order        		    */
/*   depending on the traverse_info->level variable     		    */
/*--------------------------------------------------------------------------*/

static void recursive_traverse(EL_INFO *el_info, TRAVERSE_INFO *trinfo)
{
  EL            *el = el_info->el;
  EL_INFO       el_info_new = {0};
  int           mg_level;

  if (trinfo->trav_flags & CALL_LEAF_EL) {
    if (el->child[0]) {
      fill_elinfo(0, trinfo->fill_flag, el_info, &el_info_new);
      recursive_traverse(&el_info_new, trinfo);
      fill_elinfo(1, trinfo->fill_flag, el_info, &el_info_new);
      recursive_traverse(&el_info_new, trinfo);
    } else {
      el_info->el_geom_cache.current_el = NULL;
      trinfo->el_fct(el_info, trinfo->data);
    }
    return;
  }

  if (trinfo->trav_flags & CALL_LEAF_EL_LEVEL) {
    if (el->child[0]) {
      if ((el_info->level >= trinfo->level))
	return;
      fill_elinfo(0, trinfo->fill_flag, el_info, &el_info_new);
      recursive_traverse(&el_info_new, trinfo);
      fill_elinfo(1, trinfo->fill_flag, el_info, &el_info_new);
      recursive_traverse(&el_info_new, trinfo);
    } else if (el_info->level == trinfo->level) {
      el_info->el_geom_cache.current_el = NULL;
      trinfo->el_fct(el_info, trinfo->data);
    }
    return;
  }

  if (trinfo->trav_flags & CALL_EL_LEVEL) {
    if (el_info->level == trinfo->level) {
      el_info->el_geom_cache.current_el = NULL;
      trinfo->el_fct(el_info, trinfo->data);
    } else if (el_info->level > trinfo->level) {
      return;
    } else {
      if (el->child[0]) {
	fill_elinfo(0, trinfo->fill_flag, el_info, &el_info_new);
	recursive_traverse(&el_info_new, trinfo);
	fill_elinfo(1, trinfo->fill_flag, el_info, &el_info_new);
	recursive_traverse(&el_info_new, trinfo);
      }
    }
    return;
  }

  if (trinfo->trav_flags & CALL_MG_LEVEL) {
    int dim = trinfo->mesh->dim;

    mg_level = (el_info->level + dim-1) / dim;

    if (mg_level > trinfo->level) return;

    if (!(el->child[0])) {
      el_info->el_geom_cache.current_el = NULL;
      trinfo->el_fct(el_info, trinfo->data);
      return;
    }

    if ((mg_level == trinfo->level) && ((el_info->level % dim) == 0)) {
      el_info->el_geom_cache.current_el = NULL;
      trinfo->el_fct(el_info, trinfo->data);
      return;
    }

    fill_elinfo(0, trinfo->fill_flag, el_info, &el_info_new);
    recursive_traverse(&el_info_new, trinfo);

    fill_elinfo(1, trinfo->fill_flag, el_info, &el_info_new);
    recursive_traverse(&el_info_new, trinfo);

    return;
  }

  if (trinfo->trav_flags & CALL_EVERY_EL_PREORDER) {
    el_info->el_geom_cache.current_el = NULL;
    trinfo->el_fct(el_info, trinfo->data);
  }
      
  if (el->child[0]) {
    fill_elinfo(0, trinfo->fill_flag, el_info, &el_info_new);
    recursive_traverse(&el_info_new, trinfo);

    if (trinfo->trav_flags & CALL_EVERY_EL_INORDER) {
      el_info->el_geom_cache.current_el = NULL;
      trinfo->el_fct(el_info, trinfo->data);
    }

    fill_elinfo(1, trinfo->fill_flag, el_info, &el_info_new);
    recursive_traverse(&el_info_new, trinfo);
  } else if (trinfo->trav_flags & CALL_EVERY_EL_INORDER) {
    el_info->el_geom_cache.current_el = NULL;
    trinfo->el_fct(el_info, trinfo->data);
  }

  if (trinfo->trav_flags & CALL_EVERY_EL_POSTORDER) {
    el_info->el_geom_cache.current_el = NULL;
    trinfo->el_fct(el_info, trinfo->data);
  }

  return;
}

/*--------------------------------------------------------------------------*/
/*   mesh_traverse:                                            		    */
/*   --------------                                     		    */
/*   run through all macro elements and start mesh traversal      	    */
/*   call recursive_traverse() for each macro element's element		    */
/*--------------------------------------------------------------------------*/

void mesh_traverse(MESH *mesh, int level, FLAGS flags,
		   void (*el_fct)(const EL_INFO *, void *data), void *data)
{
  FUNCNAME("mesh_traverse");
  int           n;
  EL_INFO       el_info = {0};
  TRAVERSE_INFO traverse_info = {0};

  if (mesh == NULL)
    return;

  if (mesh->parametric && !mesh->parametric->use_reference_mesh) {
    flags &= ~(FILL_COORDS|FILL_OPP_COORDS);
  }

  if (!mesh->is_periodic) {
    flags &= ~FILL_NON_PERIODIC;
  } else if (flags & FILL_OPP_COORDS) {
    /* needed to access wall-transformations on the macro level */
    flags |= FILL_MACRO_WALLS;
  }

  TEST_EXIT(get_master(mesh) != NULL || (flags & FILL_MASTER_INFO) == 0,
	    "Requested filling of master element information, but this mesh "
	    "is not a trace-mesh of some master mesh.\n");

  traverse_info.mesh       = mesh;
  traverse_info.level      = level;
  traverse_info.el_fct     = el_fct;
  traverse_info.trav_flags = flags & ~FILL_ANY;
  traverse_info.fill_flag  = flags & FILL_ANY;
  traverse_info.data       = data;

  el_info.mesh       = mesh;
  el_info.fill_flag = traverse_info.fill_flag;

  if (flags & (CALL_LEAF_EL_LEVEL | CALL_EL_LEVEL | CALL_MG_LEVEL))
    TEST_EXIT(level >= 0, "invalid level: %d\n", level);

  for(n = 0; n < mesh->n_macro_el; n++) {
    fill_macro_info(mesh, mesh->macro_els + n, &el_info);

    if(mesh->dim > 0) {
      recursive_traverse(&el_info, &traverse_info);
    } else {
      el_info.el_geom_cache.current_el = NULL;
      traverse_info.el_fct(&el_info, traverse_info.data);
    }    
  }

}


/*--------------------------------------------------------------------------*/
/*   test_traverse_fct:                              		            */
/*   ------------------                                 		    */
/*   display information about an element from EL and EL_INFO structures    */
/*--------------------------------------------------------------------------*/

static void test_traverse_fct(const EL_INFO *el_info, void *data)
{
  FUNCNAME("test_traverse_fct");
  EL *el = el_info->el;
  int i, j, dim = el_info->mesh->dim;

  MSG("\n");
  MSG("traversing element %d at %p ---------------------------\n",
      INDEX(el), el);

  print_msg("level:        %3d\n", el_info->level);

  print_msg("macro_el:     %p\n", el_info->macro_el);

  if (el->child[0]) {
    print_msg("children:      ");
    for (i=0;i<2;i++)
      if (el->child[i])
	         print_msg(" %3d at %p", INDEX(el->child[i]), el->child[i]);
      else       print_msg(" ---");
    print_msg("\n");
  }

  if (el_info->fill_flag & FILL_COORDS) {
    print_msg("coords:      ");
    for (i=0; i<N_VERTICES(dim); i++)
    {
      print_msg("%1d: (", i);
      for (j=0; j<DIM_OF_WORLD; j++)
	print_msg("%10.6lf%s", el_info->coord[i][j],
		 j < (DIM_OF_WORLD-1) ? ", " : ")\n");
      if (i < (N_VERTICES(dim)-1)) print_msg("             ");
    }
  }

  if (el_info->fill_flag & FILL_NEIGH) {
#if ALBERTA_DEBUG
    print_msg("neigh index :");
    for (i=0; i<N_NEIGH(dim); i++)
      if (el_info->neigh[i]) print_msg(" %3d", INDEX(el_info->neigh[i]));
      else                  print_msg(" ---");
    print_msg("\n");
#endif
    print_msg("opp_vertex:  ");
    for (i=0; i<N_NEIGH(dim); i++)
      if (el_info->neigh[i]) print_msg(" %3d", el_info->opp_vertex[i]);
      else                  print_msg(" ---");
    print_msg("\n");

    print_msg("neigh:      ");
    for (i=0; i<N_NEIGH(dim); i++) print_msg(" %p", el_info->neigh[i]);
    print_msg("\n");
  }

  if (el_info->fill_flag & FILL_OPP_COORDS) {
    print_msg("opp_coords:  ");
    for (i=0; i<N_NEIGH(dim); i++) {
      if (el_info->neigh[i]) {
	print_msg("%1d (ov=%1d): (", i, el_info->opp_vertex[i]);
	for (j=0; j<DIM_OF_WORLD; j++)
	  print_msg("%10.6lf%s", el_info->opp_coord[i][j],
		   j < (DIM_OF_WORLD-1) ? ", " : ")\n");
      }
      else {
	print_msg("%1d             : ---\n", i);
      }
      if (i < (N_VERTICES(dim)-1)) print_msg("             ");
    }
  }

  if(el_info->fill_flag & FILL_PROJECTION) {
    print_msg("projections:\n");
    for(i = 0; i < N_NEIGH(dim) + 1; i++)
      print_msg("   no. %d: %p\n", i, wall_proj(el_info, i));
    print_msg("  active projection: %p\n", el_info->active_projection);
  }

  return;
}

/*--------------------------------------------------------------------------*/
/*   test_traverse:                                  		            */
/*   --------------                                     		    */
/*   display information about all elements from EL and EL_INFO structures  */
/*   by calling traverse with test_traverse_fct().      		    */
/*--------------------------------------------------------------------------*/

void test_traverse(MESH *mesh, int level, FLAGS fill_flag)
{
  FUNCNAME("test_traverse");

  if(!mesh) {
    ERROR("No mesh specified.\n");
    return;
  }

  MSG("with level    : %3d\n", level);
  MSG("with fill_flag:");
  if (fill_flag & FILL_ANY) {
    if (fill_flag & FILL_COORDS)       print_msg(" FILL_COORDS");
    if (fill_flag & FILL_BOUND)        print_msg(" FILL_BOUND");
    if (fill_flag & FILL_NEIGH)        print_msg(" FILL_NEIGH");
    if (fill_flag & FILL_OPP_COORDS)   print_msg(" FILL_OPP_COORDS");
    if (fill_flag & FILL_ORIENTATION)  print_msg(" FILL_ORIENTATION");
    if (fill_flag & FILL_PROJECTION)   print_msg(" FILL_PROJECTION");
    if (fill_flag & FILL_MACRO_WALLS)     print_msg(" FILL_MACRO_WALLS");
    if (fill_flag & FILL_NON_PERIODIC) print_msg(" FILL_NON_PERIODIC");
  }
  else print_msg(" none");
  print_msg("\n");

  mesh_traverse(mesh, level, fill_flag, test_traverse_fct, NULL);
  MSG("done.\n");
}
