!Contains information about output
MODULE moduleOutput
  IMPLICIT NONE

  !Output for each node 
  TYPE, PUBLIC:: outputNode
    REAL(8):: den = 0.D0, mom(1:3) = 0.D0, tensorS(1:3,1:3) = 0.D0
    CONTAINS
      PROCEDURE, PASS(self), PRIVATE:: outputNode_equal_outputNode
      PROCEDURE, PASS(self), PRIVATE:: outputNode_equal_real
      PROCEDURE, PASS(self), PRIVATE:: outputNode_add_outputNode
      PROCEDURE, PASS(self), PRIVATE:: outputNode_sub_outputNode
      PROCEDURE, PASS(self), PRIVATE:: outputNode_mul_outputNode
      PROCEDURE, PASS(self), PRIVATE:: outputNode_div_int
      GENERIC, PUBLIC :: ASSIGNMENT(=) => outputNode_equal_outputNode, outputNode_equal_real
      GENERIC, PUBLIC :: OPERATOR(+)   => outputNode_add_outputNode
      GENERIC, PUBLIC :: OPERATOR(-)   => outputNode_sub_outputNode
      GENERIC, PUBLIC :: OPERATOR(*)   => outputNode_mul_outputNode
      GENERIC, PUBLIC :: OPERATOR(/)   => outputNode_div_int

  END TYPE

  !Type for EM data in node
  TYPE emNode
    REAL(8):: phi
    REAL(8):: B(1:3)

  END TYPE emNode

  !Output in dimensional units to print
  TYPE outputFormat
    REAL(8):: density, velocity(1:3), pressure, temperature

  END TYPE

  CHARACTER(:), ALLOCATABLE:: path
  CHARACTER(:), ALLOCATABLE:: folder
  INTEGER:: iterationDigits
  CHARACTER(:), ALLOCATABLE:: iterationFormat
  INTEGER:: triggerOutput,  counterOutput  = 0
  INTEGER:: triggerCPUTime, counterCPUTime = 0
  LOGICAL:: timeOutput = .FALSE.
  LOGICAL:: collOutput = .FALSE.
  LOGICAL:: emOutput   = .FALSE.

  CONTAINS
    PURE SUBROUTINE outputNode_equal_outputNode(self, from)
      IMPLICIT NONE

      CLASS(outputNode), INTENT(inout):: self
      CLASS(outputNode), INTENT(in):: from

      self%den     = from%den
      self%mom     = from%mom
      self%tensorS = from%tensorS

    END SUBROUTINE outputNode_equal_outputNode

    PURE ELEMENTAL SUBROUTINE outputNode_equal_real(self, from)
      IMPLICIT NONE

      CLASS(outputNode), INTENT(inout):: self
      REAL(8), INTENT(in):: from

      self%den     = from
      self%mom     = from
      self%tensorS = from

    END SUBROUTINE outputNode_equal_real

    PURE ELEMENTAL FUNCTION outputNode_add_outputNode(self, that) RESULT(total)
      IMPLICIT NONE

      CLASS(outputNode), INTENT(in):: self
      CLASS(outputNode), INTENT(in):: that
      TYPE(outputNode):: total

      total%den     = self%den     + that%den
      total%mom     = self%mom     + that%mom
      total%tensorS = self%tensorS + that%tensorS

    END FUNCTION outputNode_add_outputNode

    PURE ELEMENTAL FUNCTION outputNode_sub_outputNode(self, that) RESULT(total)
      IMPLICIT NONE

      CLASS(outputNode), INTENT(in):: self
      CLASS(outputNode), INTENT(in):: that
      TYPE(outputNode):: total

      total%den     = self%den     - that%den
      total%mom     = self%mom     - that%mom
      total%tensorS = self%tensorS - that%tensorS

    END FUNCTION outputNode_sub_outputNode

    PURE ELEMENTAL FUNCTION outputNode_mul_outputNode(self, that) RESULT(total)
      IMPLICIT NONE

      CLASS(outputNode), INTENT(in):: self
      CLASS(outputNode), INTENT(in):: that
      TYPE(outputNode):: total

      total%den     = self%den     * that%den
      total%mom     = self%mom     * that%mom
      total%tensorS = self%tensorS * that%tensorS

    END FUNCTION outputNode_mul_outputNode

    PURE ELEMENTAL FUNCTION outputNode_div_int(self, that) RESULT(total)
      IMPLICIT NONE

      CLASS(outputNode), INTENT(in):: self
      INTEGER, INTENT(in):: that
      TYPE(outputNode):: total

      total%den     = self%den     / REAL(that)
      total%mom     = self%mom     / REAL(that)
      total%tensorS = self%tensorS / REAL(that)

    END FUNCTION outputNode_div_int

    SUBROUTINE calculateOutput(rawValues, formatValues, nodeVol, speciesIn)
      USE moduleConstParam
      USE moduleRefParam
      USE moduleSpecies
      USE moduleMath
      IMPLICIT NONE

      TYPE(outputNode),   INTENT(in)::  rawValues
      TYPE(outputFormat), INTENT(out):: formatValues
      REAL(8), INTENT(in):: nodeVol
      CLASS(speciesGeneric), INTENT(in):: speciesIn
      REAL(8), DIMENSION(1:3,1:3):: tensorTemp
      REAL(8), DIMENSION(1:3):: tempVel
      REAL(8):: tempVol

      !Resets the node outputs
      formatValues%density     = 0.D0
      formatValues%velocity    = 0.D0
      formatValues%pressure    = 0.D0
      formatValues%temperature = 0.D0
      tempVol = 1.D0/(nodeVol*Vol_ref)
      IF (rawValues%den > 0.D0) THEN
        tempVel    =  rawValues%mom(:)/rawValues%den
        tensorTemp = (rawValues%tensorS(:,:) - rawValues%den*outerProduct(tempVel,tempVel))
        formatValues%density     = rawValues%den*tempVol
        formatValues%velocity(:) = tempVel
        IF (tensorTrace(tensorTemp) > 0.D0) THEN
         formatValues%pressure    = speciesIn%m*tensorTrace(tensorTemp)*tempVol/3.D0
         formatValues%temperature = formatValues%pressure/(formatValues%density*kb)

        END IF
      END IF

      formatValues%velocity    = formatValues%velocity*v_ref
      formatValues%pressure    = formatValues%pressure*m_ref*v_ref**2
      formatValues%temperature = formatValues%temperature*m_ref*v_ref**2

    END SUBROUTINE calculateOutput

    SUBROUTINE printTime(first)
      USE moduleSpecies
      USE moduleCompTime
      USE moduleCaseParam, ONLY: timeStep
      IMPLICIT NONE

      LOGICAL, INTENT(in), OPTIONAL:: first
      CHARACTER(:), ALLOCATABLE:: fileName

      fileName = 'cpuTime.dat'

      IF (timeOutput) THEN
        IF (PRESENT(first)) THEN
          IF (first) THEN
            OPEN(20, file = path // folder // '/' // fileName, action = 'write')
            WRITE(20, "(A1, 8X, A1, 9X, A1, 7(A20))") "#","t","n","total (s)","push (s)","reset (s)",        &
                                                                              "collision (s)","coulomb (s)", &
                                                                              "weighting (s)","EMField (s)"
            WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
            CLOSE(20)

          END IF

        END IF

        OPEN(20, file = path // folder // '/' // fileName, position = 'append', action = 'write')

        WRITE (20, "(I10, I10, 7(ES20.6E3))") timeStep, nPartOld, tStep, tPush, tReset, tColl, tCoul, tWeight, tEMField

        CLOSE(20)

      END IF

    END SUBROUTINE printTime

END MODULE moduleOutput

