function [p] = gui_mesh_open(p,command,parent)

% gui_mesh_open - GUI to mesh_open
%
% Usage: [p] = gui_mesh_open(p,[command],[parent])
%
% p        - struct, generated by 'eeg_toolbox_defaults'
% command  - either 'init' or 'load'
% parent   - a handle to the gui that calls this gui, used
%            to update the UserData of the parent. The p
%            struct is returned to the parent in this way.
%
% See command line version, mesh_open
%

% $Revision: 1.1 $ $Date: 2009-04-28 22:13:56 $

% Licence:  GNU GPL, no express or implied warranties
% History:  02/2002, Darren.Weber_at_radiology.ucsf.edu
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ~exist('p','var'),
 [p] = eeg_toolbox_defaults;
elseif isempty(p),
 [p] = eeg_toolbox_defaults;
end

if ~exist('command','var'),
  command = 'init';
elseif isempty(command),
  command = 'init';
end

command = lower(command);

switch command,

  case 'init',

    if exist('parent','var'),
      MESHOpen = INIT(p,parent);
    else
      MESHOpen = INIT(p);
    end

  case 'plot',

    MESHOpen = get(gcbf,'Userdata');

    MESHOpen.p = mesh_open(MESHOpen.p);

    set(MESHOpen.gui,'Userdata',MESHOpen);

    if isequal(get(MESHOpen.handles.Bhold,'Value'),1),

     [p] = gui_updateparent(MESHOpen,0);
     [p] = gui_mesh_plot(p,'init',MESHOpen.gui);

    else

     [p] = gui_updateparent(MESHOpen);
      if isfield(MESHOpen,'parent'),
        if ~isempty(MESHOpen.parent),
          gui_mesh_plot(MESHOpen.p,'init',MESHOpen.parent.gui);
        end
      else
        gui_mesh_plot(MESHOpen.p,'init','');
      end
      close gcbf;
    end

  case 'save',

    MESHOpen = get(gcbf,'Userdata');
    gui_mesh_write(MESHOpen.p);

    if isequal(get(MESHOpen.handles.Bhold,'Value'),1),
     [p] = gui_updateparent(MESHOpen,0);
    else
     [p] = gui_updateparent(MESHOpen);
      close gcbf;
    end

  case 'return',

    MESHOpen = get(gcbf,'Userdata');

    set(MESHOpen.gui,'Pointer','watch');

    MESHOpen.p = mesh_open(MESHOpen.p);

    set(MESHOpen.gui,'Userdata',MESHOpen);

    set(MESHOpen.gui,'Pointer','arrow');

    if isequal(get(MESHOpen.handles.Bhold,'Value'),1),
     [p] = gui_updateparent(MESHOpen,0);
    else
     [p] = gui_updateparent(MESHOpen);
      close gcbf;
    end

  otherwise,

    close gcbf;

end

return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [MESHOpen] = INIT(p,parent),

% GUI General Parameters

GUIwidth  = 500;
GUIheight = 120;

version = '$Revision: 1.1 $';
name = sprintf('MESH File Open [v %s]\n',version(11:15));

GUI = figure('Name',name,'Tag','MESH_OPEN',...
  'NumberTitle','off',...
  'MenuBar','none','Position',[1 1 GUIwidth GUIheight]);
movegui(GUI,'center');

MESHOpen.gui = GUI;

Font.FontName   = 'Helvetica';
Font.FontUnits  = 'Pixels';
Font.FontSize   = 12;
Font.FontWeight = 'normal';
Font.FontAngle  = 'normal';

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Mesh Data Selection and Parameters

G.Title_data = uicontrol('Parent',GUI,'Style','text','Units','Normalized',Font, ...
  'Position',[.01 .75 .17 .2],...
  'TooltipString','Mesh/Tesselation Formats (see, ''help mesh_open'')',...
  'String','Mesh Type:','HorizontalAlignment','left');

switch lower(p.mesh.type),
  case 'brainstorm',          meshType = 1;
  case 'emse',                meshType = 2;
  case 'ascii',               meshType = 3;
  case 'freesurfer_ascii',    meshType = 4;
  case 'freesurfer_surf',     meshType = 5;
  case 'freesurfer_curv',     meshType = 6;
  case 'freesurfer_overlay',  meshType = 7;
  otherwise,                  meshType = 1;
end

meshtypes = {'BrainStorm' 'EMSE' 'ASCII' ...
    'FreeSurfer_ascii' 'FreeSurfer_surf' 'FreeSurfer_curv' 'FreeSurfer_overlay' };
G.PmeshType = uicontrol('Tag','PmeshType','Parent',GUI,'Style','popupmenu',...
  'Units','Normalized',Font,  ...
  'Position',[.20 .75 .28 .2],...
  'String',meshtypes,'Value',meshType,...
  'TooltipString','Mesh/Triangulation Formats (see, ''help mesh_open'', FS = FreeSurfer)',...
  'Callback',strcat('MESHOpen = get(gcbf,''Userdata'');',...
  'MESHOpen.p.mesh.type = popupstr(MESHOpen.handles.PmeshType);',...
  'set(gcbf,''Userdata'',MESHOpen); clear MESHOpen;'));

G.BmeshReplace = uicontrol('Tag','BmeshReplace','Parent',GUI,'Style','checkbox',...
  'Units','Normalized',Font,...
  'Position',[.40 .75 .18 .2],...
  'String','Replace','Value',1,...
  'TooltipString','Reload current mesh, if any?',...
  'Visible','off',...
  'Callback',strcat('MESHOpen = get(gcbf,''Userdata'');',...
  'MESHOpen.p.mesh.reload = get(MESHOpen.handles.BmeshReplace,''Value'');',...
  'set(gcbf,''Userdata'',MESHOpen); clear MESHOpen;'));

G.Title_path = uicontrol('Parent',GUI,'Style','text','Units','Normalized',Font, ...
  'Position',[.01 .50 .17 .2],...
  'String','Path','HorizontalAlignment','left');
G.EmeshPath = uicontrol('Parent',GUI,'Style','edit','Units','Normalized',Font,  ...
  'Position',[.20 .50 .58 .2], 'String',p.mesh.path,...
  'Callback',strcat('MESHOpen = get(gcbf,''Userdata'');',...
  'MESHOpen.p.mesh.path = get(MESHOpen.handles.EmeshPath,''String'');',...
  'set(gcbf,''Userdata'',MESHOpen); clear MESHOpen;'));

G.Title_file = uicontrol('Parent',GUI,'Style','text','Units','Normalized',Font, ...
  'Position',[.01 .25 .17 .2],...
  'String','File','HorizontalAlignment','left');
G.EmeshFile = uicontrol('Parent',GUI,'Style','edit','Units','Normalized',Font,  ...
  'Position',[.20 .25 .58 .2], 'String',p.mesh.file,...
  'Callback',strcat('MESHOpen = get(gcbf,''Userdata'');',...
  'MESHOpen.p.mesh.file = get(MESHOpen.handles.EmeshFile,''String'');',...
  'set(gcbf,''Userdata'',MESHOpen); clear MESHOpen;'));

Font.FontWeight = 'bold';

