!-----------------------------------------------------------------
!
!  This file is (or was) part of SPLASH, a visualisation tool
!  for Smoothed Particle Hydrodynamics written by Daniel Price:
!
!  http://users.monash.edu.au/~dprice/splash
!
!  SPLASH comes with ABSOLUTELY NO WARRANTY.
!  This is free software; and you are welcome to redistribute
!  it under the terms of the GNU General Public License
!  (see LICENSE file for details) and the provision that
!  this notice remains intact. If you modify this file, please
!  note section 2a) of the GPLv2 states that:
!
!  a) You must cause the modified files to carry prominent notices
!     stating that you changed the files and the date of any change.
!
!  Copyright (C) 2005-2012 Daniel Price. All rights reserved.
!  Contact: daniel.price@monash.edu
!
!-----------------------------------------------------------------

!-------------------------------------------------------------------------
! this subroutine reads from the data file(s)
! change this to change the format of data input
!
! THIS VERSION IS FOR DATA FORMATS WRITTEN WITH THE H5PART LIBRARY
!
! Some choices for this format can be set using the following
!  command-line flags or environment variables:
!
!  --ndim=2 or H5SPLASH_NDIM=2     : number of spatial dimensions (overrides value inferred from data)
!  --hfac=1.2 or H5SPLASH_HFAC=1.2 : factor to use in h= hfac*(m/rho)**(1/ndim) if h not present in data
!  --hsml=1.0 or H5SPLASH_HSML=1.0 : value for global smoothing length if h not present in data
!  --typeid=MatID or H5SPLASH_TYPEID='MatID'  : name of dataset containing the particle types
!
! the data is stored in the global array dat
!
! >> this subroutine must return values for the following: <<
!
! ncolumns    : number of data columns
! ndim, ndimV : number of spatial, velocity dimensions
! nstepsread  : number of steps read from this file
!
! dat(maxplot,maxpart,maxstep) : main data array
!
! npartoftype(1:6,maxstep) : number of particles of each type in each timestep
! ntot(maxstep)       : total number of particles in each timestep
!
! time(maxstep)       : time at each step
! gamma(maxstep)      : gamma at each step
!
! most of these values are stored in global arrays
! in the module 'particle_data'
!-------------------------------------------------------------------------

!--local module to store header information so we can later set the labels
module h5partdataread
 use params
 use labels, only:lenlabel
 implicit none
 character(len=lenlabel), dimension(maxplot) :: datasetnames
 logical :: warn_labels = .true.

end module h5partdataread

module readdata_h5part
 implicit none

 public :: read_data_h5part, set_labels_h5part

 private
contains

