!*******************************************************************************
!*MOD* CRP_Wrapper
!*******************************************************************************
!> CRP wrapper module. You should only need this module to perform interpolations.
!*******************************************************************************
MODULE CRP_Wrapper
  USE CRP_Constants
  USE CRP_C3v_AtomSurface
  USE CRP_C3v_MoleculeSurface
  USE CRP_C4v_AtomSurface
  USE CRP_C4v_MoleculeSurface
  USE CRP_C6v_AtomSurface
  USE CRP_C6v_MoleculeSurface
  USE CRP_General
!  USE PotentialData

  IMPLICIT NONE

  INTEGER :: CRP_Wrapper_Module_SetUp = 0

CONTAINS

!*******************************************************************************
!*SUB* Initialize_CRP_Wrapper_Module
!*******************************************************************************
!> Initialize the CRP wrapper module. Call before using any of the CRP routines.
!*******************************************************************************
  SUBROUTINE Initialize_CRP_Wrapper_Module()
    IMPLICIT NONE

    IF ( CRP_Wrapper_Module_SetUp .LT. 1 ) THEN
      CRP_Wrapper_Module_SetUp = 1
      CALL Initialize_UnitConversion_Module()
      CALL Initialize_CRP_C3v_AtomSurface_Module()
      CALL Initialize_CRP_C3v_MoleculeSurface_Module()
      CALL Initialize_CRP_C4v_AtomSurface_Module()
      CALL Initialize_CRP_C4v_MoleculeSurface_Module()
      CALL Initialize_CRP_C6v_AtomSurface_Module()
      CALL Initialize_CRP_C6v_MoleculeSurface_Module()
    END IF
  END SUBROUTINE

