;-----------------------------------------------------------------------------
; NAME: QUBEPDS
;
; PURPOSE: To read a 3-D image QUBE object into a 3-D IDL array.
;
; CALLING SEQUENCE:
;     Result: QUBEPDS (filename, label [,/SILENT, /NOSCALE])
;
; INPUTS:
;     Filename: Scalar string containing the name of the PDS file to read
;     Label: the PDS label string array containing qube object definitions
; OUTPUTS:
;     Result: and IDL structure containing the qube array(s).
;
; OPTIONAL INPUT:
;     SILENT: suppresses any message from the procedure
;     NOSCALE: does not perform scaling and offset of values; default
;         is to scale and offset
;
; PROCEDURES USED:
;     Functions: APPLY_BITMASK, CLEAN, GET_INDEX, OBJPDS, PDSPAR,
;         POINTPDS, REMOVE_CHARS
;
; RESTRICTIONS:
;  - Assume qubes with 3-axes among the 6 possible axes
;  - All parameters are assumed to have the same variable type 
;     inside a given suffix (the first type in the list *_SUFFIX_ITEM_TYPE)
;  - Corner regions are not used
;
; MODIFICATION HISTORY:
;    Written by:     P. Khetarpal [Aug 01, 2002]
;    Last modified:  S. Martinez  [Apr 04, 2008]
;
;    For a complete list of modifications, see changelog.txt file.
;
;-----------------------------------------------------------------------------


;- level 3 -------------------------------------------------------------------

;-----------------------------------------------------------------------------
; precondition: start and end index are viable indices; so is label;
;     param is a PDS keyword to be extracted from label; required is a
;     boolean describing whether the keyword to be extracted is
;     required for the qube object or not.
; postcondition: the keyword for current qube is extracted and returned

function extract_qube_keywords, start_ind, end_ind, label, param, required
    ; initialize variables:
    flag = 0       ; 1: found, 0: not found

    ; first extract all param keywords value in the label:
    allvalues = pdspar(label, param, count = allcount, index = allindex)
    if (allcount gt 0) then begin
        ;; extract all param value between start_ind and end_ind:
        pos = where (allindex gt start_ind and allindex lt end_ind, cnt)
        if (cnt gt 0) then begin
            value = clean(allvalues[pos[0]], /space)
            flag = 1
        endif
    endif

    ; if not found then issue error:
    if (~flag) then begin
        if (required) then begin
            print, "Error: missing required " + param + " keyword from label"
        endif
        value = "-1"
    endif

    return, value
end

;-----------------------------------------------------------------------------
; precondition: keywds contains all current qube object definitions
; postcondition: all keyword values are tested for viability

