/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     traverse_r_1d.c                                                */
/*                                                                          */
/* description:                                                             */
/*           recursive mesh traversal - 1d routines:                        */
/*           fill_macro_info_1d(), fill_elinfo_1d()                         */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
/*   fill_macro_info_1d:                              		            */
/*   ----------------                                   		    */
/*   fill EL_INFO structure for a macro element         		    */
/*--------------------------------------------------------------------------*/

#if DIM_MAX >= 2
static void fill_master_macro_info_1d(struct master_info *mst_info,
				      FLAGS fill_flags,
				      MACRO_EL *mst_mel,
				      int mst_ov);
#endif

static void fill_macro_info_1d(MESH *mesh, const MACRO_EL *mel,
			       EL_INFO *el_info)
{
  FUNCNAME("fill_macro_info_1d");
  EL       *nb;
  MACRO_EL *mnb;
  int      i, ov;

  el_info->mesh     = mesh;
  el_info->macro_el = mel;
  el_info->el       = mel->el;
  el_info->parent   = NULL;
  el_info->level    = 0;

  el_info->el_type     = 0;
  el_info->orientation = 1;

  /* Set FILL_NON_PERIODIC in fill-flags s.t. we do not need to check
   * mesh->is_periodic _and_ (fill_flag & FILL_NON_PERIODIC) all the
   * time. Afterwards it suffices to look at el_info->fill_flag.
   *
   * But beware: MACRO_el->neigh also includes the non-periodic
   * neighbourhood relations, MACRO_el->wall_bound also includes the
   * non-periodic boundary types. However, when looking at the EL_INFO
   * structure it suffices to have a look a FILL_NON_PERIODIC. For
   * aesthetical reasons we should convert to FILL_PERIODIC instead of
   * ..._NON_...
   */
  if (!mesh->is_periodic) {
    el_info->fill_flag |= FILL_NON_PERIODIC;
  }

  /* Always fill the macro-wall mapping for now */
  for (i = 0; i < N_WALLS_1D; i++) {
    el_info->macro_wall[i] = i;
  }
  el_info->fill_flag |= FILL_MACRO_WALLS;

  if (el_info->fill_flag & FILL_COORDS) {
    for (i = 0; i < N_VERTICES_1D; i++) {
      DEBUG_TEST_EXIT(mel->coord[i], "no mel->coord[%d]\n",i);

      COPY_DOW(*mel->coord[i], el_info->coord[i]);
    }
  }

  if (el_info->fill_flag & (FILL_NEIGH|FILL_OPP_COORDS)) {
    for (i = 0; i < N_NEIGH_1D; i++) {
      if (mesh->is_periodic
	  && (el_info->fill_flag & FILL_NON_PERIODIC)
	  && mel->neigh_vertices[i][0] >= 0) {
	/* periodic boundary, non-periodic traversal requested, so
	 * fake a real boundary.
	 */
	el_info->neigh[i] = NULL;
      } else if ((mnb = mel->neigh[i])) {
	nb = el_info->neigh[i]      = mnb->el;
	ov = el_info->opp_vertex[i] = mel->opp_vertex[i];

	if (nb->child[0]) {
	  while (nb->child[0]) { /*--- make nb nearest element ---*/
	    if (el_info->fill_flag & FILL_OPP_COORDS) { 
	      if (nb->new_coord) {
		if (mesh->is_periodic && mel->wall_trafo[i])
		  AFFINE_DOW(mel->wall_trafo[i],
			     nb->new_coord, el_info->opp_coord[i]);
		else
		  COPY_DOW(nb->new_coord, el_info->opp_coord[i]);
	      } else {
		AXPBY_DOW(0.5, *mnb->coord[0], 0.5, *mnb->coord[1],
			  el_info->opp_coord[i]);
	      }
	    }
	    nb = el_info->neigh[i] = nb->child[1-ov];
	    /* el_info->opp_vertex[i] remains unchanged */
	  }
	} else {
	  if (el_info->fill_flag & FILL_OPP_COORDS) { 
	    if (mesh->is_periodic && mel->wall_trafo[i])
	      AFFINE_DOW(mel->wall_trafo[i],
			 nb->new_coord, el_info->opp_coord[i]);
	    else
	      COPY_DOW(*mnb->coord[ov], el_info->opp_coord[i]);
	  }
	}
      } else {
	el_info->neigh[i] = NULL;
      }
    }
  } /* FILL_NEIGH|FILL_OPP_COORDS */

  if (el_info->fill_flag & FILL_BOUND) {
    if ((el_info->fill_flag & FILL_MASTER_INFO) == 0) {
      BNDRY_FLAGS_INIT(el_info->edge_bound[0]);
      BNDRY_FLAGS_INIT(el_info->vertex_bound[0]);
      BNDRY_FLAGS_INIT(el_info->vertex_bound[1]);
    }
    if (!mesh->is_periodic) {
      for (i = 0; i < N_WALLS_1D; i++) {
	el_info->wall_bound[i] = mel->wall_bound[i];
      }
      if ((el_info->fill_flag & FILL_MASTER_INFO) == 0) {
	BNDRY_FLAGS_SET(el_info->vertex_bound[0], mel->wall_bound[1]);
	BNDRY_FLAGS_SET(el_info->vertex_bound[1], mel->wall_bound[0]);
      }
    } else {
      /* Support periodic and non-periodic neighbourhood and boundary
       * information on periodic meshes.
       */

      if (el_info->fill_flag & FILL_NON_PERIODIC) {
	/* use non-periodic boundary information */
	for (i = 0; i < N_WALLS_1D; i++) {
	  el_info->wall_bound[i] = mel->wall_bound[i];
	  if ((el_info->fill_flag & FILL_MASTER_INFO) == 0) {
	    BNDRY_FLAGS_SET(el_info->vertex_bound[1-i], mel->wall_bound[i]);
	  }
	}
      } else {
	for (i = 0; i < N_WALLS_1D; i++) {
	  if (mel->neigh_vertices[i][0] == -1) {
	    el_info->wall_bound[i] = mel->wall_bound[i];
	    if ((el_info->fill_flag & FILL_MASTER_INFO) == 0) {
	      BNDRY_FLAGS_SET(el_info->vertex_bound[1-i], mel->wall_bound[i]);
	    }
	  } else {
	    el_info->wall_bound[i] = INTERIOR;
	  }
	}
      }
    }
  }

  if(el_info->fill_flag & FILL_PROJECTION) {
    el_info->active_projection = mel->projection[0];
  }

#if DIM_MAX > 1
  if (el_info->fill_flag & FILL_MASTER_INFO) {
    MACRO_EL *mst_mel = mel->master.macro_el;
    S_CHAR   mst_ov   = mel->master.opp_vertex;

    fill_master_macro_info_1d(
      &el_info->master, el_info->fill_flag, mst_mel, mst_ov);

    if ((el_info->fill_flag & FILL_MASTER_NEIGH) != 0) {
      if (mst_mel->neigh[mst_ov] &&
	  (!mesh->is_periodic ||
	   !(el_info->fill_flag & FILL_NON_PERIODIC) ||
	   mst_mel->neigh_vertices[mst_ov][0] < 0)) {
	fill_master_macro_info_1d(&el_info->mst_neigh,
				  el_info->fill_flag,
				  mst_mel->neigh[mst_ov],
				  mst_mel->opp_vertex[mst_ov]);
      } else {
	el_info->mst_neigh.el = NULL;
      }
    }
    if ((el_info->fill_flag & FILL_BOUND) != 0) {
      /* Fetch the boundary classification from the master
       * element. Use el_info->vertex_bound to store this information.
       */
      if (!mesh->is_periodic || (el_info->fill_flag & FILL_NON_PERIODIC) == 0) {
	for (i = 0; i < N_VERTICES_1D; i++) {
	  BNDRY_FLAGS_CPY(el_info->vertex_bound[i],
			  mel->master.vertex_bound[i]);
	}
	BNDRY_FLAGS_CPY(el_info->edge_bound[0], mel->master.edge_bound[mst_ov]);
      } else {
	for (i = 0; i < N_VERTICES_1D; i++) {
	  BNDRY_FLAGS_CPY(el_info->vertex_bound[i],
			  mel->master.np_vertex_bound[i]);
	}
	BNDRY_FLAGS_CPY(el_info->edge_bound[0], mel->master.np_edge_bound[0]);
      }
    }
  }
#endif
}

