
;+
; NAME:
;
; rd_sumer
;
; PURPOSE:
;
; To read data and headers from SOHO/SUMER fits files including binary
; table extensions. Files which have created using wrt_sumer and have
; the extension '.genx' may also be read with rd_sumer.
;
; CATEGORY:
;
; Data retrieval.
;
; CALLING SEQUENCE:
;
; rd_sumer,filename,/query
; rd_sumer,filename,index,/nodata
; rd_sumer,filename,index,data
; rd_sumer,filename,index,data,data_cols=data_cols
; 
; INPUTS:
;
; filename: The filename of the fits file from which data is to be
;           read.
;
; OPTIONAL INPUTS:
;
; data_cols: The column numbers of the data columns to be returned in
;            the data structure. The data_cols keyword has no affect
;            when reading '.genx' files.
;       
; KEYWORD PARAMETERS:
;
; query: Prints information on the contents of the file to the screen.
; compressed: checks for compressed files with .Z or .gz ext (Unix only)
;
; nodata: No data is returned, only the index structure.
;
; OUTPUTS:
;
; data: A structure which contains data present in the fits file. The
;       default is to return all of the data. Subsets can be returned
;       using the optional data_cols keyword.
; 
; index: A structure which contains information about the data.
;
; OPTIONAL OUTPUTS:
;
; None.
;
; COMMON BLOCKS:
;
; Uses the Yohkoh routines make_str and str_merge to assure unique
; structure names. 
;
; SIDE EFFECTS:
;
; None?
;
; RESTRICTIONS:
;
; This routine has not been thoroughly tested and may crash at any time!
;
; PROCEDURE:
;
; The routine calls mk_query to obtain information about the data
; stored in the FITS file. This information is used to dynamically
; create index and data structures. The tag names for the data
; structure are derived from the header keyword TTYPE. The index
; structure is based on the information stored in the primary and
; binary header files as well as in the data columns.
;
; For '.genx' files, the index and data structures are read using
; rd_genx.
;
; EXAMPLE:
;
; This file has six columns of data stored in two binary extension
; tables. Note that the data columns in the different binary extension
; tables have the same names but different dimensions.
;
; IDL> filename = 'sum_960604_060805.fits'
; IDL> rd_sumer,filename,/query
; File: sum_960604_060805.fits
; C      Name    Dimensions    Wmin    Wmax     Xmin     Xmax     Ymin     Ymax
; 1 W_1533.43   (25,360,26) 1532.90 1533.96   230.25   531.31  -670.88  -670.88
; 2 W_1550.77   (25,360,26) 1550.25 1551.29   230.25   531.31  -670.88  -670.88
; 3 W_1548.19   (25,360,26) 1547.67 1548.71   230.25   531.31  -670.88  -670.88
; 4 W_1533.43   (25,360,13) 1532.90 1533.96   389.81   534.38  -670.88  -670.88
; 5 W_1550.77   (25,360,13) 1550.25 1551.29   389.81   534.38  -670.88  -670.88
; 6 W_1548.19   (25,360,13) 1547.67 1548.71     0.00   534.38  -670.88     0.00
; IDL> rd_sumer,filename,index,data
; IDL> help,/str,data
; ** Structure MS_206275602001, 6 tags, length=4212000:
;    W_1533_43_1     LONG      Array(25, 360, 26)
;    W_1550_77_1     LONG      Array(25, 360, 26)
;    W_1548_19_1     LONG      Array(25, 360, 26)
;    W_1533_43_2     LONG      Array(25, 360, 13)
;    W_1550_77_2     LONG      Array(25, 360, 13)
;    W_1548_19_2     LONG      Array(25, 360, 13)
; IDL> help,/str,index
; ** Structure <2d5d48>, 7 tags, length=4432, refs=1:
;    W_1533_43_1     STRUCT    -> <Anonymous> Array(1)
;    W_1550_77_1     STRUCT    -> <Anonymous> Array(1)
;    W_1548_19_1     STRUCT    -> <Anonymous> Array(1)
;    W_1533_43_2     STRUCT    -> <Anonymous> Array(1)
;    W_1550_77_2     STRUCT    -> <Anonymous> Array(1)
;    W_1548_19_2     STRUCT    -> <Anonymous> Array(1)
;    GEN             STRUCT    -> <Anonymous> Array(1)
; IDL> help,/str,index.(0)
; ** Structure <2cd5c8>, 9 tags, length=896, refs=4:
;    NUM_BTE         INT              1
;    REFPIX          FLOAT     Array(3)
;    REFVAL          FLOAT     Array(3)
;    DELTA           FLOAT     Array(3)
;    DIMS            INT       Array(3)
;    DESC            STRING    Array(3)
;    UNITS           STRING    Array(3)
;    DET_X           INT              0
;    DET_Y           INT              0
;    SPECTRUM        STRUCT    -> <Anonymous> Array(26)
; IDL> help,/str,index.(0).spectrum
; ** Structure <2b3fb0>, 5 tags, length=32, refs=3:
;    SUM_STATUS      BYTE      Array(5)
;    DET_TIME        DOUBLE           0.0000000
;    EXPTIME         FLOAT           25.0003
;    SOLAR_X         FLOAT           230.250
;    SOLAR_Y         FLOAT          -670.875
;    FLATFIELD       BYTE              0
;    OPTDIST         STRING           ''
;    HISTORY         STRING           ''
; 
; Note that the index and data structures can be referenced without
; using the column names explicitly. For example, one could retrieve
; the jth spectrum from the ith data column using
;   spectrum = data.(i)(*,*,j) 
; the reference pixels and x pointing could be retrieved using
;   refpix  = index.(i).refpix
;   solar_x = index.(i).spectrum(j).solar_x
;
; A .genx file that has been written with wrt_sumer may also be read
; with rd_sumer.
; IDL> filename = 'sum_960604_060805.fits'
; IDL> rd_sumer,filename,index,data
; IDL> wrt_sumer,index,data
; IDL> filename = 'sum_960604_060805.genx'
; IDL> rd_sumer,filename,index,data
;
; MODIFICATION HISTORY:
;
;   HPW 17-JUN-1996:
;
;   HPW 03-JUL-1996: Added query, nodata, and data_cols options as
;                    well as index structures.
;
;   HPW 09-JUL-1996: Fixed bugs related to multiple binary extension
;                    tables, non-unique column names, and unexpected
;                    names for data columns. Changed the index
;                    structure so that its orgainization was more
;                    similar to that of the data structure.
;
;   HPW 25-JUL-1996: Changed the routine to return refpix, refval,
;                    delta, dims, desc, and units for the solar x
;                    direction. Fixed a bug that would cause the index
;                    structure to be be concatenated with structures
;                    that had been read previously.
;
;   HPW 25-JUL-1996: Modified the routine to properly handle files
;                    with no data in them.
;
;   HPW 12-AUG-1996: Modified the routine so that the minimum value of
;                    index.(*).dims is 1.
;
;   HPW 13-AUG-1996: Modified the routine to read .genx files created
;                    with wrt_sumer.
;
;   HPW 03-SEP-1996: Added the optdist and history tag names to the
;                    index structure.
;
;   HPW 07-OCT-1996: Modified the routine to handle new format for
;                    .genx files which allows the nodata switch to
;                    work properly.
;
;   HPW 31-OCT-1996: Modified the routine to identify FITS and GENX
;                    files with arbitrary file names. Added a check on
;                    the existence of the file.
;
;   HPW 20-JUN-1997: Modified to look for TSPIX first then
;                    TRPIX. TSPIX is used as the reference pixel in
;                    the final cdrom distribution of the data.
;
;   HPW 17-JUL-1997: Modified to use rd_sumer_genx which reads a new
;                    version of the file format. The new file format
;                    allows the use of the data_cols keyword with the
;                    files written with wrt_sumer.
;
;   DMZ 13-Jan-1998: Added ability to read compressed version of
;                    file. (Unix only)
;
;   HPW 15-APR-1999: Modified the index structure to include the tag
;                    name ORIENTATION which will indicate if the
;                    exposure has been oriented so that solar north is
;                    on the top. Also added the dispersion tag.
;
;   HPW 12-AUG-1999: Replaced strreplace with SSW str_replace.
;-

