!--------------------------------------- LICENCE BEGIN -----------------------------------
!Environment Canada - Atmospheric Science and Technology License/Disclaimer,
!                     version 3; Last Modified: May 7, 2008.
!This is free but copyrighted software; you can use/redistribute/modify it under the terms
!of the Environment Canada - Atmospheric Science and Technology License/Disclaimer
!version 3 or (at your option) any later version that should be found at:
!http://collaboration.cmc.ec.gc.ca/science/rpn.comm/license.html
!
!This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
!without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
!See the above mentioned License/Disclaimer for more details.
!You should have received a copy of the License/Disclaimer along with this software;
!if not, you can write to: EC-RPN COMM Group, 2121 TransCanada, suite 500, Dorval (Quebec),
!CANADA, H9P 1J3; or send e-mail to service.rpn@ec.gc.ca
!-------------------------------------- LICENCE END --------------------------------------

!--------------------------------------------------------------------------
! > MODULE HorizontalCoord (prefix="hco")
!
! Purpose: Defined the horizontal grid coordinate for various grids
!
!
! - Subroutines
!   hco_SetupFromFile (public)
!   hco_Get           (public)
!
! - Public variables
!   struct_hco
!
! - Dependencies
!   horizontalcoord_mod 
!--------------------------------------------------------------------------

module HorizontalCoord_mod 21,1
  use MathPhysConstants_mod
  implicit none
  save
  private

  ! Public derived type
  public :: struct_hco

  ! Public Subroutines
  public :: hco_SetupFromFile, hco_Get


  type :: struct_hco
     character(len=32)    :: gridname
     logical              :: initialized = .false.
     integer              :: ni
     integer              :: nj
     character(len=1)     :: grtyp
     integer              :: ig1
     integer              :: ig2
     integer              :: ig3
     integer              :: ig4
     integer              :: EZscintID
     real(8), allocatable :: lat(:) ! in radians
     real(8), allocatable :: lon(:) ! in radians
     real(8)              :: dlat   ! in radians
     real(8)              :: dlon   ! in radians
     logical              :: global
     logical              :: rotated
     real(8)              :: xlat1
     real(8)              :: xlon1
     real(8)              :: xlat2
     real(8)              :: xlon2
  end type struct_hco

  integer, parameter :: nMaxGrid = 2
  type(struct_hco), target :: hco(nMaxGrid)

  integer :: nGrid = 0

  contains

