!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine wifisyn_shift_command(line,error)
  use gbl_message
  use wifisyn_interfaces, except_this=>wifisyn_shift_command
  use wifisyn_transform_types
  !----------------------------------------------------------------------
  ! @ private
  ! Entry point for command SHIFT
  !----------------------------------------------------------------------
  character(len=*), intent(inout) :: line
  logical,          intent(inout) :: error
  !
  type(action_t) :: user_trans
  type(user_shift_t) :: user_shift
  character(len=*), parameter :: rname='SHIFT/COMMAND'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  call wifisyn_parse_transform_bis(line,user_trans,error)
  if (error) return
  call wifisyn_shift_parse(line,user_trans,user_shift,error)
  if (error) return
  call wifisyn_shift_main(user_trans,user_shift,error)
  if (error) return
  !
end subroutine wifisyn_shift_command
!
subroutine wifisyn_shift_parse(line,user_trans,user_shift,error)
  use gbl_message
  use wifisyn_interfaces, except_this=>wifisyn_shift_parse
  use wifisyn_buffer_parameters
  use wifisyn_transform_types
  !----------------------------------------------------------------------
  ! @ private
  !----------------------------------------------------------------------
  character(len=*),   intent(inout) :: line
  type(action_t),     intent(in)    :: user_trans
  type(user_shift_t), intent(out)   :: user_shift
  logical,            intent(inout) :: error
  !
  integer, parameter :: ivisi = 4
  integer, parameter :: idx = 1
  integer, parameter :: idy = 2
  integer, parameter :: iunit = 3
  !
  character(len=*), parameter :: rname='SHIFT/PARSE'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  if (user_trans%cube.ne.code_buffer_visi) return
  call sic_r8(line,ivisi,idx,user_shift%dx,.true.,error)
  if (error) return
  call sic_r8(line,ivisi,idy,user_shift%dy,.true.,error)
  if (error) return
  call wifisyn_parse_unit_kind(line,ivisi,iunit,user_shift%unit,error)
  if (error) return
  !
end subroutine wifisyn_shift_parse
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine wifisyn_shift_main(user_trans,user_shift,error)
  use gbl_message
  use wifisyn_interfaces, except_this=>wifisyn_shift_main
  use wifisyn_transform_types
  use wifisyn_sic_buffer
  use wifisyn_cube_buffer
  !----------------------------------------------------------------------
  ! @ private
  !----------------------------------------------------------------------
  type(action_t),     intent(in)    :: user_trans
  type(user_shift_t), intent(in)    :: user_shift
  logical,            intent(inout) :: error
  !
  character(len=*), parameter :: rname='SHIFT/MAIN'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  select case (user_trans%cube)
  case (code_buffer_uv)
     call wifisyn_message(seve%e,rname,'Not yet implemented')
     error = .true.
     return
  case (code_buffer_visi)
     call wifisyn_shift_visi(user_trans%dire,user_shift,progmap,visi,error)
     if (error) return
  case (code_buffer_wifi)
     select case (user_trans%space)
     case (code_space_uv)
        call wifisyn_shift_wifi_uv(user_trans%dire,progmap,wifi,error)
        if (error) return
     case (code_space_xy)
        call wifisyn_message(seve%e,rname,'Not yet implemented')
        error = .true.
        return
     case default
        call wifisyn_message(seve%e,rname,'Unknown space')
        error = .true.
        return
     end select
  case default
     call wifisyn_message(seve%e,rname,'Unknown buffer')
     error = .true.
     return
  end select
  !
