/**@file 
 *
 * General support routines for the basis-functions library.
 *
 *      author:  Claus-Justus Heine
 *               Abteilung fuer Angewandte Mathematik                     
 *               Albert-Ludwigs-Universitaet Freiburg
 *               Hermann-Herder-Str. 10
 *               79104 Freiburg i.Br.
 *               Germany
 *               claus@mathematik.uni-freiburg.de
 *
 * Copyright (C) by Claus-Justus Heine (2008).
 *
 */

#include <alberta/alberta.h>
#include <stdarg.h>

#include "albas.h"

static const BAS_FCTS *bfcts_bubble(const char *name, unsigned dim);
static const BAS_FCTS *bfcts_wall_bubbles(const char *name, unsigned dim);
static const BAS_FCTS *bfcts_trace_bubble(const char *name, unsigned dim);
static const BAS_FCTS *bfcts_bulk_trace_bubble(const char *name, unsigned dim);
static
const BAS_FCTS *bfcts_bulk_trace_tensor_bubbles(const char *name, unsigned dim);
static const BAS_FCTS *bfcts_raviart_thomas(const char *name, unsigned dim);

const BAS_FCTS *bas_fcts_init(int dim, int dow, const char *name)
{
  FUNCNAME("bas_fcts_init");
  
  TEST_EXIT(dow == DIM_OF_WORLD,
	    "dow = %d does not match compiled-in DIM_OF_WORLD = %d.\n",
	    dow, DIM_OF_WORLD);
  TEST_EXIT((unsigned)dim <= DIM_MAX,
	    "dim = %d > DIM_MAX = %d.\n", dim, DIM_MAX);

  if (memcmp(name, "P1+bubble", strlen("P1+bubble")) == 0) {
    /* old mini-element */
    return get_old_mini_element(dim);
  } else if (memcmp(name, "Bubble", strlen("Bubble")) == 0) {
    /* Bubble_Xd_IX */
    return bfcts_bubble(name, dim);
  } else if (memcmp(name, "WallBubbles", strlen("WallBubbles")) == 0) {
    return bfcts_wall_bubbles(name, dim);
  } else if (memcmp(name, "TraceBubble", strlen("TraceBubble")) == 0) {
    return bfcts_trace_bubble(name, dim);
  } else if (memcmp(name, "BulkTraceBubble", strlen("BulkTraceBubble")) == 0) {
    return bfcts_bulk_trace_bubble(name, dim);
  } else if (memcmp(name, "BulkTraceTensorBubbles",
		    strlen("BulkTraceTensorBubbles")) == 0) {
    return bfcts_bulk_trace_tensor_bubbles(name, dim);
  } else if (memcmp(name, "RaviartThomas", strlen("RaviartThomas")) == 0) {
    return bfcts_raviart_thomas(name, dim);
  } else if (strrchr(name, '#') != NULL) {
    /* Try to generate a suitable basis-function chain */
    BAS_FCTS *tail = NULL;
    const BAS_FCTS *head;
    char _name[strlen(name)+1], *ptr;

    memcpy(_name, name, strlen(name)+1);
    while ((ptr = strrchr(_name, '#')) != NULL) {
      *ptr++ = '\0';
      head = get_bas_fcts(dim, ptr);
      if (head == NULL) {
	goto out;
      }
      tail = chain_bas_fcts(head, tail);
    }
    /* chain the first component as head of the list */
    head = get_bas_fcts(dim, _name);
    if (head == NULL) {
      goto out;
    }
    head = chain_bas_fcts(head, tail);
    if (head != NULL) {
      new_bas_fcts(head);
      return head;
    }
  out:
    /*cleanup???*/
    do {} while (false);
  }
  WARNING("Unknown basis functions type: \"%s\".\n", name);
  return NULL; /* cleanup?? */
}

static const BAS_FCTS *bfcts_bubble(const char *name, unsigned dim)
{
  FUNCNAME("bfcts_bubble");
  int str_dim = dim, inter_deg = 0;
  
  if (strcmp(name, "Bubble") != 0) {
    if (sscanf(name, "Bubble_I%d_%dd", &inter_deg, &str_dim) == 2) {
      TEST_EXIT(str_dim == dim, "Dimension mis-match (named: %d, arg: %d).\n",
	      str_dim, dim);
    } else if (sscanf(name, "Bubble_I%d", &inter_deg) != 1) {
      ERROR_EXIT("Named basis-function mismatch: \"%s\", expected \"Bubble\"."
		 "\n", name);
    } 
  }
  return get_bubble(dim, inter_deg);
}

static const BAS_FCTS *bfcts_wall_bubbles(const char *name, unsigned dim)
{
  FUNCNAME("bfcts_wall_bubbles");
  int str_dim = dim, inter_deg = 0;
  
  if (strcmp(name, "WallBubbles") != 0) {
    if (sscanf(name, "WallBubbles_I%d_%dd", &inter_deg, &str_dim) == 2) {
      TEST_EXIT(str_dim == dim, "Dimension mis-match (named: %d, arg: %d).\n",
	      str_dim, dim);
    } else if (sscanf(name, "WallBubbles_I%d", &inter_deg) != 1) {
      ERROR_EXIT("Named basis-function mismatch: \"%s\", "
		 "expected \"WallBubbles\".\n",name);
    }
  }
  return get_wall_bubbles(dim, inter_deg);
}