% BROWSE: Look for the data
browsecommand = strcat('MESHOpen = get(gcbf,''Userdata'');',...
  'cd(MESHOpen.p.mesh.path);',...
  '[file, path] = uigetfile(',...
  '{''*.dat;*.txt'', ''ASCII Tesselations (*.dat,*.txt)'';', ...
  ' ''*.asc;*.tri'', ''FreeSurfer Tesselations (*.asc,*.tri)'';', ...
  ' ''*.orig;*.smoothwm;*.white;*.pial'', ''FreeSurfer Surfaces'';', ...
  ' ''*.wfr'', ''EMSE Tesselations (*.wfr)'';', ...
  ' ''*.mat'', ''BrainStorm Tesselations (*.mat)'';', ...
  ' ''*.*'',   ''All Files (*.*)''},', ...
  '''Select Mesh/Tesselation File'');',...
  'if ~isequal(path,0), MESHOpen.p.mesh.path = path; end;',...
  'if ~isequal(file,0), MESHOpen.p.mesh.file = file; end;',...
  'set(MESHOpen.handles.EmeshPath,''String'',MESHOpen.p.mesh.path);',...
  'set(MESHOpen.handles.EmeshFile,''String'',MESHOpen.p.mesh.file);',...
  'if     findstr(file,''.mat''), value = 1; ',...
  'elseif findstr(file,''.wfr''), value = 2; ',...
  'elseif findstr(file,''.dat''), value = 3; ',...
  'elseif findstr(file,''.txt''), value = 3; ',...
  'elseif findstr(file,''.asc''), value = 3; ',...
  'elseif findstr(file,''.tri''), value = 4; ',...
  'elseif findstr(file,''.orig''),     value = 5; ',...
  'elseif findstr(file,''.smoothwm''), value = 5; ',...
  'elseif findstr(file,''.white''),    value = 5; ',...
  'elseif findstr(file,''.pial''),     value = 5; ',...
  'else, value = 3; ',...
  'end;',...
  'set(MESHOpen.handles.PmeshType,''Value'',value); ',...
  'MESHOpen.p.mesh.type = popupstr(MESHOpen.handles.PmeshType);',...
  'set(gcbf,''Userdata'',MESHOpen); clear value file path MESHOpen;');
G.BmeshFile = uicontrol('Parent',GUI,'Style','pushbutton','Units','Normalized',Font, ...
  'Position',[.01 .01 .17 .2], 'String','BROWSE',...
  'BackgroundColor',[0.8 0.8 0.0],...
  'ForegroundColor', [1 1 1], 'HorizontalAlignment', 'center',...
  'Callback', browsecommand );

% PLOT: Load & plot the data!
G.Bplot = uicontrol('Parent',GUI,'Style','pushbutton','Units','Normalized', Font, ...
  'Position',[.20 .01 .18 .2],...
  'String','PLOT','BusyAction','queue',...
  'TooltipString','Plot the mesh and return the file parameters.',...
  'BackgroundColor',[0.0 0.5 0.0],...
  'ForegroundColor', [1 1 1], 'HorizontalAlignment', 'center',...
  'Callback',strcat('MESHOpen = get(gcbf,''Userdata'');',...
  'p = gui_mesh_open(MESHOpen.p,''plot'');',...
  'clear MESHOpen;'));

% SAVE AS: Load & convert the data!
G.Bsave = uicontrol('Parent',GUI,'Style','pushbutton','Units','Normalized', Font, ...
  'Position',[.40 .01 .18 .2],...
  'String','SAVE AS','BusyAction','queue',...
  'TooltipString','Load and convert the mesh.',...
  'BackgroundColor',[0.0 0.0 0.5],...
  'ForegroundColor', [1 1 1], 'HorizontalAlignment', 'center',...
  'Callback',strcat('MESHOpen = get(gcbf,''Userdata'');',...
  'p = gui_mesh_open(MESHOpen.p,''save'');',...
  'clear MESHOpen;'));

% Return file parameters
G.Breturn = uicontrol('Parent',GUI,'Style','pushbutton','Units','Normalized', Font, ...
  'Position',[.60 .01 .18 .2],...
  'String','RETURN','BusyAction','queue',...
  'TooltipString','Close and return the file parameters.',...
  'BackgroundColor',[0.75 0.0 0.0],...
  'ForegroundColor', [1 1 1], 'HorizontalAlignment', 'center',...
  'Callback',strcat('MESHOpen = get(gcbf,''Userdata'');',...
  'p = gui_mesh_open(MESHOpen.p,''return'');',...
  'clear MESHOpen;'));

% Cancel, do not return parameters
G.Bcancel = uicontrol('Parent',GUI,'Style','pushbutton','Units','Normalized', Font, ...
  'Position',[.80 .01 .18 .2],...
  'String','CANCEL','BusyAction','queue',...
  'TooltipString','Close, do not return the p struct.',...
  'BackgroundColor',[0.75 0.0 0.0],...
  'ForegroundColor', [1 1 1], 'HorizontalAlignment', 'center',...
  'Callback',strcat('MESHOpen = get(gcbf,''Userdata'');',...
  'p = gui_mesh_open(MESHOpen.p,''cancel'');',...
  'clear MESHOpen;'));

% Help
G.Bhelp = uicontrol('Parent',GUI,'Style','pushbutton','Units','Normalized', Font, ...
  'Position',[.80 .25 .18 .2],'String','Help','BusyAction','queue',...
  'BackgroundColor',[1 1 0],...
  'ForegroundColor',[0 0 0], 'HorizontalAlignment', 'center',...
  'Callback','doc mesh_open;');

% Hold GUI Open checkbox
G.Bhold = uicontrol('Parent',GUI,'Style','checkbox','Units','Normalized', Font, ...
  'Position',[.80 .50 .18 .2],'String','Hold GUI','BusyAction','queue',...
  'TooltipString','MESH File Load GUI remains open after ''Plot'' and ''Return'' commands.',...
  'Value',p.hold,'HorizontalAlignment', 'center');

% Store userdata
if exist('parent','var'), MESHOpen.parent.gui = parent; end
MESHOpen.handles = G;
MESHOpen.p = p;
set(GUI,'HandleVisibility','callback');
set(GUI,'Userdata',MESHOpen);

return


% OtQueryOpen Open an Omnitrans query.
%
% Usage:
%    iret = OtQueryOpen(hquery)
%
% IMPORTANT: Always check the return value.
% The return value should be equal to 1, otherwise
% later calls to the OtQuery interface may cause application crashes.
%

% OtMatlab: Matlab interface to the Omnitrans transport planning software

% Copyright (c) 2008, VORtech Computing
% Copyright (c) 2010, Omnitrans International

% All rights reserved.

% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:

%     * Redistributions of source code must retain the above copyright
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright
%       notice, this list of conditions and the following disclaimer in
%       the documentation and/or other materials provided with the distribution
%     * Neither the name of VORtech Computing and Omnitrans International nor the names
%       of its contributors may be used to endorse or promote products derived
%       from this software without specific prior written permission.

% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.

% 2006-02-28 Created (Johan Meijdam, VORtech)

function iret = OtQueryOpen(hquery)
   if nargout < 1
      error('OtQueryOpen: always check the return value.');
   end
   iret = Ot('query.open', hquery);
end


function val = minc_variable(hdr,var_name,att_name)
% Read a MINC variable inside a header
% VAL = MINC_VARIABLE( HDR , [VAR_NAME] , [ATT_NAME] )
%
% HDR (structure) a minc header read with MINC_READ
% VAR_NAME (string, optional) the name of a variable.
% ATT_NAME (string, optional) the name of an attribute.
% VAL (various types)
%   if VAR_NAME and ATT_NAME are both specified, VAL is the value of the
%      specified variable/attribute.
%   If only VAR_NAME is specified, VAL is the list of attributes of the
%      variable (cell of strings).
%   If VAR_NAME is unspecified, VAL is the list of variables (cell of strings).
%
% Example:
% [hdr,vol] = minc_read('my_vol.mnc');
% dcosinesy = minc_variable(hdr,'yspace','direction_cosines');
%
% Maintainer : pierre.bellec@criugm.qc.ca
% See licensing information in the code.

% Copyright (c) Pierre Bellec, Centre de recherche de l'institut de
% gériatrie de Montréal, Département d'informatique et de recherche
% opérationnelle, Université de Montréal, 2013-2014.
% Keywords : medical imaging, I/O, reader, minc
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the "Software"), to deal
% in the Software without restriction, including without limitation the rights
% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
% copies of the Software, and to permit persons to whom the Software is
% furnished to do so, subject to the following conditions:
%
% The above copyright notice and this permission notice shall be included in
% all copies or substantial portions of the Software.
%
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
% THE SOFTWARE.

if (nargin<1)||~isstruct(hdr)||~isfield(hdr,'details')||~isfield(hdr.details,'variables')||~isfield(hdr.details,'globals')
    error('Please specify a valid HDR structure, see MINC_READ')
end

hdr = hdr.details;

list_var = {hdr.variables(:).name};

if nargin == 1
    val = list_var;
    return
end

if nargin >= 2
    ind = find(ismember(list_var,var_name));
    if isempty(ind)
        error('Could not find variable %s in HDR',var_name)
    end
    ind = ind(1);
end

varminc = hdr.variables(ind);
list_att = varminc.attributes;
if nargin == 2
    val = list_att;
    return
end

ind2 = find(ismember(list_att,att_name));
if isempty(ind2)
    error('Could not find attribute %s in variable %s',att_name,var_name)
end
val = varminc.values{ind2};


function [ graphGroup ] = GraphCutting( graphGroup, minNum )
%GRAPHCUTTING Summary of this function goes here
%   Detailed explanation goes here

    % looping

end


% ------------------------------------------------------------------------
%  Copyright (C)
%  Universitat Politecnica de Catalunya BarcelonaTech (UPC) - Spain
%
%  Jordi Pont-Tuset <jordi.pont@upc.edu>
%  June 2013
% ------------------------------------------------------------------------
%  Code obtained from:
%  https://imatge.upc.edu/web/resources/supervised-evaluation-image-segmentation
% ------------------------------------------------------------------------
% This file is part of the SEISM package presented in:
%    Jordi Pont-Tuset, Ferran Marques,
%    "Measures and Meta-Measures for the Supervised Evaluation of Image Segmentation,"
%    Computer Vision and Pattern Recognition (CVPR), 2013.
% If you use this code, please consider citing the paper.
% ------------------------------------------------------------------------
%
% This function returns the root directory where the package is installed.
% Before running the script 'install.m', this directory should be updated
%
% ------------------------------------------------------------------------

function mcg_root_dir = mcg_root_dir()
   mcg_root_dir = fullfile(fileparts(mfilename('fullpath'))); %pwd(), 'regions');
end


classdef ObjMeta
    %META Summary of this class goes here
    %   Detailed explanation goes here

    properties
        Name % file name
        Sensor % L_TM, L_ETM_PLUS, L_OLI, L_OLI_TIRS, S_MSI
        Resolution % Spatail Resolution.
        Dim
        UL
        Zen
        Azi
        ZC
        BBox % boundery.
        Output % output directory
    end

    methods
    end

end


function varargout = process_evt_combine( varargin )
% PROCESS_EVT_COMBINE: Combine different categories events into one (stimulus / response)
%
% USAGE:  OutputFiles = process_evt_combine('Run', sProcess, sInputs)

% @=============================================================================
% 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, 2012-2023

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Combine stim/response';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Events';
    sProcess.Index       = 53;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/EventMarkers?highlight=%28Combine+stim%2Fresponse%29#Events';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data', 'raw', 'matrix'};
    sProcess.OutputTypes = {'data', 'raw', 'matrix'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;
    % Event name
    sProcess.options.combine.Comment = ['Example: We have one stim (A) and two responses (B and C).
' ...
                                        'We want to create two new pairs of event categories:
' ...
                                        '    A_AB: Event A (followed by B)
' ...
                                        '    A_AC: Event A (followed by C)
' ...
                                        '    B_AB: Event B (preceded by A)
' ...
                                        '    C_AC: Event C (preceded by A)
' ...
                                        'For that, we use the following classification:
' ...
                                        '    A_AB , B_AB , A , B
' ...
                                        '    A_AC , C_AC , A , C
' ...
                                        'To prevent one category to be created, use "ignore". Example:
' ...
                                        '    A_AB , ignore , A , B
' ...
                                        'To create an extended event AB between A and B, use "extend":
' ...
                                        '    AB , extend , A , B
' ...
                                        'You may add as many combinations as needed, one per line.

'];
    sProcess.options.combine.Type    = 'textarea';
    sProcess.options.combine.Value   = ['A_AB, B_AB , A , B' 10 'A_AC, C_AC , A , C'];
    % Maximum duration between simulateous events
    sProcess.options.dt.Comment = 'Maximum delay between grouped events: ';
    sProcess.options.dt.Type    = 'value';
    sProcess.options.dt.Value   = {1, 'ms', 0};
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputs) %#ok<DEFNU>
    % Return all the input files
    OutputFiles = {};

    % ===== GET OPTIONS =====
    dt = sProcess.options.dt.Value{1};
    % Combination string
    combineStr = strtrim(sProcess.options.combine.Value);
    % Split in lines
    combine_lines = str_split(combineStr, [10 13]);
    % Split each line with the ";"
    combineCell = {};
    for i = 1:length(combine_lines)
        % No information on this line: skip
        combine_lines{i} = strtrim(combine_lines{i});
        if isempty(combine_lines{i})
            continue;
        end
        % Split with ";"
        lineCell = str_split(combine_lines{i}, ';,');
        % If there are not 4 elements on this line: ignore
        if (length(lineCell) ~= 4)
            continue;
        end
        % Add combination entry
        iComb = size(combineCell,1) + 1;
        combineCell{iComb,1} = strtrim(lineCell{1});
        combineCell{iComb,2} = strtrim(lineCell{2});
        combineCell{iComb,3} = strtrim(lineCell{3});
        combineCell{iComb,4} = strtrim(lineCell{4});
    end
    % If no combination available
    if isempty(combineCell)
        bst_report('Error', sProcess, [], 'Invalid combinations format.');
        return;
    end

    % For each file
    for iFile = 1:length(sInputs)
        % ===== GET FILE DESCRIPTOR =====
        isRaw = strcmpi(sInputs(iFile).FileType, 'raw');
        % Load the raw file descriptor
        if isRaw
            DataMat = in_bst_data(sInputs(iFile).FileName, 'F');
            sFile = DataMat.F;
        else
            DataMat = in_bst_data(sInputs(iFile).FileName, 'Events');
            sFile.events = DataMat.Events;
            sFile.epochs = [];
        end

        % Process only continuous files
        if ~isempty(sFile.epochs)
            bst_report('Error', sProcess, sInputs(iFile), 'This function can only process continuous recordings (no epochs). Skipping File...');
            continue;
        end
        % If no markers are present in this file
        if isempty(sFile.events)
            bst_report('Warning', sProcess, sInputs(iFile), 'This file does not contain any event. Skipping File...');
            continue;
        end

        % ===== PROCESS EACH COMBINATION =====
        isModified = 0;
        for iComb = 1:size(combineCell,1)
            % === GET EVENTS INVOLVED ===
            % Find all the events in the structure
            evtNewStimName = combineCell{iComb,1};
            evtNewRespName = combineCell{iComb,2};
            iEvtNewStim = find(strcmpi(evtNewStimName, {sFile.events.label}));
            iEvtNewResp = find(strcmpi(evtNewRespName, {sFile.events.label}));
            iEvtStim    = find(strcmpi(combineCell{iComb,3}, {sFile.events.label}));
            iEvtResp    = find(strcmpi(combineCell{iComb,4}, {sFile.events.label}));
            % If any event not available
            if isempty(iEvtStim)
                bst_report('Warning', sProcess, sInputs(iFile), ['Event "' combineCell{iComb,3} '" does not exist in file. Skipping combination...']);
                continue;
            end
            if isempty(iEvtResp)
                bst_report('Warning', sProcess, sInputs(iFile), ['Event "' combineCell{iComb,4} '" does not exist in file. Skipping combination...']);
                continue;
            end
            % If events are extended events: skip
            if (size(sFile.events(iEvtStim).times,1) > 1) || (size(sFile.events(iEvtResp).times,1) > 1)
                bst_report('Error', sProcess, sInputs(iFile), 'Cannot process extended events. Skipping combination...');
                continue;
            end

            % === FIND OTHER RESPONSES ===
            % Get all the other possible responses to this stim
            iOtherResp = find(strcmpi(combineCell(:,3), combineCell{iComb,3}));
            % Do not consider the target response
            iOtherResp = setdiff(iOtherResp, iComb);
            % Loop to get all the times of all the other responses
            tOtherResp = [];
            for iOther = 1:length(iOtherResp)
                % Find event
                evtOtherRespName = combineCell{iOtherResp(iOther),4};
                iEvtOtherResp = find(strcmpi(evtOtherRespName, {sFile.events.label}));
                if isempty(iEvtOtherResp) || isempty(sFile.events(iEvtOtherResp).times) || (size(sFile.events(iEvtOtherResp).times,1) > 1)
                    continue;
                end
                % Get times for this response event
                tOtherResp = [tOtherResp, sFile.events(iEvtOtherResp).times];
            end

            % === DETECT RESPONSE TO STIM ===
            % Get stim and resp times
            tStim = sFile.events(iEvtStim).times;
            tResp = sFile.events(iEvtResp).times;
            iNewStim = [];
            iNewResp = [];
            % For each stim event, look for a response
            for is = 1:length(tStim)
                % Look a TARGET response event immediately following a stim event
                if (is == length(tStim))
                    ir = find(tResp > tStim(is), 1);
                else
                    ir = find((tResp > tStim(is)) & (tResp < tStim(is+1)), 1);
                end
                % Look an OTHER response event immediately following a stim event
                if (is == length(tStim))
                    ior = find(tOtherResp > tStim(is), 1);
                else
                    ior = find((tOtherResp > tStim(is)) & (tOtherResp < tStim(is+1)), 1);
                end
                % If a response was found, and before any other response type
                if ~isempty(ir) && (isempty(ior) || (tResp(ir) <= tOtherResp(ior))) && (tResp(ir) - tStim(is) < dt)
                    iNewStim(end+1) = is;
                    iNewResp(end+1) = ir;
                end
            end

            % === CREATE NEW EVENTS ===
            % If some couples stim/response were detected: create or udpate the correpsonding events
            if ~isempty(iNewStim)
                isModified = 1;

                % Should the events include channels
                if ~isempty(sFile.events(iEvtStim).channels) || ~isempty(sFile.events(iEvtResp).channels)
                    isChannels = 1;
                    if isempty(sFile.events(iEvtStim).channels)
                        sFile.events(iEvtStim).channels = cell(1, size(sFile.events(iEvtStim).times,2));
                    end
                    if isempty(sFile.events(iEvtResp).channels)
                        sFile.events(iEvtResp).channels = cell(1, size(sFile.events(iEvtResp).times,2));
                    end
                else
                    isChannels = 0;
                end

                % Should the events include notes
                if ~isempty(sFile.events(iEvtStim).notes) || ~isempty(sFile.events(iEvtResp).notes)
                    isNotes = 1;
                    if isempty(sFile.events(iEvtStim).notes)
                        sFile.events(iEvtStim).notes = cell(1, size(sFile.events(iEvtStim).times,2));
                    end
                    if isempty(sFile.events(iEvtResp).notes)
                        sFile.events(iEvtResp).notes = cell(1, size(sFile.events(iEvtResp).times,2));
                    end
                else
                    isNotes = 0;
                end

                % === NEW STIM ===
                % Only create new stim event if name is not "ignore"
                if ~strcmpi(evtNewStimName, 'ignore')
                    % If new stim event does not exist: create
                    if isempty(iEvtNewStim)
                        % Initialize new event
                        iEvtNewStim = length(sFile.events) + 1;
                        sEvent = db_template('event');
                        sEvent.label = evtNewStimName;
                        % Get the default color for this new event
                        sEvent.color = panel_record('GetNewEventColor', iEvtNewStim, sFile.events);
                        % Add new event to list
                        sFile.events(iEvtNewStim) = sEvent;
                    end
                    % Add simple events
                    if ~strcmpi(evtNewRespName, 'extend')
                        sFile.events(iEvtNewStim).times    = [sFile.events(iEvtNewStim).times,   sFile.events(iEvtStim).times(iNewStim)];
                        sFile.events(iEvtNewStim).epochs   = [sFile.events(iEvtNewStim).epochs,  sFile.events(iEvtStim).epochs(iNewStim)];
                        % Sort
                        [sFile.events(iEvtNewStim).times, indSort] = unique(sFile.events(iEvtNewStim).times);
                        sFile.events(iEvtNewStim).epochs   = sFile.events(iEvtNewStim).epochs(indSort);
                        % Channels
                        if isChannels
                            sFile.events(iEvtNewStim).channels = [sFile.events(iEvtNewStim).channels,sFile.events(iEvtStim).channels(iNewStim)];
                            sFile.events(iEvtNewStim).channels = sFile.events(iEvtNewStim).channels(indSort);
                        end
                        % Notes
                        if isNotes
                            sFile.events(iEvtNewStim).notes = [sFile.events(iEvtNewStim).notes,   sFile.events(iEvtStim).notes(iNewStim)];
                            sFile.events(iEvtNewStim).notes = sFile.events(iEvtNewStim).notes(indSort);
                        end
                    % Add extended events
                    else
                        sFile.events(iEvtNewStim).times    = [sFile.events(iEvtNewStim).times,   [sFile.events(iEvtStim).times(iNewStim); sFile.events(iEvtResp).times(iNewResp)]];
                        sFile.events(iEvtNewStim).epochs   = [sFile.events(iEvtNewStim).epochs,  sFile.events(iEvtStim).epochs(iNewStim)];
                        if isChannels
                            sFile.events(iEvtNewStim).channels = [sFile.events(iEvtNewStim).channels,sFile.events(iEvtStim).channels(iNewStim)];
                        end
                        if isNotes
                            sFile.events(iEvtNewStim).notes    = [sFile.events(iEvtNewStim).notes,   sFile.events(iEvtStim).notes(iNewStim)];
                        end
                    end
                end

                % === NEW RESP ===
                % Only create new response event if name is not "ignore" or "extend"
                if ~strcmpi(evtNewRespName, 'ignore') && ~strcmpi(evtNewRespName, 'extend')
                    % If new stim event does not exist: create
                    if isempty(iEvtNewResp)
                        % Initialize new event
                        iEvtNewResp = length(sFile.events) + 1;
                        sEvent = db_template('event');
                        sEvent.label = evtNewRespName;
                        % Get the default color for this new event
                        sEvent.color = panel_record('GetNewEventColor', iEvtNewResp, sFile.events);
                        % Add new event to list
                        sFile.events(iEvtNewResp) = sEvent;
                    end
                    % Add occurrences
                    sFile.events(iEvtNewResp).times    = [sFile.events(iEvtNewResp).times,    sFile.events(iEvtResp).times(iNewResp)];
                    sFile.events(iEvtNewResp).epochs   = [sFile.events(iEvtNewResp).epochs,   sFile.events(iEvtResp).epochs(iNewResp)];
                    % Sort
                    [sFile.events(iEvtNewResp).times, indSort] = unique(sFile.events(iEvtNewResp).times);
                    sFile.events(iEvtNewResp).epochs   = sFile.events(iEvtNewResp).epochs(indSort);
                    % Channels
                    if isChannels
                        sFile.events(iEvtNewResp).channels = [sFile.events(iEvtNewResp).channels, sFile.events(iEvtResp).channels(iNewResp)];
                        sFile.events(iEvtNewResp).channels = sFile.events(iEvtNewResp).channels(indSort);
                    end
                    % Notes
                    if isNotes
                        sFile.events(iEvtNewResp).notes = [sFile.events(iEvtNewResp).notes,    sFile.events(iEvtResp).notes(iNewResp)];
                        sFile.events(iEvtNewResp).notes = sFile.events(iEvtNewResp).notes(indSort);
                    end
                end
            end
        end

        % ===== SAVE RESULT =====
        % Report results
        if isRaw
            DataMat.F = sFile;
        else
            DataMat.Events = sFile.events;
        end
        % Only save changes if something was change
        if isModified
            bst_save(file_fullpath(sInputs(iFile).FileName), DataMat, 'v6', 1);
        end
        % Return all the input files
        OutputFiles{end+1} = sInputs(iFile).FileName;
    end
end


function timedelay(block)
setup(block);

function setup(block)

% Register number of ports
block.NumInputPorts  = 0;
block.NumOutputPorts = 0;

% Register parameters
block.NumDialogPrms     = 1;

% Register sample times
%  [0 offset]            : Continuous sample time
%  [positive_num offset] : Discrete sample time
%
%  [-1, 0]               : Inherited sample time
%  [-2, 0]               : Variable sample time
block.SampleTimes = [-1 0];
block.SetAccelRunOnTLC(false);

block.RegBlockMethod('Outputs', @Outputs);     % Required
block.RegBlockMethod('Terminate', @Terminate); % Required
block.RegBlockMethod('WriteRTW', @WriteRTW);
%end setup

function Outputs(block) %#ok<INUSD>
%end Outputs

function Terminate(block) %#ok<INUSD>
%end Terminate

function WriteRTW(block)
block.WriteRTWParam('matrix', 'delay', block.DialogPrm(1).Data);


% mff_exportinfo - export MFF 'info.xml' file from EEGLAB structure
%
% Usage:
%   mff_exportinfo(EEG, mffFile);
%
% Inputs:
%  EEG     - EEGLAB structure
%  mffFile - filename/foldername for the MFF file (MFF file/folder must
%            already exist)

% This file is part of mffmatlabio.
%
% mffmatlabio 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.
%
% mffmatlabio 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 mffmatlabio.  If not, see <https://www.gnu.org/licenses/>.

function EEG = mff_exportinfo(EEG, mffFile)

p = fileparts(which('mff_importsignal.m'));
warning('off', 'MATLAB:Java:DuplicateClass');
javaaddpath(fullfile(p, 'MFF-1.2.2-jar-with-dependencies.jar'));
warning('on', 'MATLAB:Java:DuplicateClass');

mfffactorydelegate = javaObject('com.egi.services.mff.api.LocalMFFFactoryDelegate');
mfffactory         = javaObject('com.egi.services.mff.api.MFFFactory', mfffactorydelegate);

infoType = javaObject('com.egi.services.mff.api.MFFResourceType', javaMethod('valueOf', 'com.egi.services.mff.api.MFFResourceType$MFFResourceTypes', 'kMFF_RT_Info'));
if mfffactory.createResourceAtURI(fullfile(mffFile, 'info.xml'), infoType)
    fprintf('Info.xml file created successfully\n');
else
    fprintf('Info.xml ressource already exist, overwriting\n');
end
info = mfffactory.openResourceAtURI( fullfile(mffFile, 'info.xml'), infoType);

info.setMFFVersion(3);
if ~isfield(EEG.etc, 'recordingtime')
    error('Unknow record time');
end
begTime = mff_encodetime(EEG.etc.recordingtime, EEG.etc.timezone);
info.setRecordTime(begTime);
info.saveResource();


classdef prtDataSetBigClass < prtDataSetBig & prtDataInterfaceCategoricalTargetsBig
    % prtDataSetBigClass is a class for prtDataSetBig that are for
    % classification. It is currently a placeholder for future
    % classification specific helper methods.

    methods (Hidden)

        function self = summaryClear(self)
            % self = summaryClear(self)
            self.summaryCache = [];
            self.targetCacheInitialized = false;
        end

        function self = summaryBuild(self,force)
            % self = summaryBuild(self,force)
            %
            if nargin < 2
                force = false;
            end
            if force || isempty(self.summaryCache)
                mrSummary = prtMapReduceSummarizeDataSetClass;
                self.summaryCache = mrSummary.run(self);
                self.targetCache = self.summaryCache.targetCache;
                self.classNames = self.targetCache.classNames;
                self.targetCacheInitialized = true;
            end
        end
    end

    methods

        function self = prtDataSetBigClass(varargin)
            self = prtUtilAssignStringValuePairs(self, varargin{:});
        end

        function n = getNumFeatures(self)
            summary = self.summarize;
            n = summary.nFeatures;
        end

        function n = getNumObservations(self)
            summary = self.summarize;
            n = summary.nObservations;
        end

        function n = getNumTargetDimensions(self)
            summary = self.summarize;
            n = summary.nTargetDimensions;
        end

        function summary = summarize(self)
            summary = self.summaryCache;
            if isempty(summary)
                summary = getDefaultSummaryStruct(self);
            end
        end

        function varargout = plot(self)
            plotHandles = plot(self.getRandomBlock());
            varargout = {};
            if nargout
                varargout = {plotHandles};
            end
        end
    end
    methods (Hidden)
        function summary = getDefaultSummaryStruct(self)

            warning('prtDataSetBigClass:defaultSummary','prtDataSetBig* classes provide default values (nan) for many fields, since these take time to calculate; use ds = ds.summaryBuild to build the summary');
            warning off prtDataSetBigClass:defaultSummary

            summary = struct('upperBounds',nan,'lowerBounds',nan,'nFeatures',nan,...
                'nTargetDimensions',nan,'uniqueClasses',nan,'nClasses',nan,...
                'isMary',nan,'nObservations',nan,'nBlocks',nan,'targetCache',nan,...
                'isSummaryValid',false);
        end
    end
end


function reducedPath = tapas_uniqc_genpath_exclude_subdirs(completePath, pathsExcluded)
% works as genpath, but allows to specify excluded subfolders, which are...
% removed incl. their subfolders from the returned path
%
%    reducedPath = tapas_uniqc_genpath_exclude_subdirs(completePath, pathsExcluded)
%
% IN
%   completePath    the input path, as in genpath
%   pathsExcluded   cell(1, nPaths) of excluded sub-folders or paths
%
% OUT
%   reducedPath     string of returned genpath (e.g., use for addpath),
%                   excluding the specified pathsExcluded and their subfolders.
%
% EXAMPLE
%   tapas_uniqc_genpath_exclude_subdirs
%
%   See also

% Author:   Lars Kasper
% Created:  2020-05-22
% Copyright (C) 2020 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public License (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.
%

%create cell of folder names from path string, split at path separator ; (win) or : (unix)
recursivePath = genpath(completePath);
reducedPath = regexp(recursivePath, pathsep, 'split');
for iExcluded = 1:numel(pathsExcluded)
    % note: regexprep might have some issues w/ escape characters, like \., but helps with Windows \ vs Unix /
    idxIncludedFolders = find(cellfun('isempty', regexp(...
        regexprep(reducedPath, '\\', '/'), ...
        regexprep(pathsExcluded{iExcluded}, '\\', '/')  ) ));
    reducedPath = reducedPath(idxIncludedFolders);
end
% add pathseparator to each path, convert back into path string
reducedPath = cellfun(@(x) [x pathsep], reducedPath, 'UniformOutput', false);
reducedPath = [reducedPath{:}];


classdef MomentumParameterFactory < handle

    methods (Access = public, Static)

        function obj = create(cParams)
            switch cParams.type
                case 'CONSTANT'
                    obj = MomentumParameterConstant(cParams);
                case 'CONVEX'
                    obj = MomentumParameterConvexCase(cParams);
                case 'STRICTLY CONVEX'

            end

        end

    end

end


%% importFaceFile_tetGen
% Below is a demonstration of the features of the |importFaceFile_tetGen| function

%%
clear; close all; clc;

%% Syntax
% |[varargout]=importFaceFile_tetGen(fileName);|

%% Description
% UNDOCUMENTED
%% Examples
%
%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>
%%
% _*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/>.


function success =  runAIforDicom(inputDicomPath,outputDicomPath,...
    sessionPath,algorithm,cmdFlag,savePlanc,varargin)
% function success = runSegForDicom(inputDicomPath,outputDicomPath,...
%   sessionPath,algorithm,varargin)
%
% This function serves as a wrapper for different types of segmentations.
%---------------------------------------------------------------------------------------
% INPUT:
% inputDicomPath  - path to input DICOM directory which needs to be segmented.
% outputDicomPath - path to write DICOM RTSTRUCT for resulting segmentation.
% sessionPath     - path to write temporary segmentation metadata.
% algorithm       - string which specifies segmentation algorithm
% cmdFlag         - "condaEnv" or "singContainer"
% savePlanC       - flag to write planC to file ("yes" or "no")
% --- Optional---
% varargin{1} - Path to segmentation container.
% varargin{2} - Scan no. (replaces input scan identifier)
% varargin{3} - Output assoc. scan no. (replaces output scan identifier)
% varargin{4} - Flag (true/false) to skip export of structure masks (default:true)
%              Set to false if model requires segmentation masks as input
%%---------------------------------------------------------------------------------------
% Following directories are created within the session directory:
% --- ctCERR: contains CERR file/s of input DICOM.
% --- segmentedOrigCERR: CERR file with resulting segmentation fused with
% original CERR file.
% --- segResultCERR: CERR file with segmentation. Note that CERR file can
% be cropped based on initial segmentation.
%
% EXAMPLE: to run BABS segmentation
% inputDicomPath = '';
% outputDicomPath = '';
% sessionPath = '';
% algorithm = 'BABS';
% cmdFlag = 'singcontainer';
%savePlanc = 'yes'
% success = runSegForDicom(inputDicomPath,outputDicomPath,sessionPath,...
%           algorithm,babsPath);
% ------------------------------------------------------------------------------------
% APA, 12/14/2018
% RKP, 9/11/19 Updates for compatibility with training pipeline
% AI, 2/7/2020 Added separate DICOM export functions for BABS and DL algorithms
% AI, 3/5/2020 Updates to handle multiple algorithms
% AI, 8/12/22  Call runSegForPlanC

if nargin <=7
    scanNumV = [];
else
    scanNumV = varargin{2};
end
if nargin <=8
    assocScanNumV = [];
else
    assocScanNumV = varargin{3};
end
if nargin <=9
    skipMaskExport = true;
else
    if ischar(varargin{4})
        skipMaskExport = logical(eval(varargin{4}));
    else
        skipMaskExport = varargin{4};
    end
end

%% Create session directory to write segmentation metadata
if inputDicomPath(end) == filesep
    [~,folderNam] = fileparts(inputDicomPath(1:end-1));
else
    [~,folderNam] = fileparts(inputDicomPath);
end
dateTimeV = clock;
randStr = sprintf('%6.3f',rand*1000);
sessionDir = ['session',folderNam,num2str(dateTimeV(4)), num2str(dateTimeV(5)),...
    num2str(dateTimeV(6)), randStr];
fullSessionPath = fullfile(sessionPath,sessionDir);
while exist(fullSessionPath,'dir')
    randStr = sprintf('%6.3f',rand*1000);
    sessionDir = [sessionDir, randStr];
    fullSessionPath = fullfile(sessionPath,sessionDir);
end

% Create sub-directories
%-For  CERR files
mkdir(fullSessionPath)
cerrPath = fullfile(fullSessionPath,'dataCERR');
mkdir(cerrPath)
outputCERRPath = fullfile(fullSessionPath,'outputOrigCERR');
mkdir(outputCERRPath)
AIresultCERRPath = fullfile(fullSessionPath,'AIResultCERR');
mkdir(AIresultCERRPath)
%-For structname-to-label map
AIoutputPath = fullfile(fullSessionPath,'AIoutput');
mkdir(AIoutputPath);

%% Import DICOM to CERR
tic
recursiveFlag = true;
importDICOM(inputDicomPath,cerrPath,recursiveFlag);
toc

% Get container path
containerPath = varargin{1};

%% Run inference
newSessionFlag = false;
savePlancFlag = 0;
if strcmpi(savePlanc,'yes')
    savePlancFlag = 1;
end
if ~any(strcmpi(algorithm,'BABS'))

    % Get segmentations
    [~,origScanNumV,allLabelNamesC,dcmExportOptS] = runAIforPlanC(cerrPath,...
        fullSessionPath,algorithm,cmdFlag,newSessionFlag,[],[],...
        containerPath,scanNumV,assocScanNumV,skipMaskExport);

    % Export segmentations to DICOM RTSTRUCT files
    fprintf('\nExporting to DICOM format...');
    tic
    batchExportAISegToDICOM(cerrPath,origScanNumV,allLabelNamesC,outputCERRPath,...
        outputDicomPath,dcmExportOptS,savePlancFlag)
    toc

else

    babsPath = varargin{1};
    success = babsSegmentation(cerrPath,fullSessionPath,babsPath,AIresultCERRPath);

    % Export the RTSTRUCT file
    savePlancFlag = 0;
    if strcmpi(savePlanc,'yes')
        savePlancFlag = 1;
    end
    exportCERRtoDICOM_forBABS(origCerrPath,AIresultCERRPath,outputCERRPath,...
        outputDicomPath,dcmExportOptS,savePlancFlag)

end

% Remove session directory
rmdir(fullSessionPath, 's')

success = 0;


function aedat = TrimTime(aedat, startTime, endTime, reZero)

%{
default is not to rezero
Times come in as seconds
%}

dbstop if error

if ~exist('startTime', 'var')
    startTime = 0;
end

if ~exist('endTime', 'var') || endTime == 0
    endTime = double(aedat.info.lastTimeStamp) / 1e6;
end

% Choose the right data type to avoid converting the timestamps when a
% comparison is necessary.
if ~isfield(aedat.info, 'fileFormat') || aedat.info.fileFormat == 2
    startTime = uint32(startTime * 1e6);
    endTime = uint32(endTime * 1e6);
else
    startTime = uint64(startTime * 1e6);
    endTime = uint64(endTime * 1e6);
end

if ~isfield(aedat, 'data')
    disp('No data found')
    return
end

%% Special

if isfield(aedat.data, 'special')
    keepLogical = aedat.data.special.timeStamp >= startTime & aedat.data.special.timeStamp <= endTime;
    aedat.data.special.timeStamp = aedat.data.special.timeStamp(keepLogical);
    aedat.data.special.address   = aedat.data.special.address  (keepLogical);
end

%% Polarity

if isfield(aedat.data, 'polarity')
    keepLogical = aedat.data.polarity.timeStamp >= startTime & aedat.data.polarity.timeStamp <= endTime;
    aedat.data.polarity.timeStamp = aedat.data.polarity.timeStamp (keepLogical);
    aedat.data.polarity.x           = aedat.data.polarity.x       (keepLogical);
    aedat.data.polarity.y           = aedat.data.polarity.y       (keepLogical);
    aedat.data.polarity.polarity    = aedat.data.polarity.polarity(keepLogical);
end

%% Frames

if isfield(aedat.data, 'frame')
    keepLogical = aedat.data.frame.timeStampStart >= startTime & aedat.data.frame.timeStampStart <= endTime;
    aedat.data.frame.timeStampStart = aedat.data.frame.timeStampStart(keepLogical);
    aedat.data.frame.timeStampEnd   = aedat.data.frame.timeStampEnd  (keepLogical);
    aedat.data.frame.samples        = aedat.data.frame.samples       (keepLogical);
    aedat.data.frame.xLength        = aedat.data.frame.xLength       (keepLogical);
    aedat.data.frame.yLength        = aedat.data.frame.yLength       (keepLogical);
    aedat.data.frame.xPosition      = aedat.data.frame.xPosition     (keepLogical);
    aedat.data.frame.yPosition      = aedat.data.frame.yPosition     (keepLogical);
end

%% Imu6

if isfield(aedat.data, 'imu6')
    keepLogical = aedat.data.imu6.timeStamp >= startTime & aedat.data.imu6.timeStamp <= endTime;
    aedat.data.imu6.timeStamp = aedat.data.imu6.timeStamp (keepLogical);
    aedat.data.imu6.accelX    = aedat.data.imu6.accelX    (keepLogical);
    aedat.data.imu6.accelY    = aedat.data.imu6.accelY    (keepLogical);
    aedat.data.imu6.accelZ    = aedat.data.imu6.accelZ    (keepLogical);
    aedat.data.imu6.gyroX     = aedat.data.imu6.gyroX     (keepLogical);
    aedat.data.imu6.gyroY     = aedat.data.imu6.gyroY     (keepLogical);
    aedat.data.imu6.gyroZ     = aedat.data.imu6.gyroZ     (keepLogical);
    aedat.data.imu6.temperature = aedat.data.imu6.temperature(keepLogical);
end

%% SAMPLE, EAR TODO

%% Point1D

if isfield(aedat.data, 'point1D')
    keepLogical = aedat.data.point1D.timeStamp >= startTime ...
                & aedat.data.point1D.timeStamp <= endTime;
    aedat.data.point1D.timeStamp = aedat.data.point1D.timeStamp(keepLogical);
    aedat.data.point1D.type      = aedat.data.point1D.type     (keepLogical);
    aedat.data.point1D.x         = aedat.data.point1D.x        (keepLogical);
end

%% Point2D

if isfield(aedat.data, 'point2D')
    keepLogical = aedat.data.point2D.timeStamp >= startTime ...
                & aedat.data.point2D.timeStamp <= endTime;
    aedat.data.point2D.timeStamp = aedat.data.point2D.timeStamp(keepLogical);
    aedat.data.point2D.type      = aedat.data.point2D.type     (keepLogical);
    aedat.data.point2D.x         = aedat.data.point2D.x        (keepLogical);
    aedat.data.point2D.y         = aedat.data.point2D.y        (keepLogical);
end

%% Point3D

if isfield(aedat.data, 'point3D')
    keepLogical = aedat.data.point3D.timeStamp >= startTime ...
                & aedat.data.point3D.timeStamp <= endTime;
    aedat.data.point3D.timeStamp = aedat.data.point3D.timeStamp(keepLogical);
    aedat.data.point3D.type      = aedat.data.point3D.type     (keepLogical);
    aedat.data.point3D.x         = aedat.data.point3D.x        (keepLogical);
    aedat.data.point3D.y         = aedat.data.point3D.y        (keepLogical);
    aedat.data.point3D.z         = aedat.data.point3D.z        (keepLogical);
end

%% Tidy up

aedat = NumEventsByType(aedat);
aedat = FindFirstAndLastTimeStamps(aedat);

%% Rezero

if exist('reZero', 'var') && reZero
    aedat = ZeroTime(aedat);
end


function [metabolite_structure] = addMetFormulaCharge(metabolite_structure,startSearch,endSearch)
% This function uses getInchiString2ChargedFormula.m to calculate charge
% and neutral formula.
%
% INPUT
% metabolite_structure  metabolite structure
% startSearch           specify where the search should start in the
%                       metabolite structure. Must be numeric (optional, default: all metabolites
%                       in the structure will be search for)
% endSearch             specify where the search should end in the
%                       metabolite structure. Must be numeric (optional, default: all metabolites
%                       in the structure will be search for)
% OUTPUT
% metabolite_structure  updated metabolite structure
%
% Ines Thiele 09/21
annotationSource = 'Calculated using metaboAnnotator and inchiString';
annotationType = 'automatic';

F = fieldnames(metabolite_structure);
if ~exist('startSearch','var')
    startSearch = 1;
end
if ~exist('endSearch','var')
    endSearch = length(F);
end

for i = startSearch : endSearch
    if (isempty(metabolite_structure.(F{i}).chargedFormula) || ~isempty(find(isnan(metabolite_structure.(F{i}).chargedFormula)))) && ...
            ~isempty(metabolite_structure.(F{i}).inchiString) && isempty(find(isnan(metabolite_structure.(F{i}).inchiString)))
        % compute charged formula for each entry from inchiString
        inchiString = metabolite_structure.(F{i}).inchiString;
        [metFormulaNeutral,metFormulaCharged,metCharge] = getInchiString2ChargedFormula({metabolite_structure.(F{i}).VMHId},cellstr(inchiString));
        if ~isempty(metFormulaCharged)
            metabolite_structure.(F{i}).chargedFormula = metFormulaCharged;
            metabolite_structure.(F{i}).chargedFormula_source = [annotationSource,':',annotationType,':',datestr(now)];
            metabolite_structure.(F{i}).charge = metCharge;
            metabolite_structure.(F{i}).charge_source = [annotationSource,':',annotationType,':',datestr(now)];
        end
        metabolite_structure.(F{i}).neutralFormula = metFormulaNeutral;
        metabolite_structure.(F{i}).neutralFormula_source = [annotationSource,':',annotationType,':',datestr(now)];
    end
end


function writeCbModel(model,format,fileName,compSymbolList,compNameList,sbmlLevel,sbmlVersion)
%writeCbModel Write out COBRA models in various formats
%
% writeCbModel(model,format,fileName,compSymbolList,compNameList,sbmlLevel,sbmlVersion)
%
%INPUTS
% model             Standard COBRA model structure
% format            File format to be used ('text','xls' or 'sbml')
%
%OPTIONAL INPUTS
% fileName          File name for output file (optional, default opens
%                   dialog box)
% compSymbolList    List of compartment symbols
% compNameList      List of compartment names corresponding to compSymbolList
% sbmlLevel         SBML Level (default = 2)
% sbmlVersion       SBML Version (default = 1)
%
% Markus Herrgard 2/5/07
% Ines Thiele 01/10 - Added more options for field to write in xls format
% Richard Que (3/17/10) Added ability to specify compartment names and
%                       symbols

if ~exist('compSymbolList','var') || isempty(compSymbolList)
    compSymbolList = {'c','m','v','x','e','t','g','r','n','p','l','y'};
    compNameList = {'Cytoplasm','Mitochondrion','Vacuole','Peroxisome','Extracellular','Pool','Golgi','Endoplasmic_reticulum','Nucleus','Periplasm','Lysosome','Glycosome'};
end

if nargin < 6
    sbmlLevel = 2;
    sbmlVersion = 1;
end

[nMets,nRxns] = size(model.S);

formulas = printRxnFormula(model,model.rxns,false,false,false,1,false);

%% Open a dialog to select file name
if (nargin < 3 & ~strcmp(format,'sbml'))
    switch format
        case 'xls'
            [fileNameFull,filePath] = uiputfile({'*.xls'});
        case {'text','txt'}
            [fileNameFull,filePath] = uiputfile({'*.txt'});
        case 'xml'
            [fileNameFull,filePath] = uiputfile({'*.xml'});
        otherwise
            [fileNameFull,filePath] = uiputfile({'*'});
    end
    if (fileNameFull)
        [t1,t2,t3,t4,tokens] = regexp(fileNameFull,'(\w*)\.(\w*)');
        fileName = [filePath tokens{1}{1}];
        switch tokens{1}{2}
            case 'xls'
                format = 'xls';
            case 'txt'
                format = 'text';
                fileName = [fileName '.txt'];
            case 'xml'
                format = 'sbml';
%                 fprintf('Note that you will be asked to supply the file name again (this is a feature, not a bug)');
            otherwise
                format = 'unknown';
        end
    else
        return;
    end
end
switch format
    %% Text file
    case {'text','txt'}
        fid = fopen(fileName,'w');
        fprintf(fid,'Rxn name\t');
        if (isfield(model,'rxnNames'))
            fprintf(fid,'Rxn description\t');
        end
        fprintf(fid,'Formula\t');
        if (isfield(model,'grRules'))
            fprintf(fid,'Gene-reaction association\t');
        end
        fprintf(fid,'Reversible\tLB\tUB\tObjective\n');
        for i = 1:nRxns
            fprintf(fid,'%s\t',model.rxns{i});
            if (isfield(model,'rxnNames'))
                fprintf(fid,'%s\t',model.rxnNames{i});
            end
            fprintf(fid,'%s\t',formulas{i});
            if (isfield(model,'grRules'))
                fprintf(fid,'%s\t',model.grRules{i});
            end
            fprintf(fid,'%d\t%6.2f\t%6.2f\t%6.2f\n',model.rev(i),model.lb(i),model.ub(i),model.c(i));
        end
        fprintf(fid,'Metabolite name\tMetabolite description\tMetabolite formula\n');
        for i = 1:nMets
            fprintf(fid,'%s',model.mets{i});
            if isfield(model,'metNames')
                fprintf(fid,'\t%s',model.metNames{i});
            end
            if isfield(model,'metFormulas')
                fprintf(fid,'\t%s',model.metFormulas{i});
            end
            fprintf(fid,'\n');
        end
        fclose(fid);
        %% Excel file
    case 'xls'
        tmpData{1,1} = 'Abbreviation';
        tmpData{1,2} = 'Description';
        baseInd = 3;
        tmpData{1,baseInd} = 'Reaction';
        tmpData{1,baseInd+1} = 'GPR';
        tmpData{1,baseInd+2} = 'Genes';
        tmpData{1,baseInd+3} = 'Proteins';
        tmpData{1,baseInd+4} = 'Subsystem';
        tmpData{1,baseInd+5} = 'Reversible';
        tmpData{1,baseInd+6} = 'Lower bound';
        tmpData{1,baseInd+7} = 'Upper bound';
        tmpData{1,baseInd+8} = 'Objective';
        tmpData{1,baseInd+9} = 'Confidence Score';
        tmpData{1,baseInd+10} = 'EC Number';
        tmpData{1,baseInd+11} = 'Notes';
        tmpData{1,baseInd+12} = 'References';
        for i = 1:nRxns
            tmpData{i+1,1} = chopForExcel(model.rxns{i});
            if (isfield(model,'rxnNames'))
                tmpData{i+1,2} = chopForExcel(model.rxnNames{i});
            else
                tmpData{i+1,2} =  '';
            end

            tmpData{i+1,baseInd} = chopForExcel(formulas{i});
            if (isfield(model,'geneNameRules'))
                tmpData{i+1,baseInd+1} = chopForExcel(model.geneNameRules{i});
            elseif (isfield(model,'grRules'))
                tmpData{i+1,baseInd+1} = chopForExcel(model.grRules{i});
            else
                tmpData{i+1,baseInd+1} = '';
            end
            if (isfield(model,'geneNames'))
                geneNames = model.geneNames(model.rxnGeneMat(i,:) == 1);
                tmpData{i+1,baseInd+2} = constructGeneStr(geneNames);
            elseif (isfield(model,'genes'))
                geneNames = model.genes(model.rxnGeneMat(i,:) == 1);
                tmpData{i+1,baseInd+2} = constructGeneStr(geneNames);
            else
                tmpData{i+1,baseInd+2} = '';
            end
            if (isfield(model,'proteins'))
                tmpData{i+1,baseInd+3} = chopForExcel(model.proteins{i});
            else
                tmpData{i+1,baseInd+3} = '';
            end
            if (isfield(model,'subSystems'))
                tmpData{i+1,baseInd+4} = chopForExcel(char(model.subSystems{i}));
            else
                tmpData{i+1,baseInd+4} = '';
            end
            tmpData{i+1,baseInd+5} = model.rev(i)*1.0;
            tmpData{i+1,baseInd+6} = model.lb(i);
            tmpData{i+1,baseInd+7} = model.ub(i);
            tmpData{i+1,baseInd+8} = model.c(i);
            if (isfield(model,'confidenceScores'))
                tmpData{i+1,baseInd+9} =  chopForExcel(num2str(model.confidenceScores{i}));
            else
                tmpData{i+1,baseInd+9} = '';
            end
            if (isfield(model,'rxnECNumbers'))
                tmpData{i+1,baseInd+10} = chopForExcel(model.rxnECNumbers{i});
            else
                tmpData{i+1,baseInd+10} = '';
            end
            if (isfield(model,'rxnNotes'))
                tmpData{i+1,baseInd+11} = chopForExcel(char(model.rxnNotes{i}));
            else
                tmpData{i+1,baseInd+11} = '';
            end
            if (isfield(model,'rxnReferences'))
                tmpData{i+1,baseInd+12} = chopForExcel(char(model.rxnReferences{i}));
            else
                tmpData{i+1,baseInd+12} = '';
            end
        end
        %keyboard
        xlswrite(fileName,tmpData,'Reaction List');
        if isfield(model,'metNames')
            tmpMetData{1,1} = 'Abbreviation';
            tmpMetData{1,2} = 'Description';
            tmpMetData{1,3} = 'Neutral formula';
            tmpMetData{1,4} = 'Charged formula';
            tmpMetData{1,5} = 'Charge';
            tmpMetData{1,6} = 'Compartment';
            tmpMetData{1,7} = 'KEGG ID';
            tmpMetData{1,8} = 'PubChem ID';
            tmpMetData{1,9} = 'ChEBI ID';
            tmpMetData{1,10} = 'InChI String';
            tmpMetData{1,11} = 'SMILES';
            for i = 1:nMets
                tmpMetData{i+1,1} = chopForExcel(model.mets{i});
                tmpMetData{i+1,2} = chopForExcel(model.metNames{i});
                if isfield(model,'metFormulasNeutral')
                    tmpMetData{i+1,3} = chopForExcel(model.metFormulasNeutral{i});
                else
                    tmpMetData{i+1,3} = '';
                end
                if isfield(model,'metFormulas')
                    tmpMetData{i+1,4} = chopForExcel(model.metFormulas{i});
                else
                    tmpMetData{i+1,4} = '';
                end
                if isfield(model,'metCharge')
                    tmpMetData{i+1,5} = chopForExcel(model.metCharge(i));
                else
                    tmpMetData{i+1,5} = '';
                end
                if isfield(model,'metCompartment')
                    tmpMetData{i+1,6} = chopForExcel(model.metCompartment{i});
                else
                    tmpMetData{i+1,6} = '';
                end
                if isfield(model,'metKEGGID')
                    tmpMetData{i+1,7} = chopForExcel(model.metKEGGID{i});
                else
                    tmpMetData{i+1,7} = '';
                end
                if isfield(model,'metPubChemID')
                    if iscell(model.metPubChemID(i))
                    tmpMetData{i+1,8} = chopForExcel(model.metPubChemID{i});
                    else
                    tmpMetData{i+1,8} = chopForExcel(model.metPubChemID(i));
                    end
                else
                    tmpMetData{i+1,8} = '';
                end
                if isfield(model,'metChEBIID')

                    tmpMetData{i+1,9} = chopForExcel(model.metChEBIID(i));
                else
                    tmpMetData{i+1,9} = '';
                end
                if isfield(model,'metInchiString')
                    tmpMetData{i+1,10} = chopForExcel(model.metInchiString{i});
                else
                    tmpMetData{i+1,10} = '';
                end
                if isfield(model,'metSmiles')
                    tmpMetData{i+1,11} = chopForExcel(model.metSmiles{i});
                else
                    tmpMetData{i+1,11} = '';
                end
            end
            xlswrite(fileName,tmpMetData,'Metabolite List');
        else
            xlswrite(fileName,model.mets,'Metabolite List');
        end
        %% SBML
    case 'sbml'
        sbmlModel = convertCobraToSBML(model,sbmlLevel,sbmlVersion,compSymbolList,compNameList);
        if exist('fileName','var')&&~isempty(fileName)
            OutputSBML(sbmlModel,fileName);
        else
            OutputSBML(sbmlModel);
        end
        %% Unknown
    otherwise
        error('Unknown file format');
end

%% Chop strings for excel output
function strOut = chopForExcel(str)

if (length(str) > 5000)
    strOut = str(1:5000);
    fprintf('String longer than 5000 characters - truncated for Excel output\n%s\n',str);
else
    strOut = str;
end

%% Construct gene name string
function geneStr = constructGeneStr(geneNames)

geneStr = '';
for i = 1:length(geneNames)
    geneStr = [geneStr ' ' geneNames{i}];
end
geneStr = strtrim(geneStr);


function varargout = mrFlatMesh(varargin)
% MRFLATMESH Application M-file for mrFlatMesh.fig (cortical flattening)
%
%    FIG = MRFLATMESH launch mrFlatMesh GUI.
%
%    MRFLATMESH('callback_name', ...) invokes the named callback.
%
% Last Modified by GUIDE v2.5 17-Sep-2009 15:29:36

if (nargin == 0 || iscell(varargin{1}))  % LAUNCH GUI

    fig = openfig(mfilename,'reuse');

    % Generate a structure of handles to pass to callbacks, and store it.
    handles = guihandles(fig);
    if(nargin>0 && iscell(varargin{1}))
        % Set the specified defaults
        param = varargin{1};
        for(ii=1:2:length(param))
            if(strcmp(param{ii},'grayPath'))
                set(handles.inputGrayPath, 'String', param{ii+1});
            end
            if(strcmp(param{ii},'meshPath'))
                set(handles.inputMeshPath, 'String', param{ii+1});
            end
            if(strcmp(param{ii},'savePath'))
                set(handles.inputSavePath, 'String',  param{ii+1});
            end
            if(strcmp(param{ii},'startXYZ'))
                set(handles.editStartX, 'String', num2str(param{ii+1}(1)));
                set(handles.editStartY, 'String', num2str(param{ii+1}(2)));
                set(handles.editStartZ, 'String', num2str(param{ii+1}(3)));
            end
            if(strcmp(param{ii},'unfoldRadiusMM'))
                set(handles.editUnfoldSize, 'String', num2str(param{ii+1}));
            end
            if(strcmp(param{ii},'hemi'))
                hemi = getPopupValue(handles.popHemi,param{ii+1});
                set(handles.popHemi, 'Value',hemi);
            end
        end
    end

    guidata(fig, handles);

    if (nargout > 0)
        varargout{1} = fig;
    end % endif nargout

elseif ischar(varargin{1}) % INVOKE NAMED SUBFUNCTION OR CALLBACK

%     try
         [varargout{1:nargout}] = feval(varargin{:}); % FEVAL switchyard
%     catch
%         disp(lasterr);
%     end % end try/catch

end % end function

%| ABOUT CALLBACKS:
%| GUIDE automatically appends subfunction prototypes to this file, and
%| sets objects' callback properties to call them through the FEVAL
%| switchyard above. This comment describes that mechanism.
%|
%| Each callback subfunction declaration has the following form:
%| <SUBFUNCTION_NAME>(H, EVENTDATA, HANDLES, VARARGIN)
%|
%| The subfunction name is composed using the object's Tag and the
%| callback type separated by '_', e.g. 'slider2_Callback',
%| 'figure1_CloseRequestFcn', 'axis1_ButtondownFcn'.
%|
%| H is the callback object's handle (obtained using GCBO).
%|
%| EVENTDATA is empty, but reserved for future use.
%|
%| HANDLES is a structure containing handles of components in GUI using
%| tags as fieldnames, e.g. handles.figure1, handles.slider2. This
%| structure is created at GUI startup using GUIHANDLES and stored in
%| the figure's application data using GUIDATA. A copy of the structure
%| is passed to each callback.  You can store additional information in
%| this structure at GUI startup, and you can change the structure
%| during callbacks.  Call guidata(h, handles) after changing your
%| copy to replace the stored original so that subsequent callbacks see
%| the updates. Type "help guihandles" and "help guidata" for more
%| information.
%|
%| VARARGIN contains any extra arguments you have passed to the
%| callback. Specify the extra arguments by editing the callback
%| property in the inspector. By default, GUIDE sets the property to:
%| <MFILENAME>('<SUBFUNCTION_NAME>', gcbo, [], guidata(gcbo))
%| Add any extra arguments after the last argument, before the final
%| closing parenthesis.

%-------------------------
function retVar=browseGrayButton_Callback(h,eventData,handles)
%thisFig=gcf;
set (handles.status,'String','Waiting for gray matter segmentation file.');

grayFile = get(handles.inputGrayPath,'String');
if isempty(grayFile), grayFile = meshFindPath(handles); end

[fileName,inputGrayPath]=myUiGetFile(grayFile,{'*.?ray'; '*.mat'},'Get .gray file or coords.mat file');
if fileName == 0, disp('User canceled.'); return; end

set(handles.inputGrayPath,'String',fullfile(inputGrayPath,fileName));
set (handles.status,'String','Gray file set');
return;

%-------------------------
function retVar=browseMeshButton_Callback(h,eventData,handles)
%thisFig=gcf;
set (handles.status,'String','Waiting for mrGray mesh file *.MrM or *.mrm.');

inputMeshPath = get(handles.inputMeshPath,'String');
if isempty(inputMeshPath), inputMeshPath = meshFindPath(handles); end

[fileName,inputMeshPath]=myUiGetFile(inputMeshPath,{'*.mrm'; '*nii.gz'},'Get mesh file *.MrM or *.mrm or *.nii.gz');
if  fileName == 0, disp('User canceled.'); return; end

set(handles.inputMeshPath,'String',fullfile(inputMeshPath,fileName));
set (handles.status,'String','Mesh file set');
return;

%-------------------------
function retVar=saveMeshFile_Callback(h,eventData,handles)
%thisFig=gcf;
set (handles.status,'String','Setting output path.');

savePath = get(handles.inputSavePath,'String');
if isempty(savePath), savePath = meshFindPath(handles); end

[fileName,outputMeshPath]=myUiPutFile(savePath,'*.mat','Select an output file');
if  fileName == 0, disp('User canceled.'); return; end

set(handles.inputSavePath,'String',fullfile(outputMeshPath,fileName));
set (handles.status,'String','Output path set');
return;

%-------------------------
function retVar=go_Callback(h,eventData,handles)
% Press Go (green) button (Go!)

set (handles.status,'String','Running unfold.');
% Get filenames, coords and unfold size
meshFileName=get(handles.inputMeshPath,'String');
grayFileName=get(handles.inputGrayPath,'String');
flatFileName=get(handles.inputSavePath,'String');

xPos=str2num(get(handles.editStartX,'String'));
yPos=str2num(get(handles.editStartY,'String'));
zPos=str2num(get(handles.editStartZ,'String'));
if isempty(xPos) | isempty(yPos) | isempty(zPos)
    errordlg('You must fill in the starting positions.');
    return;
end
startPos=[xPos,yPos,zPos];

classes = get(handles.popHemi,'String');
hemi = classes{get(handles.popHemi,'value')};

% sagMM=get(handles.editScaleSag,'String');
% axMM=get(handles.editScaleAx,'String');
% corMM=get(handles.editScaleCor,'String');
% scaleFactor=[eval(sagMM),eval(axMM),eval(corMM)];
scaleFactor = [];

unfoldSize=eval(get(handles.editUnfoldSize,'String'));

contents = get(handles.popSpacing,'String');
spacingMethod = contents{get(handles.popSpacing,'Value')};
gridSpacing = str2double(get(handles.editGridSpacing,'String'));

showFigures=get(handles.showFiguresCheck,'Value');
saveExtra=get(handles.saveExtraCheck,'Value');
truePerimDist=get(handles.perimDistCheck,'Value');

set(handles.status,'UserData','unfoldMesh v1.7 2001');
% Go, go ,go !!!!
statusHandle=handles.status;
busyHandle=handles.busyBar;
unfoldMeshFromGUI(meshFileName,grayFileName,flatFileName,startPos,scaleFactor,unfoldSize,statusHandle,busyHandle,spacingMethod,gridSpacing,showFigures,saveExtra,truePerimDist,hemi);

return;

%-------------------------
function retVal=cancel_Callback(h,eventData,handles)
% This button actually says Close (in red) on the screen.
close(handles.figure1);
return;

%-------------------------
function retVal=help_Callback(h,eventData,handles)
web1 = 'web([''file:///'' which(''unfoldMeshHelp.html'')],''-browser'')';
web2 = 'web([''file:///'' which(''unfoldMeshHelp.html'')])';

eval(web1,web2);

return;

%-----------------------------------------------
function p = meshFindPath(handles)
% Method for finding the best current path during selection. This
% code is supposed to reduce the amount of clicking around to find files
%

p = get(handles.inputGrayPath,'String');
if ~isempty(p), p=fileparts(p); return;
else p = get(handles.inputMeshPath,'String');
    if ~isempty(p), p=fileparts(p); return;
    else
        p = fileparts(getAnatomyPath('wandell'));
    end
end
return;

% --- Executes during object creation, after setting all properties.
function xaxisX_CreateFcn(hObject, eventdata, handles)
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end
return;

%-------------------------
function xaxisX_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function xAxisY_CreateFcn(hObject, eventdata, handles)
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end
return;

%-------------------------
function xAxisY_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function xAxisZ_CreateFcn(hObject, eventdata, handles)
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end
return;

function xAxisZ_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function xAxisX_CreateFcn(hObject, eventdata, handles)
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

return;

function xAxisX_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function popSpacing_CreateFcn(hObject, eventdata, handles)
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end
return;

% --- Executes on selection change in popSpacing.
function popSpacing_Callback(hObject, eventdata, handles)
% Don't do anything.  We read the pop-up when we calculate in the go
% routine above.
return;

% --- Executes during object creation, after setting all properties.
function editGridSpacing_CreateFcn(hObject, eventdata, handles)
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end
return;

function editGridSpacing_Callback(hObject, eventdata, handles)
str2double(get(hObject,'String'));
return;

% --------------------------------------------------------------------
function menuFile_Callback(hObject, eventdata, handles)

% --------------------------------------------------------------------
function menuFileSaveParams_Callback(hObject, eventdata, handles)
% File |Save
% This should get the handles from the edit boxes and save them to a
% user-chosen matlab file

% something like:
% params = mrfGet(handles,'editFields');
% fullFileName = mrSelectFile('w');
% if isempty(fullFileName), disp('User canceled.'); end
% save(fullFileName,'params');%
%

disp('Not yet implemented')
return

% --------------------------------------------------------------------
function menuFileLoadParams_Callback(hObject, eventdata, handles)
% File | Load Params
% This should read a matlab file containing the editable fields and set
% them.
%
% Inverse of the save above with a
% handles = mrfSet(handles,'editFields',params);
%
disp('Not yet implemented')
return

% --------------------------------------------------------------------
function menuFileQuit_Callback(hObject, eventdata, handles)
cancel_Callback(hObject,eventdata,handles);
return;

% --------------------------------------------------------------------
function ii = getPopupValue(h,target)
% Given a handle to a popup, h, find the value
str = get(h,'String');
if ~iscell(str)  % Only one term
    if strcmpi(str,target), ii = 1; return; end
else
    for ii=1:length(str)
        if strcmpi(str{ii},target)
            return;
        end
    end
end

% Returns a safe value, but maybe it should be null?
ii = 1;
return;


function [BstMriFile, sMri, Messages] = import_mri(iSubject, MriFile, FileFormat, isInteractive, isAutoAdjust, Comment, Labels)
% IMPORT_MRI: Import a MRI file in a subject of the Brainstorm database
%
% USAGE: [BstMriFile, sMri, Messages] = import_mri(iSubject, MriFile, FileFormat='ALL', isInteractive=0, isAutoAdjust=1, Comment=[], Labels=[])
%               BstMriFiles = import_mri(iSubject, MriFiles, ...)   % Import multiple volumes at once
%
% INPUT:
%    - iSubject  : Indice of the subject where to import the MRI
%                  If iSubject=0 : import MRI in default subject
%    - MriFile   : Full filename of the MRI to import (format is autodetected)
%                  => if not specified : file to import is asked to the user
%    - FileFormat : String, one on the file formats in in_mri
%    - isInteractive : If 1, importation will be interactive (MRI is displayed after loading)
%    - isAutoAdjust  : If isInteractive=0 and isAutoAdjust=1, relice/resample automatically without user confirmation
%    - Comment       : Comment of the output file
%    - Labels        : Labels attached to this file (cell array {Nlabels x 3}: {index, text, RGB})
% OUTPUT:
%    - BstMriFile : Full path to the new file if success, [] if error
%    - sMri       : Brainstorm MRI structure
%    - Messages   : String, messages reported by this function

% @=============================================================================
% 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 =====
if (nargin < 3) || isempty(FileFormat)
    FileFormat = 'ALL';
end
if (nargin < 4) || isempty(isInteractive)
    isInteractive = 0;
end
if (nargin < 5) || isempty(isAutoAdjust)
    isAutoAdjust = 1;
end
if (nargin < 6) || isempty(Comment)
    Comment = [];
end
if (nargin < 7) || isempty(Labels)
    Labels = [];
end
% Initialize returned variables
BstMriFile = [];
sMri = [];
Messages = [];
% Get Protocol information
ProtocolInfo     = bst_get('ProtocolInfo');
ProtocolSubjects = bst_get('ProtocolSubjects');
% Default subject
if (iSubject == 0)
	sSubject = ProtocolSubjects.DefaultSubject;
% Normal subject
else
    sSubject = ProtocolSubjects.Subject(iSubject);
end

%% ===== SELECT MRI FILE =====
% If MRI file to load was not defined : open a dialog box to select it
if isempty(MriFile)
    % Get last used directories
    LastUsedDirs = bst_get('LastUsedDirs');
    % Get last used format
    DefaultFormats = bst_get('DefaultFormats');
    if isempty(DefaultFormats.MriIn)
        DefaultFormats.MriIn = 'ALL';
    end
    % Get MRI file
    [MriFile, FileFormat, FileFilter] = java_getfile( 'open', ...
        'Import MRI...', ...              % Window title
        LastUsedDirs.ImportAnat, ...      % Default directory
        'multiple', 'files_and_dirs', ... % Selection mode
        bst_get('FileFilters', 'mri'), ...
        DefaultFormats.MriIn);
    % If no file was selected: exit
    if isempty(MriFile)
        return
    end
    % Expand file selection (if inputs are folders)
    MriFile = file_expand_selection(FileFilter, MriFile);
    if isempty(MriFile)
        error(['No ' FileFormat ' file in the selected directories.']);
    end
    % Save default import directory
    LastUsedDirs.ImportAnat = bst_fileparts(MriFile{1});
    bst_set('LastUsedDirs', LastUsedDirs);
    % Save default import format
    DefaultFormats.MriIn = FileFormat;
    bst_set('DefaultFormats',  DefaultFormats);
end

%% ===== DICOM CONVERTER =====
TmpDir = [];
if strcmpi(FileFormat, 'DICOM-SPM')
    % Convert DICOM to NII
    DicomFiles = MriFile;
    TmpDir = bst_get('BrainstormTmpDir', 0, 'dicom');
    MriFile = in_mri_dicom_spm(DicomFiles, TmpDir, isInteractive);
    if isempty(MriFile)
        return;
    end
    FileFormat = 'Nifti1';
end

%% ===== LOOP ON MULTIPLE MRI =====
if iscell(MriFile) && (length(MriFile) == 1)
    MriFile = MriFile{1};
elseif iscell(MriFile) && ~strcmpi(FileFormat, 'SPM-TPM')
    % Only allow multiple import if there is already a MRI
    if isempty(sSubject.Anatomy)
        error(['You must import the first MRI in the subject folder separately.' 10 'Please select only one volume at a time.']);
    end
    % Initialize returned values
    nFiles = length(MriFile);
    BstMriFile = cell(1, nFiles);
    sMri = cell(1, nFiles);
    % Import all volumes without supervision
    for i = 1:nFiles
        [BstMriFile{i}, sMri{i}] = import_mri(iSubject, MriFile{i}, FileFormat, isInteractive, isAutoAdjust);
    end
    % All the files are imported: exit
    return;
end

%% ===== LOAD MRI FILE =====
isProgress = bst_progress('isVisible');
if ~isProgress
    bst_progress('start', 'Import MRI', 'Loading MRI file...');
end
% MNI / Atlas?
isMni = ismember(FileFormat, {'ALL-MNI', 'ALL-MNI-ATLAS'});
isAtlas = ismember(FileFormat, {'ALL-ATLAS', 'ALL-MNI-ATLAS', 'SPM-TPM'});
% Load MRI
isNormalize = 0;
sMri = in_mri(MriFile, FileFormat, isInteractive && ~isMni, isNormalize);
if isempty(sMri)
    bst_progress('stop');
    return
end
% History: File name
if iscell(MriFile)
    sMri = bst_history('add', sMri, 'import', ['Import from: ' MriFile{1}]);
else
    sMri = bst_history('add', sMri, 'import', ['Import from: ' MriFile]);
end

%% ===== DELETE TEMPORARY FILES =====
if ~isempty(TmpDir)
    file_delete(TmpDir, 1, 1);
end

%% ===== GET ATLAS LABELS =====
% Try to get associated labels
if isempty(Labels) && ~iscell(MriFile)
    Labels = mri_getlabels(MriFile, sMri, isAtlas);
end
% Save labels in the file structure
if ~isempty(Labels)   % Labels were found in the input folder
    sMri.Labels = Labels;
    tagAtlas = '_volatlas';
    isAtlas = 1;
elseif isAtlas    % Volume was explicitly imported as an atlas
    tagAtlas = '_volatlas';
else
    tagAtlas = '';
end
% Get atlas comment
if isAtlas && isempty(Comment) && ~iscell(MriFile)
    [fPath, fBase, fExt] = bst_fileparts(MriFile);
    switch (fBase)
        case 'aseg'
            Comment = 'ASEG';
        case 'aparc+aseg'
            Comment = 'Desikan-Killiany';
        case 'aparc.a2009s+aseg'
            Comment = 'Destrieux';
        case {'aparc.DKTatlas+aseg', 'aparc.mapped+aseg'}  % FreeSurfer, FastSurfer
            Comment = 'DKT';
    end
end

%% ===== MANAGE MULTIPLE MRI =====
fileTag = '';
% Add new anatomy
iAnatomy = length(sSubject.Anatomy) + 1;
% If add an extra MRI: read the first one to check that they are compatible
if (iAnatomy > 1) && (isInteractive || isAutoAdjust)
    % Load the reference MRI (the first one)
    refMriFile = sSubject.Anatomy(1).FileName;
    sMriRef = in_mri_bst(refMriFile);
    % Adding an MNI volume to an existing subject
    if isMni
        sMri = mri_reslice_mni(sMri, sMriRef, isAtlas);
        isSameSize = 1;
        errMsg = '';
    % Regular coregistration options between volumes
    else
        % If some transformation where made to the intial volume: apply them to the new one ?
        if isfield(sMriRef, 'InitTransf') && ~isempty(sMriRef.InitTransf) && any(ismember(sMriRef.InitTransf(:,1), {'permute', 'flipdim'}))
            if ~isInteractive || java_dialog('confirm', ['A transformation was applied to the reference MRI.' 10 10 'Do you want to apply the same transformation to this new volume?' 10 10], 'Import MRI')
                % Apply step by step all the transformations that have been applied to the original MRI
                for it = 1:size(sMriRef.InitTransf,1)
                    ttype = sMriRef.InitTransf{it,1};
                    val   = sMriRef.InitTransf{it,2};
                    switch (ttype)
                        case 'permute'
                            sMri.Cube = permute(sMri.Cube, [val, 4]);
                            sMri.Voxsize = sMri.Voxsize(val);
                        case 'flipdim'
                            sMri.Cube = bst_flip(sMri.Cube, val(1));
                    end
                end
                % Modifying the volume disables the option "Reslice"
                isResliceDisabled = 1;
            else
                isResliceDisabled = 0;
            end
        else
            isResliceDisabled = 0;
        end

        % === ASK REGISTRATION METHOD ===
        % Get volumes dimensions
        refSize = size(sMriRef.Cube(:,:,:,1));
        newSize = size(sMri.Cube(:,:,:,1));
        isSameSize = all(refSize == newSize) && all(round(sMriRef.Voxsize(1:3) .* 1000) == round(sMri.Voxsize(1:3) .* 1000));
        % Ask what operation to perform with this MRI
        if isInteractive
            % Initialize list of options to register this new MRI with the existing one
            strOptions = 'How to register the new volume with the reference image?
';
            cellOptions = {};
            % Register with the SPM
            strOptions = [strOptions, '
- SPM:   Coregister the two volumes with SPM (requires SPM toolbox).'];
            cellOptions{end+1} = 'SPM';
            % Register with the MNI transformation
            strOptions = [strOptions, '
- MNI:   Compute the MNI transformation for both volumes (inaccurate).'];
            cellOptions{end+1} = 'MNI';
            % Skip registration
            strOptions = [strOptions, '
- Ignore:   The two volumes are already registered.'];
            cellOptions{end+1} = 'Ignore';
            % Ask user to make a choice
            RegMethod = java_dialog('question', [strOptions '

'], 'Import MRI', [], cellOptions, 'Reg+reslice');
        % In non-interactive mode: ignore if possible, or use the first option available
        else
            RegMethod = 'Ignore';
        end
        % User aborted the import
        if isempty(RegMethod)
            sMri = [];
            bst_progress('stop');
            return;
        end

        % === ASK RESLICE ===
        if isInteractive && (~strcmpi(RegMethod, 'Ignore') || ...
            (isfield(sMriRef, 'InitTransf') && ~isempty(sMriRef.InitTransf) && ismember('vox2ras', sMriRef.InitTransf(:,1)) && ...
             isfield(sMri,    'InitTransf') && ~isempty(sMri.InitTransf)    && ismember('vox2ras', sMri.InitTransf(:,1)) && ...
             ~isResliceDisabled))
            % If the volumes don't have the same size, add a warning
            if ~isSameSize
                strSizeWarn = '
The two volumes have different sizes: if you answer no here,
you will not be able to overlay them in the same figure.';
            else
                strSizeWarn = [];
            end
            % Ask to reslice
            isReslice = java_dialog('confirm', [...
                'Reslice the volume?

' ...
                'This operation rewrites the new MRI to match the alignment,
size and resolution of the original volume.' ...
                strSizeWarn ...
                '

'], 'Import MRI');
        % In non-interactive mode: never reslice
        else
            isReslice = 0;
        end

        % === REGISTRATION ===
        switch (RegMethod)
            case 'MNI'
                % Register the new MRI on the existing one using the MNI transformation (+ RESLICE)
                [sMri, errMsg, fileTag] = mri_coregister(sMri, sMriRef, 'mni', isReslice, isAtlas);
            case 'SPM'
                % Register the new MRI on the existing one using SPM + RESLICE
                [sMri, errMsg, fileTag] = mri_coregister(sMri, sMriRef, 'spm', isReslice, isAtlas);
            case 'Ignore'
                if isReslice
                    % Register the new MRI on the existing one using the transformation in the input files (files already registered)
                    [sMri, errMsg, fileTag] = mri_reslice(sMri, sMriRef, 'vox2ras', 'vox2ras', isAtlas);
                else
                    % Just copy the fiducials from the reference MRI
                    [sMri, errMsg, fileTag] = mri_coregister(sMri, sMriRef, 'vox2ras', isReslice, isAtlas);
                    % Transform error in warning
                    if ~isempty(errMsg) && ~isempty(sMri) && isSameSize && ~isReslice
                        disp(['BST> Warning: ' errMsg]);
                        errMsg = [];
                    end
                end
                % Copy the old SCS and NCS fields to the new file (only if registered)
                if isSameSize || isReslice
                    sMri.SCS = sMriRef.SCS;
                    %sMri.NCS = sMriRef.NCS;
                end
        end
    end
    % Stop in case of error
    if ~isempty(errMsg)
        if isInteractive
            bst_error(errMsg, [RegMethod ' MRI'], 0);
            sMri = [];
            bst_progress('stop');
            return;
        else
            error(errMsg);
        end
    end
end

%% ===== SAVE MRI IN BRAINSTORM FORMAT =====
% Add a Comment field in MRI structure, if it does not exist yet
if ~isempty(Comment)
    sMri.Comment = Comment;
    importedBaseName = file_standardize(Comment);
else
    if ~isfield(sMri, 'Comment') || isempty(sMri.Comment)
        sMri.Comment = 'MRI';
    end
    % Use filename as comment
    if (iAnatomy > 1) || isInteractive || ~isAutoAdjust
        [fPath, fBase, fExt] = bst_fileparts(MriFile);
        fBase = strrep(fBase, '.nii', '');
        if isMni
            sMri.Comment = file_unique(fBase, {sSubject.Anatomy.Comment});
        else
            sMri.Comment = file_unique([fBase, fileTag], {sSubject.Anatomy.Comment});
        end
    end
    % Add MNI tag
    if isMni
        if isfield(sMri, 'NCS') && isfield(sMri.NCS, 'y_method') && ~isempty(sMri.NCS.y_method)
            sMri.Comment = [sMri.Comment ' (MNI-' sMri.NCS.y_method ')'];
        elseif isfield(sMri, 'NCS') && isfield(sMri.NCS, 'y') && isfield(sMri.NCS, 'iy') && ~isempty(sMri.NCS.y) && ~isempty(sMri.NCS.iy)
            sMri.Comment = [sMri.Comment ' (MNI-nonlin)'];
        elseif isfield(sMri, 'NCS') && isfield(sMri.NCS, 'R') && isfield(sMri.NCS, 'T') && ~isempty(sMri.NCS.R) && ~isempty(sMri.NCS.T)
            sMri.Comment = [sMri.Comment ' (MNI-linear)'];
        else
            sMri.Comment = [sMri.Comment ' (MNI)'];
        end
    end
    % Get imported base name
    [tmp__, importedBaseName] = bst_fileparts(MriFile);
    importedBaseName = strrep(importedBaseName, 'subjectimage_', '');
    importedBaseName = strrep(importedBaseName, '_subjectimage', '');
    importedBaseName = strrep(importedBaseName, '.nii', '');
end

%% ===== SAVE FILE =====
% Get subject subdirectory
subjectSubDir = bst_fileparts(sSubject.FileName);
% Produce a default anatomy filename
BstMriFile = bst_fullfile(ProtocolInfo.SUBJECTS, subjectSubDir, ['subjectimage_' importedBaseName fileTag tagAtlas '.mat']);
% Make this filename unique
BstMriFile = file_unique(BstMriFile);
% Save new MRI in Brainstorm format
sMri = out_mri_bst(sMri, BstMriFile);

%% ===== REFERENCE NEW MRI IN DATABASE ======
% New anatomy structure
sSubject.Anatomy(iAnatomy) = db_template('Anatomy');
sSubject.Anatomy(iAnatomy).FileName = file_short(BstMriFile);
sSubject.Anatomy(iAnatomy).Comment  = sMri.Comment;
% Default anatomy: do not change
if isempty(sSubject.iAnatomy)
    sSubject.iAnatomy = iAnatomy;
end
% Default subject
if (iSubject == 0)
	ProtocolSubjects.DefaultSubject = sSubject;
% Normal subject
else
    ProtocolSubjects.Subject(iSubject) = sSubject;
end
bst_set('ProtocolSubjects', ProtocolSubjects);
% Save first MRI as permanent default
if (iAnatomy == 1)
    db_surface_default(iSubject, 'Anatomy', iAnatomy, 0);
end

%% ===== UPDATE GUI =====
% Refresh tree
panel_protocols('UpdateNode', 'Subject', iSubject);
panel_protocols('SelectNode', [], 'subject', iSubject, -1 );
% Save database
db_save();
% Unload MRI (if a MRI with the same name was previously loaded)
bst_memory('UnloadMri', BstMriFile);

%% ===== MRI VIEWER =====
if isInteractive
    % First MRI: Edit fiducials
    if (iAnatomy == 1)
        % MRI Visualization and selection of fiducials (in order to align surfaces/MRI)
        hFig = view_mri(BstMriFile, 'EditMri');
        drawnow;
        bst_progress('stop');
        % Wait for the MRI Viewer to be closed
        if ishandle(hFig)
            waitfor(hFig);
        end
    % Other volumes: Display registration
    else
        % If volumes are registered
        if isSameSize || isReslice
            % Open the second volume as an overlay of the first one
            hFig = view_mri(refMriFile, BstMriFile);
            % Set the amplitude threshold to 30%
            if ~isAtlas
                panel_surface('SetDataThreshold', hFig, 1, 0.3);
            end
        else
            hFig = view_mri(BstMriFile);
        end
    end
else
    if ~isProgress
        bst_progress('stop');
    end
end