/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     Common/element.c                                               */
/*                                                                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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                                    */
/*                                                                          */
/*             Claus-Justus Heine                                           */
/*             Abteilung fuer Angewandte Mathematik                         */
/*             Universitaet Freiburg                                        */
/*             Hermann-Herder-Str. 10                                       */
/*             79104 Freiburg, Germany                                      */
/*                                                                          */
/*  http://www.alberta-fem.de                                               */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2003),                         */
/*         C.-J. Heine (2006-2007).                                         */
/*--------------------------------------------------------------------------*/

#include "alberta.h"

#include "element_0d.c"
#include "element_1d.c"
#if DIM_MAX > 1
#include "element_2d.c"
#if DIM_MAX > 2
#include "element_3d.c"
#endif
#endif

void fill_neigh_el_info(EL_INFO *neigh_info,
			const EL_INFO *el_info, int wall, int rel_perm)
{
  EL_GEOM_CACHE *elgc;
  int opp_v = el_info->opp_vertex[wall];
  int dim = el_info->mesh->dim;
  const int *vow, *vmap;
  int i, i1, i2;

  neigh_info->mesh = el_info->mesh;
  neigh_info->el = el_info->neigh[wall];
  neigh_info->macro_el = NULL;
  neigh_info->parent = NULL;
  elgc = &neigh_info->el_geom_cache;

  elgc->current_el = el_info->neigh[wall];
  elgc->fill_flag  = 0U;

  /* Make the neighbourhood information reflexive */
  neigh_info->opp_vertex[opp_v] = wall;
  neigh_info->neigh[opp_v] = el_info->el;
  neigh_info->fill_flag = FILL_NEIGH;

  for (i = 0; i < opp_v; i++) {
    neigh_info->neigh[i] = NULL;
  }
  for (++i; i < N_NEIGH_MAX; i++) {
    neigh_info->neigh[i] = NULL;
  }

  if ((el_info->fill_flag & (FILL_COORDS|FILL_OPP_COORDS))
      ==
      (FILL_COORDS|FILL_OPP_COORDS)) {

    /* FILL_OPP_COORDS is not quite correct because we only fill the
     * information for one neighbour.
     */
    neigh_info->fill_flag |= FILL_COORDS|FILL_OPP_COORDS;

    COPY_DOW(el_info->coord[wall], neigh_info->opp_coord[opp_v]);
    COPY_DOW(el_info->opp_coord[wall], neigh_info->coord[opp_v]);

    vow  = vertex_of_wall(dim, wall);
    vmap = sorted_wall_vertices(dim, opp_v, rel_perm);
 
    for (i = 0; i < dim; i++) {
      i1 = vow[i];
      i2 = vmap[i];
      COPY_DOW(el_info->coord[i1], neigh_info->coord[i2]);
    }
  }
}

#if 0 /* now defined in alberta_inlines.h */
const EL_GEOM_CACHE *fill_el_geom_cache(const EL_INFO *el_info, FLAGS fill_flag)
{
  EL_GEOM_CACHE *elgc;
  FLAGS need;
  int dim, wall;

  elgc = (EL_GEOM_CACHE *)&el_info->el_geom_cache;

  if (elgc->current_el != el_info->el) {
    elgc->fill_flag = 0;
    elgc->current_el = el_info->el;
  }

  if (!(need = (elgc->fill_flag ^ fill_flag) & fill_flag)) {
    return elgc;
  }

  dim = el_info->mesh->dim;

  if (need & FILL_EL_LAMBDA) {
    elgc->det = el_grd_lambda_dim(dim, el_info, elgc->Lambda);
    elgc->fill_flag |= FILL_EL_LAMBDA|FILL_EL_DET;
  } else if (need & FILL_EL_DET) {
    elgc->det = el_det_dim(dim, el_info);
    elgc->fill_flag |= FILL_EL_DET;
  }

  for (wall = 0; wall < N_WALLS_MAX; wall++) {
    if (need & FILL_EL_WALL_ORIENTATION(wall)) {
      DEBUG_TEST_FLAG(FILL_NEIGH, el_info);
      if (el_info->neigh[wall]) {
	EL  *el    = el_info->el;
	EL  *neigh = el_info->neigh[wall];
	int oppv   = el_info->opp_vertex[wall];

	elgc->orientation[wall][0] = wall_orientation(dim, el,    wall, NULL);
	elgc->orientation[wall][1] = wall_orientation(dim, neigh, oppv, NULL);
	elgc->fill_flag |= FILL_EL_WALL_ORIENTATION(wall);
      }
    }
    if (need & (FILL_EL_WALL_NORMAL(wall)|FILL_EL_WALL_DET(wall))) {
      elgc->wall_det[wall] =
	get_wall_normal_dim(dim, el_info, wall, elgc->wall_normal[wall]);      
      elgc->fill_flag |= FILL_EL_WALL_NORMAL(wall)|FILL_EL_WALL_DET(wall);
    }
  }

  return elgc;  
}
#endif


