;+
; NAME:
;     GET_SEGMENTS
; PURPOSE:
;     Reads all segment records from a file, in an optimized manner,
;     and returns a string list of information for each segment.
; CATEGORY:
;     OVRO APC DATA-ANALYSIS
; CALLING SEQUENCE:
;     seglist = get_segments(a,nrec[,srec][,lrec])
; INPUTS:
;     a       the ASSOC variable associated with the file to be
;               examined.
;     nrec    the number of records currently in the file
; OPTIONAL (KEYWORD) INPUT PARAMETERS:
;     srec    the start record number at which to start reading
;               segments.  If omitted, the start record is 1.
;     lrec    the end record number at which to stop reading
;               segments.  If omitted, the end record is NREC
; ROUTINES CALLED:
;     getdata, getlrecn, tl_decode, get_scantyp, get_segtyp, get_endtyp
; OUTPUTS:
;     seglist    a string array (list) of segment information, or
;                  -1 if file could not be opened, or 0 if file
;                  contained no segments.
; COMMENTS:
; SIDE EFFECTS:
; RESTRICTIONS:
; MODIFICATION HISTORY:
;     Written 25-Sep-1997 by Dale Gary
;     10-Jan-1998  DG
;       Added code to find the end of actual data in the case of an
;       untruncated real-time data file, which may have fill (zero)
;       data at the end of the file.
;     11-Jan-1998  DG
;       Changed due to use of OPENARC routine, and to use ASSOC
;       variable as argument rather than filename.
;     14-Jan-1998  DG
;       Changed to honor return of -1 from getlrecn, indicating
;       that the file is empty.
;     22-Jun-1998  DG
;       Changed calls a(rec) to getdata(rec,a) so that routines will
;       work on a UNIX system.
;     07-Sep-1998  DG
;       Added code to work with segments within segments, although
;       it only works properly if the intervening segment is only
;       one record long.
;     07-Jan-1999  DG
;       Added code to find the end of scan segments and write an
;       entry for each in the output array.
;     13-Mar-1999  DG
;       Fixed problem with CVDOY returning an Invalid day of year message,
;       when the last record was all zeroes.
;     17-Mar-1999  DG
;       Added ENDCODE response to EOS segment
;     16-May-1999  DG
;       Added SCRDUMP segment
;     18-May-1999  DG
;       Several changes to fix problems with new LAUNCHER behavior,
;       segments not in scans, and scans with no data segments.
;     01-Nov-1999  DG
;       Added a line to fix crash on checking an empty file (lastrec = 0).
;     23-Jul-2000  DG
;       Slight change to deal with zero length segments (generally a
;       blank record).
;     27-Jul-2000  DG
;       Added optional start and end record arguments, to speed up
;       finding segments when a restricted record range is to be searched.
;     25-Jan-2001  DG
;       Added code to skip DIALOG_HISTORY records, to fix a major problem
;       caused by them.
;     18-Jan-2006  DG
;       Fixed problem with empty records.  Using NSEGREC as a UINT means that
;       what used to be -1 is now 65535, so test for this case explicitly.
;-
function get_segments,a,nrec,srec,lrec

   ; Find the last record that contains good data

   if (n_elements(lrec) ne 0) then begin
      lastrec = lrec
   endif else begin
      lastrec = getlrecn(a,nrec)
      IF (lastrec EQ -1) THEN return,'No records in file.'
      IF (lastrec EQ 0) THEN return,'No records yet.'
   endelse

   if (n_elements(srec) eq 0) then srec = 1
   seglist = ' '

   ; Set the last record as end of scan, in case there is no EOS record.

   rec = lastrec
   d = getdata(lastrec-1,a)
   escan = uint(d[0])

   ; Now search backward through file, finding all of the
   ; beginning records of each segment

   while (long(rec) gt srec-1) do begin