static const BAS_FCTS *bfcts_trace_bubble(const char *name, unsigned dim)
{
  FUNCNAME("bfcts_trace_bubble");
  int str_dim = dim, inter_deg = 0;
  
  if (strcmp(name, "TraceBubble") != 0) {
    if (sscanf(name, "TraceBubble_I%d_%dd", &inter_deg, &str_dim) == 2) {
      TEST_EXIT(str_dim == dim, "Dimension mis-match (named: %d, arg: %d).\n",
	      str_dim, dim);
    } else if (sscanf(name, "TraceBubble_I%d", &inter_deg) != 1) {
      ERROR_EXIT("Named basis-function mismatch: \"%s\", "
		 "expected \"TraceBubble\".\n",name);
    }
  }
  return get_trace_bubble(dim, inter_deg);
}

static const BAS_FCTS *bfcts_bulk_trace_bubble(const char *name, unsigned dim)
{
  FUNCNAME("bfcts_bulk_trace_bubble");
  int str_dim = dim, inter_deg = 0, trace_id = -1;

  if (sscanf(name, "BulkTraceBubble@%d_I%d_%dd",
	     &trace_id, &inter_deg, &str_dim) == 3) {
    TEST_EXIT(str_dim == dim, "Dimension mis-match (named: %d, arg: %d).\n",
	      str_dim, dim);
  } else if (sscanf(name, "BulkTraceBubble@%d_I%d",
		    &trace_id, &inter_deg) != 2 &&
	     sscanf(name, "BulkTraceBubble@%d",
		    &trace_id) != 1) {
    ERROR_EXIT("Named basis-function mismatch: \"%s\", "
	       "expected \"BulkTraceBubble@TRACE_ID[_IDEG][_dDIM]\".\n", name);
  }
  return get_bulk_trace_bubble(dim, inter_deg, trace_id);
}

static
const BAS_FCTS *bfcts_bulk_trace_tensor_bubbles(const char *name, unsigned dim)
{
  FUNCNAME("bfcts_bulk_trace_tensor_bubbles");
  int str_dim = dim, inter_deg = 0, trace_id = -1, tensor_deg = 0;

  if (sscanf(name, "BulkTraceTensorBubbles@%d_T%d_I%d_%dd",
	     &trace_id, &tensor_deg, &inter_deg, &str_dim) == 4) {
    TEST_EXIT(str_dim == dim, "Dimension mis-match (named: %d, arg: %d).\n",
	      str_dim, dim);
  } else if (sscanf(name, "BulkTraceTensorBubbles@%d_T%d_I%d",
		    &trace_id, &tensor_deg, &inter_deg) != 3 &&
	     sscanf(name, "BulkTraceTensorBubbles@%d_T%d",
		    &trace_id, &tensor_deg) != 2 &&
	     sscanf(name, "BulkTraceTensorBubbles@%d_I%d",
		    &trace_id, &inter_deg) != 2 &&
	     sscanf(name, "BulkTraceTensorBubbles@%d",
		    &trace_id) != 1) {
    ERROR_EXIT("Named basis-function mismatch: \"%s\", "
	       "expected "
	       "\"BulkTraceTensorBubble@TRACE_ID[_TDEG][_IDEG][_dDIM]\".\n",
	       name);
  }
  return get_bulk_trace_tensor_bubbles(dim, tensor_deg, inter_deg, trace_id);
}

static const BAS_FCTS *bfcts_raviart_thomas(const char *name, unsigned dim)
{
  FUNCNAME("bfcts_raviart_thomas");
  int str_dim = dim, inter_deg = 0;
  
  if (strcmp(name, "RaviartThomas") != 0) {
    if (sscanf(name, "RaviartThomas_I%d_%dd", &inter_deg, &str_dim) == 2) {
      TEST_EXIT(str_dim == dim, "Dimension mis-match (named: %d, arg: %d).\n",
	      str_dim, dim);
    } else if (sscanf(name, "RaviartThomas_I%d", &inter_deg) != 1) {
      ERROR_EXIT("Named basis-function mismatch: \"%s\", "
		 "expected \"RaviartThomas\".\n",name);
    }
  }
  return get_raviart_thomas(dim, inter_deg);
}