#if DIM_MAX >= 2
static void fill_master_macro_info_1d(struct master_info *mst_info,
				      FLAGS fill_flags,
				      MACRO_EL *mst_mel,
				      int mst_ov)
{
  EL       *mst_el  = mst_mel->el;
    
  mst_info->el_type     = 0;
  mst_info->orientation = 1;

  /* This is just the same logic as in traverse_r_2d.c: the master
   * element is treated as if it was a neighbour across mel.
   */
  if (mst_el->child[0] && mst_ov != 2) {
    if (fill_flags & FILL_COORDS) {
      if (mst_el->new_coord) {
	COPY_DOW(mst_el->new_coord, mst_info->opp_coord);
      } else {
	AXPBY_DOW(0.5, *mst_mel->coord[0], 0.5, *mst_mel->coord[1],
		  mst_info->opp_coord);
      }
    }
    mst_info->el         = mst_el->child[1-mst_ov];
    mst_info->opp_vertex = 2;
  } else {
    if (fill_flags & FILL_COORDS) {
      COPY_DOW(*mst_mel->coord[mst_ov], mst_info->opp_coord);
    }
    mst_info->el         = mst_el;
    mst_info->opp_vertex = mst_ov;
  }
  /* periodic stuff is not an issue here */
}
#endif