end subroutine wifisyn_shift_main
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine wifisyn_shift_wifi_uv(idir,map,wifi,error)
  use gbl_message
  use phys_const
  use gkernel_interfaces
  use gkernel_types
  use wifisyn_interfaces, except_this=>wifisyn_shift_wifi_uv
  use wifisyn_transform_types
  use wifisyn_cube_types
  use wifisyn_gridding_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  integer,             intent(in)    :: idir
  type(map_t), target, intent(in)    :: map
  type(wifi_t),        intent(inout) :: wifi
  logical,             intent(inout) :: error
  !
  integer :: ix,iy,iu,iv
  real(kind=8) :: tophase,pha,xu,yv
  complex(kind=8) :: exppha
  type(dble_1d_t), pointer :: ucoord
  type(dble_1d_t), pointer :: vcoord
  type(dble_1d_t), pointer :: xcoord
  type(dble_1d_t), pointer :: ycoord
  type(time_t) :: time
  character(len=*), parameter :: rname='SHIFT/WIFI/UV'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  if (.not.wifi%uvkind) then
     call wifisyn_message(seve%e,rname,'WIFICUB UV space must be of type UVP => Use first FFT /DIR')
     error = .true.
     return
  endif
  ucoord => map%axes(1)%u%uvp%coord
  vcoord => map%axes(2)%u%uvp%coord
  xcoord => map%axes(1)%x%sky%coord
  ycoord => map%axes(2)%x%sky%coord
  tophase = f_to_k*map%rchan%freq
  if (idir.eq.code_inverse) then
     tophase = -tophase
  else if (idir.ne.code_direct) then
     call wifisyn_message(seve%e,rname,'Unknown direction')
     error = .true.
     return
  endif
  call gtime_init(time,wifi%cub%nv,error)
  if (error) return
  do iv=1,wifi%cub%nv
     call gtime_current(time)
     do iu=1,wifi%cub%nu
        do iy=1,wifi%cub%ny
           yv = ycoord%val(iy)*vcoord%val(iv)
           do ix=1,wifi%cub%nx
              xu = xcoord%val(ix)*ucoord%val(iu)
              pha = tophase*(xu+yv)
              exppha = cmplx(cos(pha),sin(pha))
              wifi%cub%val(:,ix,iy,iu,iv) = wifi%cub%val(:,ix,iy,iu,iv)*exppha
           enddo ! ix
        enddo ! iy
     enddo ! iu
  enddo ! iv
  !
end subroutine wifisyn_shift_wifi_uv
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine wifisyn_shift_visi(idire,user,map,visi,error)
  use gbl_message
  use wifisyn_interfaces, except_this=>wifisyn_shift_visi
  use wifisyn_transform_types
  use wifisyn_cube_types
  use wifisyn_gridding_types
  !---------------------------------------------------------------------
  ! @ private
  !  *** JP Should we change a0 and d0???
  !---------------------------------------------------------------------
  integer,            intent(in)    :: idire
  type(user_shift_t), intent(in)    :: user
  type(map_t),        intent(in)    :: map
  type(visi_t),       intent(inout) :: visi
  logical,            intent(inout) :: error
  !
  type(myshift_t) :: shift
  character(len=*), parameter :: rname='SHIFT/VISI'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  call wifisyn_shift_visi_set(map,visi,idire,user,shift,error)
  if (error) return
  if (shift%phase) then
     call wifisyn_shift_visi_phase(shift,visi,error)
     if (error) return
  else
     call wifisyn_shift_visi_grid(shift,visi,error)
     if (error) return
  endif
  !