subroutine read_data_h5part(rootname,indexstart,ipos,nstepsread)
 use particle_data,   only:dat,iamtype,npartoftype,time,gamma,maxpart,maxcol,maxstep
 use params
 use settings_data,   only:ndim,ndimV,ncolumns,ncalc,debugmode,ntypes,iverbose
 use mem_allocation,  only:alloc
 use iso_c_binding,   only:c_double,c_int64_t
 use asciiutils,      only:lcase
 use system_utils,    only:renvironment,get_environment_or_flag
 use labels,          only:ih,ipmass,irho,ix
 use h5part
 use h5partattrib,    only:h5pt_readstepattrib,h5pt_getnstepattribs,h5pt_getstepattribinfo, &
                            h5pt_readstepattrib_r8, h5pt_readstepattrib_string
 use h5partdataread
 integer, intent(in)               :: indexstart,ipos
 integer, intent(out)              :: nstepsread
 character(len=*), intent(in)      :: rootname
 integer                           :: i,j,ncolstep,nsteps,ncolsfile,icol,itypeidcol
 integer(kind=c_int64_t)           :: nattrib,iattrib,nelem,idatasettype,k,icolsfile,ierr
 integer                           :: nprint,npart_max,nstep_max,maxcolsfile,itype
 integer, dimension(maxplot)       :: iorder
 integer, dimension(maxparttypes)  :: itypemap
 integer, dimension(:),allocatable :: itypefile
 logical                           :: iexist,typeiddefault
 integer(kind=c_int64_t)           :: ifile,istep
 character(len=len(rootname)+5)    :: dumpfile
 character(len=64)                 :: attribname
 character(len=lenlabel)           :: datasetname,type_datasetname
 real(kind=doub_prec),dimension(1) :: dtime
 real                              :: hsmooth,hfac,dndim
 character(len=64)                 :: xstring

 nstepsread = 0
 nstep_max = 0
 npart_max = maxpart

 dumpfile = trim(rootname)

 if (iverbose >= 1) print "(1x,a)",'reading h5part format'
 print "(26('>'),1x,a,1x,26('<'))",trim(dumpfile)
 !
 !--check if first data file exists
 !
 inquire(file=dumpfile,exist=iexist)
 if (.not.iexist) then
    print "(a)",' *** error: '//trim(dumpfile)//': file not found ***'
    return
 endif
 !
 !--fix number of spatial dimensions (0 means no particle coords)
 !
 ndim = 0
 ndimV = 0
 j = indexstart
 nstepsread = 0

 !
 !--open the file and read the number of particles
 !
 if (debugmode) print*,'DEBUG: opening '//trim(dumpfile)
 !ierr = h5pt_set_verbosity_level(6_8)

 ifile = h5pt_openr(trim(dumpfile))
 if (ifile <= 0) then
    print "(a)",'*** ERROR opening '//trim(dumpfile)//' ***'
    return
 endif
 if (debugmode) print*,'DEBUG: file opened ok'

 !
 !--get number of steps and particles in the file
 !
 nsteps = int(h5pt_getnsteps(ifile))
 if (debugmode) print*,'DEBUG: nsteps = ',nsteps

 !
 !--read environment variable giving the name of the dataset
 !  containing the particle type ID
 !  give default value if this is not set
 !
 call get_environment_or_flag('H5SPLASH_TYPEID',type_datasetname)
 if (len_trim(type_datasetname) <= 0) then
    typeiddefault = .true.
    type_datasetname = 'MatID'
 else
    typeiddefault = .false.
 endif
 !
 !--read "header" information from all steps in file:
 !  get maximum number of particles for all steps in file
 !  and maximum number of columns (datasets) in file
 !
 npart_max = 0
 ncolstep  = 0
 maxcolsfile = 0
 itypeidcol = 0
 do istep=1,nsteps
    if (debugmode) print "(a,i2)",'DEBUG: setting step ',istep
    ierr = h5pt_setstep(ifile,istep)
    if (ierr==0) then
       npart_max = max(npart_max,int(h5pt_getnpoints(ifile)))
       ncolsfile = int(h5pt_getndatasets(ifile))
       icol = 0
       if ((istep==nsteps .and. ncolsfile > 0)) then
          do icolsfile=0,ncolsfile-1
             !ierr = h5pt_getdatasetname(ifile,icol,datasetnames(icol+1))
             if (debugmode) print*,'DEBUG: getting datasetinfo'
             ierr = h5pt_getdatasetinfo(ifile,icolsfile,datasetname,idatasettype,nelem)
             if (ierr /= 0) then
                print "(a,i3)",' ERROR reading dataset name for column ',icolsfile+1
             else
                !
                !--read only columns which contain real or double precision data
                !
                select case(idatasettype)
                case(H5PART_FLOAT32,H5PART_FLOAT64)
                   icol = icol + 1
                   datasetnames(icol) = trim(adjustl(datasetname))
                   !print*,' data set info = ',icol,trim(datasetnames(icol)),idatasettype,nelem
                case(H5PART_INT32,H5PART_INT64)
                   !
                   !--try to recognise the dataset giving the particle types
                   !
                   if (trim(type_datasetname)==trim(datasetname) .or. trim(datasetname)=='Phase') then
                      type_datasetname = trim(datasetname)
                      if (itypeidcol <= 0) itypeidcol = int(icolsfile) + 1
                      if (iverbose >= 1) print "(a)",' getting particle types from data set '//trim(type_datasetname)
                   else
                      if (iverbose >= 1) print "(a)",' skipping data set '//trim(datasetname)// &
                                                      ' of type '//h5part_type(int(idatasettype))
                   endif
                case default
                   if (iverbose >= 1) print "(a)",' skipping data set '//trim(datasetname)//&
                                                   ' of type '//h5part_type(int(idatasettype))
                end select
             endif
          enddo
       elseif (ncolsfile <= 0) then
          print*,'ERROR: number of datasets in step ',istep,' = ',ncolsfile
       endif
       ncolstep = max(ncolstep,icol)
       maxcolsfile = max(ncolsfile,maxcolsfile)
    else
       print "(a,i3)",' ERROR, could not choose step ',istep
       return
    endif
 enddo
 nprint = npart_max

 !
 !--warn if no particle type data has been read
 !
 if (itypeidcol <= 0) then
    if (typeiddefault) then
       if (iverbose >= 1) print "(a)",' Particle type dataset not found in file: Use --typeid=blah to give dataset name'
    else
       print "(a)",' WARNING: Particle type dataset '//trim(type_datasetname)//' (from --typeid) not found in file '
    endif
 endif

 !
 !--call the set_labels_h5part routine to get the initial location of coords, smoothing length etc. given dataset labels
 !
 warn_labels = .false.
 call set_labels_h5part()
 warn_labels = .true.

 !
 !--set default ordering of columns
 !
 do i=1,size(iorder)
    iorder(i) = i
 enddo
 !
 !--if coordinates are not in the first 3 columns, shift data so that they are
 !

 if (ndim > 0 .and. ix(1) /= 1) then
    do i=1,ndim
       iorder(ix(i)) = i
    enddo
    !--preserve the order of things after the coordinates
    icol = ndim
    do i=ix(1)+1,ncolstep
       if (.not.any(ix(1:ndim)==i)) then
          icol = icol + 1
          iorder(i) = icol
       endif
    enddo
    !--shuffle things before the coordinates to the end
    do i=1,ix(1)-1
       if (.not.any(ix(1:ndim)==i)) then
          icol = icol + 1
          iorder(i) = icol
       endif
    enddo
 endif
 if (debugmode) print*,'DEBUG: iorder = ',iorder

 !
 !--if smoothing length has not been set, look for an environment variable
 !  giving the smoothing length value
 !
 hsmooth = -1.
 if (ih==0) then
    hsmooth = renvironment('H5SPLASH_HSML',errval=-1.)
    if (hsmooth >= 0.) then
       ncolstep = ncolstep + 1
    elseif (ipmass > 0. .and. irho > 0 .and. ndim > 0) then
       hfac = renvironment('H5SPLASH_HFAC',errval=-1.)
       if (hfac > 0.) then
          if (iverbose >= 1) print "(/,a,f6.2,a,/)",' Setting smoothing length using h = ',hfac,&
                                  '*(m/rho)**(1/ndim) (from --hfac setting)'
       else
          hfac = 1.2
          if (iverbose >= 1) then
             print "(/,a)",' WARNING: Smoothing length not found in data: using h = hfac*(m/rho)**(1/ndim)'
             print "(a)",  '          (hfac = 1.2 by default, set e.g. --hfac=1.5 to change this)'
             print "(a,/)",'          (set constant h with --hsml=1.0 to give a global value)'
          endif
       endif
       ncolstep = ncolstep + 1
    else
       if (iverbose >= 1) print "(/,a,/)",' WARNING: Smoothing length not found in data: Set --hsml to give a global value'
    endif
 endif
 ncolumns = ncolstep

 !
 !--allocate memory for all data in the file
 !
 nstep_max = max(nsteps,indexstart,1,maxstep)
 npart_max = max(maxpart,npart_max)
 if (.not.allocated(dat) .or. (nprint > maxpart) .or. (ncolstep+ncalc) > maxcol) then
    if (itypeidcol > 0) then
       call alloc(npart_max,nstep_max,ncolstep+ncalc,mixedtypes=.true.)
    else
       call alloc(npart_max,nstep_max,ncolstep+ncalc)
    endif
 endif

