module cubedag_types
  use cubedag_parameters
  use cubedag_flag
  use cubedag_tuple

  public :: cubedag_link_t,cubedag_node_object_t,cubedag_node_pobject_t,cubedag_optimize_t
  public :: cubedag_flag_tostr,cubedag_teles_tostr,cubedag_sicvar_tostr
  private

  type :: cubedag_link_t
    integer(kind=entr_k)                  :: n=0
    type(cubedag_node_pobject_t), pointer :: list(:)=>null()
    integer(kind=8),              pointer :: flag(:)=>null()  ! Some flag, context-dependent
  end type cubedag_link_t

  type :: cubedag_node_desc_t
    integer(kind=iden_l)  :: id=0                        ! Identifier
    integer(kind=entr_k)  :: ient                        ! Entry number (backpointer to IX)
    integer(kind=code_k)  :: type=code_null              ! Fortran type identifier
    integer(kind=code_k)  :: origin=code_origin_unknown  ! Imported, created, etc
    type(tools_list_t)    :: flag                        ! Signal, noise, etc
    type(cubedag_tuple_t) :: tuple                       !
    integer(kind=iden_l)  :: history=0                   ! History identifier
    type(cubedag_link_t)  :: parents                     ! List of parents
    type(cubedag_link_t)  :: children                    ! List of children
    type(cubedag_link_t)  :: twins                       ! List of twins
    integer(kind=4)       :: nsicvar=0                   !
    character(len=varn_l) :: sicvar(dag_msicvar)=''      ! List of SIC variables pointing to the node
    ! Header components
    character(len=base_l) :: family=''                   ! Family name
    character(len=unit_l) :: unit=strg_unk               !
    character(len=sour_l) :: source=strg_unk             ! Source name
    integer(kind=code_k)  :: ptype=code_unk              !
    real(kind=coor_k)     :: a0=0d0                      !
    real(kind=coor_k)     :: d0=0d0                      !
    real(kind=coor_k)     :: pang=0d0                    !
    real(kind=coor_k)     :: lres=0d0                    !
    real(kind=coor_k)     :: mres=0d0                    !
    character(len=line_l) :: line=strg_unk               ! Line name
    real(kind=coor_k)     :: restf=0d0                   !
    real(kind=coor_k)     :: fres=0d0                    !
    real(kind=coor_k)     :: vsys=0d0                    !
    real(kind=beam_k)     :: rmaj=0d0                    !
    real(kind=beam_k)     :: rmin=0d0                    !
    real(kind=beam_k)     :: rang=0d0                    !
    integer(kind=4)       :: nteles=0                    !
    character(len=tele_l) :: teles(dag_mteles)           !
  end type cubedag_node_desc_t

  ! Placeholder for a 'node object' reference, i.e. for files indexed here. This
  ! type is extended by the external libraries using the DAG, to support their
  ! own object kinds (with their own Fortran types)
  type :: cubedag_node_object_t
    type(cubedag_node_desc_t) :: node
    procedure(ltype_interface),   pointer :: ltype=>null()     ! Return string describing the polymorphic type (used e.g. in LIST)
    procedure(memsize_interface), pointer :: memsize=>null()   ! Return the memory footprint
    procedure(memsize_interface), pointer :: disksize=>null()  ! Return the disk footprint
    procedure(memsize_interface), pointer :: datasize=>null()  ! Return the data size
  ! procedure(), pointer :: free=>null()     !
  ! procedure(), pointer :: getsize=>null()  ! Useful for garbage collecting?
  end type cubedag_node_object_t

  ! Fortran traditional trick to support list of pointers. List of
  ! pointers are more easily extensible without copying/transfering
  ! any data from the old list to the new one
  type :: cubedag_node_pobject_t
    class(cubedag_node_object_t), pointer :: p=>null()
  end type cubedag_node_pobject_t

  type cubedag_optimize_t
    integer(kind=entr_k) :: next=1  ! Number of nodes in index + 1
    type(cubedag_node_pobject_t), allocatable :: object(:)      ! Reference to the associated object
    integer(kind=4),              allocatable :: topomarker(:)  ! For efficient topological sorting
  end type cubedag_optimize_t

contains

  function ltype_interface(obj)
    !-------------------------------------------------------------------
    ! Dummy procedure providing its interface for the ltype method
    !-------------------------------------------------------------------
    character(len=2) :: ltype_interface
    class(cubedag_node_object_t), intent(in) :: obj
    ltype_interface = '??'
  end function ltype_interface

  function memsize_interface(obj)
    !-------------------------------------------------------------------
    ! Dummy procedure providing its interface for the memsize method
    !-------------------------------------------------------------------
    integer(kind=size_length) :: memsize_interface
    class(cubedag_node_object_t), intent(in) :: obj
    memsize_interface = 0
  end function memsize_interface

  subroutine cubedag_flag_tostr(node,strflag,lstrflag,error)
    !-------------------------------------------------------------------
    ! Return the flag(s) of the input node into a string
    !-------------------------------------------------------------------
    type(cubedag_node_desc_t),  intent(in)    :: node
    character(len=*), optional, intent(out)   :: strflag
    integer(kind=4),  optional, intent(out)   :: lstrflag
    logical,          optional, intent(inout) :: error
    !
    call cubedag_flaglist_tostr(node%flag,strflag,lstrflag,error)
    if (error)  return
  end subroutine cubedag_flag_tostr

  subroutine cubedag_teles_tostr(nteles,teles,strteles,error)
    use cubetools_string
    !-------------------------------------------------------------------
    ! Return the telescopes(s) of the input list into a string
    !-------------------------------------------------------------------
    integer(kind=4),  intent(in)    :: nteles
    character(len=*), intent(in)    :: teles(:)
    character(len=*), intent(out)   :: strteles
    logical,          intent(inout) :: error
    !
    if (nteles.le.0) then
      strteles = strg_unk
    else
      call cubetools_string_concat(nteles,teles,',',strteles,error)
      if (error)  return
    endif
  end subroutine cubedag_teles_tostr

  subroutine cubedag_sicvar_tostr(nsicvar,sicvar,strsicvar,error)
    use cubetools_string
    !-------------------------------------------------------------------
    ! Return the SIC variable(s) of the input list into a string
    !-------------------------------------------------------------------
    integer(kind=4),  intent(in)    :: nsicvar
    character(len=*), intent(in)    :: sicvar(:)
    character(len=*), intent(out)   :: strsicvar
    logical,          intent(inout) :: error
    !
    call cubetools_string_concat(nsicvar,sicvar,',',strsicvar,error)
    if (error)  return
  end subroutine cubedag_sicvar_tostr

end module cubedag_types