end subroutine wifisyn_shift_visi
!
subroutine wifisyn_shift_visi_set(map,visi,idire,user,shift,error)
  use gbl_message
  use phys_const
  use wifisyn_interfaces, except_this=>wifisyn_shift_visi_set
  use wifisyn_cube_types
  use wifisyn_transform_types
  use wifisyn_gridding_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(map_t), target, intent(in)    :: map
  type(visi_t),        intent(in)    :: visi
  integer,             intent(in)    :: idire
  type(user_shift_t),  intent(in)    :: user
  type(myshift_t),     intent(out)   :: shift
  logical,             intent(inout) :: error
  !
  type(axis_1d_t), pointer :: xaxis,yaxis
  type(dble_1d_t), pointer :: ucoord,vcoord
  character(len=*), parameter :: rname='SHIFT/VISI/SET'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  shift%dire = idire
  shift%freq = map%rchan%freq
  select case (user%unit)
  case (code_unit_second)
     ! Simple case: uv and xy coordinate names are intuitive
     shift%phase = visi%uvkind
     if (shift%phase) then
        shift%ucoord => map%axes(1)%a%uvp%coord
        shift%vcoord => map%axes(2)%a%uvp%coord
     endif
     xaxis => map%axes(1)%a%sky
     yaxis => map%axes(2)%a%sky
     shift%dx = user%dx*rad_per_sec
     shift%dy = user%dy*rad_per_sec
  case (code_unit_meter)
     ! Complex case: uv and xy coordinate names are exchanged to simplify
     ! later calling sequences
     shift%phase = .not.visi%uvkind
     if (shift%phase) then
        shift%ucoord => map%axes(1)%a%sky%coord
        shift%vcoord => map%axes(2)%a%sky%coord
     endif
     xaxis => map%axes(1)%a%uvp
     yaxis => map%axes(2)%a%uvp
     shift%dx = user%dx
     shift%dy = user%dy
  case default
     call wifisyn_message(seve%e,rname,'Unknown unit')
     error = .true.
     return
  end select
  !
  ! This ensures that the shift double vector matches the shift pixel vector
  call wifisyn_axis_ddx2idx(xaxis,shift%dx,shift%idx,error)
  call wifisyn_axis_idx2ddx(xaxis,shift%idx,shift%dx,error)
  call wifisyn_axis_ddx2idx(yaxis,shift%dy,shift%idy,error)
  call wifisyn_axis_idx2ddx(yaxis,shift%idy,shift%dy,error)
  !
end subroutine wifisyn_shift_visi_set
!
subroutine wifisyn_shift_visi_phase(shift,visi,error)
  use gbl_message
  use phys_const
  use gkernel_interfaces
  use gkernel_types
  use wifisyn_interfaces, except_this=>wifisyn_shift_visi_phase
  use wifisyn_transform_types
  use wifisyn_cube_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(myshift_t), intent(in)    :: shift
  type(visi_t),    intent(inout) :: visi
  logical,         intent(inout) :: error
  !
  integer :: iu,iv
  real(kind=8) :: dx,udx
  real(kind=8) :: dy,vdy
  real(kind=8) :: tophase,pha
  complex(kind=8) :: exppha
  type(time_t) :: time
  character(len=*), parameter :: rname='SHIFT/VISI/PHASE'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  tophase = f_to_k*shift%freq
  select case (shift%dire)
  case (code_direct)
     dx = +tophase*shift%dx
     dy = +tophase*shift%dy
  case (code_inverse)
     dx = -tophase*shift%dx
     dy = -tophase*shift%dy
  case default
     call wifisyn_message(seve%e,rname,'Unknown direction')
     error = .true.
     return
  end select
  !
  call gtime_init(time,visi%cub%ny,error)
  if (error) return
  do iv=1,visi%cub%ny
     call gtime_current(time)
     vdy = shift%vcoord%val(iv)*dy
     do iu=1,visi%cub%nx
        udx = shift%ucoord%val(iu)*dx
        pha = udx+vdy
        exppha = cmplx(cos(pha),sin(pha),8)
        visi%cub%val(iu,iv,:) = visi%cub%val(iu,iv,:)*exppha
     enddo ! iu
  enddo ! iv
  !