function check_qube_keywords, keywds
    ; construct new structure:
    struct = {flag: 1}
    param = ['"', "(", ")", "{", "}"]

    ; check axes:
    axes = long(clean(keywds.axes, /space))
    if (axes eq 0) then begin
        print, "Error: AXES keyword value is 0. No data in file."
        goto, endfunction
    endif else if (axes lt 1 || axes gt 6) then begin
        print, "Error: invalid AXES keyword value found: " + $
            clean(string(axes), /space)
        goto, endfunction
    endif

    ; check axis name:
    temp = remove_chars(clean(keywds.axis_name, /space), param)
    if (!version.release gt 5.2) then begin
        axis_name = strsplit(temp, ",", /extract)
    endif else begin
        axis_name = str_sep(temp, ",")    ; obsolete in idl v. > 5.2
    endelse
    if (n_elements(axis_name) ne axes) then begin
        print, "Error: invalid number of AXIS_NAME values found: " + $
            clean(string(n_elements(axis_name)), /space) + " ne " + keywds.axes
        goto, endfunction
    endif

    ; check core items:
    temp = remove_chars(clean(keywds.core_items, /space), param)
    if (!version.release gt 5.2) then begin
        core_items = long(strsplit(temp, ",", /extract))
    endif else begin
        core_items = long(str_sep(temp, ","))   ; obsolete in idl v. > 5.2
    endelse
    pos = where (core_items lt 1, cnt)
    if (n_elements(core_items) ne axes) then begin
        print, "Error: invalid number of CORE_ITEMS values found: " + $
            clean(string(n_elements(core_items)), /space) + " ne " +keywds.axes
        goto, endfunction
    endif
    if (cnt gt 0) then begin
        print, "Error: invalid CORE_ITEMS values found: " + $
            clean(string(core_items[pos[0]]), /space) + " (n >= 1)"
        goto, endfunction
    endif

    ; check core item bytes:
    core_item_bytes = long(clean(keywds.core_item_bytes, /space))
    bytesrange = [1, 2, 4]
    pos = where (core_item_bytes eq bytesrange, cnt)
    if (cnt eq 0) then begin
        print, "Error: invalid CORE_ITEM_BYTES keyword value found: " + $
            keywds.core_item_bytes
        goto, endfunction
    endif

    ; check core item type:
    core_item_type = remove_chars(clean(keywds.core_item_type, /space), param)
    if ((strpos(core_item_type, "INTEGER") lt 0) && $
       (strpos(core_item_type, "REAL") lt 0)) then begin
        print, "Error: invalid CORE_ITEM_TYPE keyword value found: " + $
            core_item_type
        goto, endfunction
    endif

    ; check core base:
    core_base = float(clean(keywds.core_base,/space))

    ; check core mulitplier:
    core_multiplier = float(clean(keywds.core_multiplier, /space))

    ; check suffix bytes:
    suffix_bytes = long(clean(keywds.suffix_bytes, /space))
    if (suffix_bytes ne 4) then begin
;Modified A.Cardesin 2006Mar09
;Do not throw an error but only a warning and go on checking
        print, "Warning: invalid SUFFIX_BYTES keyword value found: " + $
            keywds.suffix_bytes + " (must = 4)"
;        goto, endfunction
    endif

    ; check suffix items:
    temp = remove_chars(clean(keywds.suffix_items, /space), param)
    if (!version.release gt 5.2) then begin
        suffix_items = long(strsplit(temp, ',',/extract))
    endif else begin
        suffix_items = long(str_sep(temp, ','))
    endelse
    pos = where (suffix_items lt 0, cnt)
    if (cnt gt 0) then begin
        print, "Error: invalid SUFFIX_ITEMS keyword value found: " + $
            keywds.suffix_items + " (0 <= n)"
        goto, endfunction
    endif

    ; check sample, line, band suffix item bytes:
    bytesrange = [1, 2, 4, 8, 16]
    sample_suffix_item_bytes = long(clean(keywds.sample_suffix_item_bytes, /space))
    pos = where (sample_suffix_item_bytes eq bytesrange, cnt)
    if (cnt eq 0) then begin
        print, "Error: invalid SAMPLE_SUFFIX_ITEM_BYTES keyword value found: " + $
            keywds.sample_suffix_item_bytes
        goto, endfunction
    endif

    line_suffix_item_bytes = long(clean(keywds.line_suffix_item_bytes, /space))
    pos = where (line_suffix_item_bytes eq bytesrange, cnt)
    if (cnt eq 0) then begin
        print, "Error: invalid SAMPLE_SUFFIX_ITEM_BYTES keyword value found: " + $
            keywds.line_suffix_item_bytes
        goto, endfunction
    endif

    band_suffix_item_bytes = long(clean(keywds.band_suffix_item_bytes, /space))
    pos = where (band_suffix_item_bytes eq bytesrange, cnt)
    if (cnt eq 0) then begin
        print, "Error: invalid SAMPLE_SUFFIX_ITEM_BYTES keyword value found: " + $
            keywds.band_suffix_item_bytes
        goto, endfunction
    endif

    struct = create_struct(struct, "axes", axes, "axis_name", axis_name, $
        "core_items", core_items, "core_item_bytes", core_item_bytes, $
        "core_item_type", core_item_type, "core_base", core_base, $
        "core_multiplier", core_multiplier, "suffix_bytes", suffix_bytes, $
        "suffix_items", suffix_items, $
        ;Modified feb2007, smartinez
        "sample_suffix_item_bytes",sample_suffix_item_bytes, "sample_suffix_type",keywds.sample_suffix_type, $
        "line_suffix_item_bytes",line_suffix_item_bytes, "line_suffix_type",keywds.line_suffix_type, $
        "band_suffix_item_bytes",band_suffix_item_bytes, "band_suffix_type",keywds.band_suffix_type)

    return, struct
    endfunction:
        struct.flag = 0
        return, struct
