!-------------------------------------- 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 --------------------------------------
!

subroutine oda_L(statevector,column,columng,lobsSpaceData) 3,30
  use MathPhysConstants_mod
  use mpivar_mod 
  use gridstatevector_mod
  use obsSpaceData_mod
  use columnData_mod
  use timeCoord_mod
  implicit none

  !
  ! Purpose: Localisation (3D) operator
  !          Compute profiled increments from grided increments
  !

  type(struct_columnData) :: column, columng
  type(struct_obs)        :: lobsSpaceData
  type(struct_gsv)        :: statevector

  integer :: nl_ilev, ierr, myLonEndP1, myLatEndP1, status, Vcode_anl
  type(struct_vco), pointer :: vco_anl

  myLonEndP1 = statevector%myLonEnd + 1
  myLatEndP1 = min(statevector%myLatEnd + 1, statevector%nj)

  !
  !- 1.  Interpolation to obs location
  !
  if ( mpi_myid == 0 ) THEN
    write(*,*) ' ODA_L - Horizontal interpolation GD --> MVO'
  end if

  !- 1.1 Communicate extra latitude needed for interpolation  
  call tmg_start(38,'INTERP_BARR')
  call rpn_comm_barrier("GRID",ierr)
  call tmg_stop(38)
  call tmg_start(39,'INTERP_COMM')
  if (mpi_nprocs /= 1) call gsv_commLatLon(statevector)
  call tmg_stop(39)

!$OMP PARALLEL
!$OMP SECTIONS PRIVATE(nl_ilev)

    !- 1.2 3D fields interpolation
!$OMP SECTION
    if (gsv_varExist('UU') ) call gd2mvo('UU',col_getNumLev(column,'MM'))
!$OMP SECTION
    if (gsv_varExist('VV') ) call gd2mvo('VV',col_getNumLev(column,'MM'))
!$OMP SECTION
    if (gsv_varExist('HU') ) call gd2mvo('HU',col_getNumLev(column,'TH'))
!$OMP SECTION
    if (gsv_varExist('TT') ) call gd2mvo('TT',col_getNumLev(column,'TH'))

    !- 1.3 2D fields interpolation
!$OMP SECTION
    nl_ilev = 1
    if (gsv_varExist('P0') ) call gd2mvo('P0',nl_ilev)
    if (gsv_varExist('TG') ) call gd2mvo('TG',nl_ilev)

!$OMP END SECTIONS
!$OMP END PARALLEL

    !
    !- 2.  Variable conversions
    !

    !- 2.1 Mass fields (TT,PS,HU) to hydrostatic geopotential
    vco_anl => col_getVco(columng)
    status = vgd_get(vco_anl%vgrid,key='ig_1 - vertical coord code',value=Vcode_anl)
    call tmg_start(36,'INTERP_LTT2PHI')
    if(Vcode_anl .eq. 5001) then
      call ltt2phi(column,columng)
    else
      call ltt2phi_gem4(column,columng)
    endif
    call tmg_stop(36)

    !- 2.2 Rotated wind to Meteorological wind
    if ( gsv_varExist('UU') .and. gsv_varExist('VV') .and. statevector%hco%Rotated ) then
      write(*,*) 'uvrot2uv Active'
      call uvrot2uv('UU', 'VV', col_getNumLev(column,'MM')) ! IN
    end if
  
CONTAINS

