%   CLASS File_Name_Processor
% =========================================================================
%
% DESCRIPTION
%   Class to process file names with keywords
%
% EXAMPLE
%   fnp = File_Name_Processor();
%   fnp.dateKeyRep('${WWWW}/igs${WWWWD}_${6H}.clk_30s', GPS_Time(datenum('2017/02/05 23:12:11')))
%
% FOR A LIST OF CONSTANTs and METHODS use doc FTP_Server
%
% REQUIRES:
%   goGPS settings;
%
% COMMENTS
%   Nothing to report

%--- * --. --- --. .--. ... * ---------------------------------------------
%               ___ ___ ___
%     __ _ ___ / __| _ | __|
%    / _` / _ \ (_ |  _|__ \
%    \__, \___/\___|_| |___/
%    |___/                    v 1.0RC1
%
%--------------------------------------------------------------------------
%  Copyright (C) 2021 Geomatics Research & Development srl (GReD)
%  Written by:        Andrea Gatti
%  Contributors:      Andrea Gatti, Giulio Tagliaferro
%  A list of all the historical goGPS contributors is in CREDITS.nfo
%--------------------------------------------------------------------------
%
%   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/>.
%
%--------------------------------------------------------------------------
% 01100111 01101111 01000111 01010000 01010011
%--------------------------------------------------------------------------

classdef File_Name_Processor < handle

    properties (Constant)
        GPS_WEEK = '${WWWW}';
        GPS_WD = '${WWWWD}';
        GPS_DOW = '${D}';
        GPS_3H = '${3H}';
        GPS_6H = '${6H}';
        GPS_HH = '${HH}';
        GPS_YY = '${YY}';
        GPS_YYDOY = '${YYDOY}';
        GPS_YYYY = '${YYYY}';
        GPS_DOY = '${DOY}';
        GPS_FIRST_DOY_TRIMESTER = '${DOYT}';
        GPS_SESSION = '${S}';
        GPS_MM = '${MM}';
        GPS_DD = '${DD}';
        GPS_1D = '${1D}';
        GPS_QQ = '${QQ}';
        GPS_5M = '${5M}';
        VMF_RES = '${VMFR}';
        VMF_SOURCE = '${VMFS}';
    end

    properties (SetAccess = private, GetAccess = public)
        log = Logger.getInstance();
    end

    properties (SetAccess = protected, GetAccess = protected)
        addr;        % ip address (or alias)
        port = 21;   % connection port
        path = '/';  % path to files
    end

    methods
        function this = File_Name_Processor()
            % Constructor
        end

        function file_name_out = dateKeyRep(this, file_name, date, session,vmf_res,vmf_source)
            % substitute time placeholder with the proper format
            % SYNTAX: file_name = this.dateKeyRep(file_name, date, session)
            narginchk(3,6)
            if (nargin < 4)
                session = '0';
            end
            if (nargin < 5)
                vmf_res = '';
            end
            if (nargin < 6)
                vmf_source = '';
            end
            file_name_out = file_name;
            if any(file_name_out == '$') && ~isempty(regexp(file_name_out, '\$\{Y|(DOY)', 'once'))
                [year, doy] = date.getDOY();
                file_name_out = strrep(file_name_out, this.GPS_DOY, sprintf('%03d', doy));
                file_name_out = strrep(file_name_out, this.GPS_FIRST_DOY_TRIMESTER, sprintf('%03d', GPS_Time.doy2fdoytr(doy,year)));
                file_name_out = strrep(file_name_out, this.GPS_YY, sprintf('%02d', mod(year,100)));
                file_name_out = strrep(file_name_out, this.GPS_YYYY, sprintf('%04d', year));
                file_name_out = strrep(file_name_out, this.GPS_YYDOY, sprintf('%02d%03d', mod(year,100), doy));
            end
            if any(file_name_out == '$') && ~isempty(regexp(file_name_out, '\$\{(WWWW)|(WD)|(DOW)|(3H)|(6H)|(HH)|(QQ)|(5M)\}', 'once'))
                [gps_week, gps_sow, gps_dow] = date.getGpsWeek();
                file_name_out = strrep(file_name_out, this.GPS_WEEK, sprintf('%04d', gps_week(1)));
                if any(file_name_out == '$')
                    file_name_out = strrep(file_name_out, this.GPS_DOW, sprintf('%01d', gps_dow(1)));
                end
                if any(file_name_out == '$')
                    file_name_out = strrep(file_name_out, this.GPS_WD, sprintf('%04d%01d', gps_week(1), gps_dow(1)));
                    file_name_out = strrep(file_name_out, this.GPS_3H, sprintf('%02d', fix((gps_sow(1) - double(gps_dow(1)) * 86400)/(3*3600))*3));
                    file_name_out = strrep(file_name_out, this.GPS_6H, sprintf('%02d', fix((gps_sow(1) - double(gps_dow(1)) * 86400)/(6*3600))*6));
                    file_name_out = strrep(file_name_out, this.GPS_HH, sprintf('%02d', fix((gps_sow(1) - double(gps_dow(1)) * 86400)/(3600))));
                    file_name_out = strrep(file_name_out, this.GPS_QQ, sprintf('%02d', mod(15 * fix((gps_sow(1) - double(gps_dow(1)) * 86400)/(900)), 60)));
                    file_name_out = strrep(file_name_out, this.GPS_5M, sprintf('%02d', mod(5 * fix((gps_sow(1) - double(gps_dow(1)) * 86400)/(300)), 60)));
                end
            end
            if any(file_name_out == '$') && ~isempty(regexp(file_name_out, '\$\{(MM)|DD)|(1D)\}', 'once'))
                [date] = datevec(date.getMatlabTime());
                file_name_out = strrep(file_name_out, this.GPS_MM, sprintf('%02d', date(2)));
                file_name_out = strrep(file_name_out, this.GPS_DD, sprintf('%02d', date(3)));
                file_name_out = strrep(file_name_out, this.GPS_1D, sprintf('%d', date(3)));
            end
            if any(file_name_out == '$')
                file_name_out = strrep(file_name_out, this.GPS_SESSION, sprintf('%01d', session));
            end
            if any(file_name_out == '$')
                file_name_out = strrep(file_name_out, this.VMF_RES, char(vmf_res));
            end
            if any(file_name_out == '$')
                file_name_out = strrep(file_name_out, this.VMF_SOURCE, char(vmf_source));
            end
        end

        function step_sec = getStepSec(this, file_name)
            % get the shorter time keyword present in file_name [seconds]
            % SYNTEX: step_sec = this.getStepSec(file_name);
            % Check for GPS time placeholders
            step_sec = 0;
            if ~isempty(strfind(file_name, this.GPS_5M))
                step_sec = 300;
            elseif ~isempty(strfind(file_name, this.GPS_QQ))
                step_sec = 900;
            elseif ~isempty(strfind(file_name, this.GPS_HH))
                step_sec = 3600;
            elseif ~isempty(strfind(file_name, this.GPS_3H))
                step_sec = 3 * 3600;
            elseif ~isempty(strfind(file_name, this.GPS_6H))
                step_sec = 6 * 3600;
            elseif ~isempty((strfind(file_name, this.GPS_DOW))) || ~isempty(strfind(file_name, this.GPS_DD)) || ~isempty(strfind(file_name, this.GPS_WD)) || ~isempty(strfind(file_name, this.GPS_DOY)) || ~isempty(strfind(file_name, this.GPS_1D)) || ~isempty(strfind(file_name, this.GPS_YYDOY))
                step_sec = 24 * 3600;
            elseif ~isempty(strfind(file_name, this.GPS_WEEK))
                step_sec = 24 * 3600 * 7;
            elseif ~isempty(strfind(file_name, this.GPS_FIRST_DOY_TRIMESTER))
                step_sec = 24 * 3600 * 91;
            elseif ~isempty(strfind(file_name, this.GPS_YYYY)) || ~isempty(strfind(file_name, this.GPS_YY))
                step_sec = 24 * 3600 * 365;
            end
        end

        function [file_name_lst, date_list] = dateKeyRepBatch(this, file_name, date_start, date_stop, session_list, session_start, session_stop, vmf_res, vmf_source)
            % substitute time placeholder with the proper format
            % SYNTAX: file_name = this.dateKeyRepBatch(file_name, date_start, date_stop)
            % NOTE: I consider only two possible file formats:
            %         - dependend on UTC time (year, doy)
            %         - dependent on GPS time (week, day of week, h of the day)

            if nargin < 5
                session_list = '0';
                session_start = '0';
                session_stop = '0';
            end
            if nargin < 8
                vmf_res = '';
                vmf_source = '';
            end
            session = ~isempty(strfind(file_name, this.GPS_SESSION));
            sss_start = strfind(session_list, session_start);
            sss_stop = strfind(session_list, session_stop);

            % Check for GPS time placeholders
            step_sec = this.getStepSec(file_name);

            if (step_sec > 0)
                file_name_lst = {};
                date_list = date_start.getCopy();
                if not(abs(step_sec - (24 * 3600 * 91)) < 1e-3)
                    date0 = GPS_Time((floor(((date_start.getNominalTime(0.5).getMatlabTime() - GPS_Time.GPS_ZERO) * 86400) / step_sec) * step_sec) / 86400 + GPS_Time.GPS_ZERO);
                else
                    date0 = date_start.getCopy();
                end
                date0 = date0.getNominalTime(60);
                date1 = date_stop.getNominalTime(1);

                date_list.toUnixTime(); % keep an higher precision

                i = 1;
                % Find all the file in the interval of dates
                while (date0.getMatlabTime() <= date1.getMatlabTime())
                    if session
                        % run over session
                        for s = sss_start : length(session_list)
                            file_name_lst{i} = this.keyRep(file_name, this.GPS_SESSION, session_list(s)); %#ok<AGROW>
                            file_name_lst{i} = this.dateKeyRep(file_name_lst{i}, date0, '0',vmf_res,vmf_source); %#ok<AGROW>
                            i = i + 1;
                        end
                        sss_start = 1;
                    else
                        file_name_lst{i} = this.dateKeyRep(file_name, date0, '0', vmf_res,vmf_source); %#ok<AGROW>
                        i = i + 1;
                    end
                    date0.addIntSeconds(step_sec);
                    date0 = date0.getNominalTime(60);
                    if (date0.getMatlabTime() <= date1.getMatlabTime())
                        date_list.append(date0);
                    end
                end
            else
                date_list = GPS_Time();
                i = 1;
                % run over session
                if session
                    for s = sss_start : length(session_list)
                        file_name_lst{i} = this.keyRep(file_name, this.GPS_SESSION, session_list(s)); %#ok<AGROW>
                        i = i + 1;
                    end
                else
                    if iscell(file_name)
                        file_name_lst = file_name;
                    else
                        file_name_lst = {file_name};
                    end
                end
            end

            if session
                sss_clean = length(file_name_lst) - (length(session_list)-sss_stop) + 1 : length(file_name_lst);
                file_name_lst(sss_clean) = [];
            end
            if iscell(file_name_lst)
                file_name_lst = file_name_lst';
            end
        end
    end

    methods (Static)
        function dir_path_out = getInnerDir(dir_path_list)
            % Get recursive dir list
            %
            % SYNTAX
            %   dir_path_out = getInnerDir(dir_path_list)

            dir_path_out = cell(0);
            % make base_dir a cell
            if ~iscell(dir_path_list)
                dir_path_list = {dir_path_list};
            end

            % loop on base_dirs
            for d = 1 : numel(dir_path_list)
                dir_path_out = [dir_path_out dir_path_list(d)];
                dir_path = dir_path_list{d};
                fprintf(' Scanning %s for subdirs\n', dir_path);
                % Search fo subdirs
                tmp = dir(dir_path);
                tmp = tmp([tmp.isdir]);
                dir_list = {};
                for i = numel(tmp) : -1 : 1
                    % remove hidden or special dirs (log)
                    if not(tmp(i).name(1) == '.') && not(strcmp(tmp(i).name, 'log'))
                        dir_list = [dir_list {fullfile(dir_path, tmp(i).name)}];
                    end
                end

                if ~isempty(dir_list)
                    for c = 1 : numel(dir_list)
                        cur_dir = dir_list(c);
                        dir_path_out = [dir_path_out File_Name_Processor.getInnerDir(cur_dir)];
                    end
                end
            end
        end

        function file_list_out = findFiles(dir_path_list, prefix)
            % Get recursive file list
            %
            % SYNTAX
            %   file_list_out = findFiles(dir_path_list, prefix)
            file_list_out = cell(0);
            % make base_dir a cell
            if ~iscell(dir_path_list)
                dir_path_list = {dir_path_list};
            end

            % loop on base_dirs
            for d = 1 : numel(dir_path_list)
                dir_path = dir_path_list{d};
                fprintf(' Scanning %s for subdirs\n', dir_path);
                % Search fo subdirs
                tmp = dir(dir_path);
                tmp_dir_list = tmp([tmp.isdir]);
                tmp_file_list = tmp(~[tmp.isdir]);
                dir_list = {};
                for i = 1 : numel(tmp_dir_list)
                    % remove hidden or special dirs (log)
                    if not(tmp_dir_list(i).name(1) == '.') && not(strcmp(tmp_dir_list(i).name, 'log'))
                        dir_list = [dir_list {fullfile(dir_path, tmp_dir_list(i).name)}];
                    end
                end

                if ~isempty(dir_list)
                    for c = 1 : numel(dir_list)
                        cur_dir = dir_list(c);
                        file_list_out = [file_list_out File_Name_Processor.findFiles(cur_dir, prefix)];
                    end
                end

                for i = 1 : numel(tmp_file_list)
                    if length(tmp_file_list(i).name) > length(prefix) &&  strcmp(tmp_file_list(i).name(1:length(prefix)), prefix)
                        file_list_out = [file_list_out {fullfile(dir_path, tmp_file_list(i).name)}];
                    end
                end
            end
        end

        function dir_path = getFullDirPath(dir_path, dir_base, dir_fallback, empty_fallback)
            % Get the full path given the relative one and the relative dir_base
            % It changes the folder with dir_fallback only if the changed folder exist
            %
            % SYNTAX:
            %   dir_path = getFullDirPath(dir_path, <dir_base default = pwd>, fallback_dir_base);

            if nargin == 1
                dir_base = pwd;
            end
            if (nargin < 4)
                empty_fallback = dir_base;
            end
            if isempty(dir_base)
                dir_base = pwd;
            end

            dir_path_bk = dir_path;

            fnp = File_Name_Processor;
            dir_path = fnp.checkPath(dir_path);
            if isunix
                if ~isempty(dir_path)
                    if (dir_path(1) == '/')
                        dir_path = fnp.checkPath(dir_path);
                    else
                        if ~isempty(dir_base)
                            if (dir_base(1) ~= '/')
                                dir_base = fnp.getFullDirPath(dir_base, pwd);
                            end
                        end
                        dir_path = fnp.checkPath([dir_base filesep dir_path]);
                    end
                else
                    dir_path = fnp.checkPath(empty_fallback);
                end
            else
                if length(dir_path) > 1
                    if (dir_path(2) == ':')
                        dir_path = fnp.checkPath(dir_path);
                    else
                        if length(dir_base) > 1
                            if (dir_base(2) == ':')
                                dir_base = fnp.getFullDirPath(dir_base, pwd);
                            end
                        end
                        if ~(numel(dir_path) >= 2 && strcmp(dir_path(1:2), '\\'))
                            dir_path = fnp.checkPath([dir_base filesep dir_path]);
                        end
                    end
                else
                    dir_path = fnp.checkPath(empty_fallback);
                end
            end

            dir_path = File_Name_Processor.remDirModifiers(dir_path);

            % Fallback if not exist
            if (nargin >= 3) && ~isempty(dir_fallback)
                if ~exist(dir_path, 'file')
                    prefix = '';
                    if numel(dir_fallback) > 2
                        prefix = iif(strcmp(dir_fallback(1:2),'\\'), '\\', '');
                    end
                    tmp = fnp.getFullDirPath(dir_path_bk, dir_fallback);
                    % change dir_path only if the new path exist
                    if exist(tmp, 'file')
                        dir_path = tmp;
                    end
                    dir_path = [prefix dir_path];
                end
            end
        end

        function path = remDirModifiers(path)
            % Utility to remove from a path dir changes with "." and ".."
            %
            % SYNTAX
            %   path = File_Name_Processor.remDirModifiers(path)

            if ~isempty(path)
                % remove './'
                path = strrep(path, [filesep '.' filesep], filesep);

                prefix = '';
                if numel(path) > 2
                    prefix = iif(strcmp(path(1:2),'\\'), '\\', '');
                end

                % extract sub folder names
                list = regexp(path,['[^' iif(filesep == '\', '\\', filesep) ']*'],'match');

                % search for "../"
                dir_up = find(strcmp(list,'..'));
                offset = 0;
                for i = dir_up
                    list(max(1, (i - 1 : i) - offset)) = [];
                    offset = offset + 2;
                end

                % restore full path start
                if isunix
                    path = [prefix filesep strCell2Str(list, filesep)];
                else
                    path = [prefix strCell2Str(list, filesep)];
                end
            end
        end

        function dir_path = getRelDirPath(dir_path, dir_base)
            % Get the full path given the relative one and the relative dir_base
            % SYNTAX: dir_path = getRelativeDirPath(dir_path, <dir_base default = pwd>);

            if nargin == 1
                dir_base = pwd;
            end

            fnp = File_Name_Processor;
            dir_base = fnp.getFullDirPath(dir_base, pwd);
            list_base = regexp(dir_base, ['[^' iif(filesep == '\', '\\', filesep) ']*'], 'match');
            n_dir_base = numel(list_base);

            if iscell(dir_path)
                for j = 1 : numel(dir_path)
                    dir_path{j} = fnp.getFullDirPath(dir_path{j}, dir_base);
                    drive_path = regexp(dir_path{j},'.(?=\:)', 'match', 'once');
                    drive_base = regexp(dir_base,'.(?=\:)', 'match', 'once');
                    if (isempty(drive_path) || drive_path == drive_base)
                        list_path = regexp(dir_path{j}, ['[^' iif(filesep == '\', '\\', filesep) ']*'], 'match');
                        n_dir_path = numel(list_path);
                        i = 0;
                        while (i < n_dir_base) && (i < n_dir_path) && strcmp(list_base{i+1}, list_path{i+1})
                            i = i + 1;
                        end
                        if (n_dir_base -i) > 0
                            list_path = [{repmat(['..' filesep],1, n_dir_base - i)} list_path(i+1:end)];
                        else
                            list_path = list_path(i+1:end);
                        end

                        dir_path{j} = strrep(strCell2Str(list_path, filesep), [filesep filesep], filesep);
                    end
                end
            else
                % if dir_path is a network address && dir_base is not
                prefix = '';
                if (numel(dir_path) > 2 && strcmp(dir_path(1:2),'\\')) && ~((numel(dir_base) > 2 && strcmp(dir_base(1:2),'\\')))
                    n_dir_base = 0;
                    list_base = {};
                    prefix = '\\';
                end
                dir_path = fnp.getFullDirPath(dir_path, dir_base);
                drive_path = regexp(dir_path,'.(?=\:\\)', 'match', 'once');
                drive_base = regexp(dir_base,'.(?=\:\\)', 'match', 'once');
                if (isempty(drive_path) || drive_path == drive_base)
                    list_path = regexp(dir_path, ['[^' iif(filesep == '\', '\\', filesep) ']*'], 'match');
                    n_dir_path = numel(list_path);
                    i = 0;
                    while (i < n_dir_base) && (i < n_dir_path) && strcmp(list_base{i+1}, list_path{i+1})
                        i = i + 1;
                    end
                    if (n_dir_base -i) > 0
                        if i > 0
                            list_path = [{repmat(['..' filesep],1, n_dir_base - i)} list_path(i+1:end)];
                        elseif isempty(prefix)
                            prefix = filesep;
                        end

                    else
                        list_path = list_path(i+1:end);
                    end

                    dir_path = [prefix strrep(strCell2Str(list_path, filesep), [filesep filesep], filesep)];
                end
            end
        end

        function file_name = getFileName(file_name)
            % Get only the file name of a full path
            [~, name, extension] = fileparts(file_name);
            file_name = [name extension];
        end

        function path = getPath(file_name)
            % Get only the path up to the folder name from a full path
            [path, ~, ~] = fileparts(file_name);
        end

        function file_name = keyRep(file_name, key, substitution)
            % Substitute a key in the file_name with another value
            file_name = strrep(file_name, key, substitution);
        end

        function [str_cell] = toIniStringComment(str_cell)
            % Get the list of accepted substitution
            if (nargin == 0)
                str_cell = {};
            end
            str_cell = Ini_Manager.toIniStringComment('Special Keywords that can be used in file names:', str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s    4 char GPS week', File_Name_Processor.GPS_WEEK), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s 4+1 char GPS week + day of the week', File_Name_Processor.GPS_WD), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s       1 char day of the week', File_Name_Processor.GPS_DOW), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s      2 char GPS hour (00, 03, 06, 09, 12, 15, 18, 21)', File_Name_Processor.GPS_3H), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s      2 char GPS hour (00, 06, 12, 18)', File_Name_Processor.GPS_6H), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s      2 char GPS hour', File_Name_Processor.GPS_HH), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s      2 char GPS quarter of hour (00, 15, 30, 45)', File_Name_Processor.GPS_QQ), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s      2 char GPS five minutes (05, 10, ... , 55)', File_Name_Processor.GPS_5M), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s 2+3 char GPS year + day of year', File_Name_Processor.GPS_YYDOY), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s    4 char GPS year', File_Name_Processor.GPS_YYYY), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s      2 char GPS year', File_Name_Processor.GPS_YY), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s      2 char GPS month', File_Name_Processor.GPS_MM), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s      2 char GPS day', File_Name_Processor.GPS_DD), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s     3 char GPS day of the year', File_Name_Processor.GPS_DOY), str_cell);
            str_cell = Ini_Manager.toIniStringComment(sprintf(' - %s       1 char session', File_Name_Processor.GPS_SESSION), str_cell);
        end

        function full_path = getFullPath(dir_path, file_path)
            % Get the list of files combining dir_path + file_path
            % It works when file_path is a cell or a simple string
            % SYNTAX: full_path = IO_Settings.getFullPath(dir_path, file_path);

            fnp = File_Name_Processor();

            if iscell(file_path)
                full_path = cell(numel(file_path),1);
                for f = 1 : numel(file_path)
                    full_path{f} = fnp.checkPath(fullfile(dir_path, file_path{f}));
                end
            else
                full_path = fnp.checkPath(fullfile(dir_path, file_path));
            end
        end

        function [universal_path, is_valid] = checkPath(path, prj_home)
            % Conversion of path OS specific to a universal one: "/" or "\" are converted to filesep
            %
            % SYNTAX:
            %   universal_path = checkPath(path)
            %
            % INPUT:
            %   path
            %
            % OUTPUT:
            %   universal_path
            %   < is_valid >            optional, contains the status of existence
            %
            % DESCRIPTION:
            %   Conversion of path OS specific to a universal one: "/" or "\" are converted to filesep
            %   if the second parameter is present is_valid contains the status of existence
            %       2 => is a file
            %       7 => is a folder

            if instr(path, '${PRJ_HOME}')
                if nargin < 2
                %    state = Core.getState();
                %    prj_home = state.getHomeDir;
                    prj_home = '';
                end
                path = strrep(path, '${PRJ_HOME}', prj_home);
            end

            prefix = '';
            if numel(path) > 2
                prefix = iif(strcmp(path(1:2),'\\'), '\', '');
            end

            if not(isempty(path))
                if (iscell(path))
                    % for each line of the cell
                    universal_path = cell(size(path));
                    for c = 1 : length(path)
                        check_drive_win = strfind(path{c},':');
                        if (~isempty(check_drive_win))
                            path{c} = path{c}(max(1, check_drive_win - 1) : end);
                        end
                        universal_path{c} = regexprep(path{c}, '(\\(?![ ]))|(\/)', filesep);
                        universal_path{c} = regexprep(universal_path{c}, ['\' filesep '\' filesep], filesep);
                    end
                    if (nargout == 2)
                        is_valid = zeros(size(path));
                        for c = 1 : length(path)
                            is_valid(c) = exist(universal_path{c}, 'file'); % if it is a file is_valid contains 2, if it is a dir it contains 7
                        end
                    end
                else
                    if ischar(path)
                        check_drive_win = strfind(path,':');
                        if (~isempty(check_drive_win))
                            path = path(max(1, check_drive_win - 1) : end);
                        end
                        universal_path = regexprep(path, '(\\(?![ ]))|(\/)', filesep);
                        universal_path = regexprep(universal_path, ['\' filesep '\' filesep], filesep);
                        if (nargout == 2)
                            is_valid = exist(path, 'file'); % if it is a file is_valid contains 2, if it is a dir it contains 7
                        end
                    else
                        universal_path = '';
                        is_valid = 0;
                    end
                end
            else
                universal_path = '';
                is_valid = 0;
            end
            universal_path = [prefix universal_path];
        end

    end
end


function dat_comb = searchlight_applymask_collate(dat2, file_list)
% Estimate local pattern weight on train data using SVR and searchlight and
% apply weights to the test dataset
%
% :Usage:
% ::
%
%     dat_comb = searchlight_applymask(dat2, file_list)
%
% :Inputs:
%
%   **dat2:**
%        fmri_data test object
%
%   **file_list:**
%        Cellarray of list of file distributed in parallel.  Make sure
%        it is sorted correctly (e.g., sort_nat())
%
% :Output:
%
%   **dat_comb:**
%        An fmri_data object with searchlight weights applied to
%        test dataset.
%
% ..
%     Author and copyright information:
%
%     Copyright (C) 2015  Luke Chang & Wani Woo
%
%     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/>.
% ..
%
% :Example:
% ::
%
%    dat_comb = searchlight_applymask_collate(dat2, file_list);

if ~isa(dat2,'fmri_data') % Check inputs
    dat1 = fmri_data(dat2); dat2 = remove_empty(dat2);
end

if ~iscell(file_list)
    error('Make sure searchlight file list is a cellarray and that files are on the path')
end

% Collate searchlight .mat files
vox_num = size(dat2.dat,1);
dist_n = length(file_list);
dat_comb = dat2;
for i = 1:dist_n
    load(file_list{i});
    dist_indx = select_voxels(i, dist_n, vox_num);
    dat_comb.dat(dist_indx,:) = dat.dat;
end
end

% ========== SUBFUNCTION ===========

function dist_indx = select_voxels(dist_id, dist_n, vox_num)
% preparation of distribution indx

dist_indx = false(vox_num,1);

unit_num = ceil(vox_num/dist_n);
unit = true(unit_num,1);

start_point = (unit_num.*(dist_id-1)+1);
end_point = min(start_point+unit_num-1, vox_num);
dist_indx(start_point:end_point) = unit(1:end_point-start_point+1);

dist_indx = logical(dist_indx);
end


function results = run_OTBReadyTracker(seq, res_path, bSaveImage)
 % rename OTBReadyTracker to the name of the tracker you are using in the OTB evaluation
    startup;
    paths = env_paths_tracking();
    tracker_params.visualization = false;
    tracker_params.gpus = 1;
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    tracker_params.net = 'some-pretrained-network';
    tracker_params.net_gray = 'some-pretrained-network_gray';
    tracker_params.visualization = false;
    tracker_params.join.method = 'corrfilt'; % or 'xcorr'
    root_folder = 'your/path/to/otb/benchmark/root/';
    % % hyperparameters that work well for the tracker version you are using
    % tracker_params.scaleStep =
    % tracker_params.scalePenalty =
    % tracker_params.scaleLR =
    % tracker_params.wInfluence =
    % tracker_params.zLR =
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    tracker_params.imgFiles = vl_imreadjpeg(strcat(root_folder,seq.s_frames),'numThreads', 12);
    [cx, cy, w, h] = get_axis_aligned_BB(seq.init_rect);
    tracker_params.targetPosition = [cy cx];
    tracker_params.targetSize = round([h w]);
    % Call the main tracking function
    [bboxes, ~] = tracker(tracker_params);
    results = struct();
    results.res = bboxes;
    results.type = 'rect';
end


function [inplane ok] = roiLoadVol2Inplane(inplane, roiName, saveFlag)
%
% [inplane ok] = roiLoadVol2Inplane(inplane, <roiName>, <saveFlag=1>);
%
% A convenient accessor function: for an inplane view,
% load an ROI defined on the volume, and convert it over into
% the inplanes. Saves loading a separate Volume view, if this is
% all you want to do.
%
% If the ROI name is omitted, will prompt the user.
%
% If saveFlag=1, will save the xformed inplane ROI (provided it
% is nonempty).
%
% ras, 05/06.
if notDefined('inplane'), inplane = getSelectedInplane; end
if notDefined('saveFlag'), saveFlag = 1;                end

if ~isequal(inplane.viewType, 'Inplane')
    error('Works only on inplane views.')
end

hV = getSelectedGray;

if notDefined('roiName')
    % dialog
    roiName = getROIfilename(hV, 0);
end

% ensure cell ROI name
if ~iscell(roiName), roiName = {roiName};  end

for i = 1:length(roiName)
    [hV, ok] = loadROI(hV, roiName{i}, 1, [], 1);

    if ~ok
        fprintf('Couldn''t load Volume ROI %s \n', roiName{i});
        continue
    end

    ipROI = vol2ipROI(hV.ROIs(end), hV, inplane);

	% is the ROI empty? Go ahead and xform it, but warn the user and set
	% the 'ok' status to zero:
	if isempty(ipROI.coords)
		warning('[%s]: Xformed empty ROI %s.', mfilename, ipROI.name);
		ok = 0;
	end

    inplane = addROI(inplane, ipROI, 0);

    if saveFlag & ~isempty(ipROI.coords)
        inplane = saveROI(inplane, inplane.ROIs(end), [], saveFlag);
    end
end

inplane = selectROI(inplane, length(inplane.ROIs));

clear hV

return


function num = meanwords(allSNumBi)
count = 0;
for i = 1:num(allSNumBi)

end


function [n, fn] = dimlength(data, seldim, fld)

% DIMLENGTH(DATA, SELDIM, FLD) is a helper function to obtain n, the number
% of elements along dimension seldim from the appropriate field from the
% input data containing functional data.
%
% Use als
%   [n, fn] = dimlength(data, seldim, fld)
%
% It can be called with one input argument only, in which case it will
% output two cell arrays containing the size of the functional fields,
% based on the XXXdimord, and the corresponding XXXdimord fields.
%
% When the data contains a single dimord field (everything except source
% data), the cell-arrays in the output only contain one element.
%
% See also FIXSOURCE, CREATEDIMORD

% Copyright (C) 2010, Jan-Mathijs Schoffelen
%
% $Id$

if nargin<3
  fld = 'dimord';
end

if nargin==1
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % call recursively to get the dimensionality of all fields XXX which are
  % accompanied by a XXXdimord field or the general dimord field
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  % get all fields of the data structure
  fn = fieldnames(data);
  % get all dimord like fields
  fn    = fn(~cellfun('isempty', strfind(fn, 'dimord')));
  for k = 1:numel(fn)
    fndimord{k} = data.(fn{k});
  end

  for k = 1:numel(fndimord)
    dimtok = tokenize(fndimord{k}, '_');
    ndim   = numel(dimtok);
    n{k,1} = zeros(1, ndim);
    for i = 1:ndim
      n{k}(i) = dimlength(data, dimtok{i}, fn{k});
    end
  end
  return

else
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % do the actual work
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  switch seldim
    case 'rpt'
      if numel(fld)>6 && isfield(data, fld(1:end-6))
        % source level data
        dimtok = tokenize(data.(fld), '_');
        tmp    = data.(fld(1:end-6));
        % remove the first cell-dimension
        if iscell(tmp)
          if isfield(data, 'inside')
            tmp = tmp{data.inside(1)};
          else
            tmp = tmp{1};
          end
          dimtok = dimtok(2:end);
        end
        ix = find(~cellfun('isempty', strfind(dimtok, seldim)));
        n  = size(tmp, ix);

      elseif strcmp(data.(fld), 'rpt_pos')
        %FIXME HACK to be fixed
        x = setdiff(fld(data),'inside');
        for k = 1:length(x)
          dims = size(getsubfield(data,x{k}));
          if dims(2)==size(data.pos,1) && numel(dims)==2,
            n = dims(1);
            return
          end
        end

      elseif strcmp(data.(fld), 'rpt_pos_freq')
        %FIXME HACK to be fixed
        x = fld(data);
        for k = 1:length(x)
          dims = size(getsubfield(data,x{k}));
          if dims(2)==size(data.pos,1) && (numel(dims)==2 || dims(3)==length(data.freq)),
            n = dims(1);
            return
          end
        end

      elseif strcmp(data.(fld), 'rpt_pos_time')
        %FIXME HACK to be fixed
        x = fld(data);
        for k = 1:length(x)
          dims = size(getsubfield(data,x{k}));
          if dims(2)==size(data.pos,1) && (numel(dims)==2 || dims(3)==length(data.time)),
            n = dims(1);
            return
          end
        end

      elseif strcmp(data.(fld)(1:4), 'rpt_')
        n  = [];
        % generic solution for XXXspctrm
        fnames = fieldnames(data);
        tmp    = find(~cellfun('isempty', strfind(fnames, 'spctrm')));
        for k = 1:numel(tmp)
          n = [n size(data.(fnames{tmp(k)}), 1)];
        end
        % some other possibilities
        if isfield(data, 'cov'),           n = [n size(data.cov,           1)]; end
        %if isfield(data, 'crsspctrm'),     n = [n size(data.crsspctrm,     1)]; end
        %if isfield(data, 'powcovspctrm'),  n = [n size(data.powcovspctrm,  1)]; end
        %if isfield(data, 'powspctrm'),     n = [n size(data.powspctrm,     1)]; end
        if isfield(data, 'trial'),         n = [n size(data.trial,         1)]; end
        %if isfield(data, 'fourierspctrm'), n = [n size(data.fourierspctrm, 1)]; end
        if isfield(data, 'individual'),    n = [n size(data.individual,    1)]; end
        if isfield(data, 'stat'),          n = [n size(data.stat,          1)]; end
        if ~all(n==n(1))
          ft_error('inconsistent number of repetitions for dim "%s"', seldim);
        end
        n = n(1);

      else
        %error('cannot determine number of repetitions for dim "%s"', seldim);
        n = nan;
      end

    case 'rpttap'
      if numel(fld)>6 && isfield(data, fld(1:end-6)),
        dimtok = tokenize(data.(fld), '_');
        tmp    = data.(fld(1:end-6));
        % remove the first cell-dimension
        if iscell(tmp)
          if isfield(data, 'inside'),
            tmp = tmp{data.inside(1)};
          else
            tmp = tmp{1};
          end
          dimtok = dimtok(2:end);
        end
        ix = find(~cellfun('isempty', strfind(dimtok, seldim)));
        n  = size(tmp, ix);

      elseif strcmp(data.(fld)(1:7), 'rpttap_')
        n  = [];
        if isfield(data, 'cov'),           n = [n size(data.cov,           1)]; end
        if isfield(data, 'crsspctrm'),     n = [n size(data.crsspctrm,     1)]; end
        if isfield(data, 'powcovspctrm'),  n = [n size(data.powcovspctrm,  1)]; end
        if isfield(data, 'powspctrm'),     n = [n size(data.powspctrm,     1)]; end
        if isfield(data, 'trial'),         n = [n size(data.trial,         1)]; end
        if isfield(data, 'fourierspctrm'), n = [n size(data.fourierspctrm, 1)]; end
        if ~all(n==n(1))
          ft_error('inconsistent number of repetitions for dim "%s"', seldim);
        end
        n = n(1);

      else
        %error('cannot determine number of repetitions for dim "%s"', seldim);
        n = nan;
      end

    case 'subj'
      n  = [];
      if isfield(data, 'cov'),           n = [n size(data.cov,           1)]; end
      if isfield(data, 'crsspctrm'),     n = [n size(data.crsspctrm,     1)]; end
      if isfield(data, 'fourierspctrm'), n = [n size(data.fourierspctrm, 1)]; end
      if isfield(data, 'individual'),    n = [n size(data.individual,    1)]; end
      if isfield(data, 'powcovspctrm'),  n = [n size(data.powcovspctrm,  1)]; end
      if isfield(data, 'powspctrm'),     n = [n size(data.powspctrm,     1)]; end
      if isfield(data, 'trial'),         n = [n size(data.trial,         1)]; end

      if ~all(n==n(1)), ft_error('inconsistent number of repetitions for dim "%s"', seldim); end
      n = n(1);

    case 'chan'
      if ~isfield(data, 'inside'),
        n = length(data.label);
      else
        %error('cannot determine number of repetitions for dim "%s"', seldim);
        n = nan; %FIXME discuss appending label to source-like data
      end

    case 'chancmb'
      if ~isfield(data, 'inside'),
        n = size(data.labelcmb, 1);
      else
        %error('cannot determine number of repetitions for dim "%s"', seldim);
        n = nan; %FIXME discuss appending label to source-like data
      end

    case 'freq'
      n = length(data.freq);

    case 'time'
      n = length(data.time);

    case {'pos' '{pos}'}
      n = size(data.pos,1);

    case {'ori'}
      % this is comparable to rpt and to rpttap
      if numel(fld)>6 && isfield(data, fld(1:end-6)),
        dimtok = tokenize(data.(fld), '_');
        tmp    = data.(fld(1:end-6));
        if iscell(tmp)
          if isfield(data, 'inside'),
            tmp = tmp{data.inside(1)};
          else
            tmp = tmp{1};
          end
          dimtok = dimtok(2:end);
        end
        % it can be {pos}_ori_ori, in which case only the first orientation needs to be checked
        ix = find(~cellfun('isempty', strfind(dimtok, seldim)), 1, 'first');
        n  = size(tmp, ix);
      else
        %error('cannot determine number of repetitions for dim "%s"', seldim);
        n = nan;
      end

    otherwise
      ft_error('unsupported dim "%s"', seldim);
  end

end % if nargin


function [wrongTable, absentModelTable, absentMapTable, duplicateTable] = compareModelMapFormulas(model, map, excelName)
% Checks the errors in a given map using a given base model by
% comparing the reactions formulas. As different errors can exist, the
% output is separated in 4 different tables that can later be exported
% into Excel spreadsheets (see commented lines at the end).
%
% USAGE:
%
%   [wrongTable, absentModelTable, absentMapTable, duplicateTable] = compareModelMapFormulas(model, map, excelName)
%
% INPUTS:
%   model:          COBRA structure of a model
%   map:            MATLAB structure of the map obtained from the
%                   function `transformXML2Map`.
%
% OPTIONAL INPUT:
%   excelName:      Name of the excel file in which to export the info
%
% OUTPUTS:
%   wrongTable:             Table containing the information on wrong
%                           reactions. The fields are arranged as
%                           followed:
%
%                            * rxnName - Name of the reaction in the map
%                            * rxnID - ID of the reaction in the map
%                            * modelFormula - Formula of the reaction from the model
%                            * mapFormula - Formula of the reaction from the map
%
%   absentModelTable:       Table containing the information on
%                           reactions present in the map but absent
%                           from the model. The fields are arranged as
%                           followed:
%
%                            * rxnName - Name of the reaction in the map
%                            * rxnID - ID of the reaction in the map
%                            * mapFormula - Formula of the reaction from the map
%
%   absentMapTable:         Table containing the information on
%                           reactions present in the model but absent
%                           from the map. The fields are arranged as
%                           followed:
%
%                            * rxnName - Name of the reaction in the model
%                            * modelFormula - Formula of the reaction from the model
%
%   duplicateTable:         Table containing the information on
%                           duplicated reactions in the map. The fields
%                           are arranged as followed:
%
%                            * rxnName - Name of the reaction in the model
%                            * rxnID - ID of the reaction in the map
%                            * modelFormula - Formula of the reaction from the model
%                            * mapFormula - Formula of the reaction from the map
%
% .. Author: - N.Sompairac - Institut Curie, Paris, 25/07/2017.

modelReactionNameList = model.rxns;  % Getting names from model and map
mapReactionNameList = map.rxnName;

% Getting the formulas from the model and the map for further comparison
modelFormulasList = printRxnFormula(model, modelReactionNameList,0);
[mapFormulasList, mapReactionNameList] = mapFormula(map, mapReactionNameList);

% Deleting the stoechiometric numbers from the model formulas
modelFormulasList = regexprep(modelFormulasList, '[0-9.]+ ', '');

% Initialising lists that will contain corresponding info on reactions
wrong = 1;
wrongList = [];
dupl = 1;
duplicateList = [];
abs = 1;
absentMapList = [];

% Looping over the model's reaction names
for rxn = 1:length(modelReactionNameList)
    % Test if the reaction name is contained in the map
    if any(strcmp(modelReactionNameList{rxn}, mapReactionNameList))
        % Getting the index of the model reaction name in the map list
        index = find(strcmp(modelReactionNameList{rxn}, mapReactionNameList));
        % Test if there is only one reaction with this name in the map
        if length(index) == 1
            % Deleting the stoechiometric numbers from the model formula
            % Splitting the model formula for further comparison
            modelFormulaSplit = strsplit(modelFormulasList{rxn}, {'<=>', '->'});
            leftModel = strtrim(strsplit(modelFormulaSplit{1}, '+'));
            rightModel = strtrim(strsplit(modelFormulaSplit{2}, '+'));
            % Splitting the map formula for further comparison
            mapFormulaSplit = strsplit(mapFormulasList{index}, {'<=>', '->'});
            leftMap = strtrim(strsplit(mapFormulaSplit{1}, '+'));
            rightMap = strtrim(strsplit(mapFormulaSplit{2}, '+'));
            % Testing if the formulas are different and storing the info
            leftTest = setxor(leftModel, leftMap);
            rightTest = setxor(rightModel, rightMap);
            if ~isempty(leftTest) || ~isempty(rightTest)
                wrongList.name{wrong} = mapReactionNameList{index};
                wrongList.ID{wrong} = map.rxnID{strcmp(modelReactionNameList{rxn}, map.rxnName)};
                wrongList.modelFormula{wrong} = modelFormulasList{rxn};
                wrongList.mapFormula{wrong} = mapFormulasList{index};
                wrong = wrong + 1;
            end
            % Case where a reaction name is duplicated in the map
        else
            % Finding the IDs of the duplicated reactions in the map
            duplicateIDs = map.rxnID(strcmp(modelReactionNameList{rxn}, map.rxnName));
            % Looping over the duplicates to get the relevant info
            for d = 1:length(index)
                duplicateList.name{dupl} = modelReactionNameList{rxn};
                duplicateList.modelFormula{dupl} = modelFormulasList{rxn};
                duplicateList.mapFormula{dupl} = mapFormulasList{index(d)};
                duplicateList.ID{dupl} = duplicateIDs{d};
                dupl = dupl + 1;
            end
        end
        % Case where reactions are absent in the map and present in the model
    else
        absentMapList.name{abs} = modelReactionNameList{rxn};
        absentMapList.modelFormula{abs} = modelFormulasList{rxn};
        abs = abs + 1;
    end
end

if ~isempty(wrongList)

    wrongTable = table(wrongList.name', wrongList.ID', wrongList.modelFormula', wrongList.mapFormula');
    wrongTable.Properties.VariableNames = {'rxnName', 'rxnID', 'modelFormula', 'mapFormula'};

else

    wrongTable = [];

end

if ~isempty(duplicateList)
    duplicateTable = table(duplicateList.name', duplicateList.ID', duplicateList.modelFormula', duplicateList.mapFormula');
    duplicateTable.Properties.VariableNames = {'rxnName', 'rxnID', 'modelFormula', 'mapFormula'};
else
    duplicateTable = [];
end

if ~isempty(absentMapList)
    absentMapTable = table(absentMapList.name', absentMapList.modelFormula');
    absentMapTable.Properties.VariableNames = {'rxnName', 'modelFormula'};
else
    absentMapTable = [];
end

% Finding reaction names in the map that are not present in the model
differentMapRxnNamesList = setdiff(mapReactionNameList, modelReactionNameList);

if ~isempty(differentMapRxnNamesList)
    % Finding reaction names in the map in case of multiple presence
    differentMapRxnNamesList = map.rxnName(ismember(map.rxnName, differentMapRxnNamesList));
    % Finding reaction ID in the map that are not present in the model
    differentMapRxnIdList = map.rxnID(ismember(map.rxnName, differentMapRxnNamesList));
    % Finding reaction formulas in the map that are not present in the model
    differentMapRxnFormulasList = mapFormulasList(ismember(mapReactionNameList, differentMapRxnNamesList));

    % Storing the relevant info on missing reactions in the model from the map
    absentModelTable = table(differentMapRxnNamesList, differentMapRxnIdList, differentMapRxnFormulasList);
    absentModelTable.Properties.VariableNames = {'rxnName', 'rxnID', 'mapFormula'};
else
    absentModelTable = [];
end

if nargin == 3
    % Commented part to use a possible Excel output.
    fileName = excelName;
    warning('off', 'MATLAB:xlswrite:AddSheet');
    if ~isempty(wrongTable)
        writetable(wrongTable, fileName, 'Sheet', 'wrongReactions')
    end
    if ~isempty(absentMapTable)
        writetable(absentMapTable, fileName, 'Sheet', 'absentFromMapReactions')
    end
    if ~isempty(absentModelTable)
        writetable(absentModelTable, fileName, 'Sheet', 'absentFromModelReactions')
    end
    if ~isempty(duplicateTable)
        writetable(duplicateTable, fileName, 'Sheet', 'duplicatedReactions')
    end
end


function [TorF, vstr, rdate] = have_feature_evalc()
%HAVE_FEATURE_EVALC  Detect availability/version info for EVALC
%
%   Feature detection function implementing 'evalc' tag for HAVE_FEATURE
%   to detect support for EVALC function.
%
%   See also HAVE_FEATURE, EVALC.

%   MP-Opt-Model
%   Copyright (c) 2004-2020, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MP-Opt-Model.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://github.com/MATPOWER/mp-opt-model for more info.

TorF = exist('evalc') ~= 0;
if TorF
    if have_feature('matlab')
        v = have_feature('matlab', 'all');
    else
        v = have_feature('octave', 'all');
        TorF = 0;   %% In Octave, evalc was introduced in 4.2.x, but as of 6.x
                    %% we still choose to ignore evalc in Octave since it does
                    %% not capture the output of all functions (e.g. .mex/.oct
                    %% functions such as ipopt, glpk), which is one of the
                    %% main reasons we'd like to use it. Not only does it not
                    %% capture the output, it also does not prevent it from
                    %% going to the console.
    end
    vstr  = v.vstr;
    rdate = v.date;
else
    vstr = '';
    rdate = '';
end


% GETCHANLIST - Obtain indices of specified channel types.
%
% Usage:
%       >> chanlist = getchanlist(chanlocs, type)
%
% Inputs:
%     chanlocs - EEGLAB channel location structure
%     type     - [string] select channel of specified type
%                can enter a cell array to select several channel types
%
% Output:
%     chanlist - list of channel indices for the selected type(s)
%                sorted in increasing order
%
% Note: this function is not case sensitive
%
% Author: Arnaud Delorme, SCCN, INC, UCSD, 2004
%
% See also: POP_CHANEDIT

% Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, 2004
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. 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.
%
% 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 HOLDER 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.

function chanlist = getchanlist(chanlocs, type)

    if nargin < 1
        help getchanlist;
        return;
    end

    if nargin < 2 || ~isfield(chanlocs, 'type')
        chanlist = [1:length(chanlocs)];
        return;
    end

    % search channel types
    % --------------------
    if ischar(type), type = { type }; end
    type = lower(type);
    chanlist = [];
    alltypes = lower({ chanlocs.type });
    for index = 1:length(type)
        tmplist = strmatch(type{index}, alltypes, 'exact');
        if isempty(tmplist)
            fprintf('Warning: no channel of type ''%s'' found\n', type{index});
        end
        chanlist = [ chanlist tmplist' ];
    end
    chanlist = sort(chanlist);


function pstruct = tapas_condhalluc_obs2_namep(pvec)
% --------------------------------------------------------------------------------------------------
% Copyright (C) 2016 Christoph Mathys, TNU, UZH & ETHZ
%
% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public
% Licence (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/>.

pstruct = struct;

pstruct.be = pvec(1);
pstruct.nu = pvec(2);

return;


% Copyright (C) 2014-2017 Colin B. Macdonald
%
% This file is part of OctSymPy.
%
% OctSymPy 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 software 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 software; see the file COPYING.
% If not, see <http://www.gnu.org/licenses/>.

addpath(pwd)
base = 'tests_matlab';
files = dir(base);
cd(base);

% usually I'd just use cputime(), but some of our IPC mechanisms are
% light on CPU and heavy on IO.
totaltime = clock();
totalcputime = cputime();
num_tests = 0;
% do tests in random order:
%rng('shuffle')
%for i=randperm(length(files))
for i=1:length(files)
  mfile = files(i).name;
  % detect tests b/c directory contains other stuff (e.g., surdirs and
  % helper files)
  if ( (~files(i).isdir) && strncmp(mfile, 'test', 4) && mfile(end) ~= '~')
    testtime = clock();
    str = mfile(1:end-2);
    num_tests = num_tests + 1;
    fprintf(['>>> Running test(s) in: ' mfile '  ']);  % no newline
    eval(str)
    testtime = etime(clock(), testtime);
  end
end

totaltime = etime(clock(),totaltime);
totalcputime = cputime() - totalcputime;
fprintf('\n***** Ran tests from %d files, %g seconds (%gs CPU) *****\n', ...
        num_tests, totaltime, totalcputime);
cd('..')


function title = p01_title ( )

%*****************************************************************************80
%
%% P01_TITLE returns the name of problem p01.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 August 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, string TITLE, the title of the problem.
%
  title = 'Oscillatory';

  return
end


function [n,names] = plx_adchan_names(filename)
% plx_adchan_names(filename): gets the names of a/d channels in a .plx or .pl2 file
%
% [n,names] = plx_adchan_names(filename)
%
% INPUT:
%   filename - if empty string, will use File Open dialog
%
% OUTPUT:
%   names - array of a/d channel name strings
%   n - number of channels

if nargin ~= 1
    error 'expected 1 input argument';
end

[ filename, isPl2 ] = internalPL2ResolveFilenamePlx( filename );
if isPl2 == 1
    pl2 = PL2GetFileIndex(filename);
    n = numel(pl2.AnalogChannels);
    for i=1:numel(pl2.AnalogChannels)
        if i == 1
            names = pl2.AnalogChannels{i}.Name;
        else
            names = char(names, pl2.AnalogChannels{i}.Name);
        end
    end
    names = internalPL2TrimNames(names);
    return;
end

[n,names] = mexPlex(15, filename);

end


function [c, ceq] = ma_XenonPropMassConstraint(stateLog, eventID, lbProp, ubProp, bodyIDApply, celBodyData, maData)
%ma_liqFuelOxPropMassConstraint Summary of this function goes here
%   Detailed explanation goes here
    normFact = 1;

    if(ischar(eventID) && strcmpi(eventID,'final'))
        eventNum = max(stateLog(:,13));
    else
%         hMAMainGUI = findall(0,'tag','ma_MainGUI');
%         maData = getappdata(hMAMainGUI,'ma_data');
        [~, eventNum] = getEventByID(eventID, maData.script);
    end

    eventLog = stateLog(stateLog(:,13)==eventNum,:);

    bodyEventLog = eventLog(eventLog(:,8)==bodyIDApply,:);
    if(isempty(bodyEventLog))
        finalEntry = eventLog(end,:);
    else
        finalEntry = bodyEventLog(end,:);
    end

    propMass = finalEntry(12);
    bodyID = finalEntry(8);

    if(bodyID == bodyIDApply || bodyIDApply==-1)
        if(lbProp == ubProp)
            c = [0 0];
            ceq(1) = propMass - ubProp;
        else
            c(1) = lbProp - propMass;
            c(2) = propMass - ubProp;
            ceq = [0];
        end
        c = c/normFact;
        ceq = ceq/normFact;
    else
        c = [0 0];
        ceq = [0];
    end
end


function F = display(X)
% DISPLAY Overloaded

try
    X = sdpvar(X);
    display(X);
catch
    disp('Incomplete block variable.');
end


function [MriFileReg, errMsg, fileTag, sMriReg] = mri_coregister(MriFileSrc, MriFileRef, Method, isReslice, isAtlas)
% MRI_COREGISTER: Compute the linear transformations on both input volumes, then register the first on the second.
%
% USAGE:  [MriFileReg, errMsg, fileTag, sMriReg] = mri_coregister(MriFileSrc, MriFileRef, Method, isReslice)
%            [sMriReg, errMsg, fileTag, sMriReg] = mri_coregister(sMriSrc,    sMriRef, ...)
%
% INPUTS:
%    - MriFileSrc : Relative path to the Brainstorm MRI file to register
%    - MriFileRef : Relative path to the Brainstorm MRI file used as a reference
%    - sMriSrc    : Brainstorm MRI structure to register (fields Cube, Voxsize, SCS, NCS...)
%    - sMriRef    : Brainstorm MRI structure used as a reference
%    - Method     : Method used for the coregistration of the volume: 'spm', 'mni', 'vox2ras'
%    - isReslice  : If 1, reslice the output volume to match dimensions of the reference volume
%    - isAtlas    : If 1, perform only integer/nearest neighbors interpolations (MNI and VOX2RAS registration only)
%
% OUTPUTS:
%    - MriFileReg : Relative path to the new Brainstorm MRI file (containing the structure sMriReg)
%    - errMsg     : Error messages if any
%    - fileTag    : Tag added to the comment/filename
%    - sMriReg    : Brainstorm MRI structure with the registered volume

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

% ===== LOAD INPUTS =====
% Parse inputs
if (nargin < 5) || isempty(isAtlas)
    isAtlas = 0;
end
% Initialize returned variables
MriFileReg = [];
errMsg = [];
fileTag = '';
sMriReg = [];
% Progress bar
isProgress = bst_progress('isVisible');
if ~isProgress
    bst_progress('start', 'MRI register', 'Loading input volumes...');
end
% USAGE: mri_coregister(sMriSrc, sMriRef, ...)
if isstruct(MriFileSrc)
    sMriSrc = MriFileSrc;
    sMriRef = MriFileRef;
    MriFileSrc = [];
    MriFileRef = [];
% USAGE: mri_coregister(MriFileSrc, MriFileRef, ...)
elseif ischar(MriFileSrc)
    % Get the default MRI for this subject
    if isempty(MriFileRef)
        sSubject = bst_get('MriFile', MriFileSrc);
        MriFileRef = sSubject.Anatomy(sSubject.iAnatomy).FileName;
    end
    sMriSrc = in_mri_bst(MriFileSrc);
    sMriRef = in_mri_bst(MriFileRef);
else
    error('Invalid call.');
end
% % Not available for multiple volumes
% if (size(sMriRef.Cube, 4) > 1) || (size(sMriSrc.Cube, 4) > 1)
%     errMsg = 'The input files cannot contain multiple volumes.';
%     return;
% end
% Inialize various variables
isUpdateScs = 0;
isUpdateNcs = 0;

% ===== REGISTER VOLUMES =====
switch lower(Method)

    % ===== METHOD: SPM =====
    case 'spm'
        % Initialize SPM
        [isInstalled, errMsg] = bst_plugin('Install', 'spm12');
        if ~isInstalled
            if ~isProgress
                bst_progress('stop');
            end
            return;
        end
        bst_plugin('SetProgressLogo', 'spm12');

        % === SAVE FILES IN TMP FOLDER ===
        bst_progress('text', 'Saving temporary files...');
        % Get temporary folder
        TmpDir = bst_get('BrainstormTmpDir', 0, 'spmcoreg');
        % Save source MRI in .nii format
        NiiSrcFile = bst_fullfile(TmpDir, 'spm_src.nii');
        out_mri_nii(sMriSrc, NiiSrcFile);
        % Save reference MRI in .nii format
        NiiRefFile = bst_fullfile(TmpDir, 'spm_ref.nii');
        out_mri_nii(sMriRef, NiiRefFile);

        % === CALL SPM COREGISTRATION ===
        bst_progress('text', 'Calling SPM batch...');
        % Code initially coming from Olivier David's ImaGIN_anat_spm.m function
        % Initial translation according to centroids
        % Reference volume
        Vref = spm_vol([NiiRefFile, ',1']);
        [Iref,XYZref] = spm_read_vols(Vref);
        Iindex = find(Iref>max(Iref(:))/6);
        Zindex = find(max(XYZref(3,:))-XYZref(3,:)<200);
        index = intersect(Iindex,Zindex);
        CentroidRef = mean(XYZref(:,index),2);
        % Volume to register
        V2 = spm_vol([NiiSrcFile, ',1']);
        [I2,XYZ2] = spm_read_vols(V2);
        Iindex = find(I2>max(I2(:))/6);
        Zindex = find(max(XYZ2(3,:))-XYZ2(3,:)<200);
        index = intersect(Iindex,Zindex);
        Centroid2 = mean(XYZ2(:,index),2);
        % Apply translation
        B = [CentroidRef'-Centroid2' 0 0 0 1 1 1 0 0 0];
        M = spm_matrix(B);
        Mat = spm_get_space(V2.fname);
        spm_get_space(V2.fname, M*Mat);

        % Create coregistration batch
        if isReslice
            % Coreg: Estimate and reslice
            matlabbatch{1}.spm.spatial.coreg.estwrite.ref      = {[NiiRefFile, ',1']};
            matlabbatch{1}.spm.spatial.coreg.estwrite.source   = {[NiiSrcFile, ',1']};
            matlabbatch{1}.spm.spatial.coreg.estwrite.other    = {''};
            matlabbatch{1}.spm.spatial.coreg.estwrite.eoptions = spm_get_defaults('coreg.estimate');
            matlabbatch{1}.spm.spatial.coreg.estwrite.woptions = spm_get_defaults('coreg.write');
            matlabbatch{1}.spm.spatial.coreg.estwrite.woptions.outdir = TmpDir;
            % Output file
            NiiRegFile = bst_fullfile(TmpDir, 'rspm_src.nii');
        else
            % Coreg: Estimate
            matlabbatch{1}.spm.spatial.coreg.estimate.ref      = {[NiiRefFile, ',1']};
            matlabbatch{1}.spm.spatial.coreg.estimate.source   = {[NiiSrcFile, ',1']};
            matlabbatch{1}.spm.spatial.coreg.estimate.other    = {''};
            matlabbatch{1}.spm.spatial.coreg.estimate.eoptions = spm_get_defaults('coreg.estimate');
            % Output file
            NiiRegFile = NiiSrcFile;
        end
        % Run SPM batch
        spm_jobman('initcfg');
        % spm_jobman('interactive', matlabbatch)
        spm_jobman('run',matlabbatch)
        % Read output volume
        sMriReg = in_mri(NiiRegFile, 'ALL', 0, 0);
        % If an error occurred in SPM
        if isempty(sMriReg)
            errMsg = 'An unknown error occurred while executing SPM. See the logs in the command window.';
            if ~isProgress
                bst_progress('stop');
            end
            return;
        end
        % Delete the temporary files
        file_delete(TmpDir, 1, 1);
        % Output file tag
        fileTag = '_spm';
        % Remove logo
        bst_plugin('SetProgressLogo', []);

        % === UPDATE FIDUCIALS ===
        if isReslice
            % Use the reference SCS coordinates
            if isfield(sMriRef, 'SCS')
                sMriReg.SCS = sMriRef.SCS;
            end
            % Use the reference NCS coordinates
            if isfield(sMriRef, 'NCS')
                sMriReg.NCS = sMriRef.NCS;
            end
        else
            isUpdateScs = 1;
            isUpdateNcs = 1;
        end

        % ===== COMPUTE TRANSFORMATION MATRIX =====
        % Get transformations MRI=>WORLD (vox2ras) for original volume
        vox2ras_src = cs_convert(sMriSrc, 'mri', 'world');
        vox2ras_reg = cs_convert(sMriReg, 'mri', 'world');
        % If there are vox2ras transformation matrices available
        if ~isempty(vox2ras_src) && ~isempty(vox2ras_reg)
            % Compute the transformation from the original to the registered volume
            TransfRegSpm = vox2ras_reg * inv(vox2ras_src);
            % Save in the registered MRI structure (this transformation must be applied on world coordinates)
            sMriReg.InitTransf(end+1,[1 2]) = {'reg', TransfRegSpm};
        end

    % ===== METHOD: MNI =====
    case 'mni'
        % === COMPUTE MNI TRANSFORMATIONS ===
        % Source MRI
        if ~isfield(sMriSrc, 'NCS') || ~isfield(sMriSrc.NCS, 'R') || ~isfield(sMriSrc.NCS, 'T') || isempty(sMriSrc.NCS.R) || isempty(sMriSrc.NCS.T)
            [sMriSrc,errMsg] = bst_normalize_mni(sMriSrc, 'maff8');
        end
        % Reference MRI
        if ~isfield(sMriRef, 'NCS') || ~isfield(sMriRef.NCS, 'R') || ~isfield(sMriRef.NCS, 'T') || isempty(sMriRef.NCS.R) || isempty(sMriRef.NCS.T)
            [sMriRef,errMsg] = bst_normalize_mni(sMriRef, 'maff8');
        end
        % Handle errors
        if ~isempty(errMsg)
            if ~isProgress
                bst_progress('stop');
            end
            return;
        end
        % Get MNI transformations
        TransfSrc = [sMriSrc.NCS.R, sMriSrc.NCS.T; 0 0 0 1];
        TransfRef = [sMriRef.NCS.R, sMriRef.NCS.T; 0 0 0 1];

        % === RESLICE VOLUME ===
        if isReslice
            % Reslice the volume
            [sMriReg, errMsg] = mri_reslice(sMriSrc, sMriRef, TransfSrc, TransfRef, isAtlas);
        else
            % Save the original input volume
            sMriReg = sMriSrc;
            isUpdateScs = 1;
            isUpdateNcs = 0;
        end
        % Output file tag
        fileTag = '_mni';

    % ===== VOX2RAS =====
    case 'vox2ras'
        % Nothing to do, just reslice if needed
        if isReslice
            % Reslice the volume
            [sMriReg, errMsg] = mri_reslice(sMriSrc, sMriRef, 'vox2ras', 'vox2ras', isAtlas);
            % Output file tag
            fileTag = '_reg';
        else
            % Save the original input volume
            sMriReg = sMriSrc;
            isUpdateScs = 1;
            isUpdateNcs = 1;
            % Output file tag
            fileTag = '';
        end
end
% Handle errors
if ~isempty(errMsg)
    if ~isProgress
        bst_progress('stop');
    end
    return;
end

% ===== REMOVE NEW NEGATIVE VALUES =====
% If some negative values appeared just because of the registration/reslicing: remove them
if any(sMriReg.Cube(:) < 0) && ~any(sMriSrc.Cube(:) < 0)
    sMriReg.Cube(sMriReg.Cube < 0) = 0;
end

% ===== UPDATE FIDUCIALS =====
if isUpdateScs || isUpdateNcs
    % Get vox2ras transformations for all volumes
    mri2world_reg = cs_convert(sMriReg, 'mri', 'world');
    mri2world_ref = cs_convert(sMriRef, 'mri', 'world');
    % Transfer fiducials from reference MRI to registered MRI
    if ~isempty(mri2world_reg) && ~isempty(mri2world_ref)
        % Apply transformation: reference MRI => SPM RAS/world => registered MRI
        Transf = inv(mri2world_reg) * (mri2world_ref);
        % Convert to millimeters, just like the SCS and NCS transformation matrices
        Transf(1:3,4) = Transf(1:3,4) .* 1000;
        % SCS coordinates
        if isUpdateScs && isfield(sMriRef, 'SCS')
            SCS = sMriRef.SCS;
            % Update fiducials coordinates
            if all(isfield(SCS, {'NAS','LPA','RPA'})) && ~isempty(SCS.NAS) && ~isempty(SCS.LPA) && ~isempty(SCS.RPA)
                NAS = (Transf * [SCS.NAS, 1]')';
                LPA = (Transf * [SCS.LPA, 1]')';
                RPA = (Transf * [SCS.RPA, 1]')';
                sMriReg.SCS.NAS = NAS(1:3);
                sMriReg.SCS.LPA = LPA(1:3);
                sMriReg.SCS.RPA = RPA(1:3);
            end
            % Update SCS transformation
            if all(isfield(SCS, {'T','R'})) && ~isempty(SCS.R) && ~isempty(SCS.T)
                Tscs = [SCS.R, SCS.T; 0 0 0 1] * inv(Transf);
                sMriReg.SCS.R = Tscs(1:3,1:3);
                sMriReg.SCS.T = Tscs(1:3,4);
            end
        end
        % NCS coordinates
        if isUpdateNcs && isfield(sMriRef, 'NCS')
            NCS = sMriRef.NCS;
            % Update NCS fiducials
            if all(isfield(NCS, {'AC','PC','IH'})) && ~isempty(NCS.AC) && ~isempty(NCS.PC) && ~isempty(NCS.IH)
                AC = (Transf * [NCS.AC, 1]')';
                PC = (Transf * [NCS.PC, 1]')';
                IH = (Transf * [NCS.IH, 1]')';
                sMriReg.NCS.AC = AC(1:3);
                sMriReg.NCS.PC = PC(1:3);
                sMriReg.NCS.IH = IH(1:3);
            end
            % Update NCS transformation
            if all(isfield(NCS, {'T','R'})) && ~isempty(NCS.R) && ~isempty(NCS.T)
                Tncs = [NCS.R, NCS.T; 0 0 0 1] * inv(Transf);
                sMriReg.NCS.R = Tncs(1:3,1:3);
                sMriReg.NCS.T = Tncs(1:3,4);
            end
        end
    end
end

% ===== SAVE NEW FILE =====
% Add file tag
if isReslice
    fileTag = [fileTag, '_reslice'];
end
sMriReg.Comment = [sMriSrc.Comment, fileTag];
% Save output
if ~isempty(MriFileSrc)
    bst_progress('text', 'Saving new file...');
    % Get subject
    [sSubject, iSubject, iMri] = bst_get('MriFile', MriFileSrc);
    % Update comment
    sMriReg.Comment = file_unique(sMriReg.Comment, {sSubject.Anatomy.Comment});
    % Add history entry
    sMriReg.History = sMriSrc.History;
    sMriReg = bst_history('add', sMriReg, 'resample', ['MRI co-registered on default file (' Method '): ' MriFileRef]);
    % Save new file
    MriFileRegFull = file_unique(strrep(file_fullpath(MriFileSrc), '.mat', [fileTag '.mat']));
    MriFileReg = file_short(MriFileRegFull);
    % Save new MRI in Brainstorm format
    sMriReg = out_mri_bst(sMriReg, MriFileRegFull);

    % Register new MRI
    iAnatomy = length(sSubject.Anatomy) + 1;
    sSubject.Anatomy(iAnatomy) = db_template('Anatomy');
    sSubject.Anatomy(iAnatomy).FileName = MriFileReg;
    sSubject.Anatomy(iAnatomy).Comment  = sMriReg.Comment;
    % Update subject structure
    bst_set('Subject', iSubject, sSubject);
    % Refresh tree
    panel_protocols('UpdateNode', 'Subject', iSubject);
    panel_protocols('SelectNode', [], 'anatomy', iSubject, iAnatomy);
    % Save database
    db_save();
else
    % Return output structure
    MriFileReg = sMriReg;
end
% Close progress bar
if ~isProgress
    bst_progress('stop');
end


function vis_next_pred(joints, next_joints, graph)

    if nargin < 3
        next_joints = [];
    end

    colors = {'r','g','b','c','m','y'};
    markers = {'+','o','s', 'x','.'}; %,'-','s','d'};
    styles = {};
    j = 1;
    for m = 1:length(markers)
        for c = 1:length(colors)
            styles{j} = [colors{c} markers{m}];
            j = j + 1;
        end
    end

    visJoints = false;
    if visJoints
        for jj = 1:size(joints, 1)
            %fprintf('joint %d\n', jj);
            if isnan(joints(jj, 1))
                continue;
            end
            plot(joints(jj, 1), joints(jj, 2), styles{jj}, 'MarkerSize',5);
            text(joints(jj, 1), joints(jj, 2), num2str(jj), 'Color', 'g', 'FontSize', 12);
            %pause;
        end
    end

    starts = [2];
    ends = [1 3 5 6 14];
    %starts = [10];
    %ends = [3 9 14 11 1];

    if ~isempty(next_joints)
        %{
        for jj = 1:size(next_joints, 1)
            pts = zeros(2,2);
            pts(1,:) = joints(jj, :);
            pts(2,:) = joints(jj, :) + next_joints(jj,:);
            line(pts(:,1), pts(:,2), 'Color', 'r');
        end
        %}

        for jj = 1:size(next_joints, 1)
            pts = zeros(2,2);
            start = graph(jj, 1);
            end_  = graph(jj, 2);

            %if ~(start == 2 && (end_ == 1 || end_ == 3 || end_ == ))
            %    continue;
            %end
            if ~(any(start == starts) && any(end_ == ends))
                continue;
            end

            [~, idx] = find(ends == end_);
            color = colors{idx};

            %if ~((end_ == 7 ))
            %    continue;
            %end
            orig = joints(start, :);
            pts(1,:) = orig;
            pts(2,:) = orig + next_joints(jj,:);
            line(pts(:,1), pts(:,2), 'Color', color, 'LineWidth', 6);
            %{
            if jj <= 13
                line(pts(:,1), pts(:,2), 'Color', 'b');
            else
               line(pts(:,1), pts(:,2), 'Color', 'r');
            end
            %}
        end
    end
end


function getSGLogs(tg, folder)

% Author: Alexander Wischnewski     Date: 21-12-2018
%
% Description:
%   copies the log data from the speedgoat to the host computer and
%   converts them to the .mat format
%
% Input:
%   tg:     Speedgoat target object
%   folder: If given, the files are copied to the speciefied folder.
%           Otherwise the current folder is used.

% standard folder for logs
std_folder = 'C:/RoboSim/Logs/';
% check if standard folder exits
if(~exist(std_folder, 'dir'))
    mkdir(std_folder);
end

% configure time formats for automatic folder creation
format_day = 'yy_mm_dd';
format_time = 'HH_MM_SS';

% if no target folder was specified, use standard folder
if(nargin<2)
    % check if a folder with the current date exists
    if(~exist([std_folder datestr(now, format_day)], 'dir'))
       % create a new folder if this does not exits
       mkdir([std_folder datestr(now, format_day)]);
    end
    % create a new folder with the current time stamp
    folder = [std_folder datestr(now, format_day) '/'...
        datestr(now, format_time)];
    mkdir(folder);
end

% get ftp object on target
ftp = SimulinkRealTime.openFTP(tg);

% switch to logs folder
cd(ftp,'C:\Logs\');

% get directory of log files
d = dir(ftp);

% retrieve all log files into current folder
disp('Copy files from RMC');
for i = 3:numel(d)
    fileName = [d(i).name];
    disp(['Copy ' fileName]);
    mget(ftp, fileName, folder);
end

disp('Convert data to .mat');
old_folder = pwd;
cd(folder);
convertSGLogs(pwd);
cd(old_folder);

disp('Finished');


function sR = extractSphericalRegion(varargin)

% TODO: maybe consider option 'antipodal'
sR = getClass(varargin,'sphericalRegion',[],'last');
sR = sphericalRegion(sR,varargin{:});


function ll = gpDynamicsPointLogLikelihood(model, x, y)

% GPDYNAMICSPOINTLOGLIKELIHOOD Compute the log likelihood of a point under the GP dynamics model.
% DESC computes the log likelihood of a given point under the GP
% dynamics model.
% ARG model : the model for which the log likelihood is to be
% computed.
% ARG x : the input location for the model.
% ARG y : the target location for the model.
% RETURN ll : the log likelihood of the given point.
%
% COPYRIGHT : Neil D. Lawrence, 2006
%
% SEEALSO : gpDynamicsCreate, gpDynamicsLogLikelihood, gpPointLogLikelihood
%

% MODIFICATION: Carl Henrik Ek, 2009

% FGPLVM

if(isfield(model,'indexIn'))
  x = x(:,model.indexIn);
  y = y(:,model.indexIn);
end
ll = gpPointLogLikelihood(model, x, y);


function sd = loadstoragedata(sd_table, mpc)
%LOADSTORAGEDATA   Load StorageData table into StorageData struct.
%
%   SD = LOADSTORAGEDATA(SD_TABLE)
%   SD = LOADSTORAGEDATA(SD_TABLE, GEN)
%   SD = LOADSTORAGEDATA(SD_TABLE, MPC)
%
%   Loads data from a StorageData table struct, or from a M-file or MAT-file
%   that returns such a struct, and converts it to a StorageData struct.
%   StorageData contains all parameters required by MOST for storage
%   units that is not included in MPC or xGenData.
%
%   If the first argument is the name of an M-file the optional second
%   argument will be passed to it. This can be useful for cases where
%   the user wishes to use data from GEN or GENCOST to set values in the
%   StorageData.
%
%   Inputs:
%       SD_TABLE : a StorageData table struct or the name of an M-file
%                   or MAT-file that returns one, with the following fields
%           .colnames : N dimensional cell array of names corresponding
%               to the columns of the 'data' field. Valid column names
%               are the same as the output field names. All columns are
%               optional and the correpsonding defaults are listed
%               next the output field names below.
%           .data : (NS x N) matrix of data
%           .MinStorageLevel                 (optional)     (scalar)
%           .MaxStorageLevel                 (optional)     (scalar)
%           .OutEff                          (optional)     (scalar)
%           .InEff                           (optional)     (scalar)
%           .LossFactor                      (optional)     (scalar)
%           .rho                             (optional)     (scalar)
%           (values in any of the scalar fields listed above are
%            overridden by any corresponding values in the 'data' table)
%       GEN : (optional) standard GEN matrix for generators corresponding
%             to storage units in SD_TABLE
%       MPC : (optional) MATPOWER case struct containing GEN and GENCOST
%             matrices for generators corresponding to storage units in
%             SD_TABLE
%
%   Output:
%       SD : a StorageData struct, contains the following fields,
%               all of which are (NS x 1) vectors unless otherwise
%               indicated. Defaults are as provided by MD_INIT.
%           .UnitIdx                            (ns x 1)
%           .ExpectedTerminalStorageAim         (ns x 1)
%           .ExpectedTerminalStorageMin         (ns x 1)
%           .ExpectedTerminalStorageMax         (ns x 1)
%           .InitialStorage                     (ns x 1)
%           .InitialStorageLowerBound           (ns x 1)
%           .InitialStorageUpperBound           (ns x 1)
%           .InitialStorageCost                 (ns x 1)
%           .TerminalStoragePrice               (ns x 1)
%           .TerminalChargingPrice0             (ns x 1)
%           .TerminalDischargingPrice0          (ns x 1)
%           .TerminalChargingPriceK             (ns x 1)
%           .TerminalDischargingPriceK          (ns x 1)
%           .MinStorageLevel                    (ns x 1) or (1 x 1)
%           .MaxStorageLevel                    (ns x 1) or (1 x 1)
%           .OutEff                             (ns x 1) or (1 x 1)
%           .InEff                              (ns x 1) or (1 x 1)
%           .LossFactor                         (ns x 1) or (1 x 1)
%           .rho                                (ns x 1) or (1 x 1)

%   MOST
%   Copyright (c) 2013-2016, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MOST.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://github.com/MATPOWER/most for more info.

%% define named indices into data matrices
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;

%% input arg handling
if ischar(sd_table)
    infile = sprintf(' in file: ''%s''', sd_table);
else
    infile = '';
end
if nargin < 2
    args = [];
    gen = [];
else
    args = mpc;
    if isstruct(mpc)
        gen = mpc.gen;
    else
        gen = mpc;
    end
end
ns0 = size(gen, 1);      %% number of storage units in gen matrix

%% load the table
fields = {'colnames', 'data'};
sdt = loadgenericdata(sd_table, 'struct', fields, 'sd_table', args);
if isempty(sdt)
    sdt.colnames = {};
    sdt.data = [];
end
[ns, nc] = size(sdt.data);

%% check consistency of dimensions of data table, colnames and gen
if ns0 > 0 && ns > 0 && ns ~= ns0
    error('loadstoragedata: # of rows in ''data'' table (%d) do not equal rows in GEN (%d)%s', ...
        ns, ns0, infile);
end
if nc ~= length(sdt.colnames)
    error('loadstoragedata: # of columns in ''data'' table (%d) do match entries in ''colnames'' (%d)%s', ...
        nc, length(sdt.colnames), infile);
end

%% initialize storage data with default values from md_init's Storage field
sd = getfield(md_init(), 'Storage');

%% set individual fields
f = {   'MinStorageLevel', ...
        'MaxStorageLevel', ...
        'OutEff', ...
        'InEff', ...
        'LossFactor', ...
        'rho'
    };
for k = 1:length(f)
    if isfield(sd_table, f{k});
        sd.(f{k}) = sd_table.(f{k});
    end
end

%% create cell array with valid fields for checking
valid_fields = {
        'UnitIdx', ...
        'ExpectedTerminalStorageAim', ...
        'ExpectedTerminalStorageMin', ...
        'ExpectedTerminalStorageMax', ...
        'InitialStorage', ...
        'InitialStorageLowerBound', ...
        'InitialStorageUpperBound', ...
        'InitialStorageCost', ...
        'TerminalStoragePrice', ...
        'TerminalChargingPrice0', ...
        'TerminalDischargingPrice0', ...
        'TerminalChargingPriceK', ...
        'TerminalDischargingPriceK', ...
        'MinStorageLevel', ...
        'MaxStorageLevel', ...
        'OutEff', ...
        'InEff', ...
        'LossFactor', ...
        'rho'
    };

%% copy data from table
for j = 1:nc
    %% check for valid colname
    if ~any(strcmp(sdt.colnames{j}, valid_fields))
        error('loadstoragedata: ''%s'' is not a valid StorageData field name%s', sdt.colnames{j}, infile);
    end
    %% copy data
    sd.(sdt.colnames{j}) = sdt.data(:, j);
end


function ConvertKlusta2Neurosuite(shank,basepath,basename,numCores)
% Converts .kwik/kwx files from Klusta into klusters-compatible
% fet,res,clu,spk files.  Works on a single shank of a recording, assumes a
% 16bit .dat and an .xml file is present in "basepath" (home folder) and
% that they are named basename.dat and basename.xml.  Also assumes that
% subdirectories in that basepath are made for each shank with names
% specified by numbers (ie 1,2,3,4..8).  In each shank folder should be
% .kwik and .kwx files made by klusta with names as follows:
% basename_sh[shankumber].kwik/kwx.  This
%
% Inputs:
% shank - the shank number (as a number, not text) equalling the name of
%           the folder under basepath with the data of interst.  Default = 1.
% basepath - directory path to the main recording folder with .dat and .xml
%           as well as shank folders made by makeProbeMapKlusta2.m (default is
%           current directory matlab is pointed to)
% basename - shared file name of .dat and .xml (default is last part of
%           current directory path, ie most immediate folder name)
% numCores  - double between 1 and inf that sets the number of cores for a
%             parloop to run (set 2-12 on SSD or RAID0 systems, set to 1
%             for data stored on a single hard drive

% Brendon Watson 2016
%
% Modified by Luke Sjulson, 2017-10

if ~exist('shank','var')
    shank = 1;
end
if ~exist('basepath','var');
    basepath = cd;
end
if ~exist('basename','var');
    [~,basename] = fileparts(cd);
elseif strcmp(basename, 'lfpfile')
    d = dir('*lfp');
    basename = d.name(1:end-4);
end
if ~exist('numCores','var');
    numCores = 1;
end

%% Start grabbing data
datpath = fullfile(basepath,[basename '.dat']);
tkwik = fullfile(basepath,num2str(shank),[basename '_sh' num2str(shank) '.kwik']);
tkwx = fullfile(basepath,num2str(shank),[basename '_sh' num2str(shank) '.kwx']);
% kwikinfo = h5info(tkwik,['/channel_groups/' num2str(shank) '/clusters/main']);
clu = h5read(tkwik,['/channel_groups/' num2str(shank) '/spikes/clusters/main']);
cluster_names = unique(clu);

try % different klusta versions have different names for this field
    totalch = h5readatt(tkwik,'/application_data/spikedetekt','nchannels');
catch
    totalch = h5readatt(tkwik,'/application_data/spikedetekt','n_channels');
end
sbefore = h5readatt(tkwik,'/application_data/spikedetekt','extract_s_before');
safter = h5readatt(tkwik,'/application_data/spikedetekt','extract_s_after');
channellist = h5readatt(tkwik,['/channel_groups/' num2str(shank)],'channel_order')+1;

%% find channels missing from spike groups becuase they were in "bad_channels.txt"
% this reflects what the xml and klusters expect but is not present
% will fill in data from missing channels with zeros
Par = LoadParameters(fullfile(basepath,[basename '.xml']));

spkgroupchannellist = Par.SpkGrps(shank).Channels + 1;
if length(spkgroupchannellist) > length(channellist)
    missingchannelidxs = find(spkgroupchannellist==setdiff(spkgroupchannellist,channellist));
else
    missingchannelidxs = [];
end

%% Getting spiketimes
spktimes = h5read(tkwik,['/channel_groups/' num2str(shank) '/spikes/time_samples']);
% spktimes = spktimes(1:10);

%% From Azahara: setting noise as 0 and MUA as 1
% when some files were corrupted I was able to still read them with this, not sure if all the corruption are like this one...
% Code that klustaviewa uses: 0 = noise, 1 = MUA, 2 = good
for ind = 1:length(cluster_names)
    kk=h5readatt(tkwik,['/channel_groups/' num2str(shank) '/clusters/main/' num2str(cluster_names(ind))],'cluster_group');
    cluster_group(ind) = kk(1,1);
    clear kk

    if cluster_group(ind) == 0 %NOISE
        clu(find(clu==cluster_names(ind))) = 0;
    elseif cluster_group(ind) == 1 %MUA
        clu(find(clu==cluster_names(ind))) = 1;
    end
end
clear ind cluster_group

%% spike extraction from dat
dat=memmapfile(datpath,'Format','int16');
tsampsperwave = (sbefore+safter);
ngroupchans = length(spkgroupchannellist);
valsperwave = tsampsperwave * ngroupchans;
wvforms_all = zeros(length(spktimes),valsperwave,'int16');
wvranges = zeros(length(spktimes),ngroupchans);
wvpowers = zeros(1,length(spktimes));

if numCores>1
    delete(gcp('nocreate')); parpool(numCores)
    parfor j=1:length(spktimes)
        % for j=1:length(spktimes)
        try
            byteList = [];
            for i = 1:length(channellist)
                ind = (double(spktimes(j))-sbefore).*totalch+1:(double(spktimes(j))+safter).*totalch;
                byteList = [byteList; ind(double(channellist(i)):totalch:end)];
            end
            wvforms = dat.data(byteList);
            %         w = dat.data((double(spktimes(j))-sbefore).*totalch+1:(double(spktimes(j))+safter).*totalch);
            %         wvforms=reshape(w,totalch,[]);
            %         %select needed channels
            %         wvforms = wvforms(channellist,:);
            % %         % detrend
            % %         wvforms = floor(detrend(double(wvforms)));
            for midx = 1:length(missingchannelidxs)%account for bad_channels that remained in spike groups in xml
                t = missingchannelidxs(midx);
                wvforms = cat(1,wvforms(1:t-1,:),zeros(1,size(wvforms,2)),wvforms(t:end,:));
            end
            % median subtract
            wvforms = wvforms - repmat(median(wvforms')',1,sbefore+safter);
        catch
            disp(['Error extracting spike at sample ' int2str(double(spktimes(j))) '. Saving as zeros']);
            disp(['Time range of that spike was: ' num2str(double(spktimes(j))-sbefore) ' to ' num2str(double(spktimes(j))+safter) ' samples'])
            wvforms = zeros(valsperwave,1);
        end
        wvforms_all(j,:)=wvforms(:);

        %some processing for fet file
        wvaswv = reshape(wvforms,ngroupchans,tsampsperwave)';
        wvranges(j,:) = range(wvaswv);
        wvpowers(j) = sum(sum(wvaswv.^2));

        %     lastpoint = tsampsperwave*ngroupchans*(j-1);
        wvforms_all(j,:)=wvforms(:);
        %     wvforms_all(lastpoint+1 : lastpoint+valsperwave) = wvforms;
        %     wvforms_all(j,:,:)=int16(floor(detrend(double(wvforms)')));
        if rem(j,50000) == 0
            disp([num2str(j) ' out of ' num2str(length(spktimes)) ' left'])
        end
    end

else % if using only one core, don't open a parpool
    for j=1:length(spktimes)
        % for j=1:length(spktimes)
        try
            byteList = [];
            for i = 1:length(channellist)
                ind = (double(spktimes(j))-sbefore).*totalch+1:(double(spktimes(j))+safter).*totalch;
                byteList = [byteList; ind(double(channellist(i)):totalch:end)];
            end
            wvforms = dat.data(byteList);
            %         w = dat.data((double(spktimes(j))-sbefore).*totalch+1:(double(spktimes(j))+safter).*totalch);
            %         wvforms=reshape(w,totalch,[]);
            %         %select needed channels
            %         wvforms = wvforms(channellist,:);
            % %         % detrend
            % %         wvforms = floor(detrend(double(wvforms)));
            for midx = 1:length(missingchannelidxs)%account for bad_channels that remained in spike groups in xml
                t = missingchannelidxs(midx);
                wvforms = cat(1,wvforms(1:t-1,:),zeros(1,size(wvforms,2)),wvforms(t:end,:));
            end
            % median subtract
            wvforms = wvforms - repmat(median(wvforms')',1,sbefore+safter);
        catch
            disp(['Error extracting spike at sample ' int2str(double(spktimes(j))) '. Saving as zeros']);
            disp(['Time range of that spike was: ' num2str(double(spktimes(j))-sbefore) ' to ' num2str(double(spktimes(j))+safter) ' samples'])
            wvforms = zeros(valsperwave,1);
        end
        wvforms_all(j,:)=wvforms(:);

        %some processing for fet file
        wvaswv = reshape(wvforms,ngroupchans,tsampsperwave)';
        wvranges(j,:) = range(wvaswv);
        wvpowers(j) = sum(sum(wvaswv.^2));

        %     lastpoint = tsampsperwave*ngroupchans*(j-1);
        wvforms_all(j,:)=wvforms(:);
        %     wvforms_all(lastpoint+1 : lastpoint+valsperwave) = wvforms;
        %     wvforms_all(j,:,:)=int16(floor(detrend(double(wvforms)')));
        if rem(j,50000) == 0
            disp([num2str(j) ' out of ' num2str(length(spktimes)) ' left'])
        end
    end

end

clear dat
wvforms_all = reshape(wvforms_all',size(wvforms_all,1)*size(wvforms_all,2),1);
wvranges = wvranges';

%% Spike features
fets = h5read(tkwx,['/channel_groups/' num2str(shank) '/features_masks']);
fets = double(squeeze(fets(1,:,:)));

% normalizing fets to be in a good numerical range for klusters (added by Luke, 2017-10)
f1 = size(fets,1);
f2 = size(fets,2);
fets = zscore(fets(:));
fets = reshape(fets, f1, f2);

fets(fets<-5) = -5; % trimming off everything >5 SDs from mean
fets(fets>5) = 5;

fets = fets.*1000;

% %mean activity per spike
% fetmeans = mean(fets,1);

%find first pcs, make means of those...
% featuresperspike = 4;%this is dumb, should read from file - not using it any more anyway.
% firstpcslist = 1:featuresperspike:size(fets,1);
% firstpcmeans = mean(fets(firstpcslist,:),1);

nfets = size(fets,1)+1;
% fets = cat(1,fets,fetmeans,firstpcmeans,wvpowers,wvranges,double(spktimes')); % these are a bunch of extra features that many people don't find helpful.
fets = cat(1,fets,double(spktimes')); % just the PCA projections and timestamps
fets = fets';
% fets = cat(1,nfets,fets);

%% writing to clu, res, fet, spk
cluname = fullfile(basepath,[basename '.clu.' num2str(shank)]);
resname = fullfile(basepath,[basename '.res.' num2str(shank)]);
fetname = fullfile(basepath,[basename '.fet.' num2str(shank)]);
spkname = fullfile(basepath,[basename '.spk.' num2str(shank)]);

%clu
clu = cat(1,length(unique(clu)),double(clu));
fid=fopen(cluname,'w');
fprintf(fid,'%.0f\n',clu);
fclose(fid);
clear fid

%res
fid=fopen(resname,'w');
fprintf(fid,'%.0f\n',spktimes);
fclose(fid);
clear fid

%fet
SaveFetIn(fetname,fets);

%spk
fid=fopen(spkname,'w');
fwrite(fid,wvforms_all,'int16');
fclose(fid);
clear fid

disp(['Shank ' num2str(shank) ' done'])

function SaveFetIn(FileName, Fet, BufSize);

if nargin<3 | isempty(BufSize)
    BufSize = inf;
end

nFeatures = size(Fet, 2);
formatstring = '%d';
for ii=2:nFeatures
    formatstring = [formatstring,'  %d'];
end
formatstring = [formatstring,'\n'];

outputfile = fopen(FileName,'w');
fprintf(outputfile, '%d\n', nFeatures);

if isinf(BufSize)
    fprintf(outputfile,formatstring,round(Fet'));
else
    nBuf = floor(size(Fet,1)/BufSize)+1;

    for i=1:nBuf
        BufInd = [(i-1)*nBuf+1:min(i*nBuf,size(Fet,1))];
        fprintf(outputfile,formatstring,round(Fet(BufInd,:)'));
    end
end

fclose(outputfile);


function spm_eeg_inv_checkforward(varargin)
% Check M/EEG forward model
% FORMAT spm_eeg_inv_checkforward(D, val, ind)
%__________________________________________________________________________
% Copyright (C) 2008-2018 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: spm_eeg_inv_checkforward.m 7702 2019-11-22 11:32:26Z guillaume $

%-SPM data structure
%--------------------------------------------------------------------------
[D, val] = spm_eeg_inv_check(varargin{:});

forward = D.inv{val}.forward;

if nargin < 3
    str = sprintf('%s|', forward(:).modality);
    str = str(1:(end-1));

    ind = spm_input('What to display?','+1','b',str,1:numel(forward),1);
else
    ind = varargin{3};
end

try
    vol      = forward(ind).vol;
    modality = forward(ind).modality;
    if isfield(forward(ind), 'siunits') && forward(ind).siunits
        sens     = forward(ind).sensors;
    else %backward compatibility
        sens     = D.inv{val}.datareg(ind).sensors;
    end
    Mcortex  = forward(ind).mesh;
catch
    spm('alert!','Please coregister and create forward model',mfilename);
    return
end

%-Display
%--------------------------------------------------------------------------
Fgraph  = spm_figure('GetWin','Graphics');
spm_figure('Focus',Fgraph);
spm_figure('Clear',Fgraph);

spm('Pointer', 'Watch');

chanind = strmatch(modality, D.chantype);
chanind = setdiff(chanind, D.badchannels);
if isempty(chanind)
    error(['No good ' modality ' channels were found.']);
end

if ischar(vol)
    vol = ft_read_headmodel(vol);
end

face    = Mcortex.face;
vert    = Mcortex.vert;
h_ctx   = patch('vertices',vert,'faces',face,'EdgeColor','b','FaceColor','b');

hold on

[volp, sens] = ft_prepare_vol_sens(vol, sens, 'channel', D.chanlabels(chanind));

ft_plot_headmodel(vol, 'edgecolor', [0 0 0], 'facealpha', 0);

hold on

try
    ft_plot_sens(sens, 'style', '*', 'edgecolor', 'g', 'elecsize', 20, 'coil', ft_senstype(sens, 'eeg'));
catch
    ft_plot_sens(sens, 'edgecolor', 'g', 'coilshape', 'point', 'elecsize', 20, 'coil', true);
end

rotate3d on;

axis off
axis vis3d
axis equal

spm('Pointer', 'Arrow');


%% Example
% This script is used to initialize the hand guiding functionality on KUKA
% robot remotely

% Mohammad SAFEEA, 9th of June 2017

% TO use this example:
% 1- Start the server on KUKA
% 2- Start the client on Matlab
% 3- Press the white button and hand guide the robot
% 4- To save the coordinates of the robot, long click on the green button
% 5- Repeat three times
% 6- The robot gives you an interval of five seconds, clear the area around
% the robot directly, the robot will reproduce the motion tought.

fprintf('Connecting to server')

ip='172.31.1.147'; % The IP of the controller
% start a connection with the server
t=net_establishConnection( ip );

if ~exist('t','var') || isempty(t)
  warning('Connection could not be establised, script aborted');
  return;
else

startHandGuiding( t )

p1 = getJointsPos( t );

startHandGuiding( t )

p2 = getJointsPos( t );

startHandGuiding( t )

p3 = getJointsPos( t );

startHandGuiding( t )

fprintf('CLear the area round the robot, the robot is going to move');

pause(5)

relVel=0.1;
movePTPJointSpace( t , p1, relVel)
movePTPJointSpace( t , p2, relVel)
movePTPJointSpace( t , p3, relVel)

      %% turn off the server
       net_turnOffServer( t );

       fclose(t);
end


%DRAWMARKERARUCO  Draw a canonical marker image
%
%     img = cv.drawMarkerAruco(dictionary, id, sidePixels)
%     img = cv.drawMarkerAruco(..., 'OptionName',optionValue, ...)
%
% ## Input
% * __dictionary__ dictionary of markers indicating the type of markers.
% * __id__ identifier of the marker that will be returned. It has to be a
%   valid id in the specified dictionary (0-based).
% * __sidePixels__ size of the image in pixels.
%
% ## Output
% * __img__ output image with the marker, of size `sidePixels-by-sidePixels`.
%
% ## Options
% * __BorderBits__ width of the marker border. default 1
%
% This function returns a marker image in its canonical form (i.e. ready to be
% printed).
%
% See also: cv.estimatePoseBoard, cv.drawPlanarBoard, cv.drawCharucoBoard
%