function obj = readRawMetadataSegm( obj )
%READMETADATASEGM Read metadata for segment of type ZISRAWMETADATA
%   Extract information from ZISRAWMETADATA segments. The first part of the
%   segment contains the header, namely the size of the XML and the size of
%   the Attachment. After that there is the xml field and the optional
%   attachment field
%
% AUTHOR: Stefano Masneri
% Date: 13.10.2016

% Get xml info
xmlSize = int32(fread(obj.cziPtr, 1, 'int32'));
attSize = int32(fread(obj.cziPtr, 1, 'int32'));  % currently unused
empty   = int32(fread(obj.cziPtr, 62, 'int32')); % spare space

% Read xml
xmlData = fread(obj.cziPtr, xmlSize, '*char')';

% Convert to struct
metadataStruct = xml2struct(xmlData);
obj.originalMetadata = metadataStruct;

% Now we have an incredibly nested structure. go through all the fields
% and try to extract all the metadata info from it
top = metadataStruct.ImageDocument.Metadata;

% The field "Version" is not of interest, it will be ignored for the
% moment

%% The field "Information" contains several important metadata

% Microscope Info
microscopeInfo = top.Information.Instrument;
try
    obj.microscopeName = microscopeInfo.Microscopes.Microscope.System.Text;
catch
    disp('Microscope name not available')
end

try
    obj.objectiveName = microscopeInfo.Objectives.Objective.Manufacturer.Model.Text;
catch
    disp('Objective name not available')
end

try
    obj.NA = str2double(microscopeInfo.Objectives.Objective.LensNA.Text);
catch
    disp('Numerical aperture info not available')
end

try
    obj.objectiveMagnification = str2double(microscopeInfo.Objectives.Objective.NominalMagnification.Text);
catch
    disp('Objective Magnification info not available')
end

try
    obj.objectiveNA = str2double(microscopeInfo.Objectives.Objective.LensNA.Text);
catch
    disp('Objective NA info not available')
end

try
    obj.refractiveMedium = microscopeInfo.Objectives.Objective.Immersion.Text;
    if strcmpi(obj.refractiveMedium, 'Air')
        obj.refractiveIndex = 1;
    elseif strcmpi(obj.refractiveMedium, 'W') || strcmpi(obj.refractiveMedium, 'Water')
        obj.refractiveIndex = 1.33;
    elseif strcmpi(obj.refractiveMedium, 'Oil')
        obj.refractiveIndex = 1.518;
    end
catch
    disp('Refraction media info not available')
end



%   try
%     % we don't know which light source is used... so we don't know the name
%     % of the structure either!
%     obj.wavelengthExc = [];
%     lightSrc = microscopeInfo.LightSources.LightSource;
%     for k = 1:length(lightSrc)
%     	lst = lightSrc{k}.LightSourceType;
%       fn = fieldnames(lst);
%       obj.wavelengthExc = [obj.wavelengthExc, str2double(lst.(fn{1}).Wavelength.Text)];
%     end
%   catch
%     disp('Excitation wavelength info not available')
%   end

try
    ChanInfo = top.Information.Image.Dimensions.Channels.Channel;
    numCh = length(ChanInfo);
    obj.wavelengthExc = cell(1, numCh);
    obj.wavelengthEm = cell(1, numCh);
    obj.zoom = cell(1, numCh);
    obj.gain = nan(1, numCh);
    obj.timePixel = nan(1, numCh);
    obj.timeLine = nan(1, numCh);
    obj.timeFrame = nan(1, numCh);
    obj.timeStack = nan(1, numCh);
    for k = 1:length(ChanInfo)
        if iscell(ChanInfo)
            currChan = ChanInfo{k};
        else
            currChan = ChanInfo;
        end
        
        try
            %obj.wavelengthExc{k} = str2double(currChan.ExcitationWavelength.Text);
            %get all active lasers for current channel
            numLightSources = numel(currChan.LightSourcesSettings.LightSourceSettings);
            waveLengthVector = zeros(1,numLightSources);
            for i = 1:numLightSources
                if numLightSources == 1
                    waveLengthVector(i) = str2double(currChan.LightSourcesSettings.LightSourceSettings.Wavelength.Text);
                else
                    waveLengthVector(i) = str2double(currChan.LightSourcesSettings.LightSourceSettings{i}.Wavelength.Text);
                end
            end
            obj.wavelengthExc{k} = waveLengthVector;
            clear waveLengthVector numLightSources i
            
        catch
            obj.wavelengthExc{k} = nan;
        end
        try
            %obj.wavelengthEm{k} = str2double(currChan.EmissionWavelength.Text);
            obj.wavelengthEm{k} = str2double(strsplit(currChan.DetectionWavelength.Ranges.Text,'-'));
        catch
            obj.wavelengthEm{k} = nan;
        end
        try
            obj.timePixel(k) = str2double(currChan.LaserScanInfo.PixelTime.Text);
        catch
        end
        try
            obj.timeLine(k) = str2double(currChan.LaserScanInfo.LineTime.Text);
        catch
        end
        try
            obj.timeFrame{k} = str2double(currChan.LaserScanInfo.FrameTime.Text);
        catch
        end
        try
            obj.timeStack{k} = str2double(currChan.LaserScanInfo.StackTime.Text);
        catch
        end
        try
            obj.zoom{k} = [str2double(currChan.LaserScanInfo.ZoomX.Text), ...
                str2double(currChan.LaserScanInfo.ZoomX.Text)];
        catch
            obj.zoom{k} = nan;
        end
        try
            obj.gain(k) = str2double(currChan.DetectorSettings.Gain.Text);
        catch
        end
    end
    if 1 == length(ChanInfo)
        obj.zoom = obj.zoom{1};
        obj.wavelengthEm = obj.wavelengthEm{1};
        obj.wavelengthExc = obj.wavelengthExc{1};
    end