pro rd_sumer,filename,index,data,data_cols=data_cols,$
             query=query,nodata=nodata,err=err,compressed=compressed

;; ----------------------------------------------------------------------
;; Check user input
;; ----------------------------------------------------------------------

;; overwrite any input index and data structures

on_error,1

index = 0
data  = 0
INIT  = 0
err=''

if (n_elements(filename) gt 1) then begin
 err='filename must be a scalar'
 message,err,/cont 
 return
endif
filename = filename(0)

;; check for compressed version

if keyword_set(compressed) then begin
 tfile=find_compressed(filename,err=err)
endif else begin

;; check for existence of the file

 chk=loc_file(filename,err=err)
 tfile=chk(0)
endelse

if err ne '' then begin
 message,err,/cont
 return
endif

;; ----------------------------------------------------------------------
;; Simple list of the data in the file 
;; ----------------------------------------------------------------------

if keyword_set(QUERY) then begin
  mk_query,tfile
  return
endif

;; ----------------------------------------------------------------------
;; Check extension on input filename
;; ----------------------------------------------------------------------


file_type = sgt_file_type(tfile)
if (file_type eq 'UNKNOWN') then $
    message,'Input file name must be either a FITS file or a GENX file'

if (file_type eq 'GENX') then begin
  ;; read version number
  openr,unit,tfile,/xdr,/get_lun
  version = 0L
  readu,unit,version
  free_lun,unit

  if version ne 999 and version ne 998 then begin
    ;; very old (stupid) version - read with rd_genx
    rd_genx,tfile,super_struct,text=text
    index = super_struct.index
    data  = super_struct.data
  endif else begin
    ;; newer version - read data with rd_sumer_genx
    rd_sumer_genx,tfile,index,data,data_cols=data_cols,nodata=nodata
  endelse
  return
