function [cfg, artifact] = ft_artifact_tms(cfg, data)

% FT_ARTIFACT_TMS reads the data segments of interest from file and identifies
% artefacts in EEG recordings that were done during TMS stimulation.
%
% Use as
%   [cfg, artifact] = ft_artifact_tms(cfg)
% with the configuration options
%   cfg.dataset     = string with the filename
% or
%   cfg.headerfile  = string with the filename
%   cfg.datafile    = string with the filename
% and optionally
%   cfg.headerformat
%   cfg.dataformat
%
% Alternatively you can use it as
%   [cfg, artifact] = ft_artifact_tms(cfg, data)
% where the input data is a structure as obtained from FT_PREPROCESSING.
%
% In both cases the configuration should also contain
%   cfg.trl         = structure that defines the data segments of interest, see FT_DEFINETRIAL
%   cfg.continuous  = 'yes' or 'no' whether the file contains continuous data (default = 'yes')
% and
%   cfg.method      = 'detect' or 'marker', see below.
%   cfg.prestim     = scalar, time in seconds prior to onset of detected event to mark as artifactual (default = 0.005 seconds)
%   cfg.poststim    = scalar, time in seconds post onset of detected even to mark as artifactual (default = 0.010 seconds)
%
% The different methods are described in detail below.
%
% With cfg.method='detect', TMS-artifact are detected on basis of transient
% high-amplidude gradients that are typical for TMS-pulses. The data is preprocessed
% (again) with the following settings, which are optimal for identifying TMS-pulses.
% Artifacts are identified by means of thresholding the z-transformed value of the
% preprocessed data. This method acts as a wrapper around FT_ARTIFACT_ZVALUE.
%   cfg.artfctdef.tms.derivative  = 'yes'
%   cfg.artfctdef.tms.channel     = Nx1 cell-array with selection of channels, see FT_CHANNELSELECTION for details
%   cfg.artfctdef.tms.cutoff      = z-value at which to threshold (default = 4)
%   cfg.artfctdef.tms.trlpadding  = 0.1
%   cfg.artfctdef.tms.fltpadding  = 0.1
%   cfg.artfctdef.tms.artpadding  = 0.01
% Be aware that if one artifact falls within this specified range of another
% artifact, both artifact will be counted as one. Depending on cfg.prestim and
% cfg.poststim you may not mark enough data as artifactual.
%
% With cfg.method='marker', TMS-artifact onsets and offsets are based on
% markers/triggers that are written into the EEG dataset. This method acts as a
% wrapper around FT_DEFINETRIAL to determine on- and offsets of TMS pulses by reading
% markers in the EEG.
%   cfg.trialfun            = function name, see below (default = 'ft_trialfun_general')
%   cfg.trialdef.eventtype  = 'string'
%   cfg.trialdef.eventvalue = number, string or list with numbers or strings
% The cfg.trialfun option is a string containing the name of a function that you
% wrote yourself and that FT_ARTIFACT_TMS will call. The function should take the
% cfg-structure as input and should give a NxM matrix with M>=3 in the same format as
% "trl" as the output. You can add extra custom fields to the configuration structure
% to pass as arguments to your own trialfun. Furthermore, inside the trialfun you can
% use the FT_READ_EVENT function to get the event information from your data file.
%
% The output argument "artifact" is a Nx2 matrix comparable to the "trl" matrix of
% FT_DEFINETRIAL. The first column of which specifying the beginsamples of an
% artifact period, the second column contains the endsamples of the artifactperiods.
%
% To facilitate data-handling and distributed computing, you can use
%   cfg.inputfile   =  ...
% to read the input data from a *.mat file on disk. This mat files should contain
% only a single variable named 'data', corresponding to the input structure.
%
% See also FT_REJECTARTIFACT, FT_ARTIFACT_CLIP, FT_ARTIFACT_ECG, FT_ARTIFACT_EOG,
% FT_ARTIFACT_JUMP, FT_ARTIFACT_MUSCLE, FT_ARTIFACT_THRESHOLD, FT_ARTIFACT_ZVALUE

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

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init

% ft_preamble provenance is not needed because just a call to ft_artifact_zvalue
% ft_preamble loadvar data is not needed because ft_artifact_zvalue will do this

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'renamed',    {'datatype', 'continuous'});
cfg = ft_checkconfig(cfg, 'renamedval', {'continuous', 'continuous', 'yes'});
cfg = ft_checkconfig(cfg, 'required', 'method');
cfg = ft_checkconfig(cfg, 'allowedval', {'method', 'detect', 'marker'});

% set default rejection parameters
if ~isfield(cfg, 'artfctdef'),                       cfg.artfctdef                   = [];        end
if ~isfield(cfg.artfctdef, 'tms'),                   cfg.artfctdef.tms               = [];        end
if ~isfield(cfg, 'method'),                          cfg.method                      = 'detect';  end
if ~isfield(cfg, 'prestim'),                         cfg.prestim                     = 0.005;     end
if ~isfield(cfg, 'poststim'),                        cfg.poststim                    = 0.010;     end

if isfield(cfg.artfctdef.tms, 'artifact')
  fprintf('tms artifact detection has already been done, retaining artifacts\n');
  artifact = cfg.artfctdef.tms.artifact;
  return
end