end

;- level 2 -------------------------------------------------------------------

;-----------------------------------------------------------------------------
; precondition: start and end index are viable indices; so is label
; postcondition: all current qube parameters are extracted from label

function obtain_current_qube_params, start_ind, end_ind, label
    ; initialize keyword structure variable:
    keywds = {flag: 1}

    ; required keywords:
    ; axes:
    axes = extract_qube_keywords(start_ind, end_ind, label, "AXES", 1)
    if (axes eq "-1") then goto, endfunction

    ; axis name:
    axis_name = extract_qube_keywords(start_ind, end_ind, label, "AXIS_NAME",1)
    if (axis_name eq "-1") then goto, endfunction

    ; core items:
    core_items = extract_qube_keywords(start_ind, end_ind, label, $
        "CORE_ITEMS", 1)
    if (core_items eq "-1") then goto, endfunction

    ; core item bytes:
    core_item_bytes = extract_qube_keywords(start_ind, end_ind, label, $
        "CORE_ITEM_BYTES", 1)
    if (core_item_bytes eq "-1") then goto, endfunction

    ; core item type:
    core_item_type = extract_qube_keywords(start_ind, end_ind, label, $
        "CORE_ITEM_TYPE", 1)
    if (core_item_type eq "-1") then goto, endfunction

    ; core base:
    core_base = extract_qube_keywords(start_ind, end_ind, label, "CORE_BASE",1)
    if (core_base eq "-1") then goto, endfunction

    ; core multiplier:
    core_multiplier = extract_qube_keywords(start_ind, end_ind, label, $
        "CORE_MULTIPLIER", 1)
    if (core_multiplier eq "-1") then goto, endfunction

    ; suffix bytes:
    suffix_bytes = extract_qube_keywords(start_ind, end_ind, label, $
        "SUFFIX_BYTES", 1)
    if (suffix_bytes eq "-1") then goto, endfunction

    ; suffix items:
    suffix_items = extract_qube_keywords(start_ind, end_ind, label, $
        "SUFFIX_ITEMS", 1)
    if (suffix_items eq "-1") then goto, endfunction

    ;Modified feb2007, smartinez
    ;Get SAMPLE, LINE, BAND suffix bytes and types if present, else suffix_bytes or "REAL"
    sample_suffix_item_bytes = extract_qube_keywords(start_ind, end_ind, label, $
                "SAMPLE_SUFFIX_ITEM_BYTES", 0)
    if (sample_suffix_item_bytes eq "-1") then sample_suffix_item_bytes = suffix_bytes
    sample_suffix_type = extract_qube_keywords(start_ind, end_ind, label, $
                "SAMPLE_SUFFIX_ITEM_TYPE", 0)
    if (sample_suffix_type eq "-1") then sample_suffix_type = "REAL"    

    line_suffix_item_bytes = extract_qube_keywords(start_ind, end_ind, label, $
                "LINE_SUFFIX_ITEM_BYTES", 0)
    if (line_suffix_item_bytes eq "-1") then line_suffix_item_bytes = suffix_bytes
    line_suffix_type = extract_qube_keywords(start_ind, end_ind, label, $
                "LINE_SUFFIX_ITEM_TYPE", 0)
    if (line_suffix_type eq "-1") then line_suffix_type = "REAL"

    band_suffix_item_bytes = extract_qube_keywords(start_ind, end_ind, label, $
                "BAND_SUFFIX_ITEM_BYTES", 0)
    if (band_suffix_item_bytes eq "-1") then band_suffix_item_bytes = suffix_bytes
    band_suffix_type = extract_qube_keywords(start_ind, end_ind, label, $
                "BAND_SUFFIX_ITEM_TYPE", 0)
    if (band_suffix_type eq "-1") then band_suffix_type = "REAL"
                
    ; add keywords values to structure:
    ; Modified feb2007, smartinez. Added sample, line and band suffix keywords.
    keywds = create_struct(keywds, "axes", axes, "axis_name", axis_name, $
        "core_items", core_items, "core_item_bytes", core_item_bytes, $
        "core_item_type", core_item_type, "core_base", core_base, $
        "core_multiplier", core_multiplier, "suffix_bytes", suffix_bytes, $
        "suffix_items", suffix_items, $
        "sample_suffix_item_bytes",sample_suffix_item_bytes, "sample_suffix_type",sample_suffix_type, $
        "line_suffix_item_bytes",line_suffix_item_bytes, "line_suffix_type",line_suffix_type, $
        "band_suffix_item_bytes",band_suffix_item_bytes, "band_suffix_type",band_suffix_type)

    ; clean keywords structure and convert to appropriate type:
    keywds = check_qube_keywords (keywds)

    return, keywds

    endfunction:
        keywds.flag = 0
        return, keywds