endif

;; ----------------------------------------------------------------------
;; Define index structure
;; ----------------------------------------------------------------------

;; information unique to each file, not all of this information may be
;; useful.
header = headfits(tfile)
gen={INSTRUME:  fxpar(header,'INSTRUME'),$ ;; Instrument (SUMER)
     FILENAME:  fxpar(header,'FILENAME'),$ ;; Filename
     $ ;; Description of the observing sequence
     SCIENTIS:  fxpar(header,'SCIENTIS'),$ ;; Scientist 
     POPUDP:    fxpar(header,'POPUDP'),  $ ;; User defined POP number
     LOCATE:    fxpar(header,'LOCATE'),  $ ;; ID number
     OBJECT:    fxpar(header,'OBJECT'),  $ ;; Target 
     SCI_OBJ:   fxpar(header,'SCI_OBJ'), $ ;; Science objective 
     OBS_PROG:  fxpar(header,'OBS_PROG'),$ ;; Observing program
     CMP_NAME:  fxpar(header,'CMP_NAME'),$ ;; Campagin name
     OBS_SEQ:   fxpar(header,'OBS_SEQ'), $ ;; Observing sequence
     STUDY_ID:  fxpar(header,'STUDY_ID'),$ ;; Study ID  
     STUDY_NM:  fxpar(header,'STUDY_NM'),$ ;; Study number
     RASTYPE:   fxpar(header,'RASTYPE'), $ ;; Sequence type
     PROG_ID:   fxpar(header,'PROG_ID'), $ ;; Observing program name
     PROG_NM:   fxpar(header,'PROG_NM'), $ ;; Observing program number
     SEQ_NUM:   fxpar(header,'SEQ_NUM'), $ ;; Sequence number
     $ ;; Detector parameters
     DATE_OBS:  fxpar(header,'DATE_OBS'),$ ;; Begining of observation 
     DATE_END:  fxpar(header,'DATE_END'),$ ;; End of observation
     DETECTOR:  fxpar(header,'DETECTOR'),$ ;; Detector
     IXWIDTH:   fxpar(header,'IXWIDTH'), $ ;; Image width  (arcsec)
     IYWIDTH:   fxpar(header,'IYWIDTH'), $ ;; Image height (arcsec)
     SLIT:      fxpar(header,'SLIT'),    $ ;; Slit used
     INS_X:     fxpar(header,'INS_X'),   $ ;; Instrument pointing X
     INS_Y:     fxpar(header,'INS_Y'),   $ ;; Instrument pointing Y
     INS_X0:    fxpar(header,'INS_X0'),  $ ;; Instrument origin X
     INS_Y0:    fxpar(header,'INS_Y0'),  $ ;; Instrument origin Y
     $ ;; Data processing parameters
     TREATMT:   fxpar(header,'TREATMT'), $ ;; Treatment applied to data
     FFONOFF:   fxpar(header,'FFONOFF'), $ ;; Flatfielding 
     ROTCMP:    fxpar(header,'ROTCMP'),  $ ;; Solar roatation compensation
     BINX:      fxpar(header,'BINX'),    $ ;; Binning in X (1-1024)
     BINY:      fxpar(header,'BINY'),    $ ;; Binning in Y (1-360)
     COMPRESS:  fxpar(header,'COMPRESS'),$ ;; Data compression method 
     COMPAR1:   fxpar(header,'COMPAR1'), $ ;; Data compression parameter 1
     COMPAR2:   fxpar(header,'COMPAR2'), $ ;; Data compression parameter 2
     COMPAR3:   fxpar(header,'COMPAR3'), $ ;; Data compression parameter 3
     $ ;; Spacecraft parameters
     SOLAR_P0:  fxpar(header,'SOLAR_P0'),$ ;; Solar angle P0
     SOLAR_B0:  fxpar(header,'SOLAR_B0'),$ ;; Solar angle B0
     SC_X0:     fxpar(header,'SC_X0'),   $ ;; Spacecraft pointing X
     SC_Y0:     fxpar(header,'SC_Y0'),   $ ;; Spacecraft pointing X
     SC_ROLL:   fxpar(header,'SC_ROLL'), $ ;; Spacecraft roll
     XCEN:      fxpar(header,'XCEN'),    $ ;; Center of the instrument FOV X
     YCEN:      fxpar(header,'YCEN'),    $ ;; Center of the instrument FOV Y
     ANGLE:     fxpar(header,'ANGLE')}     ;; Instrument angle of rotation

