!--------------------------------------- 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 BILIN(lcolumn,statevector,lobsSpaceData) 8,33
  !*
  !***s/r BILIN  - Horizontal interpolation of the model variables
  !*               in grid-point space to observation points.
  !*               Bilinear interpolation from the 4 nearest grid points.
  !*     .         (based on HOR4 from the ARPEGE/IFS model)
  !*
  !*Author  : J. Pailleux  ECMWF    90-01-11
  !*     .    Luc Fillion  RPN/AES  Jan 1993
  !
  !**    Purpose:  Build GOMU, GOMV, GOMT, GOMGZ, GOMQ and GOMPS  with the model variables at the
  !*               observation points from the model variables in grid-points
  !*               space using bilinear interpolation.
  !*
  use MathPhysConstants_mod
  use obsSpaceData_mod
  use columnData_mod
  use gridStateVector_mod
  IMPLICIT NONE
  
  type(struct_columnData) :: lcolumn
  type(struct_gsv) :: statevector
  type(struct_obs) :: lobsSpaceData
  
  INTEGER   JLEV, JK, JK2, JGL, JLON, JOBS
  INTEGER   ILON, ILA, IERR
  
  REAL*8  :: LAT, LON
  REAL*8  :: DLDY, DLW1, DLW2, DLW3, DLW4, DLDX, ypos, xpos
  real(8) :: latrot, lonrot
  
  real*8, allocatable ::zgd(:,:,:)
  
  real*8, pointer :: uu_column(:),vv_column(:),hu_column(:)
  real*8, pointer :: tt_column(:),tr_column(:),ps_column(:),tg_column(:)
  real*8, pointer :: field_ptr(:,:,:), uu_ptr(:,:,:), vv_ptr(:,:,:)
  
  ! 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 suprep

  allocate(zgd(1:statevector%ni+1, 1:statevector%nk,1:statevector%nj))
  
  ZGD(:,:,:)=0.0d0
  field_ptr => gsv_getField3D(statevector)
  ZGD(1:STATEVECTOR%NI,1:statevector%nk,1:STATEVECTOR%NJ)= &
       field_ptr(1:STATEVECTOR%NI,1:statevector%nk,1:STATEVECTOR%NJ)

  !
  !- 1.  EXPAND Field BY REPEATING MERIDIAN 1 into INTO MERIDIAN NI+1
  !
  DO JK = 1, statevector % nk
     DO JGL = 1, STATEVECTOR % NJ
        ZGD(STATEVECTOR % NI+1,JK,JGL) = ZGD( 1,JK,JGL)
     END DO
  END DO
  
  !
  !- 2.  LOOP OVER ALL THE OBSERVATIONS
  !
  DO JOBS = 1, col_getNumCol(lcolumn)

     !- 2.1 Find the obs positin within the analysis grid
     call col_getLatLon( lcolumn, jobs,                       & ! IN
                         Lat, Lon, ypos, xpos, LatRot, LonRot ) ! OUT

     !- Make sure we are within bounds
     if ( ypos < 1.d0                           .or. &
          ypos > real(statevector % nj      ,8) .or. &
          xpos < 1.d0                           .or. &
          xpos > real(statevector % ni + 1  ,8) ) then
       write(*,*) 'bilin: Obs outside local domain for job = ', jobs
       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_end, y_end bounds          = ', statevector % ni + 1, statevector % nj
       stop
     end if

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

     if ( ypos /= real(statevector % nj,8) ) then
       ILA = floor(ypos)
     else
       ILA = floor(ypos) - 1
     end if

     !- 2.3 COMPUTE THE 4 WEIGHTS OF THE BILINEAR INTERPOLATION
     DLDX = xpos - real(ILON,8)
     DLDY = ypos - real(ILA,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
     if(col_varExist('UU')) uu_column => col_getColumn(lcolumn,jobs,'UU')
     if(col_varExist('VV')) vv_column => col_getColumn(lcolumn,jobs,'VV')
     if(col_varExist('HU')) hu_column => col_getColumn(lcolumn,jobs,'HU')
     if(col_varExist('TT')) tt_column => col_getColumn(lcolumn,jobs,'TT')
     if(col_varExist('P0')) ps_column => col_getColumn(lcolumn,jobs,'P0')
     if(col_varExist('TG')) tg_column => col_getColumn(lcolumn,jobs,'TG')
     
     DO JK = 1, gsv_getNumLev(statevector,'MM')
        if(gsv_varExist('UU')) then
           jk2=jk+gsv_getOffsetFromVarName(statevector,'UU')
           uu_column(JK) =   DLW1*zgd(ILON  ,JK2,ILA)  &
                           + DLW2*zgd(ILON+1,JK2,ILA)  &
                           + DLW3*zgd(ILON  ,JK2,ILA+1)  &
                           + DLW4*zgd(ILON+1,JK2,ILA+1)
        endif
        if(gsv_varExist('VV')) then
           jk2=jk+gsv_getOffsetFromVarName(statevector,'VV')
           vv_column(JK) =   DLW1*zgd(ILON  ,JK2,ILA)  &
                           + DLW2*zgd(ILON+1,JK2,ILA)  &
                           + DLW3*zgd(ILON  ,JK2,ILA+1)  &
                           + DLW4*zgd(ILON+1,JK2,ILA+1)
        endif
     ENDDO
     DO JK = 1, gsv_getNumLev(statevector,'TH')
        if(gsv_varExist('HU')) then
           jk2=jk+gsv_getOffsetFromVarName(statevector,'HU')
           hu_column(JK) =   DLW1*zgd(ILON  ,JK2,ILA)  &
                           + DLW2*zgd(ILON+1,JK2,ILA)  &
                           + DLW3*zgd(ILON  ,JK2,ILA+1)  &
                           + DLW4*zgd(ILON+1,JK2,ILA+1)
        endif
        if(gsv_varExist('TT')) then
           jk2=jk+gsv_getOffsetFromVarName(statevector,'TT')
           tt_column(JK) =   DLW1*zgd(ILON  ,JK2,ILA)  &
                           + DLW2*zgd(ILON+1,JK2,ILA)  &
                           + DLW3*zgd(ILON  ,JK2,ILA+1)  &
                           + DLW4*zgd(ILON+1,JK2,ILA+1)
        endif
     enddo
     if(gsv_varExist('P0')) then
        jk2=1+gsv_getOffsetFromVarName(statevector,'P0')
        ps_column(1) =   DLW1*zgd(ILON  ,jk2,ILA)  &
                       + DLW2*zgd(ILON+1,jk2,ILA)  &
                       + DLW3*zgd(ILON  ,jk2,ILA+1)  &
                       + DLW4*zgd(ILON+1,jk2,ILA+1)
     endif
     if(gsv_varExist('TG')) then
        jk2=1+gsv_getOffsetFromVarName(statevector,'TG')
        tg_column(1) =   DLW1*zgd(ILON  ,jk2,ILA)  &
                       + DLW2*zgd(ILON+1,jk2,ILA)  &
                       + DLW3*zgd(ILON  ,jk2,ILA+1)  &
                       + DLW4*zgd(ILON+1,jk2,ILA+1)
     endif
  END DO

  deallocate(zgd)
  
END SUBROUTINE BILIN