end

;-----------------------------------------------------------------------------
; precondition: type is a viable sample type keyword value for current qube
; postcondition: the architecture of the data file is returned

function obtain_qube_architecture, type
    ; initialize variable:
    arch = "MSB"

    ; determine if arch is "LSB":
    if ((strpos(type, "LSB") gt -1) || (strpos(type, "PC") gt -1) || $
        (strpos(type, "VAX") gt -1)) then begin
        arch = "LSB"
    endif

    return, arch
end

;-----------------------------------------------------------------------------
; precondition: type is the item type; bytes is the number of bytes of the item
; postcondition: the idl data type more suitable is returned

function obtain_item_idltype, type, bytes

    case bytes of
      1:  idltype = 1
      2:  idltype = (strpos(type, "UNSIGNED") gt -1) ? 12 : 2   
      4:  if (strpos(type, "INTEGER") gt -1) then begin
    idltype = (strpos(type, "UNSIGNED") gt -1) ? 13 : 3
      endif else begin
    idltype = 4
      endelse
      8:  if (strpos(type, "INTEGER") gt -1) then begin
    idltype = (strpos(type, "UNSIGNED") gt -1) ? 15 : 14
      endif else begin
    idltype = (strpos(type, "COMPLEX") gt -1) ? 6 : 5
      endelse 
      16: idltype = (strpos(type, "REAL") gt -1) ? 0 : 9   
      else: idltype = 0
    endcase

    return, idltype
end

;-----------------------------------------------------------------------------
; precondition: keywds is the idl structure definition for current
;     qube object
; postcondition: an idl structure is created to read the data from the
;     data file

function obtain_qube_structure, keywds

;    ; initialize structure:
;    bits = keywds.core_item_bytes * 8   ; temporary store bits value
;    type = keywds.core_item_type        ; temporary store item type value
;
;    ; get the idl type of the vector to be created:
;    if (bits eq 8) then begin
;        idl_type = 1
;    endif else if (bits eq 16) then begin
;        idl_type = (strpos(type, "UNSIGNED") gt -1) ? 12 : 2
;    endif else if (bits eq 32) then begin
;        if (strpos(type, "INTEGER") gt -1) then begin
;            idl_type = (strpos(type, "UNSIGNED") gt -1) ? 13 : 3
;        endif else begin
;            idl_type = 4
;        endelse
;    endif          