catch
end

% Image info
imgInfo = top.Information.Image;
pixType = imgInfo.PixelType.Text;
switch pixType
    case 'Gray8'
        obj.datatype = 'uint8';
    case 'Gray16'
        obj.datatype = 'uint16';
    case 'Gray32Float'
        obj.datatype = 'double';
    case 'Bgr24'
        obj.datatype = 'uint8';
    case 'Bgr48'
        obj.datatype = 'uint16';
    case 'Bgr96Float'
        obj.datatype = 'float';
    case 'Bgra32'
        obj.datatype = 'uint8';
    otherwise
        % one of Gray64ComplexFloat or Bgr192ComplexFloat
        warning('CZIReader.readMetadataSegm: Pixel type not supported')
end
% now the dimensions
try
    obj.channels = str2double(imgInfo.SizeC.Text);
catch
    obj.channels = 1;
end
try
    obj.stacks = str2double(imgInfo.SizeZ.Text);
catch
    obj.stacks = 1;
end
try
    obj.series = str2double(imgInfo.SizeS.Text);
catch
    obj.series = 1;
end
try
    obj.time = str2double(imgInfo.SizeT.Text);
catch
    obj.time = 1;
end
try
    obj.tile = str2double(imgInfo.SizeM.Text);
catch
    obj.tile = 1;
end
obj.pixPerTileRow = str2double(imgInfo.SizeY.Text); % mandatory
obj.pixPerTileCol = str2double(imgInfo.SizeX.Text); % mandatory

%% The field "Experiment" contains information about the tiles
try
    tileInfo = top.Experiment.ExperimentBlocks.AcquisitionBlock.TilesSetup.PositionGroups.PositionGroup;
    if iscell(tileInfo)
        tileInfo = tileInfo{1};
    end
    obj.numTilesRow = str2double(tileInfo.TilesY.Text);
    obj.numTilesCol = str2double(tileInfo.TilesX.Text);
    if (obj.numTilesRow * obj.numTilesCol) ~= obj.tile
        obj.numTilesRow = 1;
        obj.numTilesCol = 1;
        warning('CZIReader.readRawMetadataSegm: inconsistent tile info, possible errors ahead!')
    end
    obj.tileOverlap = str2double(tileInfo.TileAcquisitionOverlap.Text);
    if obj.tileOverlap > 1
        obj.tileOverlap = obj.tileOverlap / 100;
    end
    obj.width = round((obj.numTilesCol - 1) * (1 - obj.tileOverlap) * obj.pixPerTileCol + ...
        obj.pixPerTileCol);
    obj.height = round((obj.numTilesRow - 1) * (1 - obj.tileOverlap) * obj.pixPerTileRow + ...
        obj.pixPerTileRow);
catch
    disp('CZIReader.readRawMetadataSegm: field Experiment not available')
    % assume single tile
    obj.height = obj.pixPerTileRow;
    obj.width = obj.pixPerTileCol;
    obj.numTilesRow = 1;
    obj.numTilesCol = 1;
    obj.tileOverlap = 0;
end

% Laser power
try
    mts = top.Experiment.ExperimentBlocks.AcquisitionBlock.MultiTrackSetup.TrackSetup;
    obj.laserPower = cell(1, length(mts));
    for k = 1:length(mts)
        try
            if iscell(mts)
                transmissions = mts{k}.Attenuators.Attenuator.Transmissions.Transmission;
            else
                transmissions = mts.Attenuators.Attenuator.Transmissions.Transmission;
            end
            if length(transmissions) == 1
                obj.laserPower{k} = [num2str(100*str2double(transmissions.Text)) '%'];
            else
                obj.laserPower{k} = [num2str(100*str2double(transmissions{1}.Text)) '% - ' ...
                    num2str(100*str2double(transmissions{end}.Text)) '%'];
            end
        catch % only one value of power
            if iscell(mts)
                transmission = mts{k}.Attenuators.Attenuator.Transmission;
            else
                transmission = mts.Attenuators.Attenuator.Transmission;
            end
            obj.laserPower{k} = [num2str(100*str2double(transmission.Text)) '%'];
        end
    end
    if length(obj.laserPower) == 1
        obj.laserPower = obj.laserPower{1};
    end
catch
    disp('Laser Power info not available');
end

% The field "DisplaySetting" has info related to the Channels
ch = top.DisplaySetting.Channels.Channel;
if isstruct(ch)
    ch = {ch};
end
for k = 1:length(ch) %check all channels
    obj.channelInfo = [obj.channelInfo, ChannelInfo(ch{k}, 'CZI')];
end


% The field "Scaling" contain info about the pixels physical size
scale = top.Scaling.Items.Distance;
obj.scaleSize = ones(1,3);
for k = 1:length(scale)
    switch scale{k}.Attributes.Id
        case 'X'
            obj.scaleSize(1) = str2double(scale{k}.Value.Text);
        case 'Y'
            obj.scaleSize(2) = str2double(scale{k}.Value.Text);
        case 'Z'
            obj.scaleSize(3) = str2double(scale{k}.Value.Text);
        otherwise
            warning('CZIReader.readRawMetadataSegm: unrecognized dimension for scale')
    end
    obj.scaleUnits = {'m', 'm', 'm'};
end
end