!
!--now read the timestep data in the dumpfile (for all steps)
!
 istep = 0
 do j=indexstart,indexstart+nsteps-1
    istep = istep + 1
    print "(a,i4,a,i10)",' step ',istep,': ntotal = ',nprint
    ierr = h5pt_setstep(ifile,istep)
    nprint = int(h5pt_getnpoints(ifile))  ! use int() to avoid compiler warning about type conversion
!
!--get the time from the step attributes
!
    nattrib = h5pt_getnstepattribs(ifile)
    if (nattrib > 0) then
       do iattrib=0,nattrib-1   ! yes, it's written in C
          ierr = h5pt_getstepattribinfo(ifile,iattrib,attribname,nelem)
          !print*,' step attribute '//trim(attribname),' nelem = ',nelem
          if (ierr==0) then
             !
             !--match anything that looks vaguely like the time
             !
             if (nelem==1 .and. (index(lcase(attribname),'time') /= 0  &
                 .or. index(lcase(attribname),'t ') /= 0)) then
                ierr = h5pt_readstepattrib_string(ifile,attribname,xstring)
                if (ierr==0) then
                   read(xstring,'(f10.0)') time(j)
                   print "(12x,a,es10.3,a)",'time   = ',time(j),' (from '//trim(attribname)//')'
                else
                   print "(a,i2,a)",' ERROR could not read time from step ',istep,' (from '//trim(attribname)//')'
                endif
                !
                !--match gamma if possible
                !
             elseif (nelem==1 .and. (index(lcase(attribname),'gamma') /= 0  &
                 .or. index(lcase(attribname),'gam ') /= 0)) then
                ierr = h5pt_readstepattrib_string(ifile,attribname,xstring)

                if (ierr==0) then
                   gamma(j) = real(dtime(1))
                   print "(12x,a,es10.3,a)",'gamma  = ',gamma(j),' (from '//trim(attribname)//')'
                else
                   print "(a,i2,a)",' ERROR could not read gamma from step ',istep,' (from '//trim(attribname)//')'
                endif
             else
                print "(a)",' unknown attribute '//trim(attribname)
             endif
          else
             print "(a,i3,a,i2)",' ERROR reading attribute info for step ',istep,', attribute #',iattrib
          endif
       enddo
    endif