STOKES_TRIPLE stokes_triple(const char *name, unsigned dim, unsigned degree)
{
  FUNCNAME("stokes_triple");
  STOKES_TRIPLE result = { NULL, };
  int trace_id, tensor_deg;

  char bfcts_name[1024];

  TEST_EXIT(dim > 1, "Not for dim %d <= 1.\n", dim);

  if (strcmp(name, "Mini") == 0) {
    sprintf(bfcts_name, "lagrange1#Bubble_I%d", degree);
    result.velocity    = get_bas_fcts(dim, bfcts_name);
    result.pressure    = get_lagrange(dim, 1);
    result.slip_stress = NULL;
  } else if (sscanf(name, "++Mini@%d", &trace_id) == 1) {
    sprintf(bfcts_name, "lagrange1#Bubble_I%d#BulkTraceBubble@%d_I%d",
	    degree, trace_id, degree);
    result.velocity    = get_bas_fcts(dim, bfcts_name);
    result.pressure    = get_lagrange(dim, 1);
    result.slip_stress = get_discontinuous_lagrange(dim-1, 0);
  } else if (sscanf(name, "Mini+T%d@%d", &tensor_deg, &trace_id) == 2) {
    sprintf(bfcts_name,
	    "lagrange1#Bubble_I%02d#BulkTraceTensorBubbles@%02d_T%02d_I%02d",
	    degree, trace_id, tensor_deg, degree);
    result.velocity    = get_bas_fcts(dim, bfcts_name);
    result.pressure    = get_lagrange(dim, 1);
    result.slip_stress = get_discontinuous_lagrange(dim-1, tensor_deg);
  } else if (strcmp(name, "TaylorHood") == 0) {
    TEST_EXIT(degree >= 2,
	      "The pair P%d / P%d is not a stable Stokes discretisation.\n",
	      degree, degree-1);
    result.velocity    = get_lagrange(dim, degree);
    result.pressure    = get_lagrange(dim, degree-1);
    result.slip_stress = get_discontinuous_lagrange(dim-1, 0);
  } else if (sscanf(name, "TaylorHood+T%d@%d", &tensor_deg, &trace_id) == 2) {
    TEST_EXIT(degree >= 2,
	      "The pair P%d / P%d is not a stable Stokes discretisation.\n",
	      degree, degree-1);
    sprintf(bfcts_name,
	    "lagrange%d#BulkTraceTensorBubbles@%02d_T%02d_I%02d",
	    degree, trace_id, tensor_deg, tensor_deg+dim);
    result.velocity    = get_bas_fcts(dim, bfcts_name);
    result.pressure    = get_lagrange(dim, degree-1);
    result.slip_stress = get_discontinuous_lagrange(dim-1, tensor_deg);
  } else if (strcmp(name, "BernardiRaugel") == 0) {

    sprintf(bfcts_name, "lagrange1#WallBubbles_I%d", degree);
    result.velocity    = get_bas_fcts(dim, bfcts_name);

    result.pressure    = get_discontinuous_lagrange(dim, 0);
    result.slip_stress = get_discontinuous_lagrange(dim-1, 0);
  } else if (strcmp(name, "CrouzeixRaviart") == 0) {
    TEST_EXIT(degree == 2,
	      "Sorry, higher order Crouzeix-Raviart-Mansfield "
	      "elements are not implemented.\n");
    if (dim == 2) {
      sprintf(bfcts_name, "lagrange2#Bubble_I%d", degree);
      result.velocity    = get_bas_fcts(dim, bfcts_name);
      result.slip_stress = NULL;
    } else {
      sprintf(bfcts_name, "lagrange2#Bubble_I%d#WallBubbles_I%d",
	      degree, degree);
      result.velocity    = get_bas_fcts(dim, bfcts_name);
      result.slip_stress = get_discontinuous_lagrange(dim-1, 0);
    }
    result.pressure = get_discontinuous_lagrange(dim, 1);
  } else if (sscanf(name, "CrouzeixRaviart+T%d@%d", &tensor_deg, &trace_id) == 2) {
    TEST_EXIT(degree == 2,
	      "Sorry, higher order Crouzeix-Raviart-Mansfield "
	      "elements are not implemented.\n");
    if (dim == 2) {
      sprintf(bfcts_name,
              "lagrange2#Bubble_I%d#BulkTraceTensorBubbles@%02d_T%02d_I%02d",
              degree, trace_id, tensor_deg, tensor_deg+dim);
      result.velocity    = get_bas_fcts(dim, bfcts_name);
      result.slip_stress = get_discontinuous_lagrange(dim-1, tensor_deg);
    } else {
      sprintf(bfcts_name,
              "lagrange2#Bubble_I%d#WallBubbles_I%d#BulkTraceTensorBubbles@%02d_T%02d_I%02d",
              degree, degree, trace_id, tensor_deg, tensor_deg+dim);
      result.velocity    = get_bas_fcts(dim, bfcts_name);
      result.slip_stress = get_discontinuous_lagrange(dim-1, tensor_deg);
    }
    result.pressure = get_discontinuous_lagrange(dim, 1);
  } else if (strcmp(name, "RaviartThomasP0") == 0) {
    sprintf(bfcts_name, "RaviartThomas_I%d", degree);
    result.velocity = get_bas_fcts(dim, bfcts_name);
    result.pressure = get_discontinuous_lagrange(dim, 0);
    result.slip_stress = NULL;
  } else {
    ERROR("Unknown Stokes discretisation: \"%s\".\n", name);
  }
  return result;
}