end subroutine wifisyn_shift_visi_phase
!
subroutine wifisyn_shift_visi_grid(shift,visi,error)
  use gbl_message
  use wifisyn_interfaces, except_this=>wifisyn_shift_visi_grid
  use wifisyn_transform_types
  use wifisyn_cube_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(myshift_t), intent(in)    :: shift
  type(visi_t),    intent(inout) :: visi
  logical,         intent(inout) :: error
  !
  integer :: jdx,jdy
  character(len=80) :: mess
  character(len=*), parameter :: rname='SHIFT/VISI/GRID'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  select case (shift%dire)
  case (code_direct)
     jdx = -shift%idx
     jdy = -shift%idy
  case (code_inverse)
     jdx = +shift%idx
     jdy = +shift%idy
  case default
     call wifisyn_message(seve%e,rname,'Unknown direction')
     error = .true.
     return
  end select
  write(mess,'(a,i0,a,i0,a)') 'Shifting by (',jdx,',',jdy,') pixels'
  call wifisyn_message(seve%i,rname,mess)
  !
  visi%cub%val = cshift(visi%cub%val,jdx,dim=1)
  visi%cub%val = cshift(visi%cub%val,jdy,dim=2)
  !
end subroutine wifisyn_shift_visi_grid
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine wifisyn_shift_uv_head(shift,htab,error)
  use gbl_message
  use wifisyn_interfaces, except_this=>wifisyn_shift_uv_head
  use wifisyn_uv_types
  use wifisyn_gridding_types
  !----------------------------------------------------------------------
  ! @ private
  ! The output table has its U and V shifted and rotated
  ! All V values are flip to be negative
  ! UVMIN and UVMAX are computed
  !----------------------------------------------------------------------
  type(shift_t),   intent(inout) :: shift
  type(uv_head_t), intent(inout) :: htab
  logical,         intent(inout) :: error
  !
  integer :: ndata,idata
  real :: uv,uvmin,uvmax
  real :: udata,umin,umax
  real :: vdata,vmin,vmax
  real :: xdata,xmin,xmax
  real :: ydata,ymin,ymax
  character(len=*), parameter :: rname='SHIFT/UV/HEAD'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  ndata = htab%type%ndata
  call wifisyn_allocate_logi_1d('vsign',ndata,shift%vsign,error)
  if (error) return
  !
  ! Initialize dertermination of xyuv minimum and maximum
  umin = +1e38
  vmin = +1e38
  xmin = +1e38
  ymin = +1e38
  umax = -1e38
  vmax = -1e38
  xmax = -1e38
  ymax = -1e38
  uvmax = 0.0
  uvmin = +1e38
  !
  ! Flip visibility uv positions and keep associated sign information
  if (shift%cs(2).eq.0.0) then
     ! Simple case: No phase shift
     do idata=1,ndata
        udata = htab%u(idata)
        vdata = htab%v(idata)
        xdata = htab%x(idata)
        ydata = htab%y(idata)
        if (vdata.gt.0) then
           htab%u(idata) = -udata
           htab%v(idata) = -vdata
           shift%vsign%val(idata) = .false.
        else
           shift%vsign%val(idata) = .true.
        endif
        uv = udata*udata+vdata*vdata
        if (uv.ne.0) then
           uvmax = max(uvmax,uv)
           uvmin = min(uvmin,uv)
        endif
        umin = min(umin,udata)
        vmin = min(vmin,vdata)
        xmin = min(xmin,xdata)
        ymin = min(ymin,ydata)
        umax = max(umax,udata)
        vmax = max(vmax,vdata)
        xmax = max(xmax,xdata)
        ymax = max(ymax,ydata)
     enddo
  else
     ! Complex case: Phase center has been shifted
     do idata=1,ndata
        udata = htab%u(idata)
        vdata = htab%v(idata)
        xdata = htab%x(idata)
        ydata = htab%y(idata)
        htab%u(idata) = shift%cs(1)*udata - shift%cs(2)*vdata
        htab%v(idata) = shift%cs(2)*udata + shift%cs(1)*vdata
        udata = htab%u(idata)
        vdata = htab%v(idata)
        if (vdata.gt.0) then
           htab%u(idata) = -udata
           htab%v(idata) = -vdata
           shift%vsign%val(idata) = .false.
        else
           shift%vsign%val(idata) = .true.
        endif
        uv = udata*udata+vdata*vdata
        if (uv.ne.0) then
           uvmax = max(uvmax,uv)
           uvmin = min(uvmin,uv)
        endif
        ! Shifting of X and Y axis
        umin = min(umin,udata)
        vmin = min(vmin,vdata)
        xmin = min(xmin,xdata)
        ymin = min(ymin,ydata)
        umax = max(umax,udata)
        vmax = max(vmax,vdata)
        xmax = max(xmax,xdata)
        ymax = max(ymax,ydata)
     enddo
  endif
  !
  ! End determination of uv minimum and maximum
  htab%type%uvmin = sqrt(uvmin)
  htab%type%uvmax = sqrt(uvmax)
  !
  htab%type%umin = umin
  htab%type%vmin = vmin
  !
  htab%type%umax = umax
  htab%type%vmax = vmax
  !
  htab%type%xmin = xmin
  htab%type%xmax = xmax
  !
  htab%type%ymin = ymin
  htab%type%ymax = ymax
  !