skip:
      d = getdata(rec-1,a)              ; Read a record (should be last record of a segment)
      tls = tl_decode(d)
      if (tls.segmentcode eq !SEGM.DILOGHIST or tls.segmentcode eq !SEGM.MONITOR $
                                             or tls.segmentcode eq !SEGM.SCRDUMP) then begin
         rec = rec - 1
         goto,skip
      endif
      erec = uint(d[0])               ; The file rec number of this record
      nsegrec = uint(d[3])            ; Get the seg rec number of this record
      ; print,tls.segmentcode,rec,nsegrec  ; Debugging aid
      if (nsegrec eq 65535 or nsegrec eq 0) then begin
         ; Empty record, so skip it
         rec = rec - 1
      endif else begin
         data = getdata((rec - nsegrec)>0,a)  ; Now read the first segment record of this segment
         tls = tl_decode(data)     ; Decode the time/label bytes of this record
         try = 0
         if (tls.nrs ne 1) then begin
            ; This was supposed to be the first record of a segment, but it is not.
            ; Probably this is because this segment contains other segments, which
            ; extends the total length beyond that expected.  Try again...
try2:
            try = try + 1
            nsegrec = nsegrec + tls.nrs - 1
            data = getdata((rec - nsegrec)>0,a)
            tls = tl_decode(data)
            ivlist = ' '
            if (tls.nrs ne 1) then begin
               ; Try one more time:
               if (try le 3) then goto,try2
               ; Not having much luck getting synchronized, so give up
               ans = widget_message([$
                  'Error: File has unexpected structure.',$
                  'Continue anyway? (Select "No" to stop',$
                  'the program for debugging.)'],/QUESTION)
               IF (ans EQ 'No') THEN BEGIN
                  print,'Record with segrec=',tls.nrs,' was not first record in segment??'
                  stop
               ENDIF
            endif
         endif

         ; If this is a data segment, the end record (rec) is also the
         ; end of scan record.

         if (tls.segmentcode eq !SEGM.HEADER) then begin

            ; If this is a scan header segment, format the line in a special way

            scnstr = get_scntyp(tls.scancode)
            segstr = tls.yrday+ $
               string(tls.nrc,escan,format='('' REC'',1X,I5.5,'':'',I5.5)')+ $
               ' Header: '+scnstr
            escan = tls.nrc-1

         endif else begin

            ; Otherwise, format the line for the segment

            segtypstr = get_segtyp(tls.segmentcode)
            if (strmid(segtypstr,0,7) eq 'ENCODER') then begin
               nctyp = ['1H-','1D-','2H-','2D-']
               segtypstr = nctyp((data[12]-1)>0<3)+segtypstr
            endif
            segstr = '  '+strmid(tls.timstr,0,9)+ $
               string(tls.nrc,erec,format='(2X,I5.5,'':'',I5.5)')+' '+segtypstr

            ; If this is a data segment, the last record of the segment should
            ; be an EOS record so check and see.  If so, we want to write a
            ; segment line for it to indicate the end of the scan.
            if (tls.segmentcode eq !SEGM.DATA) then begin
               ; We have to do something special for the last record
               if (erec eq lastrec) then escan = erec else escan = erec
               data = getdata(escan-1,a)
               ; Check if data are good.  If data(0) = 0, no point in checking segmentcode
               if (data(0) ne 0) then begin
                  tls = tl_decode(data)
                  ; Is this last record an EOS record?
                  if (tls.segmentcode eq !SEGM.EOS) then begin
                     ; Yes, it is an EOS record, so format a line for it.
                     segtypstr = get_segtyp(tls.segmentcode)
                     endcode = ishft(data(1023),-8) and 'FF'x
                     if (endcode eq 'FF'x) then endtypstr = '' $
                                           else endtypstr = ' due to '+get_endtyp(endcode)
                     segstr = ['  '+strmid(tls.timstr,0,9)+ $
                        string(escan,escan,format='(2X,I5.5,'':'',I5.5)')+ $
                        ' '+segtypstr+endtypstr,segstr]
                  endif
               endif
            endif


         endelse

         seglist = [seglist,segstr] ; Add the formatted line to the list
         rec = (rec - nsegrec)>0    ; Skip to end of next higher segment (or zero)
      endelse
   endwhile

   ; Sort the list so that the start records are in order:

   nsegs = n_elements(seglist)
   if (nsegs eq 1) then return,0
   seglist = seglist[sort(strmid(seglist,13,5))]

return,seglist(1:nsegs-1)
end