switch cfg.method
  case 'detect'
    % settings for preprocessing
    if ~isfield(cfg.artfctdef.tms, 'derivative'),   cfg.artfctdef.tms.derivative   = 'yes';    end
    % settings for the zvalue subfunction
    if ~isfield(cfg.artfctdef.tms, 'method'),     cfg.artfctdef.tms.method      = 'zvalue';    end
    if ~isfield(cfg.artfctdef.tms, 'channel'),    cfg.artfctdef.tms.channel     = 'all';       end
    if ~isfield(cfg.artfctdef.tms, 'trlpadding'), cfg.artfctdef.tms.trlpadding  = 0.1;         end
    if ~isfield(cfg.artfctdef.tms, 'fltpadding'), cfg.artfctdef.tms.fltpadding  = 0.1;         end
    if ~isfield(cfg.artfctdef.tms, 'artpadding'), cfg.artfctdef.tms.artpadding  = 0.01;        end
    if ~isfield(cfg.artfctdef.tms, 'cutoff'),     cfg.artfctdef.tms.cutoff      = 4;           end

    % the data is either passed into the function by the user or read from file with cfg.inputfile
    hasdata = exist('data', 'var');

    % construct a temporary configuration that can be passed onto artifact_zvalue
    tmpcfg = keepfields(cfg, {'trl', 'continuous', 'dataset', 'datafile', 'headerfile', 'dataformat', 'headerformat'});
    tmpcfg.artfctdef.zvalue = cfg.artfctdef.tms;

    if hasdata
      fsample = data.fsample; % get the sampling rate from the data structure
      [tmpcfg, artifact] = ft_artifact_zvalue(tmpcfg, data);
    else
      [tmpcfg, artifact] = ft_artifact_zvalue(tmpcfg);
      % FT_ARTIFACT_ZVALUE will call FT_CHECKCONFIG with the dataset2files option
      hdr = ft_read_header(tmpcfg.headerfile, 'headerformat', tmpcfg.headerformat);
      fsample = hdr.Fs; % get the sampling rate from the file on disk
    end
    cfg.artfctdef.tms = tmpcfg.artfctdef.zvalue;

    % adjust artifact definition so that Nx2 matrix contains detected TMS
    % events with user-specified pre- and post stimulus period included.
    % The reason for this is that ft_artifact_zvalue centers the period
    % marked as artifactual around the detected event. In the case of a TMS
    % pulse the window you would like to mark as artifactual is not
    % symmetrical around the onset of the pulse.

    % get values and express in samples
    prestim = round(cfg.prestim * fsample);
    poststim = round(cfg.poststim * fsample);

    % adjust Nx2 artifact matrix to be centered non-symmetrically around
    % detected TMS-pulse
    artifact(:,1) = (artifact(:,1)+artifact(:,2))./2 - prestim;
    artifact(:,2) = artifact(:,1) + poststim;
    cfg.artfctdef.tms.artifact = artifact;

  case 'marker'
    % Check if the cfg is correct for this method
    cfg = ft_checkconfig(cfg, 'dataset2files', 'yes');
    ft_checkconfig(cfg, 'required', 'trialdef');
    cfg.trialfun = ft_getopt(cfg, 'trialfun', 'ft_trialfun_general');
    trialdef = cfg.trialdef;
    trialdef.prestim = cfg.prestim;
    trialdef.poststim = cfg.poststim;
    cfg.trialdef = ft_checkconfig(trialdef, 'required', {'eventvalue', 'eventtype'});

    % Get the trialfun
    cfg.trialfun = ft_getuserfun(cfg.trialfun, 'trialfun');

    % Evaluate the trialfun
    fprintf('evaluating trialfunction ''%s''\n', func2str(cfg.trialfun));
    trl   = feval(cfg.trialfun, cfg);

    % Prepare the found events for output
    artifact = trl(:,1:2);
    cfg.artfctdef.tms.artifact = artifact;
    fprintf('found %d events\n', size(artifact,1));

  otherwise
    % This should be redundant as ft_checkconfig does not allow other methods than the supported ones.
    ft_error('unsupported method');
end

% FIXME - not removing this causes problems when passing to ft_preprocessing
cfg = rmfield(cfg, 'method');


function printMinimalLeakageMode(model, minMetBool, minRxnBool, y, printLevel, fileName)
% Prints out the data on each leakage mode
%
% USAGE:
%
%    printMinimalLeakageMode(model, minMetBool, minRxnBool, y, printLevel, fileName)
%
% INPUTS:
%    model:         model structure
%    minMetBool:    boolean of metabolites in a positive leakage mode
%    minRxnBool:    boolean of reactions exclusively involved in a positive leakage mode
%    y:             contains `minMetBool`
%    printLevel:    verbose level
%    fileName:      name of the file

if ~exist('fileName','var')
    fprintf('%6u\t%6u\t%s\n',nnz(minMetBool),nnz(minRxnBool),'leakage mode');
    if printLevel>1 && nnz(minRxnBool)<=10
        if printLevel>2
            fprintf('%6u\t%s\n',nnz(minMetBool), 'leaking metabolites...');
            for m=1:length(model.SIntMetBool)
                if minMetBool(m)
                    fprintf('%s\t%s\n',model.mets{m},model.metNames{m});
                end
            end
        end
        fprintf('%6u\t%s\n',nnz(minRxnBool), 'inconsistent reactions...');
        formulas = printRxnFormula(model,model.rxns(minRxnBool));
    end
    if any(minMetBool)
        %fprintf('%s%6u%s\n','Net mass exchange reaction from ',nnz(minRxnBool), ' active reactions...');
        [modelTmp,rxnIDexists] = addReaction(model,'Net_mass_exchange => ',model.mets(minMetBool),y(minMetBool));
        %formulas = printRxnFormula(model,'Mass_exchange');
    end
    fprintf('%s\n','-------')
else
    fprintf('%6u\t%6u\t%6s\t%15s\t%6g\t%6g\t%6g\t%6g\n',nnz(minMetBool),nnz(minRxnBool),'leakage mode');
    fprintf('%6u\t%s\n',nnz(minMetBool), 'Leaking metabolites...');
    for m=1:length(model.SIntMetBool)
        if minMetBool(m)
            fprintf(fid,'%s\t%s\n',model.mets{m},model.metNames{m});
        end
    end
    fprintf('%6u\t%s\n',nnz(minRxnBool), 'inconsistent reactions...');
    formulas = printRxnFormula(model,model.rxns(minRxnBool));
end


function hdr = read_ctf_res4(fname)

% READ_CTF_RES4 reads the header in RES4 format from a CTF dataset
%
% hdr = read_ctf_res4(filename)
%
% Due to a non-dislosure agreement between CTF and the F.C. Donders Centre,
% the allwoed use of this function is limited. Do not distribute this function.

% This program is provided to users of CTF MEG systems as a courtesy only. Please
% do not redistribute it without permission from CTF Systems Inc.
% This program has no warranty whatsoever.

% Author(s): Jim McKay November 1999
% Last revision: Jim McKay
% Copyright (c) 1999-2000 CTF Systems Inc. All Rights Reserved.
%
% modifications Copyright (C) 2002, Ole Jensen
% modifications Copyright (C) 2003, Robert Oostenveld
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% read header information
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

fid = fopen(fname,'r','b');

% Check if header file exist
if fid == -1
    errMsg = strcat('Could not open header file:',fname);
    error(errMsg);
end

% first 8 bytes contain filetype

% Check is fileformat is correct
r_head=setstr(fread(fid,8,'char'))';
if (strcmp(r_head(1,1:7),'MEG41RS')==0),
  fclose(fid)
  errMsg = strcat('Resource file is not in CTF MEG4 format in file:',fname)
  error(errMsg);
end %if

% Read the initial parameters
appName       = setstr(fread(fid,256,'char'))' ;
dataOrigin    = setstr(fread(fid,256,'char'))' ;
dataDescrip   = setstr(fread(fid,256,'char'))' ;
no_trial_avgd = fread(fid,1,'int16')          ;
data_time     = setstr(fread(fid,255,'char'))';
data_date     = setstr(fread(fid,255,'char'))';

fseek(fid,1288,'bof');
% Read the general recording parameters
no_samples  = fread(fid,1,'int32');
no_channels = fread(fid,1,'int16');
fseek(fid,2,'cof');			% hole of 2 bytes due to improper alignment
sample_rate = fread(fid,1,'double');
epoch       = fread(fid,1,'double');
no_trials   = fread(fid,1,'int16');
fseek(fid,2,'cof');			% hole of 2 bytes due to improper alignment
preTrigpts=fread(fid,1,'int32');