/*--------------------------------------------------------------------------*/
/*   fill_el_info_1d:                                 		            */
/*   ------------                                       		    */
/*   fill EL_INFO structure for one child of an element   		    */
/*--------------------------------------------------------------------------*/

#if DIM_MAX >= 2
static void fill_master_info_1d(struct master_info *mst_info,
				const struct master_info *mst_info_old,
				const EL_INFO *el_info,
				int ichild,
				FLAGS fill_flags);
#endif

static void fill_elinfo_1d(int ichild, FLAGS mask,
			   const EL_INFO *el_info_old, EL_INFO *el_info)
{
  FUNCNAME("fill_elinfo_1d");
  int     i, ov;
  EL      *nb;
  EL      *el = el_info_old->el;
  FLAGS   fill_flag = el_info_old->fill_flag & mask;

  DEBUG_TEST_EXIT(el->child[0], "no children?\n");

  el_info->el = el->child[ichild];
  DEBUG_TEST_EXIT(el_info->el, "missing child %d?\n", ichild);

  el_info->macro_el  = el_info_old->macro_el;
  el_info->fill_flag = fill_flag;
  el_info->mesh      = el_info_old->mesh;
  el_info->parent    = el_info_old;
  el_info->level     = el_info_old->level + 1;

  el_info->el_type     = 0;
  el_info->orientation = 1;

  /* Fill the boundary mapping to the macro element in any case, it is
   * cheap enough. Maybe introduce a new fill-flag later
   * on. FILL_MACRO_WALLS or so.
   */
  el_info->macro_wall[1-ichild] = el_info_old->macro_wall[1-ichild];
  el_info->macro_wall[ichild] = -1;

  if (fill_flag & FILL_COORDS) {

    COPY_DOW(el_info_old->coord[ichild], el_info->coord[ichild]);
    if (el->new_coord) {
      COPY_DOW(el->new_coord, el_info->coord[1-ichild]);
    } else {
      AXPBY_DOW(0.5, el_info_old->coord[0], 0.5, el_info_old->coord[1],
		el_info->coord[1-ichild]);
    }
  }

  if (fill_flag & (FILL_NEIGH|FILL_OPP_COORDS)) {

    if (fill_flag & FILL_OPP_COORDS) {
      DEBUG_TEST_EXIT(fill_flag & FILL_COORDS,
		      "FILL_OPP_COORDS only with FILL_COORDS\n");
    }

    for (i = 0; i < N_NEIGH_1D; i++) {
      if (i == ichild) {
	nb = el->child[1-ichild];
	ov = 1 - i;
	if (nb && el_info->fill_flag & FILL_OPP_COORDS) {
	  COPY_DOW(el_info_old->coord[1-i], el_info->opp_coord[i]);
	}
      } else {
	nb = el_info_old->neigh[i];
	ov = el_info_old->opp_vertex[i];
	if (nb && el_info->fill_flag & FILL_OPP_COORDS) {
	  COPY_DOW(el_info_old->opp_coord[i], el_info->opp_coord[i]);
	}
      }

      if (nb) {
	while (nb->child[0]) { /*--- make nb nearest element ---*/
	  if (el_info->fill_flag & FILL_OPP_COORDS) {
	    if (nb->new_coord) {
	      const AFF_TRAFO *wt;
	      if ((wt =  wall_trafo(el_info_old, i)) != NULL)
		AFFINE_DOW(wt, nb->new_coord, el_info->opp_coord[i]);
	      else
		COPY_DOW(nb->new_coord, el_info->opp_coord[i]);
	    } else {
	      AXPBY_DOW(0.5, el_info->coord[1-i], 0.5, el_info->opp_coord[i],
			el_info->opp_coord[i]);
	    }
	  }
	  nb = nb->child[1-ov];
	  /* ov == opp_vertex remains unchanged */
	}
      }
      el_info->neigh[i] = nb;
      el_info->opp_vertex[i] = nb ? ov : -1;
    }
  }

  if (fill_flag & FILL_BOUND) {
    BNDRY_FLAGS_CPY(el_info->vertex_bound[ichild],
		    el_info_old->vertex_bound[ichild]);
    BNDRY_FLAGS_CPY(el_info->vertex_bound[1-ichild],
		    el_info_old->edge_bound[0]);
    BNDRY_FLAGS_CPY(el_info->edge_bound[0], el_info_old->edge_bound[0]);

    el_info->wall_bound[1-ichild] = el_info_old->wall_bound[1-ichild];
    el_info->wall_bound[ichild] = INTERIOR;
  }

  if (fill_flag & FILL_PROJECTION) {
    el_info->active_projection = el_info_old->active_projection;
  }

#if DIM_MAX >= 2
  if (fill_flag & FILL_MASTER_INFO) {
    fill_master_info_1d(
      &el_info->master, &el_info_old->master, el_info, ichild, fill_flag);
    if ((fill_flag & FILL_MASTER_NEIGH)) {
      if (el_info_old->mst_neigh.el) {
	fill_master_info_1d(
	  &el_info->mst_neigh,
	  &el_info_old->mst_neigh, el_info, ichild, fill_flag);
      } else {
	el_info->mst_neigh.el = NULL;
      }
    }
  }
#endif
}