!*******************************************************************************
!*SUB* SetUp_CRP_AtomSurface_PES
!*******************************************************************************
!> Set up a potential energy surface for an atom/surface system.
!>
!> @param[in] DataSet The name of the dataset on disk.
!> @param[out] PES The set up potential energy surface.
!*******************************************************************************
  SUBROUTINE SetUp_CRP_AtomSurface_PES( DataSet, PES )
    IMPLICIT NONE

    CHARACTER(LEN = *)           :: DataSet
    CHARACTER(LEN = 50)          :: Symmetry
    TYPE ( CRP_AtomSurface_PES ) :: PES
    LOGICAL                      :: Exists
    INTEGER                      :: i

    IF ( CRP_Wrapper_Module_SetUp .LT. 1 ) CALL Initialize_CRP_Wrapper_Module()

    PES%DataSet = DataSet ! The data files should reside in this folder.
    PES%Mixing = 1.0      ! By default, we set a mixing of 1

    INQUIRE( FILE=TRIM(ADJUSTL(DataSet)) // "/CRPData", EXIST=Exists )

    ! The PES seems to exist, so continue reading...
    IF ( Exists ) THEN
      OPEN( 200, FILE=TRIM(ADJUSTL(DataSet)) // "/CRPData", STATUS="OLD" )
      READ( 200, * ) Symmetry
      READ( 200, * ) PES%InputUnits%LengthUnit, PES%InputUnits%EnergyUnit
      READ( 200, * ) PES%Geometry%LatticeConstant
      PES%Geometry%LatticeConstant = Convert_Length_Units( PES%Geometry%LatticeConstant, PES%InputUnits, InternalUnits )
      READ( 200, * ) PES%Geometry%NumLayers
      CALL Allocate_Slab_Geometry(PES%Geometry)
      PES%Alloced = .TRUE.
      READ( 200, * ) PES%Geometry%InterlayerSpacings
      READ( 200, *, END=100, ERR=100 ) PES%Disp1, PES%Disp2
      GOTO 101
100   CONTINUE
      PES%Disp1 = 0.0
      PES%Disp2 = 0.0
101   CONTINUE
      DO i = 1, PES%Geometry%NumLayers - 1
        PES%Geometry%InterlayerSpacings(i) = Convert_Length_Units( PES%Geometry%InterlayerSpacings(i), PES%InputUnits, &
                                                                   InternalUnits )
      END DO
      CLOSE( 200 )

      INQUIRE( FILE=TRIM(ADJUSTL(DataSet)) // "/CRPData.info", EXIST=Exists )
      IF ( Exists ) THEN
        OPEN( 200, FILE=TRIM(ADJUSTL(DataSet)) // "/CRPData.info", STATUS="OLD" )
        READ( 200, "(A)", END=102, ERR=102 ) PES%InfoShort
        READ( 200, "(A)", END=102, ERR=102 ) PES%InfoLong
        READ( 200, "(A)", END=102, ERR=102 ) PES%InfoRef
102     CONTINUE
        CLOSE( 200 )
      END IF

      ! Here comes the symmetry dependent part: choose which version of the CRP
      ! to use...
      IF ( Symmetry .EQ. "C3v" ) THEN
        PES%Symmetry = 3
        CALL Initialize_CRP_C3v_AtomSurface_PES( PES )
      ELSE IF ( Symmetry .EQ. "C4v" ) THEN
        PES%Symmetry = 4
        CALL Initialize_CRP_C4v_AtomSurface_PES( PES )
      ELSE IF ( Symmetry .EQ. "C6v" ) THEN
        PES%Symmetry = 6
        CALL Initialize_CRP_C6v_AtomSurface_PES( PES )
      ELSE
      END IF
    ELSE
      PRINT *, "ERROR: Dataset " // TRIM(ADJUSTL(DataSet)) // " does not exist?"
      STOP
    END IF
  END SUBROUTINE

!  SUBROUTINE TEST()
!    USE CRP_Interpolation_Matrix
!
!    IMPLICIT NONE
!
!    TYPE (InterpolationMatrix) :: M
!
!    CALL Allocate_Interpolation_Matrix( M, 3 )
!
!    M%Basis(1)%BasisType = BASIS_CONSTANT
!    M%Basis(2)%BasisType = BASIS_COSINE
!    M%Basis(3)%BasisType = BASIS_COSINE
!!    M%Basis(4)%BasisType = BASIS_COSINE
!    CALL Allocate_Basis_Function( M%Basis(2) )
!    CALL Allocate_Basis_Function( M%Basis(3) )
!!    CALL Allocate_Basis_Function( M%Basis(4) )
!    M%Basis(2)%Parameters(1) = 2.0
!    M%Basis(3)%Parameters(1) = 4.0
!!    M%Basis(4)%Parameters(1) = 6.0
!
!    M%Points(1) = 0.0
!    M%Points(2) = CRP_Constants_PI / 3.0
!    M%Points(3) = CRP_Constants_PI / 2.0
!!    M%Points(4) = CRP_Constants_PI / 4.0
!
!    CALL Compute_Interpolation_Matrix( M )
!
!    CALL Invert_Interpolation_Matrix( M )
!
!    PRINT *, Compute_Interpolated( M, (/ 1.0, 2.0, 3.0 /), 0.0 )
!
!    STOP
!  END SUBROUTINE

!*******************************************************************************
!*SUB* SetUp_CRP_MoleculeSurface_PES
!*******************************************************************************
!> Set up a potential energy surface for a molecule/surface system.
!>
!> @param[in] DataSet The name of the dataset on disk.
!> @param[out] PES The set up potential energy surface.
!*******************************************************************************
  SUBROUTINE SetUp_CRP_MoleculeSurface_PES( DataSet, PES )
    IMPLICIT NONE

    CHARACTER(LEN = *)               :: DataSet
    CHARACTER(LEN = 50)              :: Symmetry
    TYPE ( CRP_MoleculeSurface_PES ) :: PES
    LOGICAL                          :: Exists
    INTEGER                          :: i

    IF ( CRP_Wrapper_Module_SetUp .LT. 1 ) CALL Initialize_CRP_Wrapper_Module()

    PES%DataSet = DataSet ! The data files should reside in this folder.
    PES%Mixing = 1.0      ! By default, we set a mixing of 1

    INQUIRE( FILE=TRIM(ADJUSTL(DataSet)) // "/CRPData", EXIST=Exists )

    ! The PES seems to exist, so continue reading...
    IF ( Exists ) THEN
      OPEN( 200, FILE=TRIM(ADJUSTL(DataSet)) // "/CRPData", STATUS="OLD" )
      READ( 200, * ) Symmetry
      READ( 200, * ) PES%InputUnits%LengthUnit, PES%InputUnits%EnergyUnit
      READ( 200, * ) PES%Geometry%LatticeConstant
      PES%Geometry%LatticeConstant = Convert_Length_Units( PES%Geometry%LatticeConstant, PES%InputUnits, InternalUnits )
      READ( 200, * ) PES%Geometry%NumLayers
      CALL Allocate_Slab_Geometry(PES%Geometry)
      PES%Alloced = .TRUE.
      READ( 200, * ) PES%Geometry%InterlayerSpacings
      DO i = 1, PES%Geometry%NumLayers - 1
        PES%Geometry%InterlayerSpacings(i) = Convert_Length_Units( PES%Geometry%InterlayerSpacings(i), PES%InputUnits, &
                                                                   InternalUnits )
      END DO
      CLOSE( 200 )

      INQUIRE( FILE=TRIM(ADJUSTL(DataSet)) // "/CRPData.info", EXIST=Exists )
      IF ( Exists ) THEN
        OPEN( 200, FILE=TRIM(ADJUSTL(DataSet)) // "/CRPData.info", STATUS="OLD" )
        READ( 200, "(A)", END=102, ERR=102 ) PES%InfoShort
        READ( 200, "(A)", END=102, ERR=102 ) PES%InfoLong
        READ( 200, "(A)", END=102, ERR=102 ) PES%InfoRef
102     CONTINUE
        CLOSE( 200 )
      END IF

      ! Here comes the symmetry dependent part: choose which version of the CRP
      ! to use...
      IF ( Symmetry .EQ. "C3v" ) THEN
        PES%Symmetry = 3
        PES%AtomSurface%Symmetry = PES%Symmetry
        CALL SetUp_CRP_AtomSurface_PES( DataSet, PES%AtomSurface )
        CALL Initialize_CRP_C3v_MoleculeSurface_PES( PES )
      ELSE IF ( Symmetry .EQ. "C4v" ) THEN
        PES%Symmetry = 4
        PES%AtomSurface%Symmetry = PES%Symmetry
        CALL SetUp_CRP_AtomSurface_PES( DataSet, PES%AtomSurface )
        CALL Initialize_CRP_C4v_MoleculeSurface_PES( PES )
      ELSE IF ( Symmetry .EQ. "C6v" ) THEN
        PES%Symmetry = 6
        PES%AtomSurface%Symmetry = PES%Symmetry
        CALL SetUp_CRP_AtomSurface_PES( DataSet, PES%AtomSurface )
        CALL Initialize_CRP_C6v_MoleculeSurface_PES( PES )
      ELSE
        PRINT *, "Unknown symmetry"
        STOP
      END IF
    ELSE
      PRINT *, "ERROR: Dataset " // TRIM(ADJUSTL(DataSet)) // " does not exist?"
      STOP
    END IF
  END SUBROUTINE

!*******************************************************************************
!*SUB* Calculate_CRP_AtomSurface_PES
!*******************************************************************************
!>
!>
!> @param PES
!> @param R
!> @param V
!> @param F
!> @param OutUnits
!*******************************************************************************
  SUBROUTINE Calculate_CRP_AtomSurface_PES( PES, R, V, F, OutUnits )
    IMPLICIT NONE

    TYPE ( CRP_AtomSurface_PES ) :: PES
    REAL             :: R(:), V, F(:)
    TYPE ( Units )               :: OutUnits

    IF ( CRP_Wrapper_Module_SetUp .LT. 1 ) CALL Initialize_CRP_Wrapper_Module()

    IF ( PES%Symmetry .EQ. 3 ) THEN
      CALL Calculate_CRP_C3v_AtomSurface_PES( PES, R, V, F, OutUnits )
    ELSE IF ( PES%Symmetry .EQ. 4 ) THEN
      CALL Calculate_CRP_C4v_AtomSurface_PES( PES, R, V, F, OutUnits )
    ELSE IF ( PES%Symmetry .EQ. 6 ) THEN
      CALL Calculate_CRP_C6v_AtomSurface_PES( PES, R, V, F, OutUnits )
    END IF
  END SUBROUTINE

!*******************************************************************************
!*SUB* Calculate_CRP_MoleculeSurface_PES
!*******************************************************************************
!>
!>
!> @param PES
!> @param R
!> @param V
!> @param F
!> @param OutUnits
!*******************************************************************************
  SUBROUTINE Calculate_CRP_MoleculeSurface_PES( PES, R, V, F, OutUnits )
    IMPLICIT NONE

    TYPE ( CRP_MoleculeSurface_PES ) :: PES
    REAL                 :: R(:), V, F(:)
    TYPE ( Units )                   :: OutUnits

    IF ( CRP_Wrapper_Module_SetUp .LT. 1 ) CALL Initialize_CRP_Wrapper_Module()

    IF ( PES%Symmetry .EQ. 3 ) THEN
      CALL Calculate_CRP_C3v_MoleculeSurface_PES( PES, R, V, F, OutUnits )
    ELSE IF ( PES%Symmetry .EQ. 4 ) THEN
      CALL Calculate_CRP_C4v_MoleculeSurface_PES( PES, R, V, F, OutUnits )
    ELSE IF ( PES%Symmetry .EQ. 6 ) THEN
      CALL Calculate_CRP_C6v_MoleculeSurface_PES( PES, R, V, F, OutUnits )
    END IF
  END SUBROUTINE
END MODULE