;    ; determine which element contains the SAMPLE axis:
;    pos = where (keywds.axis_name eq "SAMPLE")
;    vector = make_array(keywds.core_items[pos[0]], type = idl_type)
;
;    ; construct the structure with sample suffix bytes if any:
;    if (keywds.suffix_items[pos[0]] gt 0) then begin
;        bytes = keywds.suffix_items[pos[0]] * keywds.suffix_bytes
;        sample_struct = {sample:vector, sideplane:bytarr(bytes)}
;    endif else begin
;        sample_struct = {sample:vector}
;    endelse
;
;    ; replicate the above structure LINES items times:
;    pos = where (keywds.axis_name eq "LINE")
;    lines_struct = replicate(sample_struct, keywds.core_items[pos[0]])
;
;    ; construct the structure with line suffix bytes if any:
;    if (keywds.suffix_items[pos[0]] gt 0) then begin
;        bytes = keywds.suffix_items[pos[0]] * 4
;        band_struct = {line:lines_struct, bottomplane:bytarr(bytes)}
;    endif else begin
;        band_struct = {line:lines_struct}
;    endelse
;
;    ; replicate the above structure BAND items times:
;    pos = where (keywds.axis_name eq "BAND")
;    main = replicate(band_struct, keywds.core_items[pos[0]])
;    
;    return, main

    ;Modified feb2007, smartinez

    ;Qube core item type
    print, 'CORE Items: ',  keywds.core_item_type,keywds.core_item_bytes
    core_type = obtain_item_idltype(keywds.core_item_type,keywds.core_item_bytes)

    ;Qube storage order
    if (keywds.axis_name[0] eq 'SAMPLE' and keywds.axis_name[1] eq 'LINE' $             ; BSQ - (SAMPLE,LINE,BAND)
        and keywds.axis_name[2] eq 'BAND') then begin order = 0
    endif else if (keywds.axis_name[0] eq 'SAMPLE' and keywds.axis_name[1] eq 'BAND'$   ; BIL - (SAMPLE,BAND,LINE)
        and keywds.axis_name[2] eq 'LINE') then begin order = 1
    endif else if (keywds.axis_name[0] eq 'BAND' and keywds.axis_name[1] eq 'SAMPLE'$   ; BIP - (BAND,SAMPLE,LINE)
        and keywds.axis_name[2] eq 'LINE') then begin order = 2
    endif 

   print, 'Order: ',order

    ;Qube structure

    ; X core dimension 
    X_line = Make_array(keywds.core_items[0],Type=core_type,/nozero)
    
    ; SX suffix items type
    suffix_item_type = (order eq 2) ? keywds.band_suffix_type : keywds.sample_suffix_type
    suffix_item_bytes = (order eq 2) ? keywds.band_suffix_item_bytes : keywds.sample_suffix_item_bytes
    suffix_type = obtain_item_idltype(suffix_item_type,suffix_item_bytes)

    ; SX suffix plane
    if (keywds.suffix_items[0] gt 0) then begin
        SX_line = Make_array(keywds.suffix_items[0],Type=suffix_type,/nozero)
        XSX_line = {X:X_line, SX:SX_line} 
    endif else XSX_line = {X:X_line}
    SX_line = 0B    

    ; Y core dimension, SY suffix plane
    ; SY suffix items type
    if (order eq 0) then begin
        suffix_item_type = keywds.line_suffix_type
        suffix_item_bytes = keywds.line_suffix_item_bytes
    endif else if (order eq 1) then begin
        suffix_item_type = keywds.band_suffix_type
        suffix_item_bytes = keywds.band_suffix_item_bytes
    endif else begin
        suffix_item_type = keywds.sample_suffix_type
        suffix_item_bytes = keywds.sample_suffix_item_bytes
    endelse
    suffix_type = obtain_item_idltype(suffix_item_type,suffix_item_bytes)

    ; Replicate (X+SX, Y times)
    ; Add SY suffix plane with dimension (X,SY)
    if (keywds.suffix_items[1] gt 0) then begin
        SY_line = reform(Make_array(keywds.core_items[0], keywds.suffix_items[1], Type=suffix_type,/nozero), $
                keywds.core_items[0], keywds.suffix_items[1])
        XY_frame = {Y:replicate(XSX_line,keywds.core_items[1]), SY:SY_line} 
    endif else  XY_frame = {Y:replicate(XSX_line,keywds.core_items[1])}
    SY_line = 0B    
    XSX_line = 0B

    ;Z core dimension, SZ suffix plane
   
    ; SZ suffix items type
    suffix_item_type = (order eq 0) ? keywds.band_suffix_type : keywds.line_suffix_type
    suffix_item_bytes = (order eq 0) ? keywds.band_suffix_item_bytes : keywds.line_suffix_item_bytes
    suffix_type = obtain_item_idltype(suffix_item_type,suffix_item_bytes)

    if (keywds.suffix_items[2] gt 0) then begin
        SZ_line = reform(Make_array(keywds.core_items[0], keywds.core_items[1], keywds.suffix_items[2], Type=suffix_type,/nozero), $
                keywds.core_items[0], keywds.core_items[1], keywds.suffix_items[2])
        qube = {Z:replicate(XY_frame,keywds.core_items[2]), SZ:SZ_line} 
    endif else  qube = {Z:replicate(XY_frame,keywds.core_items[2])} 
    XY_frame = 0B
    SZ_line = 0B
    
    return, qube