#if 0 /* wrappers are now inline functions defined in alberta.h */
/****************************************************************************/

const REAL *coord_to_world(const EL_INFO *el_info, const REAL *l, REAL_D w)
{
  FUNCNAME("coord_to_world");

  switch(el_info->mesh->dim) {
  case 0:
    return coord_to_world_0d(el_info, l, w);
    break;
  case 1:
    return coord_to_world_1d(el_info, l, w);
    break;
#if DIM_MAX > 1
  case 2:
    return coord_to_world_2d(el_info, l, w);
    break;
#if DIM_MAX > 2
  case 3:
    return coord_to_world_3d(el_info, l, w);
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim!\n");
  }

  return NULL;                                 /* Statement is not reached. */
}

int world_to_coord(const EL_INFO *el_info,
		   const REAL *x, REAL_B lambda)
{
  FUNCNAME("world_to_coord");

  switch(el_info->mesh->dim) {
  case 0:
    ERROR_EXIT("Not implemented.\n");
    break;
  case 1:
    return world_to_coord_1d(el_info, x, lambda);
    break;
#if DIM_MAX > 1
  case 2:
    return world_to_coord_2d(el_info, x, lambda);
    break;
#if DIM_MAX > 2
  case 3:
    return world_to_coord_3d(el_info, x, lambda);
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim!\n");
  }

  return 0;                                   /* Statement is not reached. */
}

REAL el_det(const EL_INFO *el_info)
{
  int dim = el_info->mesh->dim;

  switch(dim) {
  case 0:
    return 1.0;
    break;
  case 1:
    return el_det_1d(el_info);
    break;
#if DIM_MAX > 1
  case 2:
    return el_det_2d(el_info);
    break;
#if DIM_MAX > 2
  case 3:
    return el_det_3d(el_info);
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim!\n");
  }

  return 0.0;
}

REAL el_volume(const EL_INFO *el_info)
{
  int dim = el_info->mesh->dim;

  switch(dim) {
  case 0:
    return 1.0;
    break;
  case 1:
    return el_det_1d(el_info);
    break;
#if DIM_MAX > 1
  case 2:
    return 0.5 * el_det_2d(el_info);
    break;
#if DIM_MAX > 2
  case 3:
    return el_det_3d(el_info) / 6.0;
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim!\n");
  }

  return 0.0;
}

REAL el_grd_lambda(const EL_INFO *el_info, REAL_BD grd_lam)
{
  int dim = el_info->mesh->dim;

  switch(dim) {
  case 0:
    return el_grd_lambda_0d(el_info, grd_lam);
    break;
  case 1:
    return el_grd_lambda_1d(el_info, grd_lam);
    break;
#if DIM_MAX > 1
  case 2:
    return el_grd_lambda_2d(el_info, grd_lam);
    break;
#if DIM_MAX > 2
  case 3:
    return el_grd_lambda_3d(el_info, grd_lam);
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim!\n");
  }
  
  return 0.0;
}


int *sorted_wall_indices(int dim, int wall, int permno)
{
  FUNCNAME("wall_orientation");

  switch(dim) {
  case 0:
    WARNING("Does not makes sense for dim == 0!\n");
    break;
  case 1:
    return sorted_wall_indices_1d(wall, permno);
    break;
#if DIM_MAX > 1
  case 2:
    return sorted_wall_indices_2d(wall, permno);
    break;
#if DIM_MAX > 2
  case 3:
    return sorted_wall_indices_3d(wall, permno);
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim!\n");
  }

  return NULL;                                 /* Statement is not reached. */
}

int wall_orientation(int dim,
		     const EL *el, int wall, int **vecp)
{
  FUNCNAME("wall_orientation");

  switch(dim) {
  case 0:
    WARNING("Does not makes sense for dim == 0!\n");
    break;
  case 1:
    return wall_orientation_1d(el, wall, vecp);
    break;
#if DIM_MAX > 1
  case 2:
    return wall_orientation_2d(el, wall, vecp);
    break;
#if DIM_MAX > 2
  case 3:
    return wall_orientation_3d(el, wall, vecp);
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim!\n");
  }

  return -1;                                 /* Statement is not reached. */
}

int *sort_wall_indices(int dim, const EL *el, int wall, int *vec)
{
  FUNCNAME("sort_wall_indices");

  switch(dim) {
  case 0:
    WARNING("Does not makes sense for dim == 0!\n");
    break;
  case 1:
    return sort_wall_indices_1d(el, wall, vec);
    break;
#if DIM_MAX > 1
  case 2:
    return sort_wall_indices_2d(el, wall, vec);
    break;
#if DIM_MAX > 2
  case 3:
    return sort_wall_indices_3d(el, wall, vec);
    break;
#endif
#endif
  default:
    ERROR_EXIT("Illegal dim!\n");
  }

  return NULL;                                 /* Statement is not reached. */
}

#endif

