// Automatically generated by FFC, the FEniCS Form Compiler, version 0.3.1.
// For further information, go to http://www/fenics.org/ffc/.
// Licensed under the GNU GPL Version 2.

#ifndef __P5TRI_H
#define __P5TRI_H

#include <dolfin/Mesh.h>
#include <dolfin/Cell.h>
#include <dolfin/Point.h>
#include <dolfin/Vector.h>
#include <dolfin/AffineMap.h>
#include <dolfin/FiniteElement.h>
#include <dolfin/FiniteElementSpec.h>

namespace dolfin
{
  
  class P5tri : public dolfin::FiniteElement
  {
  public:
  
    P5tri() : dolfin::FiniteElement(), tensordims(0), subelements(0)
    {
      // Element is scalar, don't need to initialize tensordims
  
      // Element is simple, don't need to initialize subelements
    }
  
    ~P5tri()
    {
      if ( tensordims ) delete [] tensordims;
      if ( subelements )
      {
        for (unsigned int i = 0; i < elementdim(); i++)
          delete subelements[i];
        delete [] subelements;
      }
    }
  
    inline unsigned int spacedim() const
    {
      return 21;
    }
  
    inline unsigned int shapedim() const
    {
      return 2;
    }
  
    inline unsigned int tensordim(unsigned int i) const
    {
      dolfin_error("Element is scalar.");
      return 0;
    }
  
    inline unsigned int elementdim() const
    {
      return 1;
    }
  
    inline unsigned int rank() const
    {
      return 0;
    }
  
    void nodemap(int nodes[], const Cell& cell, const Mesh& mesh) const
    {
      static unsigned int edge_reordering_0[2][4] = {{0, 1, 2, 3}, {3, 2, 1, 0}};
      nodes[0] = cell.vertexID(0);
      nodes[1] = cell.vertexID(1);
      nodes[2] = cell.vertexID(2);
      int alignment = cell.edgeAlignment(0);
      int offset = mesh.numVertices();
      nodes[3] = offset + 4*cell.edgeID(0) + edge_reordering_0[alignment][0];
      nodes[4] = offset + 4*cell.edgeID(0) + edge_reordering_0[alignment][1];
      nodes[5] = offset + 4*cell.edgeID(0) + edge_reordering_0[alignment][2];
      nodes[6] = offset + 4*cell.edgeID(0) + edge_reordering_0[alignment][3];
      alignment = cell.edgeAlignment(1);
      nodes[7] = offset + 4*cell.edgeID(1) + edge_reordering_0[alignment][0];
      nodes[8] = offset + 4*cell.edgeID(1) + edge_reordering_0[alignment][1];
      nodes[9] = offset + 4*cell.edgeID(1) + edge_reordering_0[alignment][2];
      nodes[10] = offset + 4*cell.edgeID(1) + edge_reordering_0[alignment][3];
      alignment = cell.edgeAlignment(2);
      nodes[11] = offset + 4*cell.edgeID(2) + edge_reordering_0[alignment][0];
      nodes[12] = offset + 4*cell.edgeID(2) + edge_reordering_0[alignment][1];
      nodes[13] = offset + 4*cell.edgeID(2) + edge_reordering_0[alignment][2];
      nodes[14] = offset + 4*cell.edgeID(2) + edge_reordering_0[alignment][3];
      offset = offset + 4*mesh.numEdges();
      nodes[15] = offset + 6*cell.id() + 0;
      nodes[16] = offset + 6*cell.id() + 1;
      nodes[17] = offset + 6*cell.id() + 2;
      nodes[18] = offset + 6*cell.id() + 3;
      nodes[19] = offset + 6*cell.id() + 4;
      nodes[20] = offset + 6*cell.id() + 5;
    }
  
    void pointmap(Point points[], unsigned int components[], const AffineMap& map) const
    {
      points[0] = map(0.000000000000000e+00, 0.000000000000000e+00);
      points[1] = map(1.000000000000000e+00, 0.000000000000000e+00);
      points[2] = map(0.000000000000000e+00, 1.000000000000000e+00);
      points[3] = map(8.000000000000000e-01, 2.000000000000000e-01);
      points[4] = map(6.000000000000000e-01, 4.000000000000000e-01);
      points[5] = map(4.000000000000000e-01, 6.000000000000000e-01);
      points[6] = map(2.000000000000000e-01, 8.000000000000000e-01);
      points[7] = map(0.000000000000000e+00, 8.000000000000000e-01);
      points[8] = map(0.000000000000000e+00, 6.000000000000000e-01);
      points[9] = map(0.000000000000000e+00, 4.000000000000000e-01);
      points[10] = map(0.000000000000000e+00, 2.000000000000000e-01);
      points[11] = map(2.000000000000000e-01, 0.000000000000000e+00);
      points[12] = map(4.000000000000000e-01, 0.000000000000000e+00);
      points[13] = map(6.000000000000000e-01, 0.000000000000000e+00);
      points[14] = map(8.000000000000000e-01, 0.000000000000000e+00);
      points[15] = map(2.000000000000000e-01, 2.000000000000000e-01);
      points[16] = map(4.000000000000000e-01, 2.000000000000000e-01);
      points[17] = map(6.000000000000000e-01, 2.000000000000000e-01);
      points[18] = map(2.000000000000000e-01, 4.000000000000000e-01);
      points[19] = map(4.000000000000000e-01, 4.000000000000000e-01);
      points[20] = map(2.000000000000000e-01, 6.000000000000000e-01);
      components[0] = 0;
      components[1] = 0;
      components[2] = 0;
      components[3] = 0;
      components[4] = 0;
      components[5] = 0;
      components[6] = 0;
      components[7] = 0;
      components[8] = 0;
      components[9] = 0;
      components[10] = 0;
      components[11] = 0;
      components[12] = 0;
      components[13] = 0;
      components[14] = 0;
      components[15] = 0;
      components[16] = 0;
      components[17] = 0;
      components[18] = 0;
      components[19] = 0;
      components[20] = 0;
    }
  
    void vertexeval(real values[], unsigned int vertex, const real x[], const Mesh& mesh) const
    {
      // FIXME: Temporary fix for Lagrange elements
      values[0] = x[vertex];
    }
  
    const FiniteElement& operator[] (unsigned int i) const
    {
      return *this;
    }
  
    FiniteElement& operator[] (unsigned int i)
    {
      return *this;
    }
  
    FiniteElementSpec spec() const
    {
      FiniteElementSpec s("Lagrange", "triangle", 5);
      return s;
    }
    
  private:
  
    unsigned int* tensordims;
    FiniteElement** subelements;
  
  };
  
}

#endif
