MODULE moduleMeshInputGmsh2
  !Reads a mesh in the Gmsh v2.0 format

  CONTAINS
    !Init a mesh to use Gmsh2 format
    SUBROUTINE initGmsh2(self)
      USE moduleMesh
      USE moduleMeshOutputGmsh2
      IMPLICIT NONE

      CLASS(meshGeneric), INTENT(inout), TARGET:: self

      IF (ASSOCIATED(meshForMCC, self)) self%printColl => printCollGmsh2
      SELECT TYPE(self)
      TYPE IS(meshParticles)
        self%printOutput  => printOutputGmsh2
        self%printEM      => printEMGmsh2
        self%readInitial  => readInitialGmsh2
        self%printAverage => printAverageGmsh2

      END SELECT
      self%readMesh    => readGmsh2

    END SUBROUTINE initGmsh2

    !Read a Gmsh 2 format
    SUBROUTINE readGmsh2(self, filename)
      USE moduleMesh3DCart
      USE moduleMesh2DCyl
      USE moduleMesh2DCart
      USE moduleMesh1DRad
      USE moduleMesh1DCart
      IMPLICIT NONE

      CLASS(meshGeneric), INTENT(inout):: self
      CHARACTER(:), ALLOCATABLE, INTENT(in):: filename
      REAL(8):: r(1:3) !3 generic coordinates
      INTEGER, ALLOCATABLE:: p(:) !Array for nodes
      INTEGER:: e = 0, n = 0, eTemp = 0, elemType = 0
      INTEGER:: totalNumElem
      INTEGER:: numEdges
      INTEGER:: boundaryType

      !Read mesh
      OPEN(10, FILE=TRIM(filename))

      !Skip header
      READ(10, *)
      READ(10, *)
      READ(10, *)
      READ(10, *)

      !Read number of nodes
      READ(10, *) self%numNodes

      !Allocate required matrices and vectors
      ALLOCATE(self%nodes(1:self%numNodes))
      SELECT TYPE(self)
      TYPE IS(meshParticles)
        ALLOCATE(self%K(1:self%numNodes, 1:self%numNodes))
        ALLOCATE(self%IPIV(1:self%numNodes, 1:self%numNodes))
        self%K = 0.D0
        self%IPIV = 0

      END SELECT

      !Read the nodes information
      DO e = 1, self%numNodes
        READ(10, *) n, r(1), r(2), r(3)
        SELECT CASE(self%dimen)
        CASE(3)
          ALLOCATE(meshNode3Dcart::self%nodes(n)%obj)

        CASE(2)
          SELECT CASE(self%geometry)
          CASE("Cyl")
            ALLOCATE(meshNode2DCyl:: self%nodes(n)%obj)

          CASE("Cart")
            ALLOCATE(meshNode2DCart:: self%nodes(n)%obj)

          END SELECT

          r(3) = 0.D0

        CASE(1)
          SELECT CASE(self%geometry)
          CASE("Rad")
            ALLOCATE(meshNode1DRad:: self%nodes(n)%obj)

          CASE("Cart")
            ALLOCATE(meshNode1DCart:: self%nodes(n)%obj)

          END SELECT
          r(2:3) = 0.D0

        END SELECT
        CALL self%nodes(n)%obj%init(n, r)

      END DO

      !Skip comments
      READ(10, *)
      READ(10, *)

      !Reads total number of elements (no nodes)
      READ(10, *) totalNumElem

      !Count edges and volume elements
      numEdges = 0
      SELECT TYPE(self)
      TYPE IS(meshParticles)
        self%numEdges = 0
        DO e = 1, totalNumElem
          READ(10, *) eTemp, elemType
          SELECT CASE(self%dimen)
          CASE(3)
            !Element type 2 is triangle in gmsh
            IF (elemType == 2)  self%numEdges = e

          CASE(2)
            !Element type 1 is segment in Gmsh
            IF (elemType == 1)  self%numEdges = e

          CASE(1)
            !Element type 15 is physical point in Gmsh
            IF (elemType == 15) self%numEdges = e

          END SELECT

        END DO

        !Substract the number of edges to the total number of elements
        !to obtain the number of volume elements
        self%numCells = TotalnumElem - self%numEdges
        ALLOCATE(self%edges(1:self%numEdges))
        numEdges = self%numEdges

        !Go back to the beggining to read elements
        DO e=1, totalNumElem
          BACKSPACE(10)
        END DO

      TYPE IS(meshCollisions)
        self%numCells = TotalnumElem
        numEdges = 0

      END SELECT

      !Allocates array of cells
      ALLOCATE(self%cells(1:self%numCells))

      SELECT TYPE(self)
      TYPE IS(meshParticles)
        !Reads edges
        DO e=1, self%numEdges
          !Reads the edge according to the geometry
          SELECT CASE(self%dimen)
          CASE(3)
            READ(10, *) n, elemType, eTemp, boundaryType
            BACKSPACE(10)

            SELECT CASE(elemType)
            CASE(2)
              !Triangular surface
              ALLOCATE(p(1:3))

              READ(10, *) n, elemType, eTemp, boundaryType, eTemp, p(1:3)

              ALLOCATE(meshEdge3DCartTria:: self%edges(e)%obj)

            END SELECT

          CASE (2)
            ALLOCATE(p(1:2))
            READ(10,*) n, elemType, eTemp, boundaryType, eTemp, p(1:2)

            SELECT CASE(self%geometry)
            CASE("Cyl")
              ALLOCATE(meshEdge2DCyl:: self%edges(e)%obj)

            CASE("Cart")
              ALLOCATE(meshEdge2DCart:: self%edges(e)%obj)

            END SELECT

          CASE(1)
            ALLOCATE(p(1:1))
            READ(10, *) n, elemType, eTemp, boundaryType, eTemp, p(1)
            SELECT CASE(self%geometry)
            CASE("Rad")
              ALLOCATE(meshEdge1DRad:: self%edges(e)%obj)

            CASE("Cart")
              ALLOCATE(meshEdge1DCart:: self%edges(e)%obj)

            END SELECT

          END SELECT

          ! If an edge was found, init it
          if (allocated(p)) then
            call self%edges(e)%obj%init(n, p, boundaryType)

            deallocate(p)

          end if

        END DO

      END SELECT

      !Read and initialize cells
      DO e = 1, self%numCells
        !Read the cell according to the geometry
        SELECT CASE(self%dimen)
        CASE(3)
          READ(10, *) n, elemType
          BACKSPACE(10)

          SELECT CASE(elemType)
          CASE(4)
            !Tetrahedron element
            ALLOCATE(p(1:4))
            READ(10, *) n, elemType, eTemp, eTemp, eTemp, p(1:4)
            ALLOCATE(meshCell3DCartTetra::  self%cells(e)%obj)

          END SELECT

        CASE(2)
          SELECT CASE(self%geometry)
          CASE("Cyl")
            READ(10,*) n, elemType
            BACKSPACE(10)

            SELECT CASE(elemType)
            CASE (2)
              !Triangular element
              ALLOCATE(p(1:3))
              READ(10,*) n, elemType, eTemp, eTemp, eTemp, p(1:3)
              ALLOCATE(meshCell2DCylTria:: self%cells(e)%obj)

            CASE (3)
              !Quadrilateral element
              ALLOCATE(p(1:4))
              READ(10,*) n, elemType, eTemp, eTemp, eTemp, p(1:4)
              ALLOCATE(meshCell2DCylQuad:: self%cells(e)%obj)

            END SELECT

          CASE("Cart")
            READ(10,*) n, elemType
            BACKSPACE(10)

            SELECT CASE(elemType)
            CASE (2)
              !Triangular element
              ALLOCATE(p(1:3))
              READ(10,*) n, elemType, eTemp, eTemp, eTemp, p(1:3)
              ALLOCATE(meshCell2DCartTria:: self%cells(e)%obj)

            CASE (3)
              !Quadrilateral element
              ALLOCATE(p(1:4))
              READ(10,*) n, elemType, eTemp, eTemp, eTemp, p(1:4)
              ALLOCATE(meshCell2DCartQuad:: self%cells(e)%obj)

            END SELECT

          END SELECT

        CASE(1)
          SELECT CASE(self%geometry)
          CASE("Rad")
            ALLOCATE(p(1:2))

            READ(10, *) n, elemType, eTemp, eTemp, eTemp, p(1:2)
            ALLOCATE(meshCell1DRadSegm:: self%cells(e)%obj)

          CASE("Cart")
            ALLOCATE(p(1:2))

            READ(10, *) n, elemType, eTemp, eTemp, eTemp, p(1:2)
            ALLOCATE(meshCell1DCartSegm:: self%cells(e)%obj)

          END SELECT

        END SELECT

        CALL self%cells(e)%obj%init(n - numEdges, p, self%nodes)
        DEALLOCATE(p)

      END DO

      CLOSE(10)

      !Call mesh connectivity
      CALL self%connectMesh

    END SUBROUTINE readGmsh2

    !Reads the initial information from an output file for an species
    SUBROUTINE readInitialGmsh2(filename, density, velocity, temperature)
      IMPLICIT NONE

      CHARACTER(:), ALLOCATABLE, INTENT(in):: filename
      REAL(8), ALLOCATABLE, INTENT(out), DIMENSION(:):: density
      REAL(8), ALLOCATABLE, INTENT(out), DIMENSION(:,:):: velocity
      REAL(8), ALLOCATABLE, INTENT(out), DIMENSION(:):: temperature
      INTEGER:: i, e
      INTEGER:: numNodes

      OPEN(10, file = TRIM(filename))

      !Skip first lines
      DO i = 1, 11
        READ(10, *)

      END DO

      !Reads number of nodes in file
      READ(10, *) numNodes
      ALLOCATE(density(1:numNodes))
      ALLOCATE(velocity(1:numNodes, 1:3))
      ALLOCATE(temperature(1:numNodes))

      DO i = 1, numNodes
        !Reads the density
        READ(10, *) e, density(i)

      END DO

      DO i = 1, 10
        READ(10, *)

      END DO

      DO i = 1, numNodes
        !Reads the velocity
        READ(10, *) e, velocity(i, 1:3)

      END DO

      !Skip uneccessary lines
      DO i = 1, 10
        READ(10, *)

      END DO

      !Assign density to nodes
      DO i = 1, numNodes
        !Skips pressure
        READ(10, *)

      END DO

      !Skip uneccessary lines
      DO i = 1, 10
        READ(10, *)

      END DO

      !Assign density to nodes
      DO i = 1, numNodes
        !Skips pressure
        READ(10, *) e, temperature(i)

      END DO

    END SUBROUTINE readInitialGmsh2

END MODULE moduleMeshInputGmsh2