end

;-----------------------------------------------------------------------------
; precondition: pointer is an idl structure containing viable data
;     file name associated with current qube object to be read, and
;     the number of bytes to skip if any; struct is the idl structure
;     to be read from the data file, and arch is the architecture of
;     the data file (LSB or MSB).
; postcondition: the data file is opened, the structure is read, and returned

function read_qube_data, pointer, struct, arch
    ; error protection:
    on_ioerror, signal

    ; initialize variable and declare flag:
    data_read = {flag: 1} ; 0: error, 1: no error

    ; open the file to read and apply swap endian if needed:
    if (arch eq "MSB") then begin
        openr, unit, pointer.datafile, /get_lun, /swap_if_little_endian
    endif else begin
        openr, unit, pointer.datafile, /get_lun, /swap_if_big_endian
    endelse

    ; set the file pointer to current object to be read:
    point_lun, unit, pointer.skip    

    ; read the qube object into structure:
    readu, unit, struct

    ; close the unit and free the unit:
    close, unit
    free_lun, unit

    data_read = create_struct(data_read, "struct", struct)

    return, data_read

    signal:
       on_ioerror, null
       print, "Error: file either corrupted or invalid parameters specified" +$
           " in label."
       data_read.flag = 0
       return, data_read
end

;-----------------------------------------------------------------------------
; precondition: current contains the raw qube data structure for
;     current qube as read from the datafile; keywds containsn all
;     current qube object keyword values.
; postcondition: extracts the qube array from the current structure,
;     converts the array values to signed integers if necessary, and
;     performs scaling and offset if noscale keyword is not supplied.

function convert_qube_data, current, label, start_ind, end_ind, keywds, noscale
    
    ; extract qube from the read data:
    ;Modified smartinez, feb2007   
    ;element = current[*].dimension1[*].dimension0
    element_core = current.Z.Y.X

    ;Modified by P.Choganwala starts
    if ((keywds.suffix_items[0] eq 0) and       $
        (keywds.suffix_items[1] eq 0) and       $
        (keywds.suffix_items[2] eq 0)) then begin
        element_suffix = 0
    endif
    if (keywds.suffix_items[2] gt 0) then begin
        element_suffix = current.SZ
    endif
    if (keywds.suffix_items[1] gt 0) then begin
        element_suffix = current.Z.SY
    endif
    if (keywds.suffix_items[0] gt 0) then begin
        element_suffix = current.Z.Y.SX
    endif
    ;Modified by P.Choganwala ends
    
    ; convert data elements for signed bytarr objects
    ; (IDL only supports byt values of 0-255):
    pos = strpos(keywds.core_item_type, "UNSIGNED")
    if ((keywds.core_item_bytes eq 1) && (pos lt 0)) then begin
        element_core = fix(element_core)
        fixitlist = where (element_core gt 127, count)
        if (count gt 0) then begin
            element_core[fixitlist] -= 256
        endif
    endif

    ; apply bit mask if applicable:
    element_core = apply_bitmask(label, start_ind, end_ind, element_core)
    element_suffix = apply_bitmask(label, start_ind, end_ind, element_suffix)

    ; process scaling factor and offset:
    if (~noscale) then begin
        if (keywds.core_multiplier ne 1.0) then begin
            element_core *= keywds.core_multiplier
        endif
        if (keywds.core_base ne 0.0) then begin
            element_core += keywds.core_base
        endif
    endif

    element = {core:element_core, sideplane:element_suffix}
    return, element
