#include <misc.h>
#include <params.h>


subroutine bilin (arrin, xin, yin, nlondin, nlonin, & 3,13
                  nlevdin, nlev, nlatin, arrout, xout, &
                  yout, nlondout, nlonout, nlevdout, nlatout)
!----------------------------------------------------------------------- 
! 
! Purpose: 
!
! Do a bilinear interpolation from input mesh defined by xin, yin to output
! mesh defined by xout, yout.  Circularity is assumed in the x-direction so
! input x-grid must be in degrees east and must start from Greenwich.  When
! extrapolation is necessary in the N-S direction, values will be copied 
! from the extreme edge of the input grid.  Vectorization is over the
! longitude dimension.  Input grid is assumed rectangular. Output grid
! is assumed ragged, i.e. xout is a 2-d mesh.
! 
! Method: Interpolate first in longitude, then in latitude.
! 
! Author: Jim Rosinski
! 
!-----------------------------------------------------------------------
   use shr_kind_mod, only: r8 => shr_kind_r8
   use abortutils,   only: endrun
!-----------------------------------------------------------------------
   implicit none
!-----------------------------------------------------------------------
!
! Input arguments
!
   integer, intent(in) :: nlondin                        ! longitude dimension of input grid
   integer, intent(in) :: nlonin                         ! number of real longitudes (input)
   integer, intent(in) :: nlevdin                        ! vertical dimension of input grid
   integer, intent(in) :: nlev                           ! number of vertical levels
   integer, intent(in) :: nlatin                         ! number of input latitudes
   integer, intent(in) :: nlatout                        ! number of output latitudes
   integer, intent(in) :: nlondout                       ! longitude dimension of output grid
   integer, intent(in) :: nlonout(nlatout)               ! number of output longitudes per lat
   integer, intent(in) :: nlevdout                       ! vertical dimension of output grid

   real(r8), intent(in) :: arrin(nlondin,nlevdin,nlatin) ! input array of values to interpolate
   real(r8), intent(in) :: xin(nlondin)                  ! input x mesh
   real(r8), intent(in) :: yin(nlatin)                   ! input y mesh
   real(r8), intent(in) :: xout(nlondout,nlatout)        ! output x mesh
   real(r8), intent(in) :: yout(nlatout)                 ! output y mesh
!
! Output arguments
!
   real(r8), intent(out) :: arrout(nlondout,nlevdout,nlatout) ! interpolated array
!
! Local workspace
!
   integer :: i, ii, iw, ie, iiprev ! longitude indices
   integer :: j, jj, js, jn, jjprev ! latitude indices
   integer :: k                     ! level index
   integer :: icount                ! number of bad values

   real(r8) :: extrap               ! percent grid non-overlap
   real(r8) :: dxinwrap             ! delta-x on input grid for 2-pi
   real(r8) :: avgdxin              ! avg input delta-x
   real(r8) :: ratio                ! compare dxinwrap to avgdxin
   real(r8) :: sum                  ! sum of weights (used for testing)
!
! Dynamic
!
   integer :: iim(nlondout)         ! interpolation index to the left
   integer :: iip(nlondout)         ! interpolation index to the right
   integer :: jjm(nlatout)          ! interpolation index to the south
   integer :: jjp(nlatout)          ! interpolation index to the north

   real(r8) :: wgts(nlatout)        ! interpolation weight to the north
   real(r8) :: wgtn(nlatout)        ! interpolation weight to the north
   real(r8) :: wgte(nlondout)       ! interpolation weight to the north
   real(r8) :: wgtw(nlondout)       ! interpolation weight to the north
   real(r8) :: igrid(nlonin)        ! interpolation weight to the north
!
! Check validity of input coordinate arrays: must be monotonically increasing,
! and have a total of at least 2 elements
!
   if (nlonin < 2 .or. nlatin < 2) then
      call endrun ('BILIN: Must have at least 2 input points for interpolation')
   end if

   if (xin(1) < 0. .or. xin(nlonin) > 360.) then
      call endrun ('BILIN: Input x-grid must be between 0 and 360')
   end if

   icount = 0
   do i=1,nlonin-1
      if (xin(i) >= xin(i+1)) icount = icount + 1
   end do

   do j=1,nlatin-1
      if (yin(j) >= yin(j+1)) icount = icount + 1
   end do

   do j=1,nlatout-1
      if (yout(j) >= yout(j+1)) icount = icount + 1
   end do

   do j=1,nlatout
      do i=1,nlonout(j)-1
         if (xout(i,j) >= xout(i+1,j)) icount = icount + 1
      end do
   end do

   if (icount > 0) then
      call endrun ('BILIN: Non-monotonic coordinate array(s) found')
   end if

   if (yout(nlatout) <= yin(1) .or. yout(1) >= yin(nlatin)) then
      call endrun ('BILIN: No overlap in y-coordinates')
   end if

#if ( ! defined SCAM )
   do j=1,nlatout
      if (xout(1,j) < 0. .or. xout(nlonout(j),j) > 360.) then
         call endrun ('BILIN: Output x-grid must be between 0 and 360')
      end if

      if (xout(nlonout(j),j) <= xin(1) .or.  &
          xout(1,j)          >= xin(nlonin)) then
         call endrun ('BILIN: No overlap in x-coordinates')
      end if
   end do
#endif
!
! Initialize index arrays for later checking
!
   do j=1,nlatout
      jjm(j) = 0
      jjp(j) = 0
   end do