;; ----------------------------------------------------------------------
;; Identify the data in the file 
;; ----------------------------------------------------------------------

mk_query,tfile,f_query,/silent

if (n_elements(f_query) eq 0) then begin
  message,'No data in the FITS file',/CONTINUE
  output_data  = 0
  output_index = create_struct('gen',gen)
  return
endif

;; check data_cols keyword
n_data = n_elements(f_query)
if keyword_set(data_cols) then begin
  if ((n_elements(data_cols) gt n_data) or $
      (max(data_cols) gt n_data) or $
      (min(data_cols) lt 1)) then begin
    message,'DATA_COLS keyword improperly specified'
  endif
endif

;; ----------------------------------------------------------------------
;; Define data structure
;; ----------------------------------------------------------------------

;; create an array which identifies which columns in the file are to
;; be returned in the index and data structures
n_cols = n_elements(f_query)    ;; total number of columns in the file
if keyword_set(data_cols) then mm=data_cols-1 else mm=indgen(n_cols)
n_cols_req = n_elements(mm)     ;; total number of columns to be returned

;; check for uniqueness of column names
names = f_query(mm).name 	    ;; column names requested
dims  = f_query(mm).dims_str	    ;; column dimensions requested
u     = uniq([names],sort([names])) ;; unique column names
n_u   = n_elements(u)		    ;; number of unique column names

