function scaninfo = scaninforead(fid, offset)
%SCANINFOREAD Read in scaninfo database of Zeiss LSM file format
%   SCANINFO = SCANINFOREAD(FID, [OFFSET])
%
%   OFFSET should be the file position for the start of the scaninfo database.
%   If FID is already at the correct file position, OFFSET should be omitted.
%

%   Peter Li 30-Aug-05
%   Some rights reserved.  Licensed under Creative Commons: http://creativecommons.org/licenses/by-nc-sa/3.0/

error(nargchk(1, 2, nargin));

LSM_H; % SCANINFO_HEXTAGMAP from header file
scaninfo = struct([]);

% Seek to the scaninfo database file position if an offset is given, otherwise
% leave FID in its current position.
if nargin > 1
    if fseek(fid, offset, 'bof'), 
        error(['Received error on file seek to SCANINFO_OFFSET(' offset '): ' ferror(fid)]); 
    end
end

% The algorithm for reading the scaninfo database depends on keeping track of a
% "level" hierarchy.  As the database is read, some entries are level
% instructions, navigating up or down the hierarchy.  The database is done when
% the hierarchy steps back to level 0.
level = 0;
while (1)
    taghex      = dec2hex(fread(fid, 1, 'uint32'));
    typecode    = fread(fid, 1, 'uint32');
    size        = fread(fid, 1, 'uint32');
    tag = structmap(['h' taghex], SCANINFO_HEXTAGMAP);

    switch typecode
        case 0 % Special case: this is a level instruction entry
            if (hex2dec(taghex) == hex2dec('FFFFFFFF'))
                level = level - 1;
            else
                level = level + 1;
            end
        case 2 % string
            count = size;
            value = char(fread(fid, count, 'uchar')');
            value = value(1:end-2);
        case 4 % int32
            count = size / 4;
            value = fread(fid, count, 'uint32');
        case 5 % float64
            count = size / 8;
            value = fread(fid, count, 'float64');
        otherwise
            fseek(fid, size, 0);
            value = '?';
    end

    % If this was just a level instruction entry ignore it, otherwise try to
    % record entry.
    if typecode > 0
        if ~isempty(tag)
            scaninfo(1).(tag) = structappend(scaninfo, tag, value);
        else
            scaninfo(1).(['h' taghex]) = structappend(scaninfo, ['h' taghex], value);
        end
    end

    if (level == 0)
        break;
    end
end
