!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module uv_rotate_shift_and_sort_tool
  use gbl_message
  !
  public :: uv_sort_main,loaduv,sortuv
  public :: uvsort_uv,chksuv
  private
  !
contains
  !
  subroutine uv_sort_main(error,sorted,shift,new,uvmax,uvmin)
    use phys_const, only: pi
    use image_def
    use gkernel_interfaces
    use mapping_interfaces, only: uvgmax
    use uv_buffers
    use uv_shift, only: uv_shift_header
    use clean_types
    !----------------------------------------------------------------------
    ! Sort the input UV table
    !----------------------------------------------------------------------
    logical,      intent(inout) :: sorted ! Is table sorted ?
    logical,      intent(inout) :: shift  ! Do we shift phase center ?
    logical,      intent(out)   :: error
    real(kind=8), intent(inout) :: new(3) ! New phase center and PA
    real,         intent(out)   :: uvmin  ! Min baseline
    real,         intent(out)   :: uvmax  ! Max baseline
    !
    integer :: nu,nv
    real :: pos(2),cs(2)
    real, pointer :: duv_previous(:,:)
    real, pointer :: duv_next(:,:)
    real(kind=8) :: freq,off(3)
    real(kind=8), parameter :: f_to_k = 2.d0*pi/299792458.d-6
    !
    ! The UV table is available in HUV
    if (huv%loca%size.eq.0) then
       call map_message(seve%e,'UV_MAP','No UV data loaded')
       error = .true.
       return
    endif
    nu = huv%gil%dim(1)
    nv = huv%gil%dim(2)
    !
    ! Correct for new phase center if required
    if (shift) then
       if (huv%gil%ptyp.eq.p_none) then
          call map_message(seve%w,'SHIFT','No previous phase center info')
          huv%gil%a0 = huv%gil%ra
          huv%gil%d0 = huv%gil%dec
          huv%gil%pang = 0.d0
          huv%gil%ptyp = p_azimuthal
       elseif (huv%gil%ptyp.ne.p_azimuthal) then
          call map_message(seve%w,'SHIFT','Previous projection type not SIN')
          huv%gil%ptyp = p_azimuthal
       endif
       call uv_shift_header(new,huv%gil%a0,huv%gil%d0,huv%gil%pang,off,shift)
       huv%gil%posi_words = def_posi_words
       huv%gil%proj_words = def_proj_words
    endif
    if (shift) then
       sorted = .false.
    else
       call check_order (duv,nu,nv,sorted)
    endif
    !
    ! Get center frequency
    freq = gdf_uv_frequency(huv,huv%gil%ref(1))
    !
    if (sorted) then
       ! If already sorted, use it
       call map_message(seve%i,'UVSORT','UV table is already sorted')
       !
       ! Compute UVMAX
       call uvgmax(huv,duv,uvmax,uvmin)
    else
       ! Else, create another copy
       call map_message(seve%i,'UVSORT','Sorting UV table...')
       !
       ! Compute observing frequency, and new phase center in wavelengths
       if (shift) then
          huv%gil%a0 = new(1)
          huv%gil%d0 = new(2)
          huv%gil%pang = new(3)
          cs(1)  =  cos(off(3))
          cs(2)  = -sin(off(3))
          ! Note that the new phase center is counter-rotated because rotations
          ! are applied before phase shift.
          pos(1) = - freq * f_to_k * ( off(1)*cs(1) - off(2)*cs(2) )
          pos(2) = - freq * f_to_k * ( off(2)*cs(1) + off(1)*cs(2) )
       else
          pos(1) = 0.0
          pos(2) = 0.0
          cs(1) = 1.0
          cs(2) = 0.0
       endif
       !
       !$ call uv_dump_buffers('UV_SORT')
       !
       ! OK, rotate, shift, sort and copy...
       nullify (duv_previous, duv_next)
       call uv_find_buffers('UV_SORT',nu,nv, duv_previous, duv_next, error)
       if (error) return
       call uvsort_uv(nu,nv,huv%gil%ntrail,duv_previous,duv_next,&
            pos,cs,uvmax,uvmin,error)
       huv%gil%basemax = uvmax
       huv%gil%basemin = uvmin
       call uv_clean_buffers(duv_previous, duv_next,error)
       if (error) return
    endif
    !
    ! Now transform UVMAX in kiloWavelength (including 2 pi factor)
    uvmax = uvmax*freq*f_to_k
    uvmin = uvmin*freq*f_to_k
    error = .false.
  end subroutine uv_sort_main
  !
  subroutine check_order(visi,np,nv,sorted)
    !----------------------------------------------------------
    ! Check if visibilites are sorted.
    ! Chksuv does a similar job, but using V values and an index.
    !----------------------------------------------------------
    integer, intent(in)  :: np       ! Size of a visibility
    integer, intent(in)  :: nv       ! Number of visibilities
    real,    intent(in)  :: visi(np,nv) ! Visibilities
    logical, intent(out) :: sorted
    !
    integer :: iv
    real :: vmax
    !
    vmax = visi(2,1)
    do iv=1,nv
       if (visi(2,iv).lt.vmax) then
          sorted = .false.
          return
       endif
       vmax = visi(2,iv)
    enddo
    sorted = .true.
  end subroutine check_order
  !
  subroutine uvsort_uv(np,nv,ntrail,vin,vout,xy,cs,uvmax,uvmin,error)