#if DIM_MAX >= 2
static void fill_master_info_1d(struct master_info *mst_info,
				const struct master_info *mst_info_old,
				const EL_INFO *el_info,
				int ichild,
				FLAGS fill_flags)
{
  /* When we reach here then we know that the old master.opp_vertex
   * is 2, if the master element again has children, then we need to
   * traverse down one level the respective child.
   */
  EL *mst_el = mst_info_old->el->child[ichild];
  int mst_ov = ichild;

  if (mst_el->child[0]) {
    if (fill_flags & FILL_COORDS) {
      if (mst_el->new_coord) {
	COPY_DOW(mst_el->new_coord, mst_info->opp_coord);
      } else {
	AXPBY_DOW(0.5, mst_info_old->opp_coord,
		  0.5, el_info->coord[ichild],
		  mst_info->opp_coord);
      }
    }
    mst_el = mst_el->child[1-mst_ov];
    mst_ov = 2;
  } else if (fill_flags & FILL_COORDS) {
    COPY_DOW(mst_info_old->opp_coord, mst_info->opp_coord);
  }
  mst_info->el          = mst_el;
  mst_info->opp_vertex  = mst_ov;
  mst_info->orientation = 1; /* must be initialized */
  mst_info->el_type     = 0; /* must be initialized */
}
#endif
