!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubesimulate_observatory
  use phys_const
  use cubetools_parameters
  use cubetools_structure
  use cubetools_obstel_prog_types
  use cubesyntax_keyval_types
  use cubesimulate_messaging
  !
  public :: observatory_comm_t,observatory_prog_t
  public :: observatory_comm,observatory_prog
  private
  !
  type :: observatory_comm_t
     type(option_t), pointer      :: comm
     type(keyval_strg_comm_t)     :: name
     type(keyvalunit_real_comm_t) :: horizon
   contains
     procedure, public  :: register => cubesimulate_observatory_comm_register
     procedure, private :: parse    => cubesimulate_observatory_comm_parse
     procedure, private :: main     => cubesimulate_observatory_comm_main
  end type observatory_comm_t
  type(observatory_comm_t), target :: observatory_comm
  !
  type observatory_user_t
     logical :: dolist = .false.
     type(keyval_strg_user_t) :: name
     type(keyvalunit_real_user_t) :: horizon
   contains
     procedure, private :: toprog => cubesimulate_observatory_user_toprog
  end type observatory_user_t
  !
  type :: observatory_prog_t
     character(len=tele_l) :: name = 'NOEMA'
     type(obstel_prog_t)   :: tel
     real(kind=real_k) :: horizon = 20d0*rad_per_deg ! [rad] Horizon (i.e. minimum elevation)
     real(kind=coor_k) :: matrix(9) = 0d0            ! [---] Matrix from Hr/Dec to Az/El
   contains
     procedure, public  :: list   => cubesimulate_observatory_prog_list
     procedure, private :: header => cubesimulate_observatory_prog_header
  end type observatory_prog_t
  type(observatory_prog_t), target :: observatory_prog
  !