;; if column names are not unique then append an underscore and a
;; number to them
if (n_u ne n_cols_req) then begin
  for i=0,n_u-1 do begin
    u_name = names(u(i))
    match  = where(names eq u_name,count)
    num    = strtrim('_'+strtrim(string(indgen(count)+1),2),2)
    names(match) = names(match) + num
  endfor
endif

;; define structure to hold data
str_def = '{dummy,'
for i=0,n_cols_req-1 do begin
  names(i) = str_replace(names(i),'.','_') ;; replace decimal points
  names(i) = str_replace(names(i),' ','_') ;; replace spaces
  str_def  = str_def + names(i)+': '+'fltarr'+dims(i)
  if (i ne n_elements(mm)-1) then str_def = str_def + ','
endfor
str_def = str_def + '}'

;; use Yohkoh routine make_str to dynamically create structure
if not(keyword_set(NODATA)) then data = make_str(str_def)

flatfield = byte(fxpar(header,'FFONOFF'))

;; information unique to each EXPOSURE
dispersion  = make_array(3,value=0,/float)
spec_struct = {sum_status:   bytarr(5), $ ;; Sumer status 
               det_time:     double(0), $ ;; Detector time
               exptime:       float(0), $ ;; Exposure time
               solar_x:       float(0), $ ;; Solar X pointing
               solar_y:       float(0), $ ;; Solar Y pointing
               flatfield:    flatfield, $ ;; Flatfielding on/off
               optdist:             '', $ ;; Optical distortion
               history:		    '', $ ;; History
               orientation:         '', $ ;; Orientation of exposure
               dispersion:  dispersion}   ;; For improved dispersion

;; information unique to each column of data
col_struct = {num_bte:         fix(0), $ ;; Binary table extension
              refpix:	    fltarr(3), $ ;; Reference pixels
              refval:	    fltarr(3), $ ;; Reference values
              delta:	    fltarr(3), $ ;; Deltas
              dims:           [1,1,1], $ ;; Array dimensions
              desc:	    strarr(3), $ ;; Array descriptions
              units:	    strarr(3), $ ;; Array units
              det_x:	       fix(0), $ ;; Detector X
              det_y:	       fix(0)}   ;; Detector Y

;; ----------------------------------------------------------------------
;; Write data to index and data structures
;; ----------------------------------------------------------------------

;; determine which binary table extensions to open
if keyword_set(data_cols) then mm = data_cols-1 else mm = indgen(n_data)
extension = f_query(mm).extension
extension = extension(uniq([extension],sort([extension])))

n_ext       = n_elements(extension) ;; number of extensions to be read
n_cols_read = 0			    ;; total number of columns read from 
				    ;; previous tables