end

;- level 1 -------------------------------------------------------------------

;-----------------------------------------------------------------------------
; precondition: filename and label are viable, and objects has at
;     least one qube object structure.
; postcondition: all qube objects are processed as defined in objects struct

function process_all_qubes, filename, label, objects, silent, noscale
    ; initialize the qube structure if more than one qube:
    if (objects.count gt 1) then data = {qubes:objects.count}

    ; start the loop:
    for i = 0, objects.count - 1 do  begin
        ;; get current start and end index pointer:
        start_ind = objects.index[i]
        end_ind = get_index(label, start_ind)
        if (end_ind eq -1) then return, -1

        ; obtain current qube object keyword values:
        keywds = obtain_current_qube_params (start_ind, end_ind, label)
        if (~keywds.flag) then return, -1

        ; obtain file data architecture:
        arch = obtain_qube_architecture(keywds.core_item_type)

        ; obtain qube structure:

        struct = obtain_qube_structure(keywds)

        ; obtain pointer information:
        pointer = pointpds(label, filename, objects.array[i])
        if (pointer.flag eq "-1") then return, -1

        ; inform user of current status:
        if (~silent) then begin
            text = "Now reading "
            for j = 0, keywds.axes - 2 do begin
                text += clean(string(keywds.core_items[j]),/space) + " by "
            endfor
            text += clean(string(keywds.core_items[keywds.axes - 1]),/space)+$
                " qube array"
            
            ;Modified feb2007, smartinez. Added suffix dimension.
            ;print, text
            text += ", "
            for j = 0, keywds.axes - 2 do begin
                text += clean(string(keywds.suffix_items[j]),/space) + " by "
            endfor
            text += clean(string(keywds.suffix_items[keywds.axes - 1]),/space)+ $
                " suffix items."
            print, text 
        endif

        ; read data:
        data_read = read_qube_data(pointer, struct, arch)
        if (~data_read.flag) then return, -1
        current = data_read.struct

        ; perform data conversion if needed:
        element = convert_qube_data(current, label, start_ind, end_ind, $
            keywds, noscale)

        ; add qube to structure:
        data = (objects.count gt 1) ? create_struct(data, objects.array[i], $
            element) : element
    endfor

    return, data
end

;- level 0 -------------------------------------------------------------------

;-----------------------------------------------------------------------------
; precondition: filename is a viable pds label file name, and label is
;     a viable pds label.
; postcondition: all qube objects defined in label are read from
;     associated data file and returned.

function qubepds, filename, label, SILENT = silent, NOSCALE = noscale
    ; error protection:
    on_error, 2

    ; check for number of parameters in function call:
    if (n_params() lt 2) then begin
        print, "Syntax Error: result = qubepds(filename, label [, /SILENT," + $
               " /NOSCALE] )"
        return, -1
    endif
    silent = keyword_set(SILENT)
    noscale = keyword_set(NOSCALE)

    ; obtain all qube objects from label:
    objects = objpds (label, "QUBE")
    if (objects.flag eq -1) then begin
        print, "Error: no QUBE object found in label"
        return, -1
    endif

    ; process all qube objects:
    data = process_all_qubes(filename, label, objects, silent, noscale)

    return, data
end