!
! For values which extend beyond N and S boundaries, set weights
! such that values will just be copied.
!
   do js=1,nlatout
      if (yout(js) > yin(1)) exit
      jjm(js) = 1
      jjp(js) = 1
      wgts(js) = 1.
      wgtn(js) = 0.
   end do

   do jn=nlatout,1,-1
      if (yout(jn) <= yin(nlatin)) exit
      jjm(jn) = nlatin
      jjp(jn) = nlatin
      wgts(jn) = 1.
      wgtn(jn) = 0.
   end do
!
! Loop though output indices finding input indices and weights
!
   jjprev = 1
   do j=js,jn
      do jj=jjprev,nlatin-1
         if (yout(j) > yin(jj) .and. yout(j) <= yin(jj+1)) then
            jjm(j) = jj
            jjp(j) = jj + 1
            wgts(j) = (yin(jj+1) - yout(j)) / (yin(jj+1) - yin(jj))
            wgtn(j) = (yout(j)   - yin(jj)) / (yin(jj+1) - yin(jj))
            goto 30
         end if
      end do
      call endrun ('BILIN: Failed to find interp values')
30    jjprev = jj
   end do

   dxinwrap = xin(1) + 360. - xin(nlonin)
!
! Check for sane dxinwrap values.  Allow to differ no more than 10% from avg
!
   avgdxin = (xin(nlonin)-xin(1))/(nlonin-1.)
   ratio = dxinwrap/avgdxin
   if (ratio < 0.9 .or. ratio > 1.1) then
      write(6,*)'BILIN: Insane dxinwrap value =',dxinwrap,' avg=', avgdxin
      call endrun
   end if
!
! Check grid overlap
!
   extrap = 100.*((js - 1.) + float(nlatout - jn))/nlatout
   if (extrap > 20.) then
      write(6,*)'BILIN:',extrap,' % of N/S output grid will have to be extrapolated'
   end if
!
! Check that interp/extrap points have been found for all outputs, and that
! they are reasonable (i.e. within 32-bit roundoff)
!
   icount = 0
   do j=1,nlatout
      if (jjm(j) == 0 .or. jjp(j) == 0) icount = icount + 1
      sum = wgts(j) + wgtn(j)
      if (sum < 0.99999 .or. sum > 1.00001) icount = icount + 1
      if (wgts(j) < 0. .or. wgts(j) > 1.) icount = icount + 1
      if (wgtn(j) < 0. .or. wgtn(j) > 1.) icount = icount + 1
   end do

   if (icount > 0) then
      call endrun ('BILIN: something bad in latitude indices or weights')
   end if
!
! Do the bilinear interpolation
!
   do j=1,nlatout
!
! Initialize index arrays for later checking
!
      do i=1,nlondout
         iim(i) = 0
         iip(i) = 0
      end do
!
! For values which extend beyond E and W boundaries, set weights such that
! values will be interpolated between E and W edges of input grid.
!
      do iw=1,nlonout(j)
         if (xout(iw,j) > xin(1)) exit
         iim(iw) = nlonin
         iip(iw) = 1
         wgtw(iw) = (xin(1)        - xout(iw,j))   /dxinwrap
         wgte(iw) = (xout(iw,j)+360. - xin(nlonin))/dxinwrap
      end do

      do ie=nlonout(j),1,-1
         if (xout(ie,j) <= xin(nlonin)) exit
         iim(ie) = nlonin
         iip(ie) = 1
         wgtw(ie) = (xin(1)+360. - xout(ie,j))   /dxinwrap
         wgte(ie) = (xout(ie,j)    - xin(nlonin))/dxinwrap
      end do
!
! Loop though output indices finding input indices and weights
!
      iiprev = 1
      do i=iw,ie
         do ii=iiprev,nlonin-1
            if (xout(i,j) > xin(ii) .and. xout(i,j) <= xin(ii+1)) then
               iim(i) = ii
               iip(i) = ii + 1
               wgtw(i) = (xin(ii+1) - xout(i,j)) / (xin(ii+1) - xin(ii))
               wgte(i) = (xout(i,j) - xin(ii))   / (xin(ii+1) - xin(ii))
               goto 60
            end if
         end do
         call endrun ('BILIN: Failed to find interp values')
60       iiprev = ii
      end do

      icount = 0
      do i=1,nlonout(j)
         if (iim(i) == 0 .or. iip(i) == 0) icount = icount + 1
         sum = wgtw(i) + wgte(i)
         if (sum < 0.99999 .or. sum > 1.00001) icount = icount + 1
         if (wgtw(i) < 0. .or. wgtw(i) > 1.) icount = icount + 1
         if (wgte(i) < 0. .or. wgte(i) > 1.) icount = icount + 1
      end do

      if (icount > 0) then
         write(6,*)'BILIN: j=',j,' Something bad in longitude indices or weights'
         call endrun
      end if
!
! Do the interpolation, 1st in longitude then latitude
!
      do k=1,nlev
         do i=1,nlonin
            igrid(i) = arrin(i,k,jjm(j))*wgts(j) + arrin(i,k,jjp(j))*wgtn(j)
         end do

         do i=1,nlonout(j)
            arrout(i,k,j) = igrid(iim(i))*wgtw(i) + igrid(iip(i))*wgte(i)
         end do
      end do
   end do

   return
end subroutine bilin