!Contains the information about species (particles)
MODULE moduleSpecies
  USE moduleCaseParam
  USE OMP_LIB
  IMPLICIT NONE
  
  TYPE, ABSTRACT:: speciesGeneric
    CHARACTER(:), ALLOCATABLE:: name
    REAL(8):: m=0.D0, weight=0.D0, qm=0.D0
    INTEGER:: n=0
  END TYPE speciesGeneric

  TYPE, EXTENDS(speciesGeneric):: speciesNeutral
    CLASS(speciesGeneric), POINTER:: ion => NULL()
    CONTAINS
      PROCEDURE, PASS:: ionize => ionizeNeutral

  END TYPE speciesNeutral

  TYPE, EXTENDS(speciesGeneric):: speciesCharged
    REAL(8):: q=0.D0
    CLASS(speciesGeneric), POINTER:: ion => NULL(), neutral => NULL()
    CONTAINS
      PROCEDURE, PASS:: ionize     => ionizeCharged
      PROCEDURE, PASS:: neutralize => neutralizeCharged

  END TYPE speciesCharged

  TYPE:: speciesCont
    CLASS(speciesGeneric), ALLOCATABLE:: obj

  END TYPE

  INTEGER:: nSpecies
  TYPE(speciesCont), ALLOCATABLE, TARGET:: species(:)

  TYPE particle
    REAL(8):: r(1:3) !Position
    REAL(8):: v(1:3) !Velocity
    CLASS(speciesGeneric), POINTER:: species !Pointer to species associated with this particle
    INTEGER:: cell !Index of element in which the particle is located
    INTEGER:: cellColl !Index of element in which the particle is located in the Collision Mesh
    REAL(8):: Xi(1:3) !Logical coordinates of particle in element e_p.
    LOGICAL:: n_in !Flag that indicates if a particle is in the domain
    REAL(8):: weight=0.D0 !weight of particle

  END TYPE particle

  !Number of old particles
  INTEGER:: nPartOld
  !Arrays that contain the particles
  TYPE(particle), ALLOCATABLE, DIMENSION(:), TARGET:: partOld !array of particles from previous iteration

  CONTAINS
    FUNCTION speciesName2Index(speciesName) RESULT(sp)
      USE moduleErrors
      IMPLICIT NONE

      CHARACTER(:), ALLOCATABLE:: speciesName
      INTEGER:: sp 
      INTEGER:: s

      sp = 0
      DO s = 1, nSpecies
        IF (speciesName == species(s)%obj%name) THEN
          sp = species(s)%obj%n
          EXIT !If a species is found, exit the loop

        END IF

      END DO

      !If no species is found, call a critical error
      IF (sp == 0) CALL criticalError('Species ' // speciesName // ' not found.', 'speciesName2Index')

    END FUNCTION speciesName2Index

    !Change particle type to corresponding ion (neutral species)
    SUBROUTINE ionizeNeutral(self, part)
      USE moduleErrors
      IMPLICIT NONE

      CLASS(speciesNeutral), INTENT(IN):: self
      TYPE(particle), INTENT(inout):: part

      IF (ASSOCIATED(self%ion)) THEN
        part%species => self%ion

      ELSE
        CALL criticalError('No ion defined for species :' // self%name, 'ionizeNeutral')

      END IF

    END SUBROUTINE ionizeNeutral

    !Change particle type to corresponding ion (charged species)
    SUBROUTINE ionizeCharged(self, part)
      USE moduleErrors
      IMPLICIT NONE

      CLASS(speciesCharged), INTENT(IN):: self
      TYPE(particle), INTENT(inout):: part

      IF (ASSOCIATED(self%ion)) THEN
        part%species => self%ion

      ELSE
        CALL criticalError('No ion defined for species :' // self%name, 'ionizeCharged')

      END IF

    END SUBROUTINE ionizeCharged

    !Change particle type to corresponding neutral
    SUBROUTINE neutralizeCharged(self, part)
      USE moduleErrors
      IMPLICIT NONE

      CLASS(speciesCharged), INTENT(in):: self
      TYPE(particle), INTENT(inout):: part

      IF (ASSOCIATED(self%neutral)) THEN
        part%species => self%neutral

      ELSE
        CALL criticalError('No neutral defined for species' // self%name, 'neutralizeCharged')

      END IF

    END SUBROUTINE neutralizeCharged

END MODULE moduleSpecies