!
!--now read the data for this step
!
    icol = 0
    do k=0,maxcolsfile-1
       ierr = h5pt_getdatasetinfo(ifile,k,datasetname,idatasettype,nelem)
       select case(idatasettype)
       case(H5PART_FLOAT32,H5PART_FLOAT64)
          icol = icol + 1
          datasetnames(iorder(icol)) = trim(datasetname)
          if (debugmode) print "(a,i3,a,i3)",'DEBUG: reading data set ',icol,&
                                             ': '//trim(datasetnames(iorder(icol)))//' into column ',iorder(icol)
          ierr = h5pt_readdata(ifile,datasetnames(iorder(icol)),dat(:,iorder(icol),j))
          if (ierr /= 0) print "(a)",' ERROR reading dataset '//trim(datasetnames(iorder(icol)))
       case default
          if (debugmode) print "(a)",' skipping data set '//trim(datasetname)//' of type '//lcase(h5part_type(int(idatasettype)))
       end select
    enddo
!
!--read the particle types for this step from the typeid dataset (specified from the type_datasetname setting)
!
    npartoftype(:,j) = 0
    if (itypeidcol > 0 .and. size(iamtype(:,1)) > 1) then
       if (debugmode) print "(a)",'DEBUG: reading particle types from '//trim(type_datasetname)
       !
       !--allocate temporary memory
       !
       if (allocated(itypefile)) deallocate(itypefile)
       allocate(itypefile(nprint),stat=ierr)
       if (ierr /= 0) stop 'ERROR allocating temporary memory for particle types'
       !
       !--read type array from file
       !
       ierr = h5pt_readdata(ifile,trim(type_datasetname),itypefile(:))
       if (ierr /= 0) then
          print "(a)",' ERROR reading dataset '//trim(type_datasetname)
       else
          !
          !--work out the number of unique particle types
          !  and map these into SPLASH particle types (1->maxtypes)
          !
          if (j==1 .and. istep==1) then
             ntypes      = 1
             itypemap(1) = minval(itypefile)
          endif
          do i=1,nprint
             !--increase the number of particle types if a particle of new type is found
             if (.not.any(itypemap(1:ntypes)==itypefile(i))) then
                ntypes = ntypes + 1
                if (ntypes <= size(itypemap)) then
                   itypemap(ntypes) = itypefile(i)
                   npartoftype(ntypes,j) = npartoftype(ntypes,j) + 1
                   iamtype(i,j) = ntypes
                endif
             else
                do itype=1,ntypes
                   if (itypefile(i)==itypemap(itype)) then
                      npartoftype(itype,j) = npartoftype(itype,j) + 1
                      iamtype(i,j) = itype
                   endif
                enddo
             endif
          enddo
          if (nprint < 1e6) then
             print "(12x,a,10(i5,1x))",'npart (by type) = ',npartoftype(1:ntypes,j)
          else
             print "(12x,a,10(i10,1x))",'npart (by type) = ',npartoftype(1:ntypes,j)
          endif
          !
          !--warn if the number of types exceeds the current limit
          !
          if (ntypes > maxparttypes) &
             print "(/,2(a,i2),a/)", &
              ' WARNING: too many particle types in dataset '//trim(type_datasetname)// &
              ' (got ',ntypes,': maximum is currently ',maxparttypes,')'
       endif
       !
       !--clean up
       !
       if (allocated(itypefile)) deallocate(itypefile)
    else