for i=0,n_ext-1 do begin

  ;; open binary header
  fxbopen,unit,tfile,extension(i),header
  
  ;; read reference pixels - look for TSPIX first
  fxbfind,unit,'TSPIX',columns,rpix_values,n_found
  if n_found eq 0 then fxbfind,unit,'TRPIX',columns,rpix_values,n_found

  ;; read reference values
  fxbfind,unit,'TRVAL',columns,rval_values,n_found
  ;; read deltas
  fxbfind,unit,'TDELT',columns,delt_values,n_found
  ;; read array dimensions
  fxbfind,unit,'TDIM',columns,dims_values,n_found
  ;; read column units
  fxbfind,unit,'TUNIT',columns,unit_values,n_found
  ;; read column descriptions
  fxbfind,unit,'TDESC',columns,desc_values,n_found
  ;; read starting x pixel in detector
  fxbfind,unit,'TDETX',columns,detx_values,n_found
  ;; read starting y pixel in detector
  fxbfind,unit,'TDETY',columns,dety_values,n_found  

  ;; count up the total number of columns in all previous binary table
  ;; extensions
  n_cols_pts = 0
  for j=1,extension(i)-1 do begin
    match      = where(f_query.extension eq j,count)
    n_cols_pts = n_cols_pts + count 
  endfor

  ;; create an array which identifies which columns in the binary
  ;; table extension are to be read
  if keyword_set(data_cols) then begin
    ;; find requested data columns from this table
    col_min = min(where(f_query.extension eq extension(i)))
    col_max = max(where(f_query.extension eq extension(i)))
    match   = where((data_cols-1 ge col_min) and (data_cols-1 le col_max))
    mm      = data_cols(match) - 1 - n_cols_pts
  endif else begin
    mm = indgen(n_found)
  endelse

  ;; construct an index structure for the table
  n_cols_req_t = n_elements(mm)    	;; number of columns from this table
  dim_3        = intarr(n_cols_req_t) 	
  for j=0,n_cols_req_t-1 do begin
    dims = destring(dims_values(mm(j)))
    if (n_elements(dims) gt 2) then dim_3(j) = dims(2) else dim_3(j) = 1
  endfor
  n_spectra = max(dim_3) ;; number of spectra per data column

  spec  = replicate(spec_struct,n_spectra)
  tmp   = create_struct(col_struct,'spectrum',spec)
  col   = replicate(tmp,n_cols_req_t)

  ;; note the binary table extension being read
  col(*).num_bte = extension(i)

  ;; retrieve information from binary table header
  for j=0,n_cols_req_t-1 do begin
    col(j).refpix = destring(rpix_values(mm(j)))
    col(j).refval = destring(rval_values(mm(j)))
    col(j).delta  = destring(delt_values(mm(j)))
    col(j).dims   = destring(dims_values(mm(j)))
    col(j).desc   = destring(desc_values(mm(j)),/TEXT)
    col(j).units  = destring(unit_values(mm(j)),/TEXT)
    col(j).det_x  = detx_values(mm(j))
    col(j).det_y  = dety_values(mm(j))
  endfor

  ;; information from binary table
  info_names = ['SUM_STATUS','DEL_TIME','EXPTIME','SOLAR_X','SOLAR_Y']
  fxbfind,unit,'TTYPE',columns,values,n_found
  values = strcompress(values,/REMOVE_ALL)

  for j=0,4 do begin
    match = where(values eq info_names(j))
    fxbread,unit,col_data,columns(match(0))

    ;; data for each column
    for k=0,n_cols_req_t-1 do begin
      ;; data for each spectrum
      dim_l = min([n_spectra,n_elements(col_data(0,*,0))])
      for l=0,dim_l-1 do begin
        tmp = reform(col_data(mm(k),l,*))
        if (n_elements(tmp) eq 1) then tmp = tmp(0)
        col(k).spectrum(l).(j) = tmp
      endfor
    endfor
  endfor

  ;; read in detector data
  if not(keyword_set(nodata)) then begin
    for j=0,n_cols_req_t-1 do begin
      fxbread,unit,col_data,columns(mm(j))
      data.(n_cols_read + j) = col_data
    endfor
  endif    

  ;; concatenate the index structure for the table with the index
  ;; structure for the file
  for j=0,n_cols_req_t-1 do begin
    if (init eq 0) then begin
      tag_name = names(j)
      index    = create_struct(tag_name,col(j))
      init     = 1
    endif else begin
      tag_name = names(n_cols_read + j)
      index    = create_struct(index,tag_name,col(j))
      init     = 1
    endelse
  endfor
  n_cols_read = n_cols_read + n_cols_req_t
  
  fxbclose,unit
endfor

;; add 'gen' struct to the index
index = create_struct(index,'gen',gen)

return
end
