// Gmsh - Copyright (C) 1997-2019 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// issues on https://gitlab.onelab.info/gmsh/gmsh/issues.

#ifndef _MTRIHEDRON_H_
#define _MTRIHEDRON_H_

#include "MElement.h"

/*
 * MTrihedron
 * A MTrihedron is a plane element composed of
 * a quadrangle and two triangles.
 * It serves as an interface between two non-conforming
 * elements
 *
 *         v
 *         ^
 *         |
 *   3-----------2
 *   |'\   |     |
 *   |  '\ |     |
 *   |     +---- | --> u
 *   |      '\   |
 *   |        '\ |
 *   0-----------1
 *
 */

class MTrihedron : public MElement {
protected:
  MVertex *_v[4];
  void _getEdgeVertices(const int num, std::vector<MVertex *> &v) const
  {
    v[0] = _v[edges_trihedron(num, 0)];
    v[1] = _v[edges_trihedron(num, 1)];
  }
  void _getFaceVertices(const int num, std::vector<MVertex *> &v) const
  {
    if(num > 0) {
      v[0] = _v[faces_trihedron(num, 0)];
      v[1] = _v[faces_trihedron(num, 1)];
      v[2] = _v[faces_trihedron(num, 2)];
    }
    else {
      v[0] = _v[0];
      v[1] = _v[1];
      v[2] = _v[2];
      v[3] = _v[3];
    }
  }

public:
  MTrihedron(MVertex *v0, MVertex *v1, MVertex *v2, MVertex *v3, int num = 0,
             int part = 0)
    : MElement(num, part)
  {
    _v[0] = v0;
    _v[1] = v1;
    _v[2] = v2;
    _v[3] = v3;
  }
  MTrihedron(const std::vector<MVertex *> &v, int num = 0, int part = 0)
    : MElement(num, part)
  {
    for(int i = 0; i < 4; i++) _v[i] = v[i];
  }
  ~MTrihedron() {}
  virtual int getDim() const { return 3; } // Can have a volume...
  virtual std::size_t getNumVertices() const { return 4; }
  virtual MVertex *getVertex(int num) { return _v[num]; }
  virtual const MVertex *getVertex(int num) const { return _v[num]; }
  virtual void setVertex(int num, MVertex *v) { _v[num] = v; }
  virtual int getNumEdges() const { return 5; }
  virtual MEdge getEdge(int num) const
  {
    return MEdge(_v[edges_trihedron(num, 0)], _v[edges_trihedron(num, 1)]);
  }
  virtual int getNumEdgesRep(bool curved) { return 5; }
  virtual void getEdgeRep(bool curved, int num, double *x, double *y, double *z,
                          SVector3 *n)
  {
    MEdge e(getEdge(num));
    _getEdgeRep(e.getVertex(0), e.getVertex(1), x, y, z, n, 0);
  }
  virtual void getEdgeVertices(const int num, std::vector<MVertex *> &v) const
  {
    v.resize(2);
    _getEdgeVertices(num, v);
  }
  virtual int getNumFaces() { return 3; }
  virtual MFace getFace(int num) const
  {
    if(num > 0)
      return MFace(_v[faces_trihedron(num, 0)], _v[faces_trihedron(num, 1)],
                   _v[faces_trihedron(num, 2)]);
    else
      return MFace(_v[0], _v[1], _v[2], _v[3]);
  }

  virtual int getNumFacesRep(bool curved) { return 2; }
  virtual void getFaceRep(bool curved, int num, double *x, double *y, double *z,
                          SVector3 *n)
  {
    static const int f[2][3] = {{0, 1, 3}, {1, 2, 3}};
    _getFaceRep(getVertex(f[num][0]), getVertex(f[num][1]),
                getVertex(f[num][2]), x, y, z, n);
  }
  virtual void getFaceVertices(const int num, std::vector<MVertex *> &v) const
  {
    v.resize((num == 0) ? 4 : 3);
    _getFaceVertices(num, v);
  }
  virtual int getType() const { return TYPE_TRIH; }
  virtual int getTypeForMSH() const { return MSH_TRIH_4; }

  virtual void reverse()
  {
    MVertex *tmp = _v[1];
    _v[1] = _v[3];
    _v[3] = tmp;
  }
  virtual int getVolumeSign() { return 0; };
  virtual double getVolume() { return 0; };
  virtual bool setVolumePositive() { return false; };
  virtual void getNode(int num, double &u, double &v, double &w) const
  {
    w = 0;
    switch(num) {
    case 0:
      u = -1.;
      v = -1.;
      break;
    case 1:
      u = 1.;
      v = -1.;
      break;
    case 2:
      u = 1.;
      v = 1.;
      break;
    case 3:
      u = -1.;
      v = 1.;
      break;
    default:
      u = 0.;
      v = 0.;
      break;
    }
  }
  virtual SPoint3 barycenterUVW() const { return SPoint3(0., 0., 0.); }
  virtual bool isInside(double u, double v, double w) const
  {
    double tol = getTolerance();
    if(u < -(1. + tol) || v < -(1. + tol) || u > (1. + tol) || v > (1. + tol) ||
       fabs(w) > tol)
      return false;
    return true;
  }
  static int edges_trihedron(const int edge, const int vert)
  {
    static const int e[5][2] = {
      {0, 1}, {1, 2}, {2, 3}, {3, 0}, {1, 3},
    };
    return e[edge][vert];
  }
  static int faces_trihedron(const int face, const int vert)
  {
    static const int f[3][4] = {
      {0, 1, 2, 3},
      {0, 3, 1, -1},
      {1, 3, 2, -1},
    };
    return f[face][vert];
  }

  // Return the number of nodes that this element must have with the other in
  // order to put an edge between them in the dual graph used during the
  // partitioning.
  virtual int numCommonNodesInDualGraph(const MElement *const other) const;
};

#endif
