/*--------------------------------------------------------------------------*/
/* 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 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>

#ifndef G_CONST
#define G_CONST
#endif


/****************************************************************************/
/*  transformations between local and global coordinates		    */
/****************************************************************************/

static int tetra_check_inside(ELEMENT3D *el, double *coord)
{
  int      i, iret = -1;
  double   cmin = -1.0E-5;

  for (i=0; i<4; i++)
    if (coord[i] < cmin) {
      cmin = coord[i];
      iret = i;
    }
  return(iret);
}

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

static int tetra_world_to_coord(ELEMENT3D *el, const double *xyz, double *coord)
{
  FUNCNAME("tetra_world_to_coord");
  EL_INFO    *el_info;
  double     a[3][3],b[3];
  int        i,j;

  ASSURE(el, "tetra_coord_to_world: no element", return(0));
  ASSURE(el_info = (EL_INFO *)el->user_data,
    "tetra_coord_to_world: no EL_INFO", return(0));

  TEST_FLAG(FILL_COORDS, el_info);

  /* compute the barycentric coordinates of Point */
  /* by solving the linear system:
         ( x0-x3  x1-x3  x2-x3 )  ( coord[0] )  =   ( x - x3)
         ( y0-y3  y1-y3  y2-y3 )  ( coord[1] )  =   ( y - y3)
         ( z0-z3  z1-z3  z2-z3 )  ( coord[2] )  =   ( z - z3)

         coord[3] : = 1 - coord[0] - coord[1] - coord[2]
  */

  for (i=0; i<3; i++) 
    for (j=0; j<3; j++) 
      a[j][i] =  el->vertex[i][j] - el->vertex[3][j];

  for (j=0; j<3; j++) 
    b[j] = xyz[j] - el->vertex[3][j];

  if(!g_solve3((void *) a, b, coord)){
    printf("tetra_coord_to_world: ERROR\n");
    return(-2);
  }
  coord[3] = 1. - coord[0] - coord[1] - coord[2];

  return(tetra_check_inside(el,coord));
}

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

static void tetra_coord_to_world(ELEMENT3D *el, const double *coord, double *world)
{
  FUNCNAME("tetra_coord_to_world");
  EL_INFO   *el_info;
  int       i, j;

  ASSURE(el, "tetra_coord_to_world: no element", return);
  ASSURE(el_info = (EL_INFO *)el->user_data,
    "tetra_coord_to_world: no EL_INFO", return);
  TEST_FLAG(FILL_COORDS, el_info);

#if 0
  for (j=0; j<3; j++) world[j] = coord[0] * el->vertex[0][j];
  for (i=1; i<4; i++) {
    for (j=0; j<3; j++) world[j] += coord[i] * el->vertex[i][j];
  }
#else
  for (j=0; j<3; j++) world[j] = coord[0] * el_info->coord[0][j];
  for (i=1; i<4; i++) {
    for (j=0; j<3; j++) world[j] += coord[i] * el_info->coord[i][j];
  }
#endif

  return;
}

/****************************************************************************/
/*  Neighbours and Boundaries                              		    */
/****************************************************************************/

static ELEMENT3D  *tetra_neighbour(ELEMENT3D *el, int pn, int ii, 
				   double *coord, double *xyz,
				   MESH_ELEMENT_FLAGS flags)
{
  FUNCNAME("tetra_neighbour");
  MSG("not yet implemented\n");

  /* traverse_neighbour(...); */

  return NULL;
}

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

static int tetra_boundary(ELEMENT3D *el, int pn)
{
  FUNCNAME("tetra_boundary");
  EL_INFO        *el_info;

  ASSURE(el, "tetra_boundary: no element", return(-2));
  ASSURE(el_info = (EL_INFO *)el->user_data,
	 "tetra_boundary: no EL_INFO", return(-2));
  TEST_FLAG(FILL_BOUND, el_info);

  return wall_bound(el_info, pn);
}

/****************************************************************************/
/*  Grape definitons for tetrahedra					    */
/****************************************************************************/

/* vertex indices of the polygons for a tetrahedron           */
static int t_v0_e[3] = {1,3,2},   t_v1_e[3] = {0,2,3};
static int t_v2_e[3] = {0,3,1},   t_v3_e[3] = {0,1,2};
/* polygon adjacencies  for a tetrahedron                     */
static int t_p0_e[3] = {2,1,3},   t_p1_e[3] = {3,0,2};
static int t_p2_e[3] = {1,0,3},   t_p3_e[3] = {2,0,1};
/* local coordinates of the vertices for a tetrahedron        */
static double t_c0[4] = {1.,0.,0.,0.}, t_c1[4] = {0.,1.,0.,0.};
static double t_c2[4] = {0.,0.,1.,0.}, t_c3[4] = {0.,0.,0.,1.};

static const int    tetra_polygon_length[4] =  {3, 3, 3, 3};
static const int    *tetra_vertex_e[4] =       {t_v0_e,t_v1_e,t_v2_e,t_v3_e};
static const int    *tetra_next_polygon_e[4] = {t_p0_e,t_p1_e,t_p2_e,t_p3_e};
static const double *tetra_coord[4] =          {t_c0,t_c1,t_c2,t_c3};

static ELEMENT3D_DESCRIPTION tetra_description_even =
{
  0,   /*  dindex. cH: offset into a C-style array. Keep it at zero */
  4,   /*  number_of_vertices */
  4,   /*  number_of_polygons */
  tetra_polygon_length, 
  tetra_vertex_e, 
  tetra_next_polygon_e,
  4, 
  tetra_coord, 
  1,
  tetra_world_to_coord,
  tetra_coord_to_world,
  tetra_check_inside,
  tetra_neighbour,
  tetra_boundary
};

/* vertex indices of the polygons for a tetrahedron           */
static int t_v0_o[3] = {1,2,3},   t_v1_o[3] = {0,3,2};
static int t_v2_o[3] = {0,1,3},   t_v3_o[3] = {0,2,1};
/* polygon adjacencies  for a tetrahedron                     */
static int t_p0_o[3] = {3,1,2},   t_p1_o[3] = {2,0,3};
static int t_p2_o[3] = {3,0,1},   t_p3_o[3] = {1,0,2};

static const int  *tetra_vertex_o[4] =       {t_v0_o,t_v1_o,t_v2_o,t_v3_o};
static const int  *tetra_next_polygon_o[4] = {t_p0_o,t_p1_o,t_p2_o,t_p3_o};

static ELEMENT3D_DESCRIPTION tetra_description_odd =
{
  0, /* <- should also be better left at 0. */
  4, 
  4,
  tetra_polygon_length,
  tetra_vertex_o,
  tetra_next_polygon_o,
  4,
  tetra_coord,
  1,
  tetra_world_to_coord,
  tetra_coord_to_world,
  tetra_check_inside,
  tetra_neighbour,
  tetra_boundary
};

/****************************************************************************/
/*  common routines for triangles and tetrahedra                            */
/*  including typedefs for: GRAPE_ELEMENT, GRAPE_MESH, F_EL_INFO, F_DATA    */
/****************************************************************************/

#include "../Common/mesh_interface_common_c.h"

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