function [Output, ChannelFile, FileFormat] = import_channel(iStudies, ChannelFile, FileFormat, ChannelReplace, ChannelAlign, isSave, isFixUnits, isApplyVox2ras, RefMriFile)
% IMPORT_CHANNEL: Imports a channel file (definition of the sensors).
%
% USAGE:  BstChannelFile = import_channel(iStudies=none, ChannelFile=[ask], FileFormat, ChannelReplace=1, ChannelAlign=[ask], isSave=1, isFixUnits=[ask], isApplyVox2ras=[ask], RefMriFile=[])
%
% INPUT:
%    - iStudies       : Indices of the studies where to import the ChannelFile
%    - ChannelFile    : Full filename of the channels list to import (default: asked to the user)
%    - FileFormat     : Format of the input file ChannelFile
%    - ChannelReplace : 0, do not replace if channel file already exist
%                       1, replace old channel file after user confirmation  (default)
%                       2, replace old channel file without user confirmation
%    - ChannelAlign   : 0, do not perform automatic headpoints-based alignment
%                       1, perform automatic alignment after user confirmation  (default)
%                       2, perform automatic alignment without user confirmation
%    - isSave         : If 1, save the new channel file in the target study
%    - isFixUnits     : If 1, tries to convert the distance units to meters automatically
%                       If 0, does not fix the distance units
%                       If [], ask for the scaling to apply
%    - isApplyVox2ras : If 0, does not use the voxel=>subject transformation
%                       If 1, uses the existing voxel=>subject transformation from the MRI file, if available
%                       If 2, uses the existing voxel=>subject transformation AND the coregistration transformation (see process_import_bids)
%                       If [], ask for user decision
%    - RefMriFile     : Relative file name to a MRI file imported for the current subject
%                       When isApplyVox2ras=1: use this file instead of the reference MRI for getting the vox2ras transformation
%                       When isApplyVox2ras=2: uses this file AND reverts the registration matrix that was possibly
%                                              applied after the volume was imported (to matche the original .nii)

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2008-2023

%% ===== PARSE INPUTS =====
Output = [];
if (nargin < 9) || isempty(RefMriFile)
    RefMriFile = [];
end
if (nargin < 8) || isempty(isApplyVox2ras)
    isApplyVox2ras = [];
end
if (nargin < 7) || isempty(isFixUnits)
    isFixUnits = [];
end
if (nargin < 6) || isempty(isSave)
    isSave = 1;
end
if (nargin < 5) || isempty(ChannelAlign)
    ChannelAlign = [];
end
if (nargin < 4) || isempty(ChannelReplace)
    ChannelReplace = 1;
end
if (nargin < 3) || isempty(ChannelFile) || isempty(FileFormat)
    ChannelFile = [];
end
if (nargin < 1) || isempty(iStudies)
    iStudies = [];
end

%% ===== SELECT CHANNEL FILE =====
% If file to load was not defined : open a dialog box to select it
if isempty(ChannelFile)
    isInteractive = 1;
    % Get default import directory and formats
    LastUsedDirs = bst_get('LastUsedDirs');
    DefaultFormats = bst_get('DefaultFormats');
    % Get MRI file
    [ChannelFile, FileFormat] = java_getfile('open', ...
            'Import channel file...', ...              % Window title
            LastUsedDirs.ImportChannel, ...        % Last used directory
            'single', 'files_and_dirs', ...        % Selection mode
            bst_get('FileFilters', 'channel'), ... % File filters
            DefaultFormats.ChannelIn);             % Default ASCII XYZ
    % If no file was selected: exit
    if isempty(ChannelFile)
        return
    end
    % Save default import directory
    LastUsedDirs.ImportChannel = bst_fileparts(ChannelFile);
    bst_set('LastUsedDirs', LastUsedDirs);
    % Save default import format
    DefaultFormats.ChannelIn = FileFormat;
    bst_set('DefaultFormats',  DefaultFormats);
else
    isInteractive = 0;
end

%% ===== LOAD CHANNEL FILE =====
ChannelMat = [];
% Progress bar
isProgressBar = bst_progress('isVisible');
if ~isProgressBar
    bst_progress('start', 'Import channel file', ['Loading file "' ChannelFile '"...']);
end
% Get the file extenstion
[fPath, fBase, fExt] = bst_fileparts(ChannelFile);
if ~isempty(fExt)
    fExt = lower(fExt(2:end));