!--only one particle type
       ntypes = 1
       npartoftype(1,j) = nprint
    endif
!
!--reset the labels now that the columns have been read in the correct order
!
    if (j==indexstart) then ! set labels based on the first step read from the file
       warn_labels = .false.
       call set_labels_h5part()
       warn_labels = .true.
    endif
!
!--if smoothing length set via environment variable, fill the extra column with the smoothing length value
!
    if (ih==0) then
       if (hsmooth >= 0.) then
          datasetnames(ncolstep) = 'h'
          ih = ncolstep
          dat(:,ih,j) = hsmooth
       elseif (ipmass > 0 .and. irho > 0 .and. ndim > 0) then
          ih = ncolstep
          datasetnames(ncolstep) = 'h'
          dndim = 1./ndim
          where (dat(:,irho,j) > tiny(0.))
             dat(:,ih,j) = hfac*(dat(:,ipmass,j)/dat(:,irho,j))**dndim
          elsewhere
             dat(:,ih,j) = 0.
          end where
       endif
    endif
!    read(iunit,*,iostat=ierr) (dat(i,icol,j),icol = 1,ncolstep)
    nstepsread = nstepsread + 1
 enddo

 ierr = h5pt_close(ifile)

 return
end subroutine read_data_h5part

!!-------------------------------------------------------------------
!! set labels for each column of data
!!
!! read these from a file called 'columns' in the current directory
!! then take sensible guesses as to which quantities are which
!! from the column labels
!!
!!-------------------------------------------------------------------

subroutine set_labels_h5part()
 use asciiutils,      only:lcase
 use labels,          only:label,ix,irho,ipmass,ih,iutherm, &
                            ipr,ivx,iBfirst,iamvec,labelvec,lenlabel !,labeltype
 !use params,          only:maxparttypes
 use settings_data,   only:ndim,ndimV,UseTypeInRenderings,iverbose
 use geometry,        only:labelcoord
 use system_utils,    only:ienvironment
 use h5partdataread
 integer                 :: i,ndimset,ndim_max
 character(len=lenlabel) :: labeli

 ndim = 0
 ndimV = 0
 ndimset = ienvironment('H5SPLASH_NDIM',errval=-1)
 ndim_max = 3
 if (ndimset >= 0) ndim_max = ndimset
 irho = 0
 ih = 0
 ipmass = 0

 do i=1,size(datasetnames)
    if (len_trim(datasetnames(i)) > 0) then
       label(i) = trim(datasetnames(i))
    else
       label(i) = ' '
    endif