!    use gildas_def
    use gkernel_interfaces
    !---------------------------------------------------------------------
    ! Rotate, Shift and Sort a UV table for map making
    ! Differential precession should have been applied before.
    !---------------------------------------------------------------------
    integer, intent(in)  :: np          ! Size of a visibility
    integer, intent(in)  :: nv          ! Number of visibilities
    integer, intent(in)  :: ntrail      ! Number of trailing daps
    real,    intent(in)  :: vin(np,nv)  ! Input visibilities
    real,    intent(out) :: vout(np,nv) ! Output visibilities
    real,    intent(in)  :: xy(2)       ! Phase shift
    real,    intent(in)  :: cs(2)       ! Frame Rotation
    real,    intent(out) :: uvmax       ! Max UV value
    real,    intent(out) :: uvmin       ! Min UV value
    logical, intent(out) :: error
    !
    logical :: sorted
    integer :: ier
    logical, allocatable :: ips(:)       ! Sign of visibility
    integer, allocatable :: ipi(:)       ! Index
    real, allocatable :: ipu(:), ipv(:)  ! U,V coordinates
    !
    ! Load U,V coordinates, applying possible rotation (CS), and
    ! making all V negative
    allocate(ips(nv),ipu(nv),ipv(nv),ipi(nv),stat=ier)
    error = ier.ne.0
    if (error) return
    call loaduv(vin,np,nv,cs,ipu,ipv,ips,uvmax,uvmin)
    !
    ! Sort if needed
    call chksuv(nv,ipv,ipi,sorted)
    if (.not.sorted) then
       call gr4_trie(ipv,ipi,nv,error)
       if (error) return
    endif
    !
    ! Apply phase shift and copy to output visibilities
    call sortuv(vin,vout,np,nv,ntrail,xy,ipu,ipv,ips,ipi)
    !
    deallocate(ips,ipu,ipv,ipi,stat=ier)
    call gagout('I-UVSORT,  Successful completion')
  end subroutine uvsort_uv
  !
  subroutine loaduv(visi,np,nv,cs,u,v,s,uvmax,uvmin)
    !---------------------------------------------------------------------
    ! Load new U,V coordinates and sign indicator into work arrays for
    ! sorting.
    !---------------------------------------------------------------------
    integer, intent(in)    :: np          ! Size of a visibility
    integer, intent(in)    :: nv          ! Number of visibilities
    real,    intent(in)    :: visi(np,nv) ! Input visibilities
    real,    intent(in)    :: cs(2)       ! Rotation parameter
    real,    intent(out)   :: u(nv)       ! Output U and V coordinates
    real,    intent(out)   :: v(nv)       ! Output U and V coordinates
    logical, intent(out)   :: s(nv)       ! V Sign indicator
    real,    intent(inout) :: uvmax       ! Maximum length
    real,    intent(inout) :: uvmin       ! Minimum baseline
    !
    integer :: iv
    real :: uv
    !
    uv = 0
    do iv=1,nv
       uv = visi(1,iv)**2 + visi(2,iv)**2
       if (uv.ne.0) exit
    enddo
    uvmax = uv
    uvmin = uv
    !
    if (cs(2).eq.0.0) then
       do iv=1,nv
          v(iv) = visi(2,iv)
          if (v(iv).gt.0) then
             u(iv) = -visi(1,iv)
             v(iv) = -v(iv)
             s(iv) = .false.
          else
             u(iv) = visi(1,iv)
             s(iv) = .true.
          endif
          uv = u(iv)*u(iv)+v(iv)*v(iv)
          if (uv.gt.uvmax) then
             uvmax = uv
          else if (uv.lt.uvmin .and. uv.ne.0) then
             uvmin = uv
          endif
       enddo
    else
       do iv=1,nv
          u(iv) = cs(1)*visi(1,iv) - cs(2)*visi(2,iv)
          v(iv) = cs(2)*visi(1,iv) + cs(1)*visi(2,iv)
          if (v(iv).gt.0) then
             u(iv) = -u(iv)
             v(iv) = -v(iv)
             s(iv) = .false.
          else
             s(iv) = .true.
          endif
          uv = u(iv)*u(iv)+v(iv)*v(iv)
          if (uv.gt.uvmax) then
             uvmax = uv
          else if (uv.lt.uvmin .and. uv.ne.0) then
             uvmin = uv
          endif
       enddo
    endif
    uvmax = sqrt(uvmax)
    uvmin = sqrt(uvmin)
  end subroutine loaduv
  !
  subroutine chksuv(nv,v,it,sorted)
    !---------------------------------------------------------------------
    ! Check if visibilities are sorted
    !---------------------------------------------------------------------
    integer, intent(in)  :: nv     ! Number of visibilities
    real,    intent(in)  :: v(nv)  ! Visibilities
    integer, intent(out) :: it(nv) ! Sorting pointer
    logical, intent(out) :: sorted ! short cut
    !
    integer :: iv
    real :: vmax
    !
    do iv=1,nv
       it(iv) = iv
    enddo
    !
    vmax = v(1)
    do iv = 1,nv
       if (v(iv).gt.0 .or. v(iv).lt.vmax) then
          sorted = .false.
          return
       endif
       vmax = v(iv)
    enddo
    sorted = .true.
  end subroutine chksuv
  !
  subroutine sortuv(vin,vout,np,nv,ntrail,xy,u,v,s,it)
    !---------------------------------------------------------------------
    ! Sort an input UV table into an output one.
    ! The output UV table is a rotated, phase shifted copy of
    ! the input one, with all V negative and increasing
    !---------------------------------------------------------------------
    integer, intent(in)  :: np          ! Size of a visibility
    integer, intent(in)  :: nv          ! Number of visibilities
    integer, intent(in)  :: ntrail      ! Number of trailing daps
    real,    intent(in)  :: vin(np,nv)  ! Input visibilities
    real,    intent(out) :: vout(np,nv) ! Output visibilities
    real,    intent(in)  :: xy(2)       ! Phase shifting factors
    real,    intent(in)  :: u(nv)       ! New U coordinates
    real,    intent(in)  :: v(nv)       ! New V coordinates
    logical, intent(in)  :: s(nv)       ! Sign of new visibility
    integer, intent(in)  :: it(nv)      ! Pointer to old visibility
    !
    integer :: ip,iv,kv,no
    real :: phi, cphi, sphi
    !
    ! There may be trailing columns in UV table
    no = np-ntrail
    !
    ! Simple case: no phase shift
    !$OMP PARALLEL &
    !$OMP & SHARED(u,v,vin,vout,no,np,xy) &
    !$OMP & PRIVATE(kv,iv,ip, phi,cphi,sphi)
    if (xy(1).eq.0 .and. xy(2).eq.0) then
       !$OMP DO
       do iv=1,nv
          kv = it(iv)
          vout(1,iv) = u(kv)
          vout(2,iv) = v(iv)       ! This one is sorted already
          vout(3,iv) = vin(3,kv)
          vout(4,iv) = vin(4,kv)
          vout(5,iv) = vin(5,kv)
          if (s(kv)) then
             vout(6,iv) = vin(6,kv)
             vout(7,iv) = vin(7,kv)
             do ip=8,np
                vout(ip,iv) = vin(ip,kv)
             enddo
          else
             vout(6,iv) = vin(7,kv)
             vout(7,iv) = vin(6,kv)
             do ip=8,no,3
                vout(ip,iv)   =  vin(ip,kv)
                vout(ip+1,iv) = -vin(ip+1,kv)
                vout(ip+2,iv) =  vin(ip+2,kv)
             enddo
          endif
          do ip=no+1,np
             vout(ip,iv)   =  vin(ip,kv)
          enddo
       enddo
    !$OMP END DO
    else
       !
       ! Complex case: phase center has been shifted
       !$OMP DO
       do iv=1,nv
          kv = it(iv)
          vout(1,iv) = u(kv)
          vout(2,iv) = v(iv)       ! This one is sorted already
          vout(3,iv) = vin(3,kv)
          vout(4,iv) = vin(4,kv)
          vout(5,iv) = vin(5,kv)
          phi = xy(1)*u(kv)+xy(2)*v(iv)
          cphi = cos(phi)
          sphi = sin(phi)
          if (s(kv)) then
             vout(6,iv) = vin(6,kv)
             vout(7,iv) = vin(7,kv)
             do ip=8,no,3
                vout(ip,iv) = vin(ip,kv)*cphi - vin(ip+1,kv)*sphi
                vout(ip+1,iv) = vin(ip,kv)*sphi + vin(ip+1,kv)*cphi
                vout(ip+2,iv) = vin(ip+2,kv)
             enddo
          else
             vout(6,iv) = vin(7,kv)
             vout(7,iv) = vin(6,kv)
             do ip=8,no,3
                vout(ip,iv) = vin(ip,kv)*cphi + vin(ip+1,kv)*sphi
                vout(ip+1,iv) = vin(ip,kv)*sphi - vin(ip+1,kv)*cphi
                vout(ip+2,iv) = vin(ip+2,kv)
             enddo
          endif
          do ip=no+1,np
             vout(ip,iv)   =  vin(ip,kv)
          enddo
       enddo
       !$OMP END DO
    endif
    !$OMP END PARALLEL
  end subroutine sortuv
end module uv_rotate_shift_and_sort_tool
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