end subroutine wifisyn_shift_uv_head
!
subroutine wifisyn_shift_uv_data(shift,tabin,tabout,error)
  use gbl_message
  use wifisyn_interfaces, except_this=>wifisyn_shift_uv_data
  use wifisyn_uv_types
  use wifisyn_gridding_types
  !----------------------------------------------------------------------
  ! @ private
  ! The output UV table is a rotated, phase shifted copy of
  ! the input one
  !----------------------------------------------------------------------
  type(shift_t), intent(inout) :: shift
  type(uv_t),    intent(in)    :: tabin
  type(uv_t),    intent(inout) :: tabout
  logical,       intent(inout) :: error
  !
  integer :: ndata,idata
  real :: phi,cphi,sphi
  character(len=*), parameter :: rname='SHIFT/UV/DATA'
  !
  call wifisyn_message(seve%t,rname,'Welcome')
  !
  ndata = tabin%head%type%ndata
  if (inconsistent_size(rname,'input  table',ndata,'output table',tabout%head%type%ndata,error)) return
  if (inconsistent_size(rname,'input  table',tabin%head%type%nchan,'output table',tabout%head%type%nchan,error)) return
  if (inconsistent_size(rname,'input  table',ndata,'v sign array',shift%vsign%n,error)) return
  !
  if (shift%xy(1).eq.0 .and. shift%xy(2).eq.0) then
     ! Simple case: No phase shift
     do idata=1,ndata
        if (shift%vsign%val(idata)) then
           tabout%data(idata,:,:) = tabin%data(idata,:,:)
        else
           tabout%data(idata,1,:) =  tabin%data(idata,1,:)
           tabout%data(idata,2,:) = -tabin%data(idata,2,:)
           tabout%data(idata,3,:) =  tabin%data(idata,3,:)
        endif
     enddo
  else
     ! Complex case: Phase center has been shifted
     do idata=1,ndata
        phi = shift%xy(1)*tabout%head%u(idata)+shift%xy(2)*tabout%head%v(idata)
        cphi = cos(phi)
        sphi = sin(phi)
        if (shift%vsign%val(idata)) then
           tabout%data(idata,1,:) = tabin%data(idata,1,:)*cphi - tabin%data(idata,2,:)*sphi
           tabout%data(idata,2,:) = tabin%data(idata,1,:)*sphi + tabin%data(idata,2,:)*cphi
           tabout%data(idata,3,:) = tabin%data(idata,3,:)
        else
           tabout%data(idata,1,:) = tabin%data(idata,1,:)*cphi + tabin%data(idata,2,:)*sphi
           tabout%data(idata,2,:) = tabin%data(idata,1,:)*sphi - tabin%data(idata,2,:)*cphi
           tabout%data(idata,3,:) = tabin%data(idata,3,:)
        endif
     enddo
  endif
  ! Clean up
  call wifisyn_free_logi_1d(shift%vsign,error)
  if (error) return
  !
end subroutine wifisyn_shift_uv_data
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