fseek(fid,1360,'bof');
% read in the meg4Filesetup structure
run_name     = setstr(fread(fid,32,'char')');
run_title    = setstr(fread(fid,256,'char')');
instruments  = setstr(fread(fid,32,'char')');
coll_desc    = setstr(fread(fid,32,'char')');
subj_id      = setstr(fread(fid,32,'char')');
operator     = setstr(fread(fid,32,'char')') ;
sensFilename = setstr(fread(fid,60,'char')') ;

fseek(fid,1839,'bof');
% Read in the run description length
rd_len=fread(fid,1,'uint8');
% Go to the run description and read it in
fseek(fid,1844,'bof');
run_desc=setstr(fread(fid,rd_len,'char')');

% read in the filter information
temp=fread(fid,2,'uint8');
num_filt=temp(2);
for fi=0:(num_filt-1),
  filt_info=fread(fid,18,'uint8');
  num_fparm=filt_info(18);
  if num_fparm ~= 0,
    filt_parm=fread(fid,8*num_fparm,'uint8');
  end % if
end % for fi

% Read in the channel names
for i=1:no_channels,
    temp=fread(fid,32,'char')';
    temp(find(temp<32 )) = ' ';		% remove non-printable characters
    temp(find(temp>126)) = ' ';		% remove non-printable characters
    endstr = findstr(temp, '-'); temp(endstr:end) = ' ';	% cut off at '-'
    endstr = findstr(temp, ' '); temp(endstr:end) = ' ';	% cut off at ' '
    chan_name(i,:) = char(temp);		% as char array
    chan_label{i}  = deblank(char(temp));	% as cell array
end %for

% pre-allocate some memory space
sensGain = zeros([no_channels,1]);
qGain    = zeros([no_channels,1]);
ioGain   = zeros([no_channels,1]);
sensType = zeros([no_channels,1]);

% Read in the sensor information
fp = ftell(fid);
for chan=1:no_channels,
    fread(fid,1,'uint8');			% Read and ignore 1 byte from enum
    sensType(chan)=fread(fid,1,'uint8');	% Read sensor type
    fread(fid,2,'uint8');			% Read and ignore originalRunNum
    fread(fid,4,'uint8');			% Read and ignore coilShape
    sensGain(chan)=fread(fid,1,'double');	% Read sensor gain in Phi0/Tesla
    qGain(chan)=fread(fid,1,'double');		% Read qxx gain (usually 2^20 for Q20)
    ioGain(chan)=fread(fid,1,'double');		% Read i/o gain of special sensors (usually 1.0)
    ioOffset(chan)=fread(fid,1,'double');
    numCoils(chan)=fread(fid,1,'int16');
    grad_order_no(chan)=fread(fid,1,'int16');
    fread(fid,4,'char');

    % read the coil positions and orientations
    for i=1:8
      Chan(chan).coil(i).pos = fread(fid,3,'double')';
      fread(fid,1,'double');
      Chan(chan).coil(i).ori = fread(fid,3,'double')';
      fread(fid,3,'double');
    end

    % read the coil positions and orientations in head coordinates(?)
    for i=1:8
      Chan(chan).coilHC(i).pos = fread(fid,3,'double')';
      fread(fid,1,'double');
      Chan(chan).coilHC(i).ori = fread(fid,3,'double')';
      fread(fid,3,'double');
    end

    % jump to the next sensor info record
    fseek(fid, fp+chan*1328, 'bof');
end % for chan

% close the header file
fclose(fid);

% determine the different channel types
rowMEG  = [];
rowEEG  = [];
rowTRIG = [];
rowREF  = [];
rowALL  = 1:no_channels;
for k=1:no_channels
    if strcmp(char(chan_name(k,1)),'M')
        rowMEG = [rowMEG k];
    end
    if strcmp(char(chan_name(k,1:3)),'EEG')
        rowEEG = [rowEEG k];
    end
    if strcmp(char(chan_name(k,1:4)),'STIM')
        rowTRIG = [rowTRIG k];
    end
    if chan_name(k,1)=='B' | chan_name(k,1)=='G' | chan_name(k,1)=='P' | chan_name(k,1)=='Q' | chan_name(k,1)=='R'
        rowREF = [rowREF k];
    end
end

% assign all the variables that should be outputted as header information
hdr.Fs          = sample_rate;
hdr.nChans      = no_channels;
hdr.nSamples    = no_samples;
hdr.nSamplesPre = preTrigpts;
hdr.timeVec     = (1:no_samples)/sample_rate - preTrigpts/sample_rate;
hdr.nTrials     = no_trials;
hdr.gainV       = ioGain./(qGain.*sensGain);
hdr.label       = chan_label(:);
hdr.nameALL     = chan_name;
hdr.rowMEG      = rowMEG;
hdr.rowEEG      = rowEEG;
hdr.rowTRIG     = rowTRIG;
hdr.rowREF      = rowREF;
hdr.Chan        = Chan;
% hdr.nameEEG     = [];
% hdr.nameMEG     = [];
% hdr.nameEOG     = [];
% hdr.trigV       = [];
% hdr.SwapData    = [];

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% construct the gradiometer system in DEWAR coordinates
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% combine the bottom and top coil of the MEG channels into hardware gradiometers
numMEG = length(hdr.rowMEG);
for i=1:numMEG
  coil1_pos(i,:) = hdr.Chan(hdr.rowMEG(i)).coil(1).pos;
  coil1_ori(i,:) = hdr.Chan(hdr.rowMEG(i)).coil(1).ori;
  coil2_pos(i,:) = hdr.Chan(hdr.rowMEG(i)).coil(2).pos;
  coil2_ori(i,:) = hdr.Chan(hdr.rowMEG(i)).coil(2).ori;
end

% apparently, some coils are oriented to the wrong side
tmp = coil2_pos - coil1_pos;
sel = find(dot(coil1_ori, tmp, 2)<1);
coil1_ori(sel,:) = -coil1_ori(sel,:);
coil2_ori(sel,:) = -coil2_ori(sel,:);

gradDEWAR.pnt = [coil1_pos; coil2_pos];
gradDEWAR.ori = [coil1_ori; coil2_ori];
gradDEWAR.tra = [eye(numMEG) -eye(numMEG)];
gradDEWAR.label = hdr.label(rowMEG);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% construct the gradiometer system in HEAD coordinates
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% combine the bottom and top coil of the MEG channels into hardware gradiometers
numMEG = length(hdr.rowMEG);
for i=1:numMEG
  coil1_pos(i,:) = hdr.Chan(hdr.rowMEG(i)).coilHC(1).pos;
  coil1_ori(i,:) = hdr.Chan(hdr.rowMEG(i)).coilHC(1).ori;
  coil2_pos(i,:) = hdr.Chan(hdr.rowMEG(i)).coilHC(2).pos;
  coil2_ori(i,:) = hdr.Chan(hdr.rowMEG(i)).coilHC(2).ori;
end

% apparently, some coils are oriented to the wrong side
tmp = coil2_pos - coil1_pos;
sel = find(dot(coil1_ori, tmp, 2)<1);
coil1_ori(sel,:) = -coil1_ori(sel,:);
coil2_ori(sel,:) = -coil2_ori(sel,:);

gradHC.pnt = [coil1_pos; coil2_pos];
gradHC.ori = [coil1_ori; coil2_ori];
gradHC.tra = [eye(numMEG) eye(numMEG)];
gradHC.label = hdr.label(rowMEG);

hdr.grad      = gradHC;		% default is in head-coordinates
hdr.grad.unit = 'cm';		% this is default in all CTF software


function dateStringUtc = local_time_to_utc( dateVectorLocal, format )
%
% local_time_to_utc -- convert local time/date information to UTC.
%
% dateStringUtc = local_time_to_utc( dateVectorLocal ) converts a vector of local
%    date/time information to UTC dates and times.  The format of dateVectorLocal can be
%    either Matlab datenumbers (N x 1 or 1 x N) or else a vector of Matlab date vectors
%    (ie, N x 6, for example [2008 10 22 13 41 00 ; 2008 10 22 13 41 12]).  The time/date
%    values will be converted to UTC and returned in 'dd-mmm-yyyy HH:MM:SS Z' format.
%
% dateStringUtc = local_time_to_utc( dateVectorLocal, format ) returns the UTC time / date
%    values in a format specified by numeric argument format.  The format values match
%    those used by the Matlab datestr function.  Supported formats are as follows:
%
%    Number           String                     Example
%    ===========================================================================
%       0             'dd-mmm-yyyy HH:MM:SS Z'   01-Mar-2000 15:45:17 Z
%      13             'HH:MM:SS Z'               15:45:17 Z
%      15             'HH:MM Z'                  15:45 Z
%      21             'mmm.dd,yyyy HH:MM:SS Z'   Mar.01,2000 15:45:17 Z
%      30 (ISO 8601)  'yyyymmddTHHMMSSZ'         20000301T154517Z
%      31             'yyyy-mm-dd HH:MM:SS Z'    2000-03-01 15:45:17 Z
%
% Version date:  2008-November-10.
%

% Modification History:
%
%    2008-November-12, PT:
%        switch to use of Java methods and eliminate check for unix vs PC (java methods
%        work on all platforms).
%    2008-November-10, PT:
%        added a bunch of disp statements which should help diagnose mysterious smoke test
%        failures in PMD and PA.
%
%=========================================================================================

% arguments -- if the argument is a vector of datenums, convert to a vector of time/date
% vectors

  if ( size(dateVectorLocal,2) ~= 6 || max(dateVectorLocal(:,2)) > 12 )
      dateVectorLocal = datevec(dateVectorLocal) ;
  end

% check that the format number is supported

  if ( nargin == 1)
      format = 0 ;
  end
  supportedFormats = [0 13 15 21 30 31] ;
  if ~ismember(format,supportedFormats)
      error('common:localTimeToUtc:unsupportedFormat' , ...
          'local_time_to_utc:  unsupported format requested' ) ;
  end

% call the converter vector

  dateVectorUtc = local_date_vector_to_utc( dateVectorLocal ) ;

% convert the date vectors to the desired format

  dateStringUtc = datestr(dateVectorUtc,format) ;
  stringLength = size(dateStringUtc,2) ;
  nStrings = size(dateStringUtc,1) ;

% append the 'Z'

  dateStringUtc = append_z_to_date_string( dateStringUtc, format ) ;

return

% and that's it!

%
%
%

%=========================================================================================

% subfunction which converts a Matlab date vector in local time to one in UTC

function dateVectorUtc = local_date_vector_to_utc( dateVectorLocal )

  nDates = size(dateVectorLocal,1) ;
  dateVectorUtc = zeros(nDates,6) ;

% import the Java classes needed for this process

  import java.text.SimpleDateFormat ;
  import java.util.Date ;
  import java.util.TimeZone ;

% instantiate a SimpleDateFormat object with a fixed time/date format and UTC time zone

  utcFormatObject = SimpleDateFormat('yyyy-MM-dd HH:mm:ss') ;
  utcFormatObject.setTimeZone(TimeZone.getTimeZone('UTC')) ;

% loop over date strings

  for iDate = 1:nDates

      dateVec = dateVectorLocal(iDate,:) ;

%     instantiate a Java Date class object with the local time.  Note that Java year is
%     year since 1900, and Java month is zero-based

      localDateObject = Date(dateVec(1)-1900, dateVec(2)-1, dateVec(3), ...
                             dateVec(4), dateVec(5), dateVec(6)) ;

%     convert the date object to a string in the correct format and in UTC

      dateStringUtc = char(utcFormatObject.format(localDateObject)) ;

%     pick through the resulting string and extract the data we want, converting to
%     numbers as we go

      dateVectorUtc(iDate,1) = str2num(dateStringUtc(1:4)) ;
      dateVectorUtc(iDate,2) = str2num(dateStringUtc(6:7)) ;
      dateVectorUtc(iDate,3) = str2num(dateStringUtc(9:10)) ;
      dateVectorUtc(iDate,4) = str2num(dateStringUtc(12:13)) ;
      dateVectorUtc(iDate,5) = str2num(dateStringUtc(15:16)) ;
      dateVectorUtc(iDate,6) = str2num(dateStringUtc(18:19)) ;

  end % loop over dates

return

%=========================================================================================

% append a 'Z', signifying UTC, to a Matlab date string.

function datestrZ = append_z_to_date_string( datestr, format )

  nStrings = size(datestr,1) ;
  stringLength = size(datestr,2) ;
  if (format ~= 30)
      addString = repmat(' Z',nStrings,1) ;
      stringSizeIncrease = 2 ;
  else
      addString = repmat('Z',nStrings,1) ;
      stringSizeIncrease = 1 ;
  end
  datestrZ = [datestr(:) ; addString(:)] ;
  datestrZ = reshape(datestrZ,nStrings,stringLength+stringSizeIncrease) ;

return


function DatsMetadata = bz_DatFileMetadata(basepath)
% Store basic info about the original dat files: names and bytes
% Brendon Watson 2016-8

%% Input and directory handling
if ~exist('basepath','var')
    basepath = cd;
elseif isempty(basepath)
    basepath = cd;
end

basename = bz_BasenameFromBasepath(basepath);

%% First find out what system was used to record this... based on files present
% check if amplipex (.metas present) or intan (rhd.infos present)... or neither
recsys = '';

%Amplipex case
dmeta = dir(fullfile(basepath,'*.meta'));
if ~isempty(dmeta)
    recsys = 'Amplipex';
end

%Intan case
drhd = dir(basepath);
for didx = length(drhd):-1:1
   ok = 0;
   if drhd(didx).isdir
       t = dir(fullfile(basepath,drhd(didx).name,'info.rhd'));
       if ~isempty(t)%only use folders with info.rhd inside
            recsys = 'Intan';
            ok = 1;
       end
   end
   if ~ok
        drhd(didx) = [];
   end
end

%% Now gather info
switch recsys
    case 'Amplipex'%if from an amplipex
        d = dmeta;
        DatsMetadata.RecordingSystem = 'Amplipex';
        for idx = 1:length(d);
            DatsMetadata.Recordings.Names{idx} = d(idx).name(1:end-5);
            tdat = strcat(d(idx).name(1:end-5),'.dat');
            t = dir(fullfile(basepath,tdat));
            if ~isempty(t) %if original .dat still around
                DatsMetadata.Recordings.Bytes(idx) = t.bytes;
            else%if no .dat around (ie anymore), get bytes from .meta
                disp([tdat ' not found']);
                DatsMetadata.Recordings.Bytes{idx} = str2num(bz_ReadAmplipexMetafileAspects(fullfile(basepath,d(idx).name),'filebytes'));
            end
            NChannels(idx) = str2num(bz_ReadAmplipexMetafileAspects(fullfile(basepath,d(idx).name),'NumChans'));
            SamplingRate(idx) = str2num(bz_ReadAmplipexMetafileAspects(fullfile(basepath,d(idx).name),'SamplingRate'));
            Amplification(idx) = str2num(bz_ReadAmplipexMetafileAspects(fullfile(basepath,d(idx).name),'Gain'));
        end
        if ~isempty(NChannels) %if some files were found
            %Channels
            if sum(abs(diff(NChannels)))%if different numbers of channels differed across recordings in session, error out
                warning('Rercordings contain different numbers of channels - per .meta files.  Will use count from first recording.')
            end
            DatsMetadata.Parameters.NumberOfChannels = NChannels(1);

            %Sampling Rate
            if sum(abs(diff(SamplingRate)))%if different numbers of channels differed across recordings in session, error out
                warning('Rercordings contain different SamplingRates - per .meta files.  Will use number from first recording.')
            end
            DatsMetadata.Parameters.SampleRate = SamplingRate(1);

            %Amplification
            if sum(abs(diff(Amplification)))%if different numbers of channels differed across recordings in session, error out
                warning('Rercordings contain different Amplifications - per .meta files.  Will use number from first recording.')
            end
            DatsMetadata.Parameters.Amplification = Amplification(1);

            DatsMetadata.Parameters.VoltsPerUnit = VoltsPerUnit_Amplirec(basename,basepath);%calculate from .metas/.inis
            DatsMetadata.Parameters.BitsPerSample = 16;%amplirec default
            DatsMetadata.Parameters.VoltageRange = 10;%not used except to make xml
        end
    case 'Intan' %if not amplipex, look for intan
        d = drhd;
        NAmpChannels = [];
        NAuxChannels = [];
        NDigitalChannels = [];
        SamplingRate = [];
        thisidx = 0;

        for didx = 1:length(d)
            if d(didx).isdir
                t = dir(fullfile(basepath,d(didx).name,'info.rhd'));
                if ~isempty(t)%only use folders with info.rhd inside

                   DatsMetadata.RecordingSystem = 'Intan';
                   rhd = fullfile(basepath,d(didx).name,'info.rhd');
    %                ampdats{end+1} = fullfile(basepath,d(didx).name,'amplifier.dat');
    %                recordingdiridxs = didx;

                   thisidx = thisidx + 1;

                   DatsMetadata.Recordings.Names{thisidx} = d(didx).name;

                   % read the info.rhd file for each individual recorded
                   % file/folder
                   try
                      [amplifier_channels, notes, aux_input_channels, spike_triggers,...
                          board_dig_in_channels, supply_voltage_channels, frequency_parameters ] =...
                          read_Intan_RHD2000_file(fullfile(basepath,d(didx).name),'info.rhd');%function from Intan
                      DatsMetadata.Recordings.IntanRHDInfoRaw{thisidx} = v2struct(amplifier_channels, notes, aux_input_channels, spike_triggers,...
                          board_dig_in_channels, supply_voltage_channels, frequency_parameters);
                      NAmpChannels(thisidx) = length(amplifier_channels);
                      NAuxChannels(thisidx) = length(aux_input_channels);
                      NDigitalChannels(thisidx) = length(board_dig_in_channels);
                      SamplingRate(thisidx) = frequency_parameters.amplifier_sample_rate;

                   catch%if read error, set values based on prior
                       if ~isempty(NAmpChannels)
%                            if ~isnan(NAmpChannels(1));
                          NAmpChannels(thisidx) = NAmpChannels(1);
                       else
                          sess = bz_getSessionInfo(basepath,'noPrompts',true);
                          NAmpChannels(thisidx) = sess.nChannels;
                       end
                       if ~isempty(NAuxChannels)
                           NAuxChannels(thisidx) = NAuxChannels(1);
                       else
                           NAuxChannels(thisidx) = nan;
                       end
                       if ~isempty(NDigitalChannels)
                           NDigitalChannels(thisidx) = NDigitalChannels(1);
                       else
                           NDigitalChannels(thisidx) = nan;
                       end
                       if ~isempty(SamplingRate)
                           SamplingRate(thisidx) = SamplingRate(1);
                       else
                           SamplingRate(thisidx) = nan;
                       end
                   end

                   t = dir(fullfile(basepath,d(didx).name,'amplifier.dat'));
                   t2 = dir(fullfile(basepath,d(didx).name,'amplifier_analogin_auxiliary_int16.dat'));
                   if ~isempty(t2) %if original .dat still around.  Handling Luke's RHD software to put all 16bit into same file
                       DatsMetadata.Recordings.Bytes(thisidx) = t2.bytes;
                   elseif ~isempty(t) %if original .dat still around
                       DatsMetadata.Recordings.Bytes(thisidx) = t.bytes;
                   else%if no .dat around
                       disp([DatsMetadata.Recordings.Names{end} ' not found']);
                       timedat = dir(fullfile(basepath,d(didx).name,'time.dat'));
                       if ~isnan(NAmpChannels(end));
                          DatsMetadata.Recordings.Bytes(thisidx) = timedat(1).bytes/2*NAmpChannels(end);
                       else
                           sess = bz_getSessionInfo(basepath);
                           DatsMetadata.Recordings.Bytes(thisidx) = timedat(1).bytes/2*sess.nChannels;
                       end
                   end

               end
            end
        end
        %handle errors created by improper read of rhd.info files
        if isnan(sum(abs(diff(NAmpChannels))))
            warning('At least one read error on rhd.info files.  Using values from other files to populate.')
            ok = find(~isnan(NAmpChannels),1,'first');
            if isempty(ok)
                warning('No good reads on NumberofAmpChannels. Assuming based on probe maps')
                sess = bz_getSessionInfo(basepath);
                NAmpChannels(isnan(NAmpChannels)) = sess.nChannels;
            else
                NAmpChannels(isnan(NAmpChannels)) = NAmpChannels(ok);
            end
        end
        if isnan(sum(abs(diff(NAuxChannels))))
            warning('At least one read error on rhd.info files.  Using values from other files to populate.')
            ok = find(~isnan(NAuxChannels),1,'first');
            if isempty(ok)
                error('No good reads on NAuxChannels, assuming 0')
                NAuxChannels(isnan(NAuxChannels)) = 0;
            else
                NAuxChannels(isnan(NAuxChannels)) = NAuxChannels(ok);
            end
        end
        if isnan(sum(abs(diff(NDigitalChannels))))
            warning('At least one read error on rhd.info files.  Using values from other files to populate.')
            ok = find(~isnan(NDigitalChannels),1,'first');
            if isempty(ok)
                error('No good reads on NDigitalChannels, assuming 0')
                NDigitalChannels(isnan(NDigitalChannels)) = 0;
            else
                NDigitalChannels(isnan(NDigitalChannels)) = NDigitalChannels(ok);
            end
        end
        if isnan(sum(abs(diff(SamplingRate))))
            warning('At least one read error on rhd.info files.  Using values from other files to populate.')
            ok = find(~isnan(SamplingRate),1,'first');
            if isempty(ok)
                error('No good reads on SamplingRate, taking from AnimalMetadata')
                sess = bz_getSessionInfo(basepath);
                SamplingRate(isnan(SamplingRate)) = sess.rates.wideband;
            else
                SamplingRate(isnan(SamplingRate)) = SamplingRate(ok);
            end
        end

        %handle errors where different parameters read for different files (rather than uniform)
        if sum(abs(diff(NAmpChannels)))%if different numbers of channels differed across recordings in session, error out
            warning('Rercordings contain different numbers of channels - per info.rhd files.  Will use count from first recording.')
        end
        DatsMetadata.Parameters.NumberOfChannels = NAmpChannels(1);

        if sum(abs(diff(NAuxChannels)))%if different numbers of channels differed across recordings in session, error out
            warning('Rercordings contain different numbers auxiliary of channels - per info.rhd files.  Will use count from first recording.')
        end
        DatsMetadata.NumAuxiliaryChannels = NAuxChannels(1);

        if sum(abs(diff(NDigitalChannels)))%if different numbers of channels differed across recordings in session, error out
            warning('Rercordings contain different numbers digital of channels - per info.rhd files.  Will use count from first recording.')
        end
        DatsMetadata.NumDigitalChannels = NAuxChannels(1);

        %sampling rate
        if sum(abs(diff(SamplingRate)))%if different numbers of channels differed across recordings in session, error out
            warning('Rercordings contain different SamplingRates - per info.rhd files.  Will use number from first recording.')
        end
        DatsMetadata.Parameters.SampleRate = SamplingRate(1);

        % Not gather-able from intan info.rhd files
        DatsMetadata.Parameters.Amplification = 1;%digitized on chip, let's say 1
        DatsMetadata.Parameters.VoltsPerUnit = 0.0000002;%intan default
        DatsMetadata.Parameters.BitsPerSample = 16;%intan default
        DatsMetadata.Parameters.VoltageRange = 10;%not used except to make xml

        %Now that everything is in order, calculate seconds from bytes
        DatsMetadata.Recordings.Seconds = DatsMetadata.Recordings.Bytes/DatsMetadata.Parameters.NumberOfChannels/2/DatsMetadata.Parameters.SampleRate;
    case '' %if no known recording system used
        warning('No recording system found, using defaults.  May manually set values in guts of this .m file')
        DatsMetadata.RecordingSystem = 'Unknown';
        DatsMetadata.Recordings.Names = {};
        DatsMetadata.Recordings.Bytes = [];
        DatsMetadata.Recordings.Seconds = [];%can manually write in here if desired

        try
            sess = bz_getSessionInfo(basename);
        catch
            disp('No metadata or session info detected, quitting bz_DatFileMetadata')
            return
        end

        DatsMetadata.Parameters.NumberOfChannels = sess.nChannels;
        DatsMetadata.Parameters.SampleRate = sess.rates.wideband;%Lab default
        DatsMetadata.Parameters.Amplification = 1;%Intan digitized on chip, let's say 1
        DatsMetadata.Parameters.VoltsPerUnit = 0.0000002;%Intan default
        DatsMetadata.Parameters.BitsPerSample = sess.nBits;%Intan default
        DatsMetadata.Parameters.VoltageRange = 10;%not used except to make xml
end

save(fullfile(basepath,[basename '_DatsMetadata.mat']),'DatsMetadata')


function OutputFile = export_ssp(Projectors, ChannelNames, OutputFile)
% IMPORT_SSP: Saves SSP projectors to a file.
%
% USAGE:  SspFile = export_ssp(Projectors, ChannelNames, OutputFile=Ask)

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

% Ask for filename if not defined
if (nargin < 2) || isempty(OutputFile)
    % Get default directories and formats
    LastUsedDirs = bst_get('LastUsedDirs');
    % Build a default file name
    OutputFile = bst_process('GetNewFilename', LastUsedDirs.ExportData, 'proj.mat');
    [fPath,fBase,fExt] = bst_fileparts(OutputFile);
    OutputFile = bst_fullfile(LastUsedDirs.ExportData, [fBase, fExt]);
    % Get filename where to store the filename
    [OutputFile, ProjFormat] = java_getfile('save', 'Save projectors', OutputFile, 'single', 'files', ...
                            {{'.fif'}, 'Elekta-Neuromag/MNE (*.fif)',        'FIF'; ...
                            {'_proj'}, 'Brainstorm SSP (*proj*.mat)', 'BST'}, 2);
    if isempty(OutputFile)
        return;
    end
    % Save new default export path
    LastUsedDirs.ExportData = bst_fileparts(OutputFile);
    bst_set('LastUsedDirs', LastUsedDirs);
end

% Get file extension
[fPath, fBase, fExt] = bst_fileparts(OutputFile);
% Save file
switch (fExt)
    case '.fif'
        out_projector_fif(OutputFile, ChannelNames, Projectors);
    case '.mat'
        NewMat.Projector = Projectors;
        NewMat.RowNames = ChannelNames;
        bst_save(OutputFile, NewMat, 'v7');
    otherwise
        error(['Unknown file extension "' fExt '"']);
end


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

%%
clear; close all; clc;

%% Syntax
% |[VV]=triPatchSmoothRayTraced(F,V,optionStruct);|

%% 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 oM  = HKLOrientationMapping(varargin)

warning('Starting with MTEX 5.1 "HKLOrientationMapping" has been replaced by "ipfHKLKey".')

oM = ipfHKLKey(varargin{:});

end


function data = dcm2ml_Element(el, tag, varargin)
%"dcm2ml_element"
%   Convert a Java SimpleDicomElement object into a Matlab datatype.
%
%   TO DO: Define VRs labeled "Needs implementation."  All elements require
%   testing on various plans.
%
%   Discription for all the VR types
%     FD => 'double',
%     FL => 'float',
%     OB => 'int8u',
%     OF => 'float',
%     OW => 'int16u',
%     SL => 'int32s',
%     SS => 'int16s',
%     UL => 'int32u',
%     US => 'int16u',
%
%JRA 6/1/06
%   DK
%       Added support for multiple VR types.
%
%Usage:
%   data = dcm2ml_element(Java SimpleDicomElement)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

%Set cache buffer to true.
%{
buf = 1;

%Set character set to [], default.
cs = [];

%Get the tag value as a char array.
try
    tag = char(org.dcm4che2.util.TagUtils.toString(el.tag)); %removed el.tag
catch
    data = '';
    return;
end

%Get the VR, cast to ML char array.
vr = char(el.vr.toString);
%}
%modalityTag = '00080060';

vr = char (el.getVR(hex2dec(tag)));
% vr = cell(el.getVR(hex2dec(tag)));
% vr = vr{1}; % speedup

buf = 1;
cs = [];

switch upper(vr)
    case 'AE'
        %Needs implementation
        data = '';
    case 'AS'
        %Needs implementation
        data = '';
    case 'AT'
        data = dec2hex(el.getInts(buf));
    case {'CS', 'LO', 'SH', 'ST'}
        data = el.getStrings(cs, buf);
        %If more than one string, put in cell array.
        if numel(data) > 1
            data = cell(data);
        else
            data = char(data);
        end
    case 'DA'
        %Date string format: YYYYMMDD
        data = char(el.getString(cs, buf));
    case 'DS'
        data = el.getDoubles(buf);
    case 'DT'
        data = el.getDate(buf);

    case 'FL'
        %Needs implementation
        %wy
        %data =  float(el.getFloat(buf));
        data =  el.getFloats(buf);

    case 'FD'
        data = el.getDoubles(buf);
    case 'IS'
        data = el.getInts(buf);
    case 'LT'
        data = char(el.getString(cs, buf));
    case 'OB'
        %%%%%% Modified to import compressed data AI 02/06/17 %%%%%%%
        txSyntax = varargin{1};
        switch txSyntax
            case {'1.2.840.10008.1.2.4.50'
                    '1.2.840.10008.1.2.4.57'
                    '1.2.840.10008.1.2.4.70'
                    '1.2.840.10008.1.2.4.90'
                    '1.2.840.10008.1.2.4.91'
                    }
                %Decompress JPEG frame
                nElements = el.countItems;
                %if nElements>2
                %    warning(' dcm2ml_Element does not support multiple fragments');
                %    return
                %else
                    data = [];
                    for iFrag = 1:nElements
                    fragment = typecast(el.getFragment(iFrag-1),'uint8');
                    if ~isempty(fragment)
                    fileName = getTempName;
                    fid = fopen(fileName,'w');
                    if (fid < 0)
                        error('dcm2ml_element:Could not create temp file');
                    end
                    fwrite(fid,fragment,'uint8');
                    fclose(fid);
                    %tmp = onCleanup(@() delete(fileName));
                    %dataTmpV = imread(fileName).';
                    dataTmpV = imread(fileName);
                    if ndims(dataTmpV) == 3
                        dataTmpV = rgb2gray(dataTmpV); % temp for SM modality
                    end
                    dataTmpV = permute(dataTmpV,[2,1,3]);
                    data = [data;dataTmpV(:)];
                    end
                    end
                    delete(fileName)
                %end

            case '1.2.840.10008.1.2.5'

                % To do: Decompress RLE frame

            case {'1.2.840.10008.1.2'  %Implicit VR Little Endian (default)
                    '1.2.840.10008.1.2.1' %Explicit VR Little Endian
                    '1.2.840.10008.1.2.2' %Explicit VR Big Endian
                    '1.2.840.113619.5.2' %Implicit VR Big Endian (GE pvt)
                    '1.3.46.670589.33.1.4.1'}%Explicit VR Little Endian (Philips pvt)

                data = el.getBytes;

            otherwise
                error('dc2ml_Element : Encoding not supported');
        end
        %%%%%%%%%%%%%%%%%%% End Modified %%%%%%%%%%%%%%%%%%%%%

    case 'OF'
        data = el.getFloats(buf);
    case 'OW'
        %OW contains 16 bit words.  Conversion of this data into meaningful
        %values is the responsibility of the calling function.

        %wy it should be uint16 or int16, which depends on the value in data
        %representation fields, but the data conversion b/w matlab and java is int32.

        %data = uint16(el.getInts(buf));
        data = el.getInts(buf);
    case 'PN'
        nameObj = org.dcm4che3.data.PersonName(el.getString(cs, buf));

        %The # in get(#) as defined by dcm4che2, PersonName class.
        data.FamilyName = char(nameObj.get(0));
        data.GivenName  = char(nameObj.get(1));
        data.MiddleName = char(nameObj.get(2));
        data.NamePrefix = char(nameObj.get(3));
        data.NameSuffix = char(nameObj.get(4));
    case 'SL'
        %
         data = el.getInt(buf);
    case 'SQ'
        nElements = el.countItems;
        data = [];
        for i=0:nElements-1
            data.(['Item_' num2str(i+1)]) = dcm2ml_Object(el.getDicomObject(i));
        end
    case 'SS'
        %Needs implementation
        data = '';
    case 'TM'
        %Time string format: HHMMSS.ss where "ss" is fraction of a second.
        data = char(el.getString(cs, buf));
    case 'UI'
        data = char(el.getString(cs, buf));
    case 'UL'
        data = el.getInts(buf);
    case 'UN'
        data = el.getBytes;
    case 'US'
        data = el.getInt(buf);
    case 'UT'
        %Needs implementation
        data = '';
    otherwise
        error('Unrecognized VR type.'); %%Consider more gracious exit.
end

%DEBUGGING: remove this once all DICOM VRs are implemented and fully
%tested.  Until then, reaching this point in the code indicates that a VR
%MUST be defined for proper functioning of a called module.
if ~exist('data', 'var');
    disp(['DEBUGGING: ' vr ' is not defined.  Implement it in dcm2ml_Element.m']);
    data = '';
else
    %Handle empty data situations -- this needs to be tailored to individual
    %VR values if matching MATLAB's dicominfo function output is desired.
    if isempty(data);
        data = '';
    end
end

end % end of function

%% Function to generate temp filename
function tempfile = getTempName()
nFiles = 1;
persistent tempfiles counter;
if (isempty(tempfiles))
    for p = 1:nFiles
        tempfiles{p} = tempname;
    end

    counter = 0;
end
counter = rem(counter, nFiles) + 1;
tempfile = tempfiles{counter};

end % end of function


function editParamsROE(hObj,hData,hFig,planC)
% Edit model parameters
% AI 05/12/21

ud = guidata(hFig);
tag = get(hObj,'Tag');
indexS = planC{end};

%Get input data
idx = hData.Indices(1);
val = hData.EditData;
val2num = str2num(val);
if isempty(val2num) %Convert from string if numerical
    val2num = val;
end
prtcNum = ud.PrtcNum;
modelsC = ud.Protocols(prtcNum).model;
if isfield(ud,'ModelNum')
    modelNum = ud.ModelNum;
end

%Update parameter
switch(tag)
    case 'strSel'
        if hData.Indices(2)==1
            parameterS = modelsC{modelNum}.parameters;
            inputStructC = fieldnames(parameterS.structures);
            inputStructC = strcat('Select structure',{' '},inputStructC);
            matchIdx = find(strcmp(inputStructC,val));
            modelsC{modelNum}.inputStrNum = matchIdx;
            if ~isfield(modelsC{modelNum},'strNum') || ...
                    modelsC{modelNum}.strNum(matchIdx)==0
                hObj.Data{2} = 'Select from list';
            else
                allStrC = {planC{indexS.structures}.structureName};
                strIdx = modelsC{modelNum}.strNum(matchIdx);
                hObj.Data{2} = allStrC{strIdx};
            end
        else
            strListC = {'Select structure',planC{indexS.structures}.structureName};
            matchIdx = find(strcmp(strListC,val));
            inputStrNum = modelsC{modelNum}.inputStrNum;
            modelsC{modelNum}.strNum(inputStrNum) = matchIdx - 1;
            if isfield(ud.Protocols(prtcNum),'constraints')
                criteriaS = ud.Protocols(prtcNum).constraints;
                expectedStrName = strrep(hObj.Data{1},'Select structure ','');
                selectedStrName = strListC{matchIdx};
                %Update expected str name in criteria data stucture
                if isfield(criteriaS.structures,expectedStrName)
                    expS = criteriaS.structures.(expectedStrName);
                    selectedStrName = strrep(selectedStrName,' ','_');
                    criteriaS.structures.(selectedStrName) = expS;
                    criteriaS.structures = rmfield(criteriaS.structures,expectedStrName);
                    ud.Protocols(prtcNum).constraints = criteriaS;
                end
            end
        end
    case 'doseSel'
        if hData.Indices(2)==1
            return
        else
            dosListC = {'Select Plan',planC{indexS.dose}.fractionGroupID};
            matchIdx = find(strcmp(dosListC,val));
            %modelsC{modelNum}.planNum = matchIdx - 1;
            ud.planNum = matchIdx - 1;
            %Clear pre-computed dvh
            for p = 1:length(ud.Protocols)
                modelC = ud.Protocols(p).model;
                for modNum = 1:length(modelC)
                    if isfield(modelC{modNum},'dv')
                        modelC{modNum} = rmfield(modelC{modNum},'dv');
                    end
                end
                ud.Protocols(p).model = modelC;
            end
            %Auto-populate precribed dose if available
            RxField = ud.handle.tab1H(12);
            if ~isfield(planC{indexS.dose}(matchIdx - 1),'prescribedDose')
                try
                    prescribedDose = getPrescribedDose(matchIdx-1,planC);
                    set(RxField,'String',num2str(prescribedDose));
                    ud.handle.tab1H(12) = RxField;
                end
            else
                prescribedDose = planC{indexS.dose}(matchIdx - 1).prescribedDose;
                set(RxField,'String',num2str(prescribedDose));
                ud.handle.tab1H(12) = RxField;
            end
        end
    case 'fieldEdit'
        modelsC{modelNum} = modelsC{modelNum};
        parName = hObj.Data{idx,1};
        modelsC{modelNum}.(parName) = val2num;
        modelsC{modelNum} = modelsC{modelNum};
        set(ud.handle.tab1H(8),'Enable','On');  %Enable save
    case 'paramEdit'
        %Update modelC
        parName = hObj.Data{idx,1};
        strParam = 0;
        if isfield(modelsC{modelNum}.parameters,'structures')
            structS = modelsC{modelNum}.parameters.structures;
            if isstruct(structS)
                stC = fieldnames(structS);
                found = 0;
                t = 0;
                while ~found & t<numel(stC)
                    found = isfield(structS.(stC{t+1}),parName);
                    t = t+1;
                end
                if found
                    strParam = 1;
                    type = structS.(stC{t}).(parName).type;
                end
            end
        end
        if ~strParam
            type = modelsC{modelNum}.parameters.(parName).type;
        end
        if strcmpi(type{1},'bin')
            desc = modelsC{modelNum}.parameters.(parName).desc;
            ctgIdx = strcmp(desc,val2num);
            value = modelsC{modelNum}.parameters.(parName).cteg(ctgIdx);
            modelsC{modelNum}.parameters.(parName).val = value;
        else
            modelsC{modelNum}.parameters.(parName).val = val2num;
        end
        set(ud.handle.tab1H(8),'Enable','On');  %Enable save
end
ud.Protocols(prtcNum).model = modelsC;
guidata(hFig,ud);

end


function neigh = grNeighborEdges(edges, node)
%GRNEIGHBOREDGES Find adjacent edges of a given node.
%
%   Deprecated, use 'grAdjacentEdges' instead.
%
%   NEIGHS = grNeighborEdges(EDGES, NODE)
%   EDGES  : the complete edges list
%   NODE   : index of the node
%   NEIGHS : the indices of edges containing the node index
%
%   See also
%     grAdjacentEdges

% ------
% Author: David Legland
% e-mail: david.legland@inrae.fr
% Created: 2003-08-13
% Copyright 2003 INRA - TPV URPOI - BIA IMASTE

warning('MatGeom:graphs:deprecated', ...
    'function grNeighborEdges is obsolete, use grAdjacentEdges instead');

neigh = find(edges(:,1) == node | edges(:,2) == node);


function experiments = stack_vot2019_rgbd()

set_global_variable('bundle', 'http://data.votchallenge.net/vot2019/rgbd/description.json');

unsupervised.name = 'rgbd-unsupervised';
unsupervised.converter = [];
unsupervised.type = 'unsupervised';
unsupervised.tags = {};

unsupervised.parameters.repetitions = 1;
unsupervised.parameters.burnin = 0;

unsupervised.analysis = {'overlap'};

experiments = {unsupervised};

end