!--------------------------------------------------------------------------
! hco_SetupFromFile
!--------------------------------------------------------------------------

  subroutine hco_SetupFromFile(TemplateFile, EtiketName, GridName) 6,3
    implicit none

    character(len=*), intent(in) :: TemplateFile
    character(len=*), intent(in) :: EtiketName
    character(len=*), intent(in) :: GridName

    real, allocatable :: lat2d_4(:,:)
    real, allocatable :: lon2d_4(:,:)

    real(8), allocatable :: lat_8(:)
    real(8), allocatable :: lon_8(:)

    real    :: xlat1_4, xlon1_4, xlat2_4, xlon2_4

    integer :: iu_template = 0
    integer :: fnom, fstouv, fstfrm, fclos, ezqkdef, vfstlir
    integer :: key, fstinf, fstprm, ier, fstinl, EZscintID
    integer :: ni, nj, ni_t, nj_t, nlev_t, i, j, gdll
    integer :: dateo, deet, npas, nk, nbits, datyp
    integer :: ip1, ip2, ip3, swa, lng, dltf, ubc
    integer :: extra1, extra2, extra3
    integer :: ig1, ig2, ig3, ig4
    integer :: ig1_tictac, ig2_tictac, ig3_tictac, ig4_tictac

    logical :: FileExist, global, rotated

    character(len=4 ) :: nomvar
    character(len=2 ) :: typvar
    character(len=1 ) :: grtyp, grtyp_tictac
    character(len=12) :: etiket

    nGrid = nGrid + 1

    if ( nGrid > nMaxGrid ) then
      write(*,*)
      write(*,*) 'hco_SetupFromFile: Too many grid coordinates!', nMaxGrid
      stop
    end if

    !
    !- 1.  Open/Check template file
    !
    inquire(file=trim(TemplateFile), exist=FileExist)

    if ( FileExist ) then
      ier = fnom(iu_template,trim(TemplateFile),'RND+OLD+R/O',0)
      if ( ier == 0 ) then
        write(*,*)
        write(*,*) 'Template File :', trim(TemplateFile)
        write(*,*) 'opened as unit file ',iu_template
        ier = fstouv(iu_template,'RND+OLD')
      else
        write(*,*)
        write(*,*) 'hco_SetupFromFile: Error in opening the template grid file'
        write(*,*) trim(TemplateFile)
        stop
      end if
    else
      write(*,*)
      write(*,*) 'hco_SetupFromFile: template grid file DOES NOT EXIST'
      write(*,*) trim(TemplateFile)
      stop
    end if

    !
    !- 2.  Get Horizontal grid info
    !

    !- 2.1 Grid size and grid projection info
    dateo  = -1
    etiket = EtiketName
    ip1    = -1
    ip2    = -1
    ip3    = -1
    typvar = ' '
    nomvar = 'P0'

    key = fstinf( iu_template,                                & ! IN
                  ni, nj, nk,                                 & ! OUT
                  dateo, etiket, ip1, ip2, ip3, typvar, nomvar )! IN

    if (key < 0) then
      write(*,*)
      write(*,*) 'hco_SetupFromFile: Unable to find output horiz grid info using = ',nomvar
      stop
    end if

    ier = fstprm( key,                                             & ! IN
                  dateo, deet, npas, ni, nj, nk, nbits,            & ! OUT
                  datyp, ip1, ip2, ip3, typvar, nomvar, etiket,    & ! OUT
                  grtyp, ig1, ig2, ig3,                            & ! OUT
                  ig4, swa, lng, dltf, ubc, extra1, extra2, extra3 ) ! OUT

    if ( trim(grtyp) == 'G' .and. ig2 == 0 ) then
       write(*,*) 
       write(*,*) 'The latitute in the input gaussian grid are ordered from SOUTH to NORTH (ig2=0)'
       write(*,*) '  --> reordering from NORTH to SOUTH (ig2=1)'
       ig2 = 1
    end if

    EZscintID  = ezqkdef( ni, nj, grtyp, ig1, ig2, ig3, ig4, iu_template )   ! IN

    allocate(lat_8(1:nj))
    allocate(lon_8(1:ni))

    !- 2.2 Rotated grid
    if ( trim(grtyp) == 'Z' ) then

       !-  2.2.1 Read the Longitudes
       dateo  = -1
       etiket = EtiketName
       ip1    = ig1
       ip2    = ig2
       ip3    = -1
       typvar = 'X'
       nomvar = '>>'

       ier = vfstlir( lon_8,                                      & ! OUT 
                      iu_template,                                & ! IN
                      ni_t, nj_t, nlev_t,                         & ! OUT
                      dateo, etiket, ip1, ip2, ip3, typvar,nomvar)  ! IN

       if (ier < 0) then
         write(*,*)
         write(*,*) 'hco_SetupFromFile: Unable to find >> grid descriptors'
         stop
       end if

       !  Test if the dimensions are compatible with the grid
       if ( ni_t /= ni .or. nj_t /= 1 ) then
         write(*,*)
         write(*,*) 'hco_SetupFromFile: Incompatible >> grid descriptors !'
         write(*,*) 'Found     :', ni_t, nj_t
         write(*,*) 'Should be :', ni, 1
         stop 
       end if

       !-  2.2.2 Read the latitudes
       dateo  = -1
       etiket = EtiketName
       ip1    = ig1
       ip2    = ig2
       ip3    = -1
       typvar = 'X'
       nomvar = '^^'

       ier = vfstlir( lat_8,                                      & ! OUT 
                      iu_template,                                & ! IN
                      ni_t, nj_t, nlev_t,                         & ! OUT
                      dateo, etiket, ip1, ip2, ip3, typvar,nomvar)  ! IN

       if (ier < 0) then
         write(*,*)
         write(*,*) 'hco_SetupFromFile: Unable to find ^^ grid descriptors'
         stop
       end if

       !  Test if the dimensions are compatible with the grid
       if ( ni_t /= 1 .or. nj_t /= nj ) then
         write(*,*)
         write(*,*) 'hco_SetupFromFile: Incompatible ^^ grid descriptors !'
         write(*,*) 'Found     :', ni_t, nj_t
         write(*,*) 'Should be :', 1, nj
         stop 
       end if

       !- 2.2.3 Do we have a rotated grid ?
       dateo  = -1
       etiket = EtiketName
       ip1    = ig1
       ip2    = ig2
       ip3    = -1
       typvar = 'X'
       nomvar = '^^'

       key = fstinf( iu_template,                                   & ! IN
                     ni_t, nj_t, nk,                                & ! OUT
                     dateo, etiket, ip1, ip2, ip3, typvar, nomvar )   ! IN

       ier = fstprm( key,                                           & ! IN
                     dateo, deet, npas, ni_t, nj_t, nk, nbits,      & ! OUT
                     datyp, ip1, ip2, ip3, typvar, nomvar, etiket,  & ! OUT
                     grtyp_tictac, ig1_tictac, ig2_tictac,          & ! OUT
                     ig3_tictac, ig4_tictac, swa, lng, dltf,        & ! OUT
                     ubc, extra1, extra2, extra3 )                    ! OUT

       call cigaxg ( grtyp_tictac,                                  & ! IN
                     xlat1_4, xlon1_4, xlat2_4, xlon2_4,            & ! OUT
                     ig1_tictac, ig2_tictac, ig3_tictac, ig4_tictac ) ! IN

       if ( xlat1_4 == xlat2_4 .and. xlon1_4 == xlon2_4 ) then
          rotated = .false.
       else
          rotated = .true.
       end if

    !- 2.3 Gaussian Grid
    elseif ( trim(grtyp) == 'G' ) then

      !-  2.3.1 Find the latitudes and longitudes
      allocate(lat2d_4(1:ni,1:nj))
      allocate(lon2d_4(1:ni,1:nj))

      ier = gdll( EZscintID,       & ! IN
                  lat2d_4, lon2d_4 ) ! OUT

      lon_8(:) = real(lon2d_4(:,nj/2),8)
      if (ig2 == 1 .and. (lat2d_4(1,2) - lat2d_4(1,1)) > 0.0d0 ) then 
        ! Revert latitudes since gdll does not handle properly this grid type
        do j = 1, nj
          lat_8(j) = real(lat2d_4(1,nj-j+1),8)
        end do 
      else
        lat_8(:) = real(lat2d_4(1,:),8)
      end if

      deallocate(lat2d_4)
      deallocate(lon2d_4)

      ! This grid type is not rotated
      rotated = .false.
      xlat1_4 =   0.0
      xlon1_4 = 180.0
      xlat2_4 =   0.0
      xlon2_4 = 180.0

    else
      write(*,*)
      write(*,*) 'hco_SetupFromFile: Only grtyp = Z or G are supported !, grtyp = ', trim(grtyp)
      stop 
    end if

    !
    !- 3.  Is this a global or a LAM domain ?
    !
    call global_or_lam( global,     & ! OUT
                        lon_8, ni )   ! IN

    !
    !- 4.  Initialized Horizontal Grid Structure
    !
    allocate(hco(nGrid) % lat(1:nj))
    allocate(hco(nGrid) % lon(1:ni))

    hco(nGrid) % gridname     = trim(gridname)
    hco(nGrid) % ni           = ni
    hco(nGrid) % nj           = nj
    hco(nGrid) % grtyp        = trim(grtyp) 
    hco(nGrid) % ig1          = ig1
    hco(nGrid) % ig2          = ig2
    hco(nGrid) % ig3          = ig3
    hco(nGrid) % ig4          = ig4
    hco(nGrid) % EZscintID    = EZscintID
    hco(nGrid) % lon(:)       = lon_8(:) * MPC_RADIANS_PER_DEGREE_R8
    hco(nGrid) % lat(:)       = lat_8(:) * MPC_RADIANS_PER_DEGREE_R8
    hco(nGrid) % dlon         = (lon_8(2) - lon_8(1)) * MPC_RADIANS_PER_DEGREE_R8
    hco(nGrid) % dlat         = (lat_8(2) - lat_8(1)) * MPC_RADIANS_PER_DEGREE_R8
    hco(nGrid) % global       = global
    hco(nGrid) % rotated      = rotated
    hco(nGrid) % xlat1        = real(xlat1_4,8)
    hco(nGrid) % xlon1        = real(xlon1_4,8)
    hco(nGrid) % xlat2        = real(xlat2_4,8)
    hco(nGrid) % xlon2        = real(xlon2_4,8)
    hco(nGrid) % initialized  = .true.

    deallocate(lat_8)
    deallocate(lon_8)

    !
    !- 4.  Close the input file
    !
    ier = fstfrm(iu_template)
    ier = fclos (iu_template)

  end subroutine hco_SetupFromFile