contains
  !
  subroutine cubesimulate_observatory_command(line,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    character(len=*), intent(in)    :: line
    logical,          intent(inout) :: error
    !
    type(observatory_user_t) :: user
    character(len=*), parameter :: rname='OBSERVATORY>COMMAND'
    !
    call cubesimulate_message(simulateseve%trace,rname,'Welcome')
    !
    call observatory_comm%parse(line,user,error)
    if (error) return
    call observatory_comm%main(user,observatory_prog,error)
    if (error) continue
  end subroutine cubesimulate_observatory_command
  !
  !----------------------------------------------------------------------
  !
  subroutine cubesimulate_observatory_comm_register(comm,error)
    use cubetools_unit
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(observatory_comm_t), intent(inout) :: comm
    logical,                   intent(inout) :: error
    !
    character(len=*), parameter :: rname='OBSERVATORY>COMM>REGISTER'
    !
    call cubesimulate_message(simulateseve%trace,rname,'Welcome')
    !
    ! Syntax
    call cubetools_register_command(&
         'OBSERVATORY','',&
         'Set or list the observatory setup',&
         'Without options, list the current observatory name, telescope&
         & metadata, and horizon. /NAME and /HORIZON update the shared&
         & observatory setup used by simulate commands. /HORIZON must stay&
         & within [3,90] degree after unit conversion.',&
         cubesimulate_observatory_command,&
         comm%comm,&
         error)
    if (error) return
    call comm%name%register(&
         'NAME',&
         'Observatory name to load',&
         strg_emp,&
         observatory_prog%name,&
         'Observatory name. Default is NOEMA. Unknown names are&
         & accepted, but telescope coordinates can remain undefined (NaN).',&
         error)
    if (error) return
    call comm%horizon%register(&
         'HORIZON',&
         'Minimum elevation cutoff',&
         strg_emp,&
         observatory_prog%horizon,&
         code_unit_pang,&
         'Use "*" to keep the default value (20 degree). The horizon&
         & must be in [3,90] degree after unit conversion.',&
         error)
    if (error) return
  end subroutine cubesimulate_observatory_comm_register
  !
  subroutine cubesimulate_observatory_comm_parse(comm,line,user,error)
    !----------------------------------------------------------------------
    ! OBSERVATORY /NAME name
    !----------------------------------------------------------------------
    class(observatory_comm_t), intent(in)    :: comm
    character(len=*),          intent(in)    :: line
    type(observatory_user_t),  intent(out)   :: user
    logical,                   intent(inout) :: error
    !
    character(len=*), parameter :: rname='OBSERVATORY>COMM>PARSE'
    !
    call cubesimulate_message(simulateseve%trace,rname,'Welcome')
    !
    if (cubetools_nopt().eq.0) then
       user%dolist = .true.
    else
       user%dolist = .false.
       call comm%name%parse(line,user%name,error)
       if (error) return
       call comm%horizon%parse(line,user%horizon,error)
       if (error) return
    endif
  end subroutine cubesimulate_observatory_comm_parse
  !
  subroutine cubesimulate_observatory_comm_main(comm,user,prog,error) 
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(observatory_comm_t), intent(in)    :: comm
    type(observatory_user_t),  intent(inout) :: user
    type(observatory_prog_t),  intent(inout) :: prog
    logical,                   intent(inout) :: error
    !
    character(len=*), parameter :: rname='OBSERVATORY>COMM>MAIN'
    !
    call cubesimulate_message(simulateseve%trace,rname,'Welcome')
    !
    if (user%dolist) then
       call prog%header(comm,error)
       if (error) return
       call prog%list(comm,error)
       if (error) return
    else
       call user%toprog(comm,prog,error)
       if (error) return
       call prog%header(comm,error)
       if (error) return
    endif
  end subroutine cubesimulate_observatory_comm_main
  !
  !------------------------------------------------------------------------
  !
  subroutine cubesimulate_observatory_user_toprog(user,comm,prog,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(observatory_user_t), intent(in)    :: user
    type(observatory_comm_t),  intent(in)    :: comm    
    type(observatory_prog_t),  intent(out)   :: prog
    logical,                   intent(inout) :: error
    !
    character(len=*), parameter :: rname='OBSERVATORY>USER>TOPROG'
    !
    call cubesimulate_message(simulateseve%trace,rname,'Welcome')
    !
    call user%name%toprog(comm%name,prog%name,error)
    if (error) return
    call user%horizon%toprog(comm%horizon,prog%horizon,error)
    if (error) return
  end subroutine cubesimulate_observatory_user_toprog
  !
  !------------------------------------------------------------------------
  !
  subroutine cubesimulate_observatory_prog_header(prog,comm,error)
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(observatory_prog_t), intent(inout) :: prog
    type(observatory_comm_t),  intent(in)    :: comm
    logical,                   intent(inout) :: error
    !
    character(len=*), parameter :: rname='OBSERVATORY>PROG>HEADER'
    !
    call cubesimulate_message(simulateseve%trace,rname,'Welcome')
    !
    call prog%tel%create_from_name(prog%name,error)
    if (error) return
    prog%matrix = hrdec2azel_matrix(prog%tel%lat)
    call check_horizon(prog%horizon*deg_per_rad,error)
    if (error) return
    !
  contains
    !
    function hrdec2azel_matrix(latitude) result(matrix)
      !---------------------------------------------------------------------
      ! Define the matrix to translate from Hr/Dec to Az/El
      !---------------------------------------------------------------------
      real(kind=coor_k), intent(in) :: latitude
      real(kind=coor_k)             :: matrix(9)  ! intent(out)
      !
      real(kind=coor_k) :: sinlat,coslat
      !
      sinlat=dsin(latitude)
      coslat=dcos(latitude)
      matrix(1)=-sinlat
      matrix(2)=0.d0
      matrix(3)=coslat
      matrix(4)=0.d0
      matrix(5)=-1.d0
      matrix(6)=0.d0
      matrix(7)=coslat
      matrix(8)=0.d0
      matrix(9)=sinlat
    end function hrdec2azel_matrix
    !
    subroutine check_horizon(horizon,error)
      real(kind=coor_k), intent(in)    :: horizon
      logical,           intent(inout) :: error
      !
      if (horizon.lt.3d0) then
         ! Too low
         call cubesimulate_message(seve%e,rname,'Observatory horizon lower than 3 deg')
         error = .true.
         return
      else if (horizon.gt.90d0) then
         ! Too high
         call cubesimulate_message(seve%e,rname,'Observatory horizon larger than 90 deg')
         error = .true.
         return
      endif
    end subroutine check_horizon
  end subroutine cubesimulate_observatory_prog_header
  !
  subroutine cubesimulate_observatory_prog_list(prog,comm,error)
    use cubetools_unit
    use cubesyntax_keyvalunit_list_tool
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(observatory_prog_t), intent(inout) :: prog
    type(observatory_comm_t),  intent(in)    :: comm
    logical,                   intent(inout) :: error
    !
    type(keyvalunit_real_prog_t) :: keyvalunit_real
    character(len=*), parameter :: rname='OBSERVATORY>PROG>LIST'
    !
    call cubesimulate_message(simulateseve%trace,rname,'Welcome')
    !
    call cubesimulate_message(seve%r,rname,blankstr)
    call cubesimulate_message(seve%r,rname,'Observatory')
    !
    call prog%tel%list(error)
    if (error) return
    call keyvalunit_real%list(comm%horizon,prog%horizon,error)
    if (error) return
  end subroutine cubesimulate_observatory_prog_list
end module cubesimulate_observatory
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