end
% Import options
ImportOptions = db_template('ImportOptions');
ImportOptions.EventsMode = 'ignore';
ImportOptions.DisplayMessages = 0;
% Load file
switch (FileFormat)
    % ===== MEG/EEG =====
    case 'CTF'
        ChannelMat = in_channel_ctf(ChannelFile);
        FileUnits = 'm';
    case {'FIF', '4D', 'KIT', 'BST-BIN', 'KDF', 'RICOH', 'ITAB', 'MEGSCAN-HDF5'}
        [sFile, ChannelMat] = in_fopen(ChannelFile, FileFormat, ImportOptions);
        if isempty(ChannelMat)
            bst_progress('stop');
            return;
        end
        FileUnits = 'm';
    case 'BST'
        ChannelMat = in_bst_channel(ChannelFile);
        FileUnits = 'm';

    % ===== EEG ONLY =====
    case {'BIDS-SCANRAS-MM', 'BIDS-MNI-MM', 'BIDS-ACPC-MM', 'BIDS-ALS-MM', 'BIDS-CAPTRAK-MM'}
        ChannelMat = in_channel_bids(ChannelFile, 0.001);
        FileUnits = 'mm';
    case {'BIDS-SCANRAS-CM', 'BIDS-MNI-CM', 'BIDS-ACPC-CM', 'BIDS-ALS-CM', 'BIDS-CAPTRAK-CM'}
        ChannelMat = in_channel_bids(ChannelFile, 0.01);
        FileUnits = 'cm';
    case {'BIDS-SCANRAS-M', 'BIDS-MNI-M', 'BIDS-ACPC-M', 'BIDS-ALS-M', 'BIDS-CAPTRAK-M'}
        ChannelMat = in_channel_bids(ChannelFile, 1);
        FileUnits = 'm';

    case 'BESA' % (*.sfp;*.elp;*.eps/*.ela)
        switch (fExt)
            case 'sfp'
                ChannelMat = in_channel_ascii(ChannelFile, {'Name','-Y','X','Z'}, 0, .01);
                ChannelMat.Comment = 'BESA channels';
            case 'elp'
                ChannelMat = in_channel_ascii(ChannelFile, {'Name','Y','X','Z'}, 0, .01);
                ChannelMat.Comment = 'BESA channels';
            case {'eps','ela'}
                ChannelMat = in_channel_besa_eps(ChannelFile);
        end
        FileUnits = 'cm';

    case 'BRAINVISION'
        ChannelMat = in_channel_brainvision(ChannelFile);
        FileUnits = 'm';

    case 'CARTOOL' % (*.els;*.xyz)
        switch (fExt)
            case 'els'
                ChannelMat = in_channel_cartool_els(ChannelFile);
                FileUnits = 'mm';
            case 'xyz'
                ChannelMat = in_channel_ascii(ChannelFile, {'-Y','X','Z','Name'}, 1, .001);
                ChannelMat.Comment = 'Cartool channels';
                FileUnits = 'cm';
        end

    case 'CURRY' % (*.res;*.rs3;*.pom)
        switch (fExt)
            case 'res'
                ChannelMat = in_channel_ascii(ChannelFile, {'%d','-Y','X','Z','%d','Name'}, 0, .001);
                ChannelMat.Comment = 'Curry channels';
            case 'rs3'
                ChannelMat = in_channel_curry_rs3(ChannelFile);
            case 'pom'
                ChannelMat = in_channel_curry_pom(ChannelFile);
        end
        FileUnits = 'mm';

    case 'EEGLAB' % (*.ced;*.xyz)
        switch (fExt)
            case 'ced'
                ChannelMat = in_channel_ascii(ChannelFile, {'indice','Name','%f','%f','X','Y','Z','%f','%f','%f'}, 1, .0875); % Convert normalized coord => average head radius
                ChannelMat.Comment = 'EEGLAB channels';
            case 'xyz'
                ChannelMat = in_channel_ascii(ChannelFile, {'indice','-Y','X','Z','Name'}, 0, .01);
                ChannelMat.Comment = 'EEGLAB channels';
            case 'set'
                ChannelMat = in_channel_eeglab_set(ChannelFile, isFixUnits);
        end
        FileUnits = 'cm';

    case 'EETRAK' % (*.elc)
        ChannelMat = in_channel_ascii(ChannelFile, {'X','Y','Z'}, 3, .001);
        ChannelMat.Comment = 'EETRAK channels';
        FileUnits = 'mm';

    case 'EGI'  % (*.sfp)
        ChannelMat = in_channel_ascii(ChannelFile, {'Name','-Y','X','Z'}, 0, .01);
        ChannelMat.Comment = 'EGI channels';
        FileUnits = 'cm';

    case 'EMSE'  % (*.elp)
        ChannelMat = in_channel_emse_elp(ChannelFile);
        FileUnits = 'm';

    case 'FREESURFER-TSV'
        % Read file
        ChannelMat = in_channel_bids(ChannelFile, 0.001);
        FileUnits = 'm';
        % If we know the destination study: convert from FreeSurfer/Surface coordinates to SCS coordinates
        if ~isempty(iStudies)
            % Get the subject for the first study
            sStudy = bst_get('Study', iStudies(1));
            sSubject = bst_get('Subject', sStudy.BrainStormSubject);
            % Get the subject's MRI
            if isempty(sSubject.Anatomy) || isempty(sSubject.Anatomy(1).FileName)
                error('You need to import the FreeSurfer anatomy before.');
            end
            % Load the MRI
            MriFile = file_fullpath(sSubject.Anatomy(1).FileName);
            sMri = in_mri_bst(MriFile);
            if isempty(sMri) || ~isfield(sMri, 'SCS') || ~isfield(sMri.SCS, 'NAS') || isempty(sMri.SCS.NAS)
                error('Missing fiducials.');
            end
            % Convert coordinates: FreeSurfer surface => MRI
            fcnTransf = @(Loc)bst_bsxfun(@plus, Loc', (size(sMri.Cube(:,:,:,1))/2 + [0 1 0]) .* sMri.Voxsize / 1000)';
            AllChannelMats = channel_apply_transf(ChannelMat, fcnTransf, [], 1);
            ChannelMat = AllChannelMats{1};
            % Convert coordinates: MRI => SCS
            fcnTransf = @(Loc)cs_convert(sMri, 'mri', 'scs', Loc')';
            AllChannelMats = channel_apply_transf(ChannelMat, fcnTransf, [], 1);
            ChannelMat = AllChannelMats{1};
        end

    case {'INTRANAT', 'INTRANAT_MNI'}
        switch (fExt)
            case 'pts'
                ChannelMat = in_channel_ascii(ChannelFile, {'name','X','Y','Z'}, 3, .001);
            case 'csv'
                if strcmpi(FileFormat, 'INTRANAT_MNI')
                    ChannelMat = in_channel_tsv(ChannelFile, 'contact', 'MNI', .001);
                else
                    ChannelMat = in_channel_tsv(ChannelFile, 'contact', 'T1pre Scanner Based', .001);
                end
        end
        ChannelMat.Comment = 'Contacts';
        FileUnits = 'mm';
        [ChannelMat.Channel.Type] = deal('SEEG');

    case 'MEGDRAW'
        ChannelMat = in_channel_megdraw(ChannelFile);
        FileUnits = 'cm';

    case 'LOCALITE'
        ChannelMat = in_channel_ascii(ChannelFile, {'%d','name','X','Y','Z'}, 1, .001);
        ChannelMat.Comment = 'Localite channels';
        FileUnits = 'mm';

    case 'MFF'  % (coordinates.xml)
        [tmp, ChannelMat] = in_fopen_mff(ChannelFile, ImportOptions, 1);
        FileUnits = 'mm';

    case 'NEUROSCAN'  % (*.dat;*.tri;*.txt;*.asc)
        switch (fExt)
            case {'dat', 'txt'}
                ChannelMat = in_channel_neuroscan_dat(ChannelFile);
                FileUnits = 'cm';
            case 'tri'
                ChannelMat = in_channel_neuroscan_tri(ChannelFile);
                FileUnits = 'cm';
            case 'asc'
                ChannelMat = in_channel_neuroscan_asc(ChannelFile);
                FileUnits = 'mm';
        end

    case 'POLHEMUS'  % (*.pos;*.elp)
        switch (fExt)
            case 'pos'
                ChannelMat = in_channel_pos(ChannelFile);
                FileUnits = 'cm';
            case {'pol','txt'}
                ChannelMat = in_channel_ascii(ChannelFile, {'name','X','Y','Z'}, 1, .01);
                ChannelMat.Comment = 'Polhemus';
                FileUnits = 'cm';
            case 'elp'
                ChannelMat = in_channel_emse_elp(ChannelFile);
                FileUnits = 'mm';
        end

    case 'SIMNIBS'
        ChannelMat = in_channel_ascii(ChannelFile, {'%s','X','Y','Z','name'}, 0, .001);
        ChannelMat.Comment = '10-10 electrodes';
        FileUnits = 'mm';

    case 'TVB'
        ChannelMat = in_channel_tvb(ChannelFile);
        FileUnits = 'm';

    case 'XENSOR' % ANT Xensor (*.elc)
        ChannelMat = in_channel_ant_xensor(ChannelFile);
        FileUnits = 'mm';

    case {'ASCII_XYZ', 'ASCII_XYZ_MNI', 'ASCII_XYZ_WORLD'}  % (*.*)
        ChannelMat = in_channel_ascii(ChannelFile, {'X','Y','Z'}, 0, .001);
        ChannelMat.Comment = 'Channels';
        FileUnits = 'mm';
    case {'ASCII_NXYZ', 'ASCII_NXYZ_MNI', 'ASCII_NXYZ_WORLD'}  % (*.*)
        ChannelMat = in_channel_ascii(ChannelFile, {'Name','X','Y','Z'}, 0, .001);
        ChannelMat.Comment = 'Channels';
        FileUnits = 'mm';
    case {'ASCII_XYZN', 'ASCII_XYZN_MNI', 'ASCII_XYZN_WORLD'}  % (*.*)
        ChannelMat = in_channel_ascii(ChannelFile, {'X','Y','Z','Name'}, 0, .001);
        ChannelMat.Comment = 'Channels';
        FileUnits = 'mm';
    case 'ASCII_NXY'  % (*.*)
        ChannelMat = in_channel_ascii(ChannelFile, {'Name','X','Y'}, 0, .000875);
        ChannelMat.Comment = 'Channels';
        FileUnits = 'mm';
    case 'ASCII_XY'  % (*.*)
        ChannelMat = in_channel_ascii(ChannelFile, {'X','Y'}, 0, .000875);
        ChannelMat.Comment = 'Channels';
        FileUnits = '';
    case 'ASCII_NTP'  % (*.*)
        ChannelMat = in_channel_ascii(ChannelFile, {'Name','TH','PHI'}, 0, .0875);
        ChannelMat.Comment = 'Channels';
        FileUnits = '';
    case 'ASCII_TP'  % (*.*)
        ChannelMat = in_channel_ascii(ChannelFile, {'TH','PHI'}, 0, .0875);
        ChannelMat.Comment = 'Channels';
        FileUnits = '';
    otherwise
        error(['File format is not supported: ' FileFormat]);
end
% No data imported
isHeadPoints = isfield(ChannelMat, 'HeadPoints') && ~isempty(ChannelMat.HeadPoints.Loc);
if isempty(ChannelMat) || ((~isfield(ChannelMat, 'Channel') || isempty(ChannelMat.Channel)) && ~isHeadPoints)
    disp('BST> Warning: No channel information was read from the file.');
    bst_progress('stop');
    return
end
% Are the SCS coordinates defined for this file?
isScsDefined = isfield(ChannelMat, 'SCS') && all(isfield(ChannelMat.SCS, {'NAS','LPA','RPA'})) && (length(ChannelMat.SCS.NAS) == 3) && (length(ChannelMat.SCS.LPA) == 3) && (length(ChannelMat.SCS.RPA) == 3);
% Use world coordinates by defaults for some specific file formats
if isempty(isApplyVox2ras)
    if ismember(FileFormat, {'ASCII_XYZ_WORLD', 'ASCII_NXYZ_WORLD', 'ASCII_XYZN_WORLD', 'SIMNIBS'})
        isApplyVox2ras = 1;   % Use the current vox2ras matrix in the MRI file
    elseif ismember(FileFormat, {'ASCII_XYZ', 'ASCII_NXYZ', 'ASCII_XYZN', 'BIDS-ALS-MM', 'BIDS-ALS-CM', 'BIDS-ALS-M'})
        isApplyVox2ras = 0;   % Disable vox2ras for ASCII formats that are explicitly in SCS
    elseif ismember(FileFormat, {'BIDS-SCANRAS-MM', 'BIDS-SCANRAS-CM', 'BIDS-SCANRAS-M'})
        isApplyVox2ras = 2;   % Use the vox2ras matrix AND reverts the registration done in Brainstorm, to match the original file
    end
end

%% ===== CHECK DISTANCE UNITS =====
if ~isempty(FileUnits) && ~isequal(isFixUnits, 0)
    if isempty(isFixUnits)
        isConfirmFix = 1;
    else
        isConfirmFix = 0;
    end
    ChannelMat = channel_fixunits(ChannelMat, FileUnits, isConfirmFix);
end

%% ===== MNI TRANSFORMATION =====
if ismember(FileFormat, {'ASCII_XYZ_MNI', 'ASCII_NXYZ_MNI', 'ASCII_XYZN_MNI', 'INTRANAT_MNI', 'BIDS-MNI-MM', 'BIDS-MNI-CM', 'BIDS-MNI-M'})
    % Warning for multiple studies
    if (length(iStudies) > 1)
        warning(['WARNING: When importing MNI positions for multiple subjects: the MNI transformation from the first subject is used for all of them.' 10 ...
                 'Please consider importing your subjects seprately.']);
    end
    % If we know the destination study: convert from MNI to SCS coordinates
    if ~isempty(iStudies)
        % Get the subject for the first study
        sStudy = bst_get('Study', iStudies(1));
        sSubject = bst_get('Subject', sStudy.BrainStormSubject);
        % Get the subject's MRI
        if isempty(sSubject.Anatomy) || isempty(sSubject.Anatomy(1).FileName)
            error('You need the subject anatomy in order to load sensor positions in MNI coordinates.');
        end
        % Load the MRI
        MriFile = file_fullpath(sSubject.Anatomy(1).FileName);
        sMri = in_mri_bst(MriFile);
        if ~isfield(sMri, 'SCS') || ~isfield(sMri.SCS, 'R') || isempty(sMri.SCS.R) || ~isfield(sMri, 'NCS') || ((~isfield(sMri.NCS, 'R') || isempty(sMri.NCS.R)) && (~isfield(sMri.NCS, 'y') || isempty(sMri.NCS.y)))
            error(['The SCS and MNI transformations must be defined for this subject' 10 'in order to load sensor positions in MNI coordinates.']);
        end
        % Convert all the coordinates: MNI => SCS
        fcnTransf = @(Loc)cs_convert(sMri, 'mni', 'scs', Loc')';
        AllChannelMats = channel_apply_transf(ChannelMat, fcnTransf, [], 1);
        ChannelMat = AllChannelMats{1};
    end
    % Do not convert the positions to SCS
    isAlignScs = 0;

%% ===== ACPC TRANSFORMATION =====
elseif ismember(FileFormat, {'BIDS-ACPC-MM', 'BIDS-ACPC-CM', 'BIDS-ACPC-M'})
    % Warning for multiple studies
    if (length(iStudies) > 1)
        warning(['WARNING: When importing ACPC positions for multiple subjects: the ACPC transformation from the first subject is used for all of them.' 10 ...
                 'Please consider importing your subjects seprately.']);
    end
    % If we know the destination study: convert from ACPC to SCS coordinates
    if ~isempty(iStudies)
        % Get the subject for the first study
        sStudy = bst_get('Study', iStudies(1));
        sSubject = bst_get('Subject', sStudy.BrainStormSubject);
        % Get the subject's MRI
        if isempty(sSubject.Anatomy) || isempty(sSubject.Anatomy(1).FileName)
            error('You need the subject anatomy in order to load sensor positions in ACPC coordinates.');
        end
        % Load the MRI
        MriFile = file_fullpath(sSubject.Anatomy(1).FileName);
        sMri = in_mri_bst(MriFile);
        if ~isfield(sMri, 'SCS') || ~isfield(sMri.SCS, 'R') || isempty(sMri.SCS.R) || ~isfield(sMri.NCS, 'AC') || isempty(sMri.NCS.AC) || ~isfield(sMri.NCS, 'PC') || isempty(sMri.NCS.PC) || ~isfield(sMri.NCS, 'IH') || isempty(sMri.NCS.IH)
            error(['All fiducials must be defined for this subject (NAS,LPA,RPA,AC,PC,IH)' 10 'in order to load sensor positions in ACPC coordinates.']);
        end
        % Convert all the coordinates: ACPC => SCS
        fcnTransf = @(Loc)cs_convert(sMri, 'acpc', 'scs', Loc')';
        AllChannelMats = channel_apply_transf(ChannelMat, fcnTransf, [], 1);
        ChannelMat = AllChannelMats{1};
    end
    % Do not convert the positions to SCS
    isAlignScs = 0;

%% ===== CAPTRAK TRANSFORMATION =====
elseif ismember(FileFormat, {'BIDS-CAPTRAK-MM', 'BIDS-CAPTRAK-CM', 'BIDS-CAPTRAK-M'})
    % Warning for multiple studies
    if (length(iStudies) > 1)
        warning(['WARNING: When importing CapTrak positions for multiple subjects: the CapTrak transformation from the first subject is used for all of them.' 10 ...
                 'Please consider importing your subjects seprately.']);
    end
    % If we know the destination study: convert from CapTrak to SCS coordinates
    if ~isempty(iStudies)
        % Get the subject for the first study
        sStudy = bst_get('Study', iStudies(1));
        sSubject = bst_get('Subject', sStudy.BrainStormSubject);
        % Get the subject's MRI
        if isempty(sSubject.Anatomy) || isempty(sSubject.Anatomy(1).FileName)
            error('You need the subject anatomy in order to load sensor positions in CapTrak coordinates.');
        end
        % Load the MRI
        MriFile = file_fullpath(sSubject.Anatomy(1).FileName);
        sMri = in_mri_bst(MriFile);
        if ~isfield(sMri, 'SCS') || ~isfield(sMri.SCS, 'R') || isempty(sMri.SCS.R) || ~isfield(sMri.SCS, 'NAS') || isempty(sMri.SCS.NAS) || ~isfield(sMri.SCS, 'LPA') || isempty(sMri.SCS.LPA) || ~isfield(sMri.SCS, 'RPA') || isempty(sMri.SCS.RPA)
            error(['All fiducials must be defined for this subject (NAS,LPA,RPA)' 10 'in order to load sensor positions in CapTrak coordinates.']);
        end
        % Convert all the coordinates: CapTrak => SCS
        fcnTransf = @(Loc)cs_convert(sMri, 'captrak', 'scs', Loc')';
        AllChannelMats = channel_apply_transf(ChannelMat, fcnTransf, [], 1);
        ChannelMat = AllChannelMats{1};
    end
    % Do not convert the positions to SCS
    isAlignScs = 0;

%% ===== MRI/NII TRANSFORMATION =====
% If the SCS coordinates are not defined (NAS/LPA/RPA fiducials), try to use the MRI=>subject transformation available in the MRI (eg. NIfTI sform/qform)
% Only available if there is one study in output
elseif ~isScsDefined && ~isequal(isApplyVox2ras, 0) && ~isempty(iStudies)
    % Get the folders
    sStudies = bst_get('Study', iStudies);
    if (length(sStudies) > 1) && ~all(strcmpi(sStudies(1).BrainStormSubject, {sStudies.BrainStormSubject}))
        warning(['WARNING: When importing sensor positions for multiple subjects: the SCS transformation from the first subject is used for all of them.' 10 ...
                 'Please consider importing your subjects seprately.']);
    end
    % Get subject for first file only
    sSubject = bst_get('Subject', sStudies(1).BrainStormSubject);

    % SIMNIBS: Disable auto-alignment based on fiducials
    if strcmpi(FileFormat, 'SIMNIBS')
        isAlignScs = 0;
    else
        isAlignScs = 1;
    end
    % If there is a MRI for this subject
    if ~isempty(sSubject.Anatomy) && ~isempty(sSubject.Anatomy(1).FileName)
        % Get the reference MRI (specified in input, or selected in the database)
        if isempty(RefMriFile) || ~file_exist(file_fullpath(RefMriFile))
            % If there are multiple MRIs
            if (length(sSubject.Anatomy) > 1)
                % Consider only the volumes that are not volume atlases
                iNoAtlas = find(cellfun(@(c)isempty(strfind(c, '_volatlas')), {sSubject.Anatomy.FileName}));
                if (length(iNoAtlas) == 1)
                    iMri = iNoAtlas;
                % Interactive: Ask which MRI volume to use
                elseif isInteractive
                    mriComment = java_dialog('combo', '<HTML>Select the reference MRI:<BR><BR>', 'Import as MRI scanner coordinates', [], {sSubject.Anatomy(iNoAtlas).Comment});
                    if isempty(mriComment)
                        bst_progress('stop');
                        return
                    end
                    iMri = iNoAtlas(find(strcmp({sSubject.Anatomy(iNoAtlas).Comment}, mriComment), 1));
                % Non-interactive: Use the default MRI
                else
                    iMri = sSubject.iAnatomy;
                end
            else
                iMri = 1;
            end
            RefMriFile = file_fullpath(sSubject.Anatomy(iMri).FileName);
        end
        % Load the reference MRI (which contains the vox2mri transformation that should be used to interpret the coordinates)
        sMri = load(file_fullpath(RefMriFile), 'InitTransf', 'SCS', 'Voxsize');
        % If there is a valid transformation
        if isfield(sMri, 'InitTransf') && ~isempty(sMri.InitTransf) && ismember('vox2ras', sMri.InitTransf(:,1))
            % Ask user if necessary
            if isempty(isApplyVox2ras)
                isApplyVox2ras = java_dialog('confirm', [...
                    'There is a transformation to subject coordinates available in the MRI.' 10 ...
                    'Would you like to use it to align the sensors with the MRI?' 10 10 ...
                    'Answer NO to use the NAS/LPA/RPA fiducials from the input file.' 10], 'Apply MRI transformation');
            end
            % Apply transformation
            if isApplyVox2ras
                % Get the transformation WORLD=>MRI (in meters)
                Transf = cs_convert(sMri, 'world', 'mri');
                % Applies the coregistration transformation to the sensor positions, if requested
                if (isApplyVox2ras == 2)
                    iTransfReg = find(strcmpi(sMri.InitTransf(:,1), 'reg'), 1);
                    if ~isempty(iTransfReg)
                        Transf = Transf * sMri.InitTransf{iTransfReg,2};
                    end
                end
                % Add the transformation MRI=>SCS
                if isfield(sMri,'SCS') && isfield(sMri.SCS,'R') && ~isempty(sMri.SCS.R) && isfield(sMri.SCS,'T') && ~isempty(sMri.SCS.T)
                    Transf = [sMri.SCS.R, sMri.SCS.T./1000; 0 0 0 1] * Transf;
                else
                    error(['The SCS coordinates are not defined for this subject, the sensors will not be aligned on the anatomy. ' 10 'Consider defining the NAS/LPA/RPA fiducials before importing the sensors positions.']);
                end
                % Convert all the coordinates
                AllChannelMats = channel_apply_transf(ChannelMat, Transf, [], 1);
                ChannelMat = AllChannelMats{1};
                % Disable alignment based on SCS fiducials
                isAlignScs = 0;
            end
        end
    end

elseif isfield(ChannelMat, 'TransfMegLabels') && iscell(ChannelMat.TransfMegLabels) && ismember('Native=>Brainstorm/CTF', ChannelMat.TransfMegLabels)
    % No need to duplicate this transformation if it was previously
    % computed, e.g. in in_channel_ctf. (It would be identity the second
    % time.)
    isAlignScs = 0;
else
    isAlignScs = 1;
end

%% ===== DETECT CHANNEL TYPES =====
% Remove fiducials (expect for BIDS files)
isRemoveFid = isempty(strfind(FileFormat, 'BIDS-'));
% Detect auxiliary EEG channels + align channel
ChannelMat = channel_detect_type(ChannelMat, isAlignScs, isRemoveFid);

%% ===== APPLY NEW CHANNEL FILE =====
% If some studies were defined
if isSave && ~isempty(iStudies)
    if isempty(ChannelAlign)
        iMEG = good_channel(ChannelMat.Channel, [], 'MEG');
        ChannelAlign = ~isempty(iMEG);
    end
    % History: Import channel file
    ChannelMat = bst_history('add', ChannelMat, 'import', ['Import from: ' ChannelFile ' (Format: ' FileFormat ')']);
    % Add channel file to all the target studies
    for i = 1:length(iStudies)
        ChannelFile = db_set_channel(iStudies(i), ChannelMat, ChannelReplace, ChannelAlign);
    end
    % Returned value
    Output = ChannelFile;
else
    Output = ChannelMat;
end

% Progress bar
if ~isProgressBar
    bst_progress('stop');
end


function [data] = spm2fieldtrip(D)

% SPM2FIELDTRIP converts an SPM8 meeg object into a FieldTrip raw data structure
%
% Use as
%   data = spm2fieldtrip(D)
% where D is the SPM meeg object which you can load in with SPM_EEG_LOAD
% and where data is a FieldTrip raw data structure as if it were returned
% by FT_PREPROCESSING.
%
% See also FT_PREPROCESSING, SPM_EEG_LOAD

% Copyright (C) 2011, Donders Centre for Cognitive Neuroimaging
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

if ~ft_hastoolbox('spm8up')
  % it should be version spm8 or higher, since spm99, spm2 and spm5 did not yet the "meeg" object
  ft_error('this requires SPM8 or later to be on your MATLAB path');
end

if ~isa(D, 'meeg')
  ft_error('this requires an SPM "meeg" object as input')
end

% this is how SPM8 represents it
data = D.ftraw(0);

clist      = D.condlist;
conditions = D.conditions;

data.trialinfo = zeros(D.ntrials,1);
for k = 1:numel(clist)
  fprintf('mapping condition label "%s" to condition code %d\n', clist{k}, k);
  sel=strcmp(clist{k}, conditions);
  data.trialinfo(sel) = k;
end

% FIXME the following is not correct
%
% data.sampleinfo = zeros(ntrials,2);
% for i=1:ntrials
%   data.sampleinfo(i,1) = D.indsample(i);
%   data.sampleinfo(i,2) = D.indsample(i) + D.nsamples;
% end


function h = mrViewer(mr, format, varargin)
% mrVista general-purpose MRI data viewer.
%
%   h = mrViewer([data file or mr struct], [format], [options]);
%
% This tool allows you to visualize various forms of 2D,  3D,  or
% 4D MRI data,  including structural and functional data.
%
% Returns a handle to the viewer UI. Info about the UI,  including
% the base MR object and any maps,  are contained in this object's
% UserData. To get the viewer struct alone,  without initializing a
% GUI,  use mrViewInit.
%
% Optional additional arguments:
%   'session', [val]:   session struct from which the data are taken,
%                       or handle to a session viewer. Enables saving
%                       ROIs drawn using the viewer to the session.
%
%   'tSeries', [val]:   paths to time series file/files (string or cell
%                       of strings),  to associate with the data.
%
%   'stim', [val]:      stimulus files (.par files) to load and associate
%                       with the tSeries. Enables plotting time courses of
%                       ROIs drawn with the viewer.
%
%   'dock':             always dock all uipanels to a single figure,
%                       rather than the default of putting some panels
%                       in their own figures.
%
%
% ras,  07/05.

% Code notes:
% this m-file opens the mrViewer figure. Other files refresh
% or perform operations on it.

if notDefined('format'),  format = ''; end

% if MRI data not specified,  prompt via mrLoad:
if notDefined('mr'),   mr = mrLoad([],  format);
elseif ischar(mr),     mr = mrLoad(mr,  format);
end

if ~ispref('VISTA', 'dockFlag'), setpref('VISTA', 'dockFlag', 1); end
dockFlag = getpref('VISTA', 'dockFlag');

% initialize the UI struct
ui = mrViewInit(mr);
clear mr;

% parse options here
for i = 1:length(varargin)
    switch lower(varargin{i})
        case 'dock',  dockFlag = 1;
        case 'undock',  dockFlag = 0;
        case 'session',  ui.session = varargin{i+1};
        case 'tseries',  ui = mrViewAttachTSeries(ui,  varargin{i+1});
        case 'stim',  ui = mrViewAttachStim(ui,  varargin{i+1});
    end
end

% open the figure
ui.fig = figure('Units', 'Normalized', ...
    'Position', [.12 .23 .4 .4], ...
    'UserData', ui, ...
    'Color', 'k', ...
    'NumberTitle', 'off', ...
    'MenuBar', 'none', ...
    'NextPlot', 'add', ...
    'Name', sprintf('%s [%s]', ui.mr.name, ui.tag), ...
    'CloseRequestFcn', sprintf('mrViewClose(''%s'');', ui.tag), ...
    'Tag', ui.tag);

% add main panel for axes
ui.panels.display = uipanel('Position', [0 0 1 1], ...
                           'BackgroundColor', [0 0 0], ...
                           'ShadowColor', [0 0 0], ...
                           'BorderType', 'none');       % [0 0 .4]

% add control panels
ui = mrViewNavPanel(ui, dockFlag);
ui = mrViewGrayscalePanel(ui, dockFlag);
ui = mrViewROIPanel(ui, 1); % dockFlag
ui = mrViewInfoPanel(ui, 1);
ui = mrViewMeshPanel(ui, 1);
ui = mrViewColorbarPanel(ui, 1);

% add menus
ui = mrViewFileMenu(ui);
ui = mrViewEditMenu(ui);
ui = mrViewViewMenu(ui);
ui = mrViewPlotMenu(ui);
% uimenu(ui.fig, 'Label', 'Window', 'Callback', winmenu('callback'));
ui = mrViewSpaceMenu(ui);
ui = mrViewMeshMenu(ui);
uimenu('Label', '     '); % spacer
ui = mrViewHelpMenu(ui);

% set the initial coordinate space to pixel space
ui = mrViewSet(ui, 'space', ui.settings.space);

% % guess a good initial clip value for the anatomies
% [binCnt binCenters] = hist(ui.mr.data(:), 100);
% clipvals = binCenters( mrvMinmax(find(binCnt>histThresh) ));
% ui = mrViewSetGrayscale(ui, 'clip', clipvals);

% refresh view
ui = mrViewRefresh(ui);

h = ui.fig;

return


%kExpAny 'Eports khoros files into many different format '
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros ExpAny.pane file
%
% Parameters:
% InputFile: i 'Input File', required: 'First Input data object'
% OutputFile: o 'Output File', required: 'Resulting output data object'
% Toggle: color 'RGB-Color ', default: 0: 'generate a color-image'
% Toggle: nonormalize 'Do not normalize', default: 0: 'if selected, no normalization will be performed. Range is usually 0..255'
%
% Example: o = kExpAny(i, {'i','';'o','';'color',0;'nonormalize',0})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% ExpAny - Eports khoros files into many different format
%
%  DESCRIPTION
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
%
%  RESTRICTIONS
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1996-2003, Rainer Heintzmann,  All rights reserved.
%

function varargout = kExpAny(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kExpAny(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'o', '__output';'color', 0;'nonormalize', 0};
maxval={0,0,0,0};
minval={0,0,0,0};
istoggle=[0,0,1,1];
was_set=istoggle * 0;
paramtype={'InputFile','OutputFile','Toggle','Toggle'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'expany"  '],Inputs,narglist);


%  internal function

%  - Jimmy Shen (jimmy@rotman-baycrest.on.ca)

function [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB)

   if ~exist('hdr','var') | ~exist('filetype','var') | ~exist('fileprefix','var') | ~exist('machine','var')
      error('Usage: [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx],[old_RGB]);');
   end

   if ~exist('img_idx','var') | isempty(img_idx) | hdr.dime.dim(5)<1
      img_idx = [];
   end

   if ~exist('dim5_idx','var') | isempty(dim5_idx) | hdr.dime.dim(6)<1
      dim5_idx = [];
   end

   if ~exist('dim6_idx','var') | isempty(dim6_idx) | hdr.dime.dim(7)<1
      dim6_idx = [];
   end

   if ~exist('dim7_idx','var') | isempty(dim7_idx) | hdr.dime.dim(8)<1
      dim7_idx = [];
   end

   if ~exist('old_RGB','var') | isempty(old_RGB)
      old_RGB = 0;
   end

   %  check img_idx
   %
   if ~isempty(img_idx) & ~isnumeric(img_idx)
      error('"img_idx" should be a numerical array.');
   end

   if length(unique(img_idx)) ~= length(img_idx)
      error('Duplicate image index in "img_idx"');
   end

   if ~isempty(img_idx) & (min(img_idx) < 1 | max(img_idx) > hdr.dime.dim(5))
      max_range = hdr.dime.dim(5);

      if max_range == 1
         error(['"img_idx" should be 1.']);
      else
         range = ['1 ' num2str(max_range)];
         error(['"img_idx" should be an integer within the range of [' range '].']);
      end
   end

   %  check dim5_idx
   %
   if ~isempty(dim5_idx) & ~isnumeric(dim5_idx)
      error('"dim5_idx" should be a numerical array.');
   end

   if length(unique(dim5_idx)) ~= length(dim5_idx)
      error('Duplicate index in "dim5_idx"');
   end

   if ~isempty(dim5_idx) & (min(dim5_idx) < 1 | max(dim5_idx) > hdr.dime.dim(6))
      max_range = hdr.dime.dim(6);

      if max_range == 1
         error(['"dim5_idx" should be 1.']);
      else
         range = ['1 ' num2str(max_range)];
         error(['"dim5_idx" should be an integer within the range of [' range '].']);
      end
   end

   %  check dim6_idx
   %
   if ~isempty(dim6_idx) & ~isnumeric(dim6_idx)
      error('"dim6_idx" should be a numerical array.');
   end

   if length(unique(dim6_idx)) ~= length(dim6_idx)
      error('Duplicate index in "dim6_idx"');
   end

   if ~isempty(dim6_idx) & (min(dim6_idx) < 1 | max(dim6_idx) > hdr.dime.dim(7))
      max_range = hdr.dime.dim(7);

      if max_range == 1
         error(['"dim6_idx" should be 1.']);
      else
         range = ['1 ' num2str(max_range)];
         error(['"dim6_idx" should be an integer within the range of [' range '].']);
      end
   end

   %  check dim7_idx
   %
   if ~isempty(dim7_idx) & ~isnumeric(dim7_idx)
      error('"dim7_idx" should be a numerical array.');
   end

   if length(unique(dim7_idx)) ~= length(dim7_idx)
      error('Duplicate index in "dim7_idx"');
   end

   if ~isempty(dim7_idx) & (min(dim7_idx) < 1 | max(dim7_idx) > hdr.dime.dim(8))
      max_range = hdr.dime.dim(8);

      if max_range == 1
         error(['"dim7_idx" should be 1.']);
      else
         range = ['1 ' num2str(max_range)];
         error(['"dim7_idx" should be an integer within the range of [' range '].']);
      end
   end

   [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB);

   return					% load_nii_img

%---------------------------------------------------------------------
function [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB)

   switch filetype
   case {0, 1}
      fn = [fileprefix '.img'];
   case 2
      fn = [fileprefix '.nii'];
   end

   fid = fopen(fn,'r',machine);

   if fid < 0,
      msg = sprintf('Cannot open file %s.',fn);
      error(msg);
   end

   %  Set bitpix according to datatype
   %
   %  /*Acceptable values for datatype are*/
   %
   %     0 None                     (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN
   %     1 Binary                         (ubit1, bitpix=1) % DT_BINARY
   %     2 Unsigned char         (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8
   %     4 Signed short                  (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16
   %     8 Signed integer                (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32
   %    16 Floating point    (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32
   %    32 Complex, 2 float32      (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64
   %    64 Double precision  (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64
   %   128 uint8 RGB                 (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24
   %   256 Signed char            (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8
   %   511 Single RGB              (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96
   %   512 Unsigned short               (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16
   %   768 Unsigned integer             (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32
   %  1024 Signed long long              (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64
   %  1280 Unsigned long long           (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64
   %  1536 Long double, float128  (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128
   %  1792 Complex128, 2 float64  (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128
   %  2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128
   %
   switch hdr.dime.datatype
   case   1,
      hdr.dime.bitpix = 1;  precision = 'ubit1';
   case   2,
      hdr.dime.bitpix = 8;  precision = 'uint8';
   case   4,
      hdr.dime.bitpix = 16; precision = 'int16';
   case   8,
      hdr.dime.bitpix = 32; precision = 'int32';
   case  16,
      hdr.dime.bitpix = 32; precision = 'float32';
   case  32,
      hdr.dime.bitpix = 64; precision = 'float32';
   case  64,
      hdr.dime.bitpix = 64; precision = 'float64';
   case 128,
      hdr.dime.bitpix = 24; precision = 'uint8';
   case 256
      hdr.dime.bitpix = 8;  precision = 'int8';
   case 511
      hdr.dime.bitpix = 96; precision = 'float32';
   case 512
      hdr.dime.bitpix = 16; precision = 'uint16';
   case 768
      hdr.dime.bitpix = 32; precision = 'uint32';
   case 1024
      hdr.dime.bitpix = 64; precision = 'int64';
   case 1280
      hdr.dime.bitpix = 64; precision = 'uint64';
   case 1792,
      hdr.dime.bitpix = 128; precision = 'float64';
   otherwise
      error('This datatype is not supported');
   end

   hdr.dime.dim(find(hdr.dime.dim < 1)) = 1;

   %  move pointer to the start of image block
   %
   switch filetype
   case {0, 1}
      fseek(fid, 0, 'bof');
   case 2
      fseek(fid, hdr.dime.vox_offset, 'bof');
   end

   %  Load whole image block for old Analyze format or binary image;
   %  otherwise, load images that are specified in img_idx, dim5_idx,
   %  dim6_idx, and dim7_idx
   %
   %  For binary image, we have to read all because pos can not be
   %  seeked in bit and can not be calculated the way below.
   %
   if hdr.dime.datatype == 1 | isequal(hdr.dime.dim(5:8),ones(1,4)) | ...
	(isempty(img_idx) & isempty(dim5_idx) & isempty(dim6_idx) & isempty(dim7_idx))

      %  For each frame, precision of value will be read
      %  in img_siz times, where img_siz is only the
      %  dimension size of an image, not the byte storage
      %  size of an image.
      %
      img_siz = prod(hdr.dime.dim(2:8));

      %  For complex float32 or complex float64, voxel values
      %  include [real, imag]
      %
      if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792
         img_siz = img_siz * 2;
      end

      %MPH: For RGB24, voxel values include 3 separate color planes
      %
      if hdr.dime.datatype == 128 | hdr.dime.datatype == 511
	 img_siz = img_siz * 3;
      end

      img = fread(fid, img_siz, sprintf('*%s',precision));

      d1 = hdr.dime.dim(2);
      d2 = hdr.dime.dim(3);
      d3 = hdr.dime.dim(4);
      d4 = hdr.dime.dim(5);
      d5 = hdr.dime.dim(6);
      d6 = hdr.dime.dim(7);
      d7 = hdr.dime.dim(8);

      if isempty(img_idx)
         img_idx = 1:d4;
      end

      if isempty(dim5_idx)
         dim5_idx = 1:d5;
      end

      if isempty(dim6_idx)
         dim6_idx = 1:d6;
      end

      if isempty(dim7_idx)
         dim7_idx = 1:d7;
      end
   else

      d1 = hdr.dime.dim(2);
      d2 = hdr.dime.dim(3);
      d3 = hdr.dime.dim(4);
      d4 = hdr.dime.dim(5);
      d5 = hdr.dime.dim(6);
      d6 = hdr.dime.dim(7);
      d7 = hdr.dime.dim(8);

      if isempty(img_idx)
         img_idx = 1:d4;
      end

      if isempty(dim5_idx)
         dim5_idx = 1:d5;
      end

      if isempty(dim6_idx)
         dim6_idx = 1:d6;
      end

      if isempty(dim7_idx)
         dim7_idx = 1:d7;
      end

      %  compute size of one image
      %
      img_siz = prod(hdr.dime.dim(2:4));

      %  For complex float32 or complex float64, voxel values
      %  include [real, imag]
      %
      if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792
         img_siz = img_siz * 2;
      end

      %MPH: For RGB24, voxel values include 3 separate color planes
      %
      if hdr.dime.datatype == 128 | hdr.dime.datatype == 511
         img_siz = img_siz * 3;
      end

      % preallocate img
      img = zeros(img_siz, length(img_idx)*length(dim5_idx)*length(dim6_idx)*length(dim7_idx) );
      currentIndex = 1;

      for i7=1:length(dim7_idx)
         for i6=1:length(dim6_idx)
            for i5=1:length(dim5_idx)
               for t=1:length(img_idx)

                  %  Position is seeked in bytes. To convert dimension size
                  %  to byte storage size, hdr.dime.bitpix/8 will be
                  %  applied.
                  %
                  pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, 1, ...
			img_idx(t), dim5_idx(i5),dim6_idx(i6),dim7_idx(i7)) -1;
                  pos = pos * hdr.dime.bitpix/8;

                  if filetype == 2
                     fseek(fid, pos + hdr.dime.vox_offset, 'bof');
                  else
                     fseek(fid, pos, 'bof');
                  end

                  %  For each frame, fread will read precision of value
                  %  in img_siz times
                  %
                  img(:,currentIndex) = fread(fid, img_siz, sprintf('*%s',precision));
                  currentIndex = currentIndex +1;

               end
            end
         end
      end
   end

   %  For complex float32 or complex float64, voxel values
   %  include [real, imag]
   %
   if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792
      img = reshape(img, [2, length(img)/2]);
      img = complex(img(1,:)', img(2,:)');
   end

   fclose(fid);

   %  Update the global min and max values
   %
   hdr.dime.glmax = double(max(img(:)));
   hdr.dime.glmin = double(min(img(:)));

   %  old_RGB treat RGB slice by slice, now it is treated voxel by voxel
   %
   if old_RGB & hdr.dime.datatype == 128 & hdr.dime.bitpix == 24
      % remove squeeze
      img = (reshape(img, [hdr.dime.dim(2:3) 3 hdr.dime.dim(4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)]));
      img = permute(img, [1 2 4 3 5 6 7 8]);
   elseif hdr.dime.datatype == 128 & hdr.dime.bitpix == 24
      % remove squeeze
      img = (reshape(img, [3 hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)]));
      img = permute(img, [2 3 4 1 5 6 7 8]);
   elseif hdr.dime.datatype == 511 & hdr.dime.bitpix == 96
      img = double(img(:));
      img = single((img - min(img))/(max(img) - min(img)));
      % remove squeeze
      img = (reshape(img, [3 hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)]));
      img = permute(img, [2 3 4 1 5 6 7 8]);
   else
      % remove squeeze
      img = (reshape(img, [hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)]));
   end

   if ~isempty(img_idx)
      hdr.dime.dim(5) = length(img_idx);
   end

   if ~isempty(dim5_idx)
      hdr.dime.dim(6) = length(dim5_idx);
   end

   if ~isempty(dim6_idx)
      hdr.dime.dim(7) = length(dim6_idx);
   end

   if ~isempty(dim7_idx)
      hdr.dime.dim(8) = length(dim7_idx);
   end

   return						% read_image


classdef ProductComputer < handle

    properties (Access = protected)
        secondOrder
        fourthOrder
        secondOrderOut
    end

    methods (Access = public, Static)

        function s = compute(C,e)
            factory = ProductComputerFactory();
            productComputer = factory.create(C,e);
            s = productComputer.getOutputSecondOrder();
        end

    end

    methods (Access = protected)

        function generate(obj,C,e)
            obj.init(C,e)
            obj.createOutSecondOrder()
            obj.computeProduct()
        end

    end

    methods (Access = private)

        function init(obj,C,e)
            obj.fourthOrder = C;
            obj.secondOrder = e;
        end

        function createOutSecondOrder(obj)
            C = obj.fourthOrder;
            e = obj.secondOrder;
            outCreator = ProductComputerOutputCreator(C,e);
            obj.secondOrderOut = outCreator.getSecondOrderOut;
        end

        function s = getOutputSecondOrder(obj)
            s = obj.secondOrderOut;
        end
    end

    methods (Access = protected, Abstract)
        computeProduct(obj)
    end

end


function out = void(f, out)

% out = void(f, out)
%
% This is a helper function to enable more complex anonymous functions.
%
% While some functions don't return anything, it can be useful to return
% something after calling them. This function calls the input function and
% then returns either 1 (the default) or the second input, which can be any
% value of the user's choosing. Consider the following anonymous function:
%
% plot_it = @() {figure(1), ...
%                clf(), ...
%                plot(randn(1, 50), '.'), ...
%                axis('off')};
%
% plot_it() % This throws an error.
%
% Clearly, this should open a figure, clear it, plot some points, and turn
% off the axis. However, the axis('off') command doesn't return anything,
% and something is necessary for that position of the cell array, so MATLAB
% throws an error. However, if we just pass the function to the "void"
% command, as below, it works just fine, returning a 1 that we simply don't
% use but that fills the cell array as MATLAB requires.
%
% plot_it = @() {figure(1), ...
%                clf(), ...
%                plot(randn(1, 50), '.'), ...
%                void(@() axis('off'))};
%
% plot_it() % This works.
%
% Tucker McClure
% Copyright 2013 The MathWorks, Inc.

    if nargin == 1, out = 1; end
    f();

end


function n = r8vec_read_size ( input_file )

%*****************************************************************************80
%
%% R8VEC_READ_SIZE reads the size of an R8VEC from a file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 August 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILE, the name of the file to be read.
%
%    Output, integer N, the size of the R8VEC.
%
  n = 0

  input_unit = fopen ( input_file );

  if ( input_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8VEC_READ_SIZE - Error!\n' );
    fprintf ( 1, '  Could not open the file.\n' );
    error ( 'R8VEC_READ_SIZE - Error!' );
    return;
  end

  while ( 1 )

    line = fgets ( input_unit );

    if ( line == -1 )
      break;
    end

    n = n + 1;

  end

  fclose ( input_unit );

  return
end


function Y = modelSamp(model, X)

% MODELSAMP Give a sample from a model for given X.

% MLTOOLS

fhandle = str2func([model.type 'Samp']);
Y = fhandle(model, X);


function include_params = include_codes(segm_params, filepath_params)
%INCLUDE_CODES is used for putting codes in path according to the parameter
% settings
%
% @authors:     Ahmad Humayun
% @contact:     ahumayun@cc.gatech.edu
% @affiliation: Georgia Institute of Technology
% @date:        Fall 2013 - Summer 2014

    extern_dir = filepath_params.extern_src_dir;
    code_root_dir = filepath_params.code_root_dir;
    extern_codes_dir = filepath_params.extern_codes_dir;
    utils_dir = filepath_params.utils_src_dir;

    include_params.vlfeat_dir = fullfile(extern_dir, 'toolboxes', ...
                                         'vlfeat');
    addpath(fullfile(include_params.vlfeat_dir, 'toolbox'));
%    vl_setup;

    include_params.pb_dir = fullfile(extern_dir, ...
                                     'segmentation', 'segbench');

    % includes from piotr's toolbox for sketchtokens
    piotr_toolbox_dir = fullfile(extern_dir, 'toolboxes', ...
                                 'piotr_toolbox');

    switch segm_params.boundaries_method
        case 'Gb'
            addpath(fullfile(include_params.pb_dir, 'lib', 'matlab'));
            include_params.gb_dir = fullfile(extern_dir, ...
                                             'segmentation', ...
                'boundaries--leordeanu_ECCV_2012_gb');
            addpath(include_params.gb_dir);
            addpath(fullfile(extern_codes_dir, 'extra_gb_code'));
        case 'GPb'
            include_params.globalpb_dir = fullfile(extern_dir, ...
                                                   'segmentation', ...
                'boundaries+segments--arbelaez_PAMI_2010_bsr');
            addpath(include_params.globalpb_dir);
            addpath(fullfile(include_params.globalpb_dir, 'lib'));
        case 'Pb'
            addpath(fullfile(include_params.pb_dir, 'lib', 'matlab'));
        case 'SketchTokens'
            include_params.sketchtokens_dir = fullfile(extern_dir, ...
                                                       'segmentation', ...
                'boundaries--lim_CVPR_2013_sketchtokens');
            addpath(include_params.sketchtokens_dir);

            addpath(fullfile(piotr_toolbox_dir, 'channels'));
        case 'StructEdges'
            include_params.structedges_dir = fullfile(extern_dir, ...
                                                      'segmentation', ...
                'boundaries--dollar_ICCV_2013_structedges');
            addpath(include_params.structedges_dir);

            addpath(fullfile(piotr_toolbox_dir, 'channels'));
        otherwise
    end

    % add parametric min-cut code to the path
    pmc_maxflow_method = segm_params.pmc_maxflow_method;
    if strcmp(pmc_maxflow_method, 'hochbaum')
        addpath(fullfile(extern_codes_dir, 'para_pseudoflow'));
    elseif strcmp(pmc_maxflow_method, 'kolmogorov')
        addpath(fullfile(extern_dir, ...
                'optimization/maxflow--boykov_PAMI_2004_maxflow/mex_maxflow'));
    elseif ~isempty(strfind(pmc_maxflow_method, 'nodynamic')) || ...
           ~isempty(strfind(pmc_maxflow_method, 'kohli')) || ...
           ~isempty(strfind(pmc_maxflow_method, 'multiseed'))
       addpath(fullfile(code_root_dir, 'boykov_maxflow'));
    else
    end

    addpath(filepath_params.fuxin_lib_dir);
    addpath(fullfile(filepath_params.fuxin_lib_dir, 'boosting'));

    % if using the seed sampler, include stuff from fuxin's library
%    if any(strcmpi(segm_params.graph_seed_gen_method, 'sp_seed_sampling'))
%        addpath(fullfile(filepath_params.fuxin_lib_dir, 'myqueue_1.1'));
%    end

   % only include vgg if planning to use FelzHutten superpixels
   if any(cellfun(@(x) any(strcmp(x, 'felzenszwalb_seeds_caller')), ...
          segm_params.graph_seed_params))
       addpath(fullfile(extern_dir, 'segmentation', 'imrender', 'vgg'));
   end

   addpath(fullfile(extern_dir, 'segmentation', ...
                    'stein_boundaryprocessing'));

   addpath(utils_dir);
   addpath(extern_dir);

%     % get export_fig in the path
%     addpath(fullfile(extern_dir, 'utils', 'export_fig'));
end


function dataSize = getSize(MRSIStruct)
    dataSize = MRSIStruct.sz;
end


function set_device(device_id)
% set_device(device_id)
%   set Caffe's GPU device ID

CHECK(isscalar(device_id) && device_id >= 0, ...
  'device_id must be non-negative integer');
device_id = double(device_id);

caffe_('set_device', device_id);

end


%% For CUDA functionality before running matlab, run
% export LD_LIBRARY_PATH=/pkgs_local/cuda-4.0/lib64/
% export PATH=$PATH:/pkgs_local/cuda-4.0/bin/
% export CUDA_PATH='/pkgs_local/cuda-4.0';

%% If running multiple instances of code on the same machine the number of threads for openmp should be restricted by
% export OMP_NUM_THREADS=4;

%% For Jacket GPU functionality
% addpath ~norouzi/research/jacket/engine;
% ginfo      % shows the gpu information
% gselect(1) % selects the first gpu

% Compilation of the mex files
mex loss_aug_inf_triplet_mex0.cpp CXXFLAGS="\$CXXFLAGS -Wall -fopenmp" LDFLAGS="\$LDFLAGS -fopenmp"
mex loss_aug_inf_mex0.cpp CXXFLAGS="\$CXXFLAGS -Wall -fopenmp" LDFLAGS="\$LDFLAGS -fopenmp"
mex utils/hammknn_mex.cpp utils/linscan.cpp CXXFLAGS="\$CXXFLAGS -fopenmp" LDFLAGS="\$LDFLAGS -fopenmp";

% adding some stuff to the path.
addpath utils; % You should be at thre root of the project to run this line.
addpath /YAEL_HOME/matlab/ % download and install yael library and change this line

% parameters & RUN2
DATA = 'mnist';  % either 'mnist' or 'cifar10'
REG = 1;         % always 1 -- the term that enforces bits to have a mean of zero
NB = 32;         % number of bits
LOSS = 1;        % loss type: 0 -> pairwise hinge / 1 -> triplet ranking
LSCALE = 1;      % An scaling parameter multiplied by the loss
NN = 1000;       % Number of same-class items to be used in the positive set for training.
                 % NN=Inf -> all of the items from the same class
                 % NN=1000 -> find same-class 1000-NN in euclidean distance and set them as target positive examples for each item
GPU = 0;         % GPU=0 -> No GPU / GPU=1 -> GPU (change this to 1 if you have jacket configured)
SHRINK_W = [3e-2 3e-3 3e-4];
                 % weight decay parameter -- cross validating on this parameter on 3 candidates
RUN2             % given the above parameters runs the code

% Linear hash functions on CIFAR-10
DATA = 'cifar10'; REG = 1; NB = 128; LOSS = 1; LSCALE = 1; NN = Inf; GPU = 0; SHRINK_W = 1e-3;
RUN2

% Neural Net because NB is two dimensional. Calles compute_NN_output and compute_NN_grad
DATA = 'mnist'; REG = 1; NB = [512 32]; LOSS = 1; LSCALE = 1; NN = Inf; GPU = 0; SHRINK_W = 1e-4*[1; 1];
RUN2


%CODINGCONVENTIONS specify coding conventions for MatGeom files
%
%   General
%
%   This file specifies coding convention for future files. Current files
%   do not necessarily follows the requirements, as they were created
%   before conventions.
%
%   There are three levels of conventions:
%   MUST: an obligation for new files, and existing files not respecting
%     this convention should be changed to respect it.
%   SHOULD: it is better to do it, but not necessary.
%   MAY: it is optional, but commonly used by some developpers.
%
%   Name of functions
%
%   Java convention for file names must be used:
%   * Each function name must be written in lower case, using english
%       denomination.
%   * For compound function names, the first letter of each component after
%       the first one is capitalized.
%       Ex: 'distancePointPlane'.
%
%   Other convention should be used to avoid name conflicts:
%   * For functions which work on a complex data type, the name of the data
%       type should be used as a prefix of the function name.
%       Ex: functions working on polygons have a prototype on the form
%       'polygonLength', 'polygonArea'...
%   * Functions working with geometrical primitives can use a suffix for
%       indicating the dimension of application.
%       Ex: 'distancePointLine3d'.
%       This convention was not used for the beginning of the library
%       development, but new functions will try to conform to it.
%   * Conversion functions use 'To' between radicals. Ex:
%       ellipseToPolygon, edgeToLine, circleArcToPolyline...
%
%   Function header
%
%   * First line of documentation must contain the name of the function,
%   followed by a short description. Name and function may be separated by
%   colon. Name must be capitalized, without punctuation before
%   description (to remain valid with matlab documentation tools).
%   Ex:   'SLICER interactive visualization of 3D images'
%
%   * Main part of header describes usage of function/script with various
%   parameters. Function name should be the real name. Variable names
%   should be capitalized. This does not follow matlab's standard, but
%   allows to copy-paste pieces of code.
%   Ex: 'RES = myBasicFunction(PARAM1, PARAM2, OPTION);'
%
%   * The description of each calling syntax should be written using
%   conjugated verbs.
%
%   * End of header should contain name of author, with date of creation.
%   It may also contains author affiliation (company ...), and date of last
%   revision.
%
%
%   Function code
%
%   * Please comment the code ! For algorithmic parts, as much comments as
%   code is a good start ...
%   * Indentation level should be marked by four space characters.
%
%
%   Tips & Tricks
%
%   * The use of '...' at the end of a line can cause compatibility
%   problems from Linux to Windows.
%   * matlab help uses the file 'Contents.m', not 'contents.m' !
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - MIA MathCell
%   created the 23/03/2005
%

%   HISTORY
%   2007-03-05 add convention for datatype prefix for function names, and
%       library organization
%   2011-01-11 update for matGeom project

help codingConventions;


classdef PTKSaveCoronalAnalysisResults < PTKPlugin
    % PTKSaveCoronalAnalysisResults. Plugin for performing analysis of density using bins
    % along the anterior-posterior axis
    %
    %     This is a plugin for the Pulmonary Toolkit. Plugins can be run using
    %     the gui, or through the interfaces provided by the Pulmonary Toolkit.
    %     See PTKPlugin.m for more information on how to run plugins.
    %
    %     Plugins should not be run directly from your code.
    %
    %     PTKSaveCoronalAnalysisResults divides the cranial-caudal axis into bins and
    %     performs analysis of the tissue density, air/tissue fraction and
    %     emphysema percentage in each bin.
    %
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2013.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    properties
        ButtonText = 'Coronal analysis'
        ToolTip = 'Performs density analysis in bins along the anterior-posterior axis'
        Category = 'Slice-by-slice'
        Mode = 'Analysis'

        Context = PTKContextSet.LungROI
        AllowResultsToBeCached = true
        AlwaysRunPlugin = true
        PluginType = 'DoNothing'
        HidePluginInDisplay = false
        FlattenPreviewImage = false
        PTKVersion = '2'
        ButtonWidth = 6
        ButtonHeight = 2
        GeneratePreview = false
        Location = 31
        Icon = 'cor_slices.png'
    end

    methods (Static)
        function results = RunPlugin(dataset, context, reporting)

            % Generate the results over the lungs and lobes
            contexts = {PTKContextSet.Lungs, PTKContextSet.SingleLung, PTKContextSet.Lobe};
            results = dataset.GetResult('PTKCoronalAnalysis', contexts);

            % Convert the results into a MimResultsTable
            image_info = dataset.GetImageInfo;
            uid = image_info.ImageUid;
            patient_name = dataset.GetPatientName;
            table = PTKConvertMetricsToTable(results, patient_name, uid, reporting);

            % Save the results table as a series of CSV files
            dataset.SaveTableAsCSV('PTKSaveCoronalAnalysisResults', 'Coronal analysis', 'CoronalResults', 'Density analysis in bins along the anterior-posterior axis', table, MimResultsTable.ContextDim, MimResultsTable.SliceNumberDim, MimResultsTable.MetricDim, []);

            % Generate graphs of the results
            y_label = 'Distance along coronal axis (%)';

            context_list_both_lungs = [PTKContext.Lungs];
            PTKSaveCoronalAnalysisResults.DrawGraphAndSave(dataset, table, y_label, context_list_both_lungs, '_CombinedLungs', reporting);

            context_list_single_lungs = [PTKContext.LeftLung, PTKContext.RightLung];
            PTKSaveCoronalAnalysisResults.DrawGraphAndSave(dataset, table, y_label, context_list_single_lungs, '_Lungs', reporting);

            context_list_lobes = [PTKContext.LeftLowerLobe, PTKContext.LeftUpperLobe, PTKContext.RightLowerLobe, PTKContext.RightMiddleLobe, PTKContext.RightUpperLobe];
            PTKSaveCoronalAnalysisResults.DrawGraphAndSave(dataset, table, y_label, context_list_lobes, '_Lobes', reporting);

            results = [];
        end

        function enabled = IsEnabled(gui_app)
            enabled = gui_app.IsDatasetLoaded() && gui_app.IsCT();
        end
    end

    methods (Static, Access = private)
        function DrawGraphAndSave(dataset, table, y_label, context_list, file_suffix, reporting)
            figure_handle = PTKGraphMetricVsDistance(table, 'MeanDensityGml', 'StdDensityGml', context_list, [], y_label, reporting);
            dataset.SaveFigure(figure_handle, 'PTKSaveCoronalAnalysisResults', 'Coronal analysis', ['DensityVsCoronalDistance' file_suffix], 'Graph of density vs distance along the anterior-posterior axis');
            close(figure_handle);

            figure_handle = PTKGraphMetricVsDistance(table, 'EmphysemaPercentage', [], context_list, [], y_label, reporting);
            dataset.SaveFigure(figure_handle, 'PTKSaveCoronalAnalysisResults', 'Coronal analysis', ['EmphysemaVsCoronalDistance' file_suffix], 'Graph of emphysema vs distance along the anterior-posterior axis');
            close(figure_handle);
        end
    end
end


function niter = loopy_converged(engine)
% LOOPY_CONVERGED Did loopy belief propagation converge? 0 means no, eles we return the num. iterations.
% function niter = loopy_converged(engine)
%
% We use a simple heuristic: we say convergence occurred if the number of iterations
% used was less than the maximum allowed.

if engine.niter == engine.max_iter
  niter = 0;
else
  niter = engine.niter;
end


clear; close all; clc;

%%

filePath=mfilename('fullpath');
toolboxPath=fileparts(fileparts(fileparts(filePath)));

libFolder=fullfile(toolboxPath,'lib');
helpFolder=fullfile(toolboxPath,'docs');
outputFolder=fullfile(toolboxPath,'docs');

%%

%Get list of M-files in library
fileListLib = dir(fullfile(libFolder,'*.m'));
fileListLib={fileListLib(1:end).name};
fileListLib=sort(fileListLib(:));
NumberOfLibFiles=numel(fileListLib);

%Get list of M-files in help folder
fileListHelp = dir(fullfile(helpFolder,'*.m'));
fileListHelp={fileListHelp(1:end).name};
fileListHelp=sort(fileListHelp(:));
NumberOfHelpFiles=numel(fileListHelp);

%%

licenseBoilerPlate=fullfile(toolboxPath,'licenseBoilerPlate.txt');
[T_license]=txtfile2cell(licenseBoilerPlate);

footerTargetText='% _*GIBBON footer text*_ ';
licenseLink='https://github.com/gibbonCode/GIBBON/blob/master/LICENSE';

%Add comment symbols
for q=1:1:numel(T_license)
    T_license{q}=['% ',T_license{q}];
end

%Add target header
T_license=[{'%% '};{footerTargetText};{'% '};{['% License: <',licenseLink,'>']};T_license(1:end)];

%%

if exist(outputFolder,'dir')==0
   mkdir(outputFolder);
end
addpath(outputFolder);

hw = waitbar(0,'Creating documentation templates...');
for q=1:1:NumberOfLibFiles
   libFileName=fileListLib{q};
   libFileNameFull=fullfile(libFolder,libFileName);
   helpFileName=fullfile(helpFolder,['HELP_',libFileName]);
   helpFileNameNew=fullfile(outputFolder,['HELP_',libFileName]);

   if exist(helpFileName,'file')==0
       funcName=libFileName(1:end-2);
       [T_func]=txtfile2cell(libFileNameFull); %A cell containing the M-file text
       funcLine=T_func{1}; %First line containing function statement
       funcLine = strtrim(funcLine); %First line with leading/trailing spaces removed
       funcLine = regexprep(funcLine,' +',' '); %Remove possible extra spaces
       codeSyntaxSimple=funcLine(10:end);

       %Build M-file text
       T={};
       T{1,1}=['%% ',funcName];
       T{end+1,1}=['% Below is a demonstration of the features of the |',funcName,'| function'];
       T{end+1,1}='';
       T{end+1,1}='%%';
       T{end+1,1}='clear; close all; clc;';
       T{end+1,1}='';
       T{end+1,1}='%% Syntax';
       T{end+1,1}=['% |',codeSyntaxSimple,';|'];
       T{end+1,1}='';
       T{end+1,1}='%% Description ';
       T{end+1,1}='% UNDOCUMENTED ';
       T{end+1,1}='%% Examples ';
       T{end+1,1}='% ';
       T{end+1,1}='%%';
       T{end+1,1}='% ';
       T{end+1,1}='% <<gibbVerySmall.gif>>';
       T{end+1,1}='% ';
       T{end+1,1}='% _*GIBBON*_ ';
       T{end+1,1}='% <www.gibboncode.org>';
       T{end+1,1}='% ';
       T{end+1,1}='% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>';
       T(end+1:end+numel(T_license),1)=T_license;

       cell2txtfile(helpFileNameNew,T,0);

   end
   waitbar(q/NumberOfLibFiles,hw,sprintf('%6.2f',q/NumberOfLibFiles))
end
delete(hw);

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.