!--------------------------------------------------------------------------
! hco_Get
!--------------------------------------------------------------------------

  function hco_Get(gridname) result(hco_ptr) 15,1
    implicit none

    type(struct_hco), pointer     :: hco_ptr
    character(len=*), intent(in)  :: gridname

    integer :: id, gridid

    call hco_GetGridIndex(gridid, gridname)

    hco_ptr => hco(gridid)

  end function hco_Get

!--------------------------------------------------------------------------
! hco_GetGridIndex
!--------------------------------------------------------------------------

  subroutine hco_GetGridIndex(GridIndex, gridname) 1
    implicit none

    integer,          intent(out) :: gridindex
    character(len=*), intent(in)  :: gridname

    integer :: id, gridid

    if ( nGrid == 0 ) then
      write(*,*)
      write(*,*) 'hco_GetGridIndex: HorizontalGrid module not initialized'
      stop
    endif

    gridid = -1
    do id = 1, nGrid
      if ( trim(hco(id) % gridname) == trim(gridname) ) then
        gridid = id
        exit
      end if
    end do

    if ( gridid /= -1 ) then
      gridindex = gridid
    else
      write(*,*)
      write(*,*) 'hco_GetGridIndex: Unknown Grid ', trim(gridname)
      stop
    end if

  end subroutine hco_GetGridIndex

!--------------------------------------------------------------------------
! Global_or_lam
!--------------------------------------------------------------------------

  subroutine global_or_lam(global, lon, ni) 1
    implicit none

    integer, intent(in)  :: ni
    real(8), intent(in)  :: lon(ni)
    logical, intent(out) :: global

    real(8) :: dx, next_lon

    dx       = lon(2) - lon(1)
    next_lon = lon(ni) + dx

    write(*,*)
    write(*,*) 'dx       = ',dx
    write(*,*) 'lon(ni)  = ',lon(ni)
    write(*,*) 'next_lon = ',next_lon
    write(*,*) 'lon(1)   = ',lon(1)

    if ( abs(next_lon - 360.0d0 - lon(1)) < dx/2.0d0 .or. &
         lon(1) == lon(ni) ) then

      global = .true.
      if ( lon(1) == lon(ni) ) then
        write(*,*)
        write(*,*) ' *** Global Grid where i = ni (repetition) '
      else  
        write(*,*)
        write(*,*) ' *** Global Grid where i /= ni '
      end if

    else

      global = .false.
      write(*,*)
      write(*,*) ' *** Limited-Area Grid '

    end if

  end subroutine global_or_lam

end module HorizontalCoord_mod