function [lsminf,scaninf,imfinf] = lsminfo(filename)
%LSMINFO read headers and entry directories for Zeiss LSM files
%   LSMINF = LSMINFO(FILENAME)
%
%   This will give you access to the notes that the Zeiss software stores in
%   .lsm files.  All the info is returned in the LSMINF structure, with major
%   substructures such as LSMINF.ScanInfo.  For backwards compatibility you can
%   also call the function as [LSMINF, SCANINF] = LSMINFO(FILENAME) to get the
%   large scaninfo substructure as its own separate return, but this is
%   deprecated.
%
%   You can also get the standard TIFF IMFINFO headers by calling the function as
%   [LSMINF, SCANINF, IMFINF] = LSMINFO(FILENAME).
%
%   Unfortunately, I haven't implemented a way to read the actual images out of
%   the LSM file; MatLab's builtin IMREAD should read TIFF files, but doesn't
%   seem to work with LSM.  The method I use is to export the LSM stack to a
%   series of single image files or a stacked TIFF using the Zeiss LSM Image
%   Browser and then use a FOR loop and IMREAD to get the data into MatLab.
%   There are some TIFF reader implementations available online that say they
%   will work to get image data out of LSM, but I haven't checked those out much
%   myself.
%
%   Also note that if you have the .mdb file that the Zeiss software puts out,
%   this can be read with Microsoft Access and includes much of the same
%   information.
%
%   See comments in file for license info, change log, and acknowledgements.
%

%    Updates from version 1.0.0 (30-Aug-05):
%       Most of the code in the current version is derived from LSM Toolbox, a
%       plugin for the image processing program ImageJ maintained by Patrick
%       Pirrotte and Jerome Mutterer.   However, there is some functionality
%       from my original version that I've kept for backwards compatibility.
%
%       LSM_H
%           Filled in many more field names read from the initial LSM header; no
%           more 'unknown' headers.  Also changed the names to match the LSM
%           Toolbox for ImageJ.  However, to maintain backward compatibility I
%           am leaving the old LSMINF fields there, even in cases where they are
%           duplicated in the new fields.
%
%           Filled in a few more names for the scaninfo tag map.  Should be up
%           to date with all names available from ImageJ now.  This has a small
%           possibility of breaking old code if you were using a scaninfo field
%           that hadn't been properly named before.  In this case, you were
%           accessing the scaninfo field by its hex value alone, e.g.
%           'scaninf.hD0000003'.  You will have to change your code to use the
%           new proper field name; look up the proper name from the tag map
%           struct in LSM_H.
%
%       lsminfo
%           Added functions to read channelcolors and timestamps info, thanks to
%           David Kolin from Montreal for the idea. This new information is put 
%           directly into lsminf.  I also put scaninf directly into lsminf as 
%           lsminf.ScanInfo, so now you can run lsminfo with only one output.
%
%           If you run lsminf without the 3rd output, it won't bother to run the
%           imfinf, as this was taking a lot of time and Norbert Kirchgessner
%           suggested I take it out.
%
%           Added check for fopen/fclose bug.  If fid was already opened before
%           running lsminfo, we will not fclose it when we are done.
%
%       New files:
%           channelinforead
%           timestampsread

%   Peter Li 12-Apr-07
%   Some rights reserved.  Licensed under Creative Commons: http://creativecommons.org/licenses/by-nc-sa/3.0/

error(nargchk(1, 1, nargin));
if ~exist(filename, 'file'), error(['Could not find file: ' filename]); end

LSM_H; % LSMINF from header file

openedfids = fopen('all');
fid = fopen(filename);
if fid == -1, error(['Could not open file: ' filename]); end

% Read special LSM IFD
ifd = ifdread(fid);
lsmifd = ifd([ifd.tagcode] == 34412);
if ~length(lsmifd), error('Could not find LSMINFO IFD entry'); end

% Read LSMINFO header
    % Old version of LSMINF for backward compatibility
    if fseek(fid, lsmifd.offset, 'bof'), error(['Received error on file seek to LSMInfoOffset(' lsmifd.offset '): ' ferror(fid)]); end
    lsminf = structread(fid, LSMINF);

    % New version of LSMINF
    if fseek(fid, lsmifd.offset, 'bof'), error(['Received error on file seek to LSMInfoOffset(' lsmifd.offset '): ' ferror(fid)]); end
    lsminf2 = structread(fid, LSMINF_2);

    lsminf = structcombine(lsminf, lsminf2);

% Read additional small databases and incorporate them into LSMINFO
lsminf = channelinforead(fid, lsminf);
lsminf = timestampsread(fid, lsminf);

% Read SCANINFO directory
scaninf = scaninforead(fid, lsminf.OffsetScanInformation);
lsminf.ScanInfo = scaninf;

% Watch out for fopen bug; if fid was already opened before running lsminfo,
% then we should leave it open.
if ~length(openedfids == fid)
    fclose(fid);
end

% Use MatLab imfinfo to read standard TIFF IFD entries
if nargout > 2
    imfinf = imfinfo(filename);
end