!--now try to recognise the column based on the dataset name
!  compare all strings in lower case, trimmed and with no preceding spaces
!
    labeli = trim(adjustl(lcase(label(i))))
    if (index(labeli,'coords') /= 0 .and. index(labeli,'_') /= 0 .or. labeli(1:1)=='x') then
       if (ndim < ndim_max) then
          ndim = ndim + 1
          ix(ndim) = i
          label(ix(ndim)) = labelcoord(ndim,1)
       endif
    elseif (index(labeli,'vel_') /= 0 .and. (ivx==0 .or. i <= ivx+ndim)) then
       if (ndimV < 3) ndimV = ndimV + 1
       if (index(labeli,'_0') /= 0) ivx = i
    elseif (index(labeli,'dens') /= 0 .and. irho==0) then
       irho = i
    elseif (index(labeli,'mass') /= 0 .and. ipmass==0) then
       ipmass = i
    elseif (ih==0 .and. (index(labeli,'smoothing') /= 0 .or. labeli(1:1)=='h')) then
       ih = i
    elseif (labeli(1:1)=='u') then
       iutherm = i
       !--identify vector quantities based on _0, _1, _2 labelling
    elseif (index(labeli,'_0') /= 0) then
       !print*,'labelling ',labeli(1:index(labeli,'_0')-1),' as vector, column ',i
       iamvec(i) = i
       labelvec(i) = labeli(1:index(labeli,'_0')-1)
    elseif (index(labeli,'_1') /= 0 .and. i > 1 .and. ndim >= 2) then
       if (iamvec(i-1) > 0) then
          iamvec(i) = i-1
          labelvec(i) = labelvec(i-1)
       endif
    elseif (index(labeli,'_2') /= 0 .and. i > 2 .and. ndim >= 3) then
       if (iamvec(i-2) > 0) then
          iamvec(i) = i-2
          labelvec(i) = labelvec(i-2)
       endif
    endif
 enddo

 if (ndim < 1) ndimV = 0
 if (ndimV > ndim) ndimV = ndim

 if (warn_labels .and. iverbose >= 1) then
    if (ndimset > 0) then
       if (ndim /= ndimset) then
          print "(2(a,i1))",' WARNING: ndim = ',ndimset, &
                             ' from --ndim setting but coords not found in data: using ndim = ',ndim
       else
          print "(a,i1,a)",' Assuming number of dimensions = ',ndim,' from --ndim setting'
       endif
    else
       if (ndim > 0) print "(a,i1,a)",' Assuming number of dimensions = ',ndim,' (set --ndim=3 to override)'
    endif

    if (ndimV > 0) print "(a,i1)",' Assuming vectors have dimension = ',ndimV
    if (irho > 0) print "(a,i2)",' Assuming density in column ',irho
    if (ipmass > 0) print "(a,i2)",' Assuming particle mass in column ',ipmass
    if (ih > 0) print "(a,i2)",' Assuming smoothing length in column ',ih
    if (iutherm > 0) print "(a,i2)",' Assuming thermal energy in column ',iutherm
    if (ipr > 0) print "(a,i2)",' Assuming pressure in column ',ipr
    if (ivx > 0) then
       if (ndimV > 1) then
          print "(a,i2,a,i2)",' Assuming velocity in columns ',ivx,' to ',ivx+ndimV-1
       else
          print "(a,i2)",' Assuming velocity in column ',ivx
       endif
    endif
    if (ndim==0 .or. irho==0 .or. ipmass==0 .or. ih==0) then
       print "(4(/,a))",' NOTE: Rendering capabilities cannot be enabled', &
                    '  until positions of density, smoothing length and particle', &
                    '  mass are known (for the h5part read this means labelling ', &
                    '  the dataset appropriately)'
    endif

    !
    !--assign vectors (don't do this on the first call otherwise it will remain assigned to the wrong columns)
    !
    if (ivx > 0) then
       iamvec(ivx:ivx+ndimV-1) = ivx
       labelvec(ivx:ivx+ndimV-1) = 'v'
    endif
    if (iBfirst > 0) then
       iamvec(iBfirst:iBfirst+ndimV-1) = ivx
       labelvec(iBfirst:iBfirst+ndimV-1) = 'B'
    endif

    !
    !--set labels for vector quantities
    !
    do i=1,size(datasetnames)
       if (iamvec(i) /= 0) then
          label(i) = trim(labelvec(iamvec(i)))//'_'//trim(labelcoord(i-iamvec(i)+1,1))
       endif
    enddo
 endif
 !
 !--set labels for each particle type
 !  (for h5part this is done in the read_data_h5part routine)
 !

 !ntypes = 1 !!maxparttypes
!  labeltype(1) = 'gas'
!  labeltype(2) = 'gas'
!  labeltype(3) = 'gas'
!  labeltype(4) = 'gas'
 UseTypeInRenderings(:) = .true.


!-----------------------------------------------------------

 return
end subroutine set_labels_h5part
end module readdata_h5part