!--------------------------------------------------------------------------
! GD2MVO
!--------------------------------------------------------------------------

  SUBROUTINE GD2MVO(varName,klev) 6,8
    !*
    !***s/r GD2MVO  - Horizontal interpolation of the model variables
    !*                in grid-point space to observation locations.
    !*                Bilinear interpolation from the 4 nearest grid points.
    !*                (adapted from BILIN)
    !
    !*
    !*     VARNAME: identificator of the variable to be treated
    !*     KLEV     : number of levels (typically 1 for 2D-fields and COL_GETNUMLEV(COLUMN) for 3D-fields)
    !*
    implicit none
    
    character(len=*), intent(in) :: varName
    integer,          intent(in) :: klev
    
    INTEGER :: JLEV, JLAT, JLON, headerIndex, JSTEP
    INTEGER :: ILON, ILAT, ILO
    
    REAL(8) :: DLMEAN, DLMEAS, DLDLON
    REAL(8) :: DLDX, DLDY, DLDX_new, DLDY_new,DLW1, DLW2, DLW3, DLW4
    real(8) :: xpos, ypos, lat, lon, latrot, lonrot
    
    real(8), pointer :: column_ptr(:,:)
    real(8), pointer :: field_ptr(:,:,:,:)

    field_ptr  => gsv_getField(statevector,varName)

    if (col_getNumCol(column) > 0) then
       column_ptr => col_getAllColumns(column,varName)
       column_ptr(:,:) = 0.0d0
    end if

    ! Note: We assume here the all the obs between the poles and the last grid points
    !       (i.e. outside the grid) have been moved within the grid by sugomobs

    do jstep = 1, statevector%numStep
      !
      !- LOOP OVER ALL THE OBSERVATIONS
      !
      DO headerIndex = 1, col_getNumCol(column)

        if ( tim_getTimeInterpWeight(headerIndex,jstep) > 0.0d0 .and. &
             .not. btest(obs_headElem_i(lobsSpaceData,OBS_ST1,headerIndex),5) ) then

          !- 2.1 Find the obs position within the analysis grid
          call col_getLatLon( column, headerIndex,                 & ! IN
                              Lat, Lon, ypos, xpos, LatRot, LonRot ) ! OUT

          !- Make sure we are within bounds
          if ( ypos < real(statevector%myLatBeg,8) .or. &
               ypos > real(myLatEndP1          ,8) .or. &
               xpos < real(statevector%myLonBeg,8) .or. &
               xpos > real(myLonEndP1          ,8) ) then
            write(*,*) 'oda_L: Obs outside local domain for headerIndex = ', headerIndex
            write(*,*) '       obs lat, lon position               = ', Lat*MPC_DEGREES_PER_RADIAN_R8, Lon*MPC_DEGREES_PER_RADIAN_R8
            write(*,*) '       obs x, y     position               = ', xpos, ypos
            write(*,*) '       domain x_start, x_end, y_start, y_end bounds = ',  &
                       statevector%myLonBeg, myLonEndP1, statevector%myLatBeg, myLatEndP1

            ! if obs above or below latitude band, move it to the edge of this latitude band
            if( ypos < real(statevector%myLatBeg,8) ) ypos = real(statevector%myLatBeg,8)
            if( ypos > real(myLatEndP1          ,8) ) ypos = real(myLatEndP1          ,8)

            ! abort if obs is to the left or right of the analysis domain
            if( xpos < real(statevector%myLonBeg,8) ) xpos = real(statevector%myLonBeg,8)
            if( xpos > real(myLonEndP1          ,8) ) xpos = real(myLonEndP1          ,8)
          end if

          !- 2.2 Find the lower-left grid point next to the observation
          if ( xpos /= real(myLonEndP1,8) ) then
            ILON = floor(xpos)
          else
            ILON = floor(xpos) - 1
          end if

          if ( ypos /= real(myLatEndP1,8) ) then
            ILAT = floor(ypos)
          else
            ILAT = floor(ypos) - 1
          end if

          !- 2.3 Compute the 4 weights of the bilinear interpolation
          DLDX = xpos - real(ILON,8)
          DLDY = ypos - real(ILAT,8)

          DLW1 = (1.d0-DLDX) * (1.d0-DLDY)
          DLW2 =       DLDX  * (1.d0-DLDY)
          DLW3 = (1.d0-DLDX) *       DLDY
          DLW4 =       DLDX  *       DLDY

          !- 2.4 Interpolate the model state to the obs point
          DO JLEV = 1, KLEV
            column_ptr(JLEV,headerIndex) = column_ptr(JLEV,headerIndex)  +  &
                                    tim_getTimeInterpWeight(headerIndex,jstep) *  &
                                    ( DLW1 * field_ptr(ILON  ,JLEV,ILAT  ,JSTEP) &
                                    + DLW2 * field_ptr(ILON+1,JLEV,ILAT  ,JSTEP) &
                                    + DLW3 * field_ptr(ILON  ,JLEV,ILAT+1,JSTEP) &
                                    + DLW4 * field_ptr(ILON+1,JLEV,ILAT+1,JSTEP) )
          END DO

        end if

      END DO ! Obs

    END DO ! JSTEP

  END subroutine gd2mvo

!--------------------------------------------------------------------------
! UVROT2UV
!--------------------------------------------------------------------------

  subroutine uvrot2uv (UUvarName,VVvarName,klev) 1,6
    !
    !- uvrot2uv - Transforms tangential (U,V) wind components at observation
    !             locations on GEM rotated frame to the real sphere.
    use WindRotation_mod
    implicit none

    character(len=*), intent(in) :: UUvarName
    character(len=*), intent(in) :: VVvarName
    integer,          intent(in) :: klev
    
    real(8) :: lat, lon, latrot, lonrot, xpos, ypos
    real(8), pointer :: UUcolumn(:), VVcolumn(:)

    integer :: headerIndex

    !
    !- 1.  Loop over all the observation locations
    !
    do headerIndex = 1, col_getNumCol(column)

      !- 1.1 Extract (rotated) wind profiles
      UUColumn => col_getColumn(column,headerIndex,UUvarName)
      VVColumn => col_getColumn(column,headerIndex,VVvarName)
       
      !- 1.2 Find the latitudes and longitudes
      call col_getLatLon( column, headerIndex,                   & ! IN
                          Lat, Lon, ypos, xpos, LatRot, LonRot )   ! OUT

      !- 1.3 Rotate Winds
      call uvr_RotateWind( UUColumn, VVColumn,       & ! INOUT
                           Lat, Lon, LatRot, LonRot, & ! IN
                           'ToMetWind', klev )         ! IN

    end do

  end subroutine uvrot2uv

end subroutine oda_L