// SpaFEDTe, a Template based C++ library for creating 
// Discontinuous Finite Element Spaces,
// Copyright (C) 2012 Lorenzo Alessio Botti

/* This library is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Lesser General Public */ 
/* License as published by the Free Software Foundation either */ 
/* version 3.0 of the License, or (at your option) any later version. */

/* This software 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 */
/* Lesser General Public License for more details. */

/* You should have received a copy of the GNU Lesser General Public */
/* License along with this software; if not, a copy of the full */
/* GNU Lesser General Public License can be found at */
/* http://www.gnu.org/licenses/. */

#ifndef __BasisFunction_h__
#define __BasisFunction_h__

#include <iostream>
#include <stdexcept>
#include "Eigen/Dense"
#include "EnumElemType.hpp"

template<unsigned int Dim>
class BasisFunction
{
public:
  typedef Eigen::MatrixXd ShapeMatrix;
  typedef Eigen::Matrix<ShapeMatrix, Dim, 1> ShapeDerivMatrix;

  BasisFunction():
    _order(0),
    _itemType(INVALID_ELEM),
    _quadratureOrder(0),
    _computeShape(true),
    _computeShapeDeriv(true)
    {
      ;
    } 
  
  BasisFunction(unsigned int order, ElemType itemType):
    _order(order),
    _itemType(itemType),
    _quadratureOrder(0),
    _computeShape(true),
    _computeShapeDeriv(true)
    {
      ;
    } 
  
  ~BasisFunction() { }

  const ElemType& getItemType() const { return _itemType; }
  
  ElemType& getItemType() { return _itemType; }

  const unsigned int& getOrder() const { return _order; }
  
  unsigned int& getOrder() { return _order; }
  
  const unsigned int& getQorder() const { return _quadratureOrder; }
  
  unsigned int& getQorder() { return _quadratureOrder; }

  bool computeShape() { return _computeShape; }

  bool computeShapeDeriv() { return _computeShapeDeriv; }
  
  bool computeShape() const { return _computeShape; }

  bool computeShapeDeriv() const { return _computeShapeDeriv; }
  
  void doComputeShape(bool computeShape) { _computeShape = computeShape; }

  void doComputeShapeDeriv(bool computeShapeDeriv) { _computeShapeDeriv = computeShapeDeriv; }
  
  unsigned int nbQuadraturePoint() const 
  {
    if (this->computeShape())
      return this->_shape.cols();
    else if (this->computeShapeDeriv())
      return this->_shapeDeriv(0).cols();
    std::cout<<" BasisFunction: nbQuadraturePoint() asked but class not initialized"<<std::endl;
    std::logic_error("halt"); 
    return 0;
  }

  unsigned int nbShapeMode() const 
  {
    if (this->computeShape())
      return this->_shape.rows(); 
    else if (this->computeShapeDeriv())
      return this->_shapeDeriv(0).rows();
    std::cout<<" BasisFunction: nbShapeMode() asked but class not initialized"<<std::endl;
    std::logic_error("halt"); 
    return 0;
  }
  
  void resizeShapeFunction(unsigned int i, unsigned int qp)
  {
    this->_shape.resize(i,qp);
  }
  
  void resizeShapeFunctionDeriv(unsigned int i, unsigned int qp)
  {
    for (unsigned int d = 0; d < Dim; d++)
    {
      this->_shapeDeriv(d).resize(i,qp);
    }
  }
  
  ShapeDerivMatrix& getShapeFunctionDeriv() 
  {
    return this->_shapeDeriv; 
  }

  ShapeMatrix& getShapeFunction() 
  {
    return this->_shape; 
  }
  
  const ShapeDerivMatrix& getShapeFunctionDeriv() const 
  {
    return this->_shapeDeriv; 
  }

  const ShapeMatrix& getShapeFunction() const
  {
    return this->_shape; 
  }

protected:

  ShapeMatrix _shape;
  
  ShapeDerivMatrix _shapeDeriv;

private:
  
  unsigned int _order;

  ElemType _itemType;
  
  unsigned int _quadratureOrder;
  
  bool _computeShape;

  bool _computeShapeDeriv;
  
};

#endif
