function varargout = process_dipole_scanning( varargin )
% PROCESS_DIPOLE_SCANNING: Generates a brainstorm dipole file from the GLS and GLS-P inverse solutions.

% @=============================================================================
% 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: Elizabeth Bock, John C. Mosher, Francois Tadel, 2013-2019

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Dipole scanning';
    sProcess.Category    = 'File';
    sProcess.SubGroup    = 'Sources';
    sProcess.Index       = 327;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/TutDipScan';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'results'};
    sProcess.OutputTypes = {'dipoles'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;
    % Definition of the options
     % === Time window
    sProcess.options.timewindow.Comment = 'Time window:';
    sProcess.options.timewindow.Type    = 'timewindow';
    sProcess.options.timewindow.Value   = [];
%     % === fit frequency
%     sProcess.options.downsample.Comment = 'Time between dipoles: ';
%     sProcess.options.downsample.Type    = 'value';
%     sProcess.options.downsample.Value   = {0.000, 'ms', []};
    % === Separator
    sProcess.options.sep2.Type = 'separator';
    sProcess.options.sep2.Comment = ' ';
    % === CLUSTERS
    sProcess.options.scouts.Comment = 'Limit scanning to selected scouts';
    sProcess.options.scouts.Type    = 'scout_confirm';
    sProcess.options.scouts.Value   = {};
end

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

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInput) %#ok<DEFNU>
    OutputFiles = {};
    % === Get options
     % Time window to process
     %FitPeriod  = sProcess.options.downsample.Value{1};
    TimeWindow = sProcess.options.timewindow.Value{1};
    FitPeriod  = 0;
    AtlasList  = sProcess.options.scouts.Value;

    % === Get the sources
    % Read the source file
    sResultP = in_bst_results(sInput.FileName, 0);
    % Only accept dipole modeling in input
    switch sResultP.Function
        case {'gls_p','glsp','lcmvp'}
            % do nothing, valid
        otherwise
            bst_report('Error', sProcess, [], 'Dipole scanning is only available for the "Dipole modeling" and "LCMV" options (GLS-performance in the old interface).');
            return;
    end
    % Get the scouts structures
    if ~isempty(AtlasList)
        [sScouts, AtlasNames, sSurf] = process_extract_scout('GetScoutsInfo', sProcess, sInput, sResultP.SurfaceFile, AtlasList);
    else
        sScouts = [];
    end

    % === Get the results
    if isempty(sResultP.DataFile)
        DataMatP.Time = sResultP.Time;
        DataMatP.F = [];
        DataMatP.Comment = sResultP.Comment;
        % DataMatP.nAvg = sResultP.nAvg;
        DataMatP.Leff = sResultP.Leff;
    else
        DataMatP = in_bst_data(sResultP.DataFile);
        % Not supported for raw files
        if isstruct(DataMatP.F)
            bst_report('Error', sProcess, [], 'Dipole scanning is not supported for raw files. Import some data blocks first.');
            return;
        end
    end

    if isempty(sResultP.ImageGridAmp)
        % this can be massive, we should find a more iterative way
        % For now (March 2016), assume being only used for short data
        % sequences. (Mosher April 2016)
        sResultP.ImageGridAmp = sResultP.ImagingKernel * DataMatP.F(sResultP.GoodChannel,:);
    end

    % The function in_bst_results has already scaled the ImagingKernel to account for
    % data that has been averaged, so that the noise covariance scaling is correct.
    % So ImageGridAmp should also be correct.
    % But we need to calculate the possible Factor here for any other
    % manipulation of the data, as needed below.
    Factor = sqrt(DataMatP.Leff / sResultP.Leff); % is unity if both equal.
    if Factor ~= 1
        fprintf('BST Dipole Scanning> Need additional factor of %.2f (sqrt of %.1f) to account for average.\n',Factor, Factor^2);
    end

    % === Get the time
    if ~isempty(TimeWindow)
        SamplesBounds = bst_closest(TimeWindow, DataMatP.Time);
    else
        SamplesBounds = [1, size(sResultP.ImageGridAmp,2)];
    end
    timeVector = DataMatP.Time(SamplesBounds(1):SamplesBounds(2));

    % The "performance" image matrix
    Perf = sResultP.ImageGridAmp(:,SamplesBounds(1):SamplesBounds(2));
    % For constrained or mixed models this will flatten to norm
    sResultPFlat = process_source_flat('Compute',sResultP, 'rms');
    Pscan = sResultPFlat.ImageGridAmp(:,SamplesBounds(1):SamplesBounds(2));

    % === Find the index of 'best fit' at every time point
    % Get the selected scouts
    if ~isempty(sScouts)
        scoutVerts = [];
        for iScout = 1:length(sScouts)
            scoutVerts = [scoutVerts, sScouts(iScout).Vertices(:)'];
        end
        [mag,ind] = max(Pscan(scoutVerts,:));
        maxInd = scoutVerts(ind);
    else
        % don't use scouts, use all vertices
        [mag,maxInd] = max(Pscan,[],1);
    end

    % === Prepare a mask for downsampling the number of dipoles to save
    NumDipoles = size(Perf,2);
    if FitPeriod > 0
        tTotal = timeVector(end) - timeVector(1);
        nNewDipoles = tTotal/FitPeriod;
        dsFactor = round(NumDipoles/nNewDipoles);
    else
        dsFactor = 1;
    end
    temp = zeros(1,dsFactor);
    temp(1) = 1;
    dsMask = repmat(temp, 1, floor(NumDipoles/dsFactor));
    dsMask = logical([dsMask zeros(1,NumDipoles-length(dsMask))]);
    % downsample
    dsTimeVector = timeVector(dsMask);
    dsMaxInd = maxInd(dsMask);

    % So we have an index to each dipole location that has maximum
    % performance that we seek. Now find orientation and dipole amplitude

    % Get headmodel parameters
    sHeadModel = bst_get('HeadModelForStudy', sInput.iStudy);
    if isempty(sHeadModel)
        HeadModelFile = sResultP.HeadModelFile;
        if isempty(HeadModelFile)
            error('No headmodel available for this result file.');
        end
    else
        HeadModelFile = sHeadModel.FileName;
    end

    % HeadModelMat = in_bst_headmodel(HeadModelFile, 0, 'Gain', 'GridLoc', 'GridOrient');
    % Don't need large gain matrix
    HeadModelMat = in_bst_headmodel(HeadModelFile, 0, 'GridLoc', 'GridOrient');

    % === find the orientations
    switch (sResultP.nComponents)
        case 0  % MIXED HEAD MODEL
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
error('Not supported yet.');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
            %fullPerf = zeros(3, size(Perf,1), size(Perf,2));
            nTime = size(Perf,2);
            fullPerf = {};
            % Loop on all the regions
            for iScout = 1:length(sResultP.GridAtlas.Scouts)
                % Get the grid indices for this scouts
                iGrid = sResultP.GridAtlas.Scouts(iScout).GridRows;
                % If no vertices to read from this region: skip
                if isempty(iGrid)
                    continue;
                end
                % Get correpsonding row indices based on the type of region (constrained or unconstrained)
                switch (sResultP.GridAtlas.Scouts(iScout).Region(3))
                    case 'C'
                        fullPerf{end+1} = repmat(sResultP.GridOrient(iGrid,:)', 1, 1, nTime);
                    case {'U','L'}
                        % Convert from indices in GridLoc to indices in the source matrix
                        iSourceRows = bst_convert_indices(iGrid, sResultP.nComponents, sResultP.GridAtlas, 0);
                        fullPerf{end+1} = reshape(Perf(iSourceRows,:), 3, [], size(Perf,2));
                    otherwise
                        error(['Invalid region "' sResultP.GridAtlas.Scouts(iScout).Region '"']);
                end
            end
            % Concatenate the blocks
            fullPerf = cat(2, fullPerf{:});
            % Dipole orientation = amplitude in the three orientations
            orient = zeros(size(fullPerf,1),length(dsMaxInd)); % allocate
            for jj = 1:length(dsMaxInd)
                orient(:,jj) = fullPerf(:,dsMaxInd(jj),jj);
            end
        case 1
            % use the headmodel orientations
            kk = sResultP.nComponents; % notation convenience in below loop
            fullPerf = reshape(Perf,kk,[],size(Perf,2));
            orient = HeadModelMat.GridOrient(dsMaxInd,:)'; % simply the grid orient

            % now calculate the amplitude and embed in the orientation.
            % xhat = orientation * pinv(A * orientation) times data
            % A and data whitened and decomposed already, a rank one model
            for jj = 1:length(dsMaxInd)
                temp_ForwardModel = sResultP.SourceDecompSa(1,dsMaxInd(jj)); % scale factor from SVD
                temp_ForwardModel = temp_ForwardModel * Factor; % need to account for average vs epoched data.
                % Now pseudo invert this to get the scalar amplitude of source.
                % Note that fullPerf (the Perf value) already has the left singular
                % vectors.
                temp_estimate = fullPerf(1,dsMaxInd(jj),jj)/temp_ForwardModel;
                % replace unit orient with this orientation times the amplitude
                orient(:,jj) = temp_estimate * orient(:,jj);
            end
        case 2
            error('Not supported.');
        case 3
            % calculate the dipole orientations
            % first extract the calculation from the kernel, which is
            % performance:
            kk = sResultP.nComponents; % notation convenience in below loop
            fullPerf = reshape(Perf,kk,[],size(Perf,2));
            % Now calculate the optimal orientation from the performance
            orient = zeros(size(fullPerf,1),length(dsMaxInd)); % allocate
            for jj = 1:length(dsMaxInd)
                orient(:,jj) = fullPerf(:,dsMaxInd(jj),jj);
                % this is the raw orientation ("p") vector from the
                % performance measure. Since it is a single time slice (for
                % now April 2016), then no need to SVD. To get the optimal
                % orientation, we need to multiply it by the corresponding
                % head model decomposition components
                % Note, scale factor not important here since we normalize.
                orient(:,jj) = sResultP.SourceDecompVa(:,((1-kk):0)+(dsMaxInd(jj)*kk)) * diag(sResultP.SourceDecompSa(:,dsMaxInd(jj))) * orient(:,jj);
                orient(:,jj) = orient(:,jj) / norm(orient(:,jj)); % unit norm

                % with the orientation optimized, now calculate the
                % amplitude and embed in the orientation.
                % xhat = orientation * pinv(A * orientation) times data
                % A and data whitened and decomposed already, a rank three
                % model
                temp_ForwardModel = diag(sResultP.SourceDecompSa(:,dsMaxInd(jj))) * sResultP.SourceDecompVa(:,((1-kk):0)+(dsMaxInd(jj)*kk))'*orient(:,jj); % particular orientation
                temp_ForwardModel = temp_ForwardModel * Factor; % need to account for average vs epoched data.
                % Now pseudo invert this to get the scalar amplitude of source.
                % Note that fullPerf (the Perf value) already has the left singular
                % vectors.
                % pseudo invert temp_ForwardModel, a 3 x 1, by simply
                % transposing and dividing by the norm^2
                temp_estimate = (temp_ForwardModel'/ (temp_ForwardModel'*temp_ForwardModel)) * fullPerf(:,dsMaxInd(jj),jj);
                % replace unit orient with this orientation times the amplitude
                orient(:,jj) = temp_estimate * orient(:,jj);

            end
    end

%     % Dipole Amplitude, embed in orient. Use "fullPerf" generated above
%     %TBD: can be simplified from calculation given here, but this is direct
%     for jj = 1:length(dsMaxInd),
%         % form the source model, without the leading left singular vectors
%         kk = sResultP.nComponents; % notation convenience below
%         temp_ForwardModel = diag(sResultP.SourceDecompSa(:,dsMaxInd(jj))) * sResultP.SourceDecompVa(:,((1-kk):0)+(dsMaxInd(jj)*kk))'*orient(:,jj); % particular orientation
%         temp_ForwardModel = temp_ForwardModel * Factor; % need to account for average vs epoched data.
%         % Now pseudo invert this to get the scalar amplitude of source.
%         % Note that fullPerf (the Perf value) already has the left singular
%         % vectors.
%         temp_estimate = pinv(temp_ForwardModel) * fullPerf(:,dsMaxInd(jj),jj);
%         % replace unit orient with this orientation times the amplitude
%         orient(:,jj) = temp_estimate * orient(:,jj);
%     end
%
    % === Goodness of fit
    % square the performance at every source, therefore resulting in a
    % scalar squared performance value at every dipolar source
    %P2 = sum(reshape(abs(Perf).^2,sResultP.nComponents,[]),1);
    %P2 = reshape(P2,[],size(Perf,2));
    P2 = Pscan .^ 2;

    % get the squared norm of the whitened data, account for averaged data
    % factor as well.
    % Use the correct whitener, depending on lcmv vs gls
    switch sResultP.Options.InverseMethod
        case 'lcmv'
            wd2 = sum(abs(Factor * sResultP.DataWhitener * DataMatP.F(sResultP.GoodChannel,SamplesBounds(1):SamplesBounds(2))).^2,1);
        otherwise
            wd2 = sum(abs(Factor * sResultP.Whitener * DataMatP.F(sResultP.GoodChannel,SamplesBounds(1):SamplesBounds(2))).^2,1);
    end

    % the goodness of fit is now calculated by dividing the norm into each
    % performance value
    gof = P2 * diag(1./wd2);

    % === Chi-square
    % the chi square is the difference of the norm and the performance
    % resulting in the error for every source at every time point
    ChiSquare = repmat(wd2,size(P2,1),1) - P2;

    % The reduced chi-square is found by dividing by the degrees of freedom in
    % the error, which (for now) is simply a scalar, since we assume all
    % sources have the same degrees of freedom. Thus ROI modeling will require
    % that all ROIs have the same DOF.
    DOF = size(sResultP.ImagingKernel,2) - sResultP.nComponents;

    % downsample
    dsChiSquare = ChiSquare(:,dsMask);
    dsGOF = gof(:,dsMask);
    dsP = Pscan(:,dsMask);

    NumDipoles = length(dsMaxInd);

    %% === CREATE OUTPUT STRUCTURE ===
    bst_progress('start', 'Dipole File', 'Saving result...');
    % Get output study
    [sStudy, iStudy] = bst_get('Study', sInput.iStudy);
    % Comment: forced in the options
    if isfield(sProcess.options, 'Comment') && isfield(sProcess.options.Comment, 'Value') && ~isempty(sProcess.options.Comment.Value)
        Comment = sProcess.options.Comment.Value;
    % Comment: process default
    else
        Comment = [DataMatP.Comment ' | dipole-scan'];
    end
    % Get base filename
    [fPath, fBase, fExt] = bst_fileparts(sInput.FileName);
    % Create base structure
    DipolesMat = db_template('dipolemat');
    DipolesMat.Comment = Comment;
    DipolesMat.Time    = unique(dsTimeVector);

    % Fill structure
    for i = 1:NumDipoles
        DipolesMat.Dipole(i).Index          = 1;
        DipolesMat.Dipole(i).Time           = dsTimeVector(i);
        DipolesMat.Dipole(i).Origin         = [0 0 0];
        DipolesMat.Dipole(i).Loc            = HeadModelMat.GridLoc(dsMaxInd(i),:)';
        DipolesMat.Dipole(i).Amplitude      = orient(:,i);
        DipolesMat.Dipole(i).Errors         = 0;
        DipolesMat.Dipole(i).Noise          = [];
        DipolesMat.Dipole(i).SingleError    = [];
        DipolesMat.Dipole(i).ErrorMatrix    = [];
        DipolesMat.Dipole(i).ConfVol        = [];
        DipolesMat.Dipole(i).Probability    = [];
        DipolesMat.Dipole(i).NoiseEstimate  = [];
        DipolesMat.Dipole(i).Perform        = dsP(dsMaxInd(i),i);

        if ~isempty(dsGOF)
           DipolesMat.Dipole(i).Goodness = dsGOF(dsMaxInd(i),i);
        end
        if ~isempty(dsChiSquare)
           DipolesMat.Dipole(i).Khi2 = dsChiSquare(dsMaxInd(i),i);
        end
        if ~isempty(DOF)
           DipolesMat.Dipole(i).DOF = DOF;
        end
    end

    % Create the dipoles names list
    dipolesList = unique([DipolesMat.Dipole.Index]); %unique group names
    DipolesMat.DipoleNames = cell(1,length(dipolesList));
    k = 1; %index of names for groups with subsets
    nChanSet = 1;
    for i = 1:(length(dipolesList)/nChanSet)
        % If more than one channel subset, name the groups according to index and subset number
        if nChanSet > 1
            for j = 1:nChanSet
                DipolesMat.DipoleNames{k} = sprintf('Group #%d (%d)', dipolesList(i), j);
                DipolesMat.Subset(k) = j;
                k=k+1;
            end
        % If only one subsets, name the groups according to index
        else
            DipolesMat.DipoleNames{i} = sprintf('Group #%d', dipolesList(i));
            DipolesMat.Subset(i) = 1;
        end

    end
    DipolesMat.Subset = unique(DipolesMat.Subset);
    % Attach the new file to the input file
    DipolesMat.DataFile = sInput.FileName;
    % Add History field
    DipolesMat = bst_history('add', DipolesMat, 'scanning', ['Generated from: ' sInput.FileName]);

    % ===== SAVE NEW FILE =====
    % Create output filename
    ProtocolInfo = bst_get('ProtocolInfo');
    DipoleFile = bst_fullfile(ProtocolInfo.STUDIES, bst_fileparts(sStudy.FileName), 'dipoles_fit.mat');
    DipoleFile = file_unique(DipoleFile);
    % Save new file in Brainstorm format
    bst_save(DipoleFile, DipolesMat);

    % ===== UPDATE DATABASE =====
    % Create structure
    BstDipolesMat = db_template('Dipoles');
    BstDipolesMat.FileName = file_short(DipoleFile);
    BstDipolesMat.Comment  = Comment;
    BstDipolesMat.DataFile = sInput.FileName;
    % Add to study
    iDipole = length(sStudy.Dipoles) + 1;
    sStudy.Dipoles(iDipole) = BstDipolesMat;

    % Save study
    bst_set('Study', iStudy, sStudy);
    % Update tree
    panel_protocols('UpdateNode', 'Study', iStudy);
    % Save database
    db_save();
    % Return output file
    OutputFiles{1} = DipoleFile;
end


function test_suite=test_moxunit_util_is_message_identifier
    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
        test_functions=localfunctions();
    catch % no problem; early Matlab versions can use initTestSuite fine
    end
    initTestSuite;

function test_moxunit_util_is_message_identifier_non_char
    aet=@(varargin)assertExceptionThrown(@()...
                    moxunit_util_is_message_identifier(varargin{:}),'');

    aet(struct());                      % struct
    aet({'id:id2'});                    % cell
    aet(2);                             % numeric
    aet(false);                         % boolean
    aet(true);                          % boolean
    aet([]);                          % boolean

function test_moxunit_util_is_message_identifier_true
    true_strs={'a:b',...
                'a___:b___',...
                'abc:def:ghi:jkl:mno',...
                'a_b:c_123',...
                };

    for k=1:numel(true_strs)
        s=true_strs{k};
        assertTrue(moxunit_util_is_message_identifier(s),s);
    end

function test_test_moxunit_util_is_message_identifier_false
    false_strs={'',...                  % empty string
                'a',...                 % missing colon
                'ab',...                % missing colon
                'ab: cd',...            % contains space
                sprintf('ab:c\td'),...  % contains tabs
                '_a:b',...              % starts with underscore
                'a:_b',...              % starts with underscore
                '1ab:def',...           % has a number
                ':abc:def:',...         % starts with colon
                'abc:def:',...          % ends with colon
                'a-b',...               % no colon
                };

    for k=1:numel(false_strs)
        s=false_strs{k};

        assertFalse(moxunit_util_is_message_identifier(s),s);
    end

function test_test_moxunit_util_is_message_identifier_octave
    % in octave, the '-' is allowed to be in message identifers;
    % in Matlab this is not allowed.
    % For compatibility we allow the '-' to be allowed in message
    % identifiers
    true_strs={'a-b:b:cdef-ghij',...
                'a-b:c',...
                'a:b-c',...
                };

    for k=1:numel(true_strs)
        s=true_strs{k};
        assertTrue(moxunit_util_is_message_identifier(s),s);
    end


function planC = chopStructure(structureNum,minZ,maxZ,planC)
%function planC = chopStructure(structureNum,minZ,maxZ,planC)
%
%This function creates a new function which lies within minZ and maxZ.
%Use-case: To chop Skin structure so that dose is computed only around the prostate
%region.
%
%Usage: planC = chopStructure(2,-0.2,12.7)
%
%APA, 04/28/2009
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

global stateS

if ~exist('planC','var')
    global planC
end
indexS = planC{end};

%Get associated scan number
scanNum = getStructureAssociatedScan(structureNum);

[xVals, yVals, zVals] = getScanXYZVals(planC{indexS.scan}(scanNum));

minSlice = max(find(zVals <= minZ));
maxSlice = min(find(zVals >= maxZ));

newStr = newCERRStructure(scanNum,planC);

numStructs = length(planC{indexS.structures});
toAdd = numStructs + 1;
for i=1:length(zVals)
    newStr.contour(i).segments.points = [];
end
newStr.contour(minSlice:maxSlice) = planC{indexS.structures}(structureNum).contour(minSlice:maxSlice);
newStr.structureName = [planC{indexS.structures}(structureNum).structureName, '_chopped'];
newStr.strUID = createUID('structure');
newStr.assocScanUID = planC{indexS.scan}(scanNum).scanUID;
newStr.visible = 1;
planC{indexS.structures} = dissimilarInsert(planC{indexS.structures}, newStr, toAdd);
planC = getRasterSegs(planC, toAdd);

planC = updateStructureMatrices(planC, toAdd);

stateS.structsChanged = 1;
sliceCallBack('refresh');


% UTILS Utility package for MTEST unit testing framework
%
% Array Comparison
%   compareFloats            - Compare floating-point arrays using tolerance
%
% Test Case Discovery Functions
%   isTestCaseSubclass       - True for name of TestCase subclass
%
% String Functions
%   containsRegexp           - True if string contains regular expression
%   isSetUpString            - True for string that looks like a setup function
%   isTearDownString         - True for string that looks like teardown function
%   isTestString             - True for string that looks like a test function
%
% Miscellaneous Functions
%   generateDoc              - Publish test scripts in mtest/doc
%   parseFloatAssertInputs   - Common input-parsing logic for several functions

% Undocumented Functions
%   isAlmostEqual        - Floating-point equality test using relative tolerance

%   Steven L. Eddins
%   Copyright 2008-2009 The MathWorks, Inc.


function labels = context_labels(cls, ds, train_set, train_year)
% Get classification training labels for training the context rescoring
% classifier.
%   labels = context_labels(cls, ds, train_set, train_year)
%
% Return value
%   labels      Binary labels {-1,+1} for each detection in boxes
%
% Arguments
%   cls         Object class
%   ds          Detections
%   train_set   Training dataset
%   train_year  Training dataset year

% AUTORIGHTS
% -------------------------------------------------------
% Copyright (C) 2011-2012 Ross Girshick
% Copyright (C) 2008, 2009, 2010 Pedro Felzenszwalb, Ross Girshick
%
% This file is part of the voc-releaseX code
% (http://people.cs.uchicago.edu/~rbg/latent/)
% and is available under the terms of an MIT-like license
% provided in COPYING. Please retain this notice and
% COPYING if you use this file (or a portion of it) in
% your project.
% -------------------------------------------------------

conf = voc_config('pascal.year', train_year);
cachedir = conf.paths.model_dir;
VOCopts  = conf.pascal.VOCopts;

try
  load([cachedir cls '_context_labels_' train_set '_' train_year]);
catch
  fprintf('Constructing training labels (this will take a little while)...\n');
  [gt, npos] = get_ground_truth(cls, train_set, train_year);
  [gtids, t] = textread(sprintf(VOCopts.imgsetpath, train_set),'%s %d');

  labels = cell(length(gtids),1);

  L = 0;
  for i = 1:length(gtids)
    L = L + size(ds{i},1);
  end

  detections = zeros(L,7);
  I = 1;
  for i = 1:length(gtids)
    if ~isempty(ds{i})
      l = size(ds{i},1);
      % Detection scores
      detections(I:I+l-1,1) = ds{i}(:,end);
      % Detection windows
      detections(I:I+l-1,2:5) = ds{i}(:,1:4);
      % The image (i) the detections came from
      detections(I:I+l-1,6) = i;
      % The index in ds{i} for each detection
      detections(I:I+l-1,7) = 1:l;
      labels{i} = zeros(l,1);
      I = I+l;
    else
      labels{i} = [];
    end
  end

  [sc, si] = sort(-detections(:,1));
  ids = detections(si,6);
  idx = detections(si,7);
  BB = detections(si,2:5)';

  % Adapted from the VOCdevkit m-file VOCevaldet.m

  % assign detections to ground truth objects
  nd=length(si);
  for d=1:nd
    % find ground truth image
    i=ids(d);

    % assign detection to ground truth object if any
    bb=BB(:,d);
    ovmax=-inf;
    for j=1:size(gt(i).boxes,2)
      bbgt=gt(i).boxes(:,j);
      bi=[max(bb(1),bbgt(1)) ; max(bb(2),bbgt(2)) ; min(bb(3),bbgt(3)) ; min(bb(4),bbgt(4))];
      iw=bi(3)-bi(1)+1;
      ih=bi(4)-bi(2)+1;
      if iw>0 & ih>0
        % compute overlap as area of intersection / area of union
        ua=(bb(3)-bb(1)+1)*(bb(4)-bb(2)+1)+...
           (bbgt(3)-bbgt(1)+1)*(bbgt(4)-bbgt(2)+1)-...
           iw*ih;
        ov=iw*ih/ua;
        if ov>ovmax
          ovmax=ov;
          jmax=j;
        end
      end
    end
    % assign detection as true positive/don't care/false positive
    if ovmax>=VOCopts.minoverlap
      if ~gt(i).diff(jmax)
        if ~gt(i).det(jmax)
          % True positive
          gt(i).det(jmax)=true;
          labels{i}(idx(d)) = 1;
        else
          % false positive (multiple detection)
          labels{i}(idx(d)) = -1;
        end
      else
        labels{i}(idx(d)) = 1;   % difficult
      end
    else
      % false positive (low overlap)
      labels{i}(idx(d)) = -1;
    end
  end
  save([cachedir cls '_context_labels_' train_set '_' train_year], 'labels');
  fprintf('done!\n');
end

function [gt, npos] = get_ground_truth(cls, dataset, year)
% Load and cache ground-truth annontation data.
% Most of this code is borrowed from the PASCAL devkit.

conf = voc_config('pascal.year', year);
cachedir = conf.paths.model_dir;
VOCopts  = conf.pascal.VOCopts;
VOCyear  = conf.pascal.year;

try
  load([cachedir cls '_gt_anno_' dataset '_' VOCyear]);
catch
  % load ground truth objects
  [gtids, t] = textread(sprintf(VOCopts.imgsetpath,dataset),'%s %d');
  npos = 0;
  for i = 1:length(gtids)
    % display progress
    tic_toc_print('%s: loading ground truth: %d/%d\n',cls,i,length(gtids));

    % read annotation
    rec = PASreadrecord(sprintf(VOCopts.annopath,gtids{i}));

    % extract objects of class
    clsinds = strmatch(cls,{rec.objects(:).class},'exact');
    gt(i).boxes = cat(1,rec.objects(clsinds).bbox)';
    gt(i).diff = [rec.objects(clsinds).difficult];
    gt(i).det = false(length(clsinds),1);
    npos = npos+sum(~gt(i).diff);
  end
  save([cachedir cls '_gt_anno_' dataset '_' VOCyear], 'gt', 'npos');
end


function r = and(p,q)
% this function compares the x, y, and z values of the structures that I use in
% the FVtool.
%
% SYNOPSIS:
%
%
% PARAMETERS:
%
%
% RETURNS:
%
%
% EXAMPLE:
%
% SEE ALSO:
%

% Copyright (c) 2012-2016 Ali Akbar Eftekhari
% See the license file

if (isa(p, 'CellVector')&&isa(q, 'CellVector'))
    r=p;
    r.xvalue = p.xvalue&q.xvalue;
    r.yvalue = p.yvalue&q.yvalue;
    r.zvalue = p.zvalue&q.zvalue;
elseif isa(p, 'CellVector')
    r=p;
    r.xvalue = p.xvalue&q;
    r.yvalue = p.yvalue&q;
    r.zvalue = p.zvalue&q;
else
    r=q;
    r.xvalue = p&q.xvalue;
    r.yvalue = p&q.yvalue;
    r.zvalue = p&q.zvalue;
end


% eeg_helpsigproc() - Help file for EEGLAB

function noname();

command = { ...
'pophelp(''acsobiro.m'');' ...
'pophelp(''adjustlocs.m'');' ...
'pophelp(''angtimewarp.m'');' ...
'pophelp(''axcopy.m'');' ...
'pophelp(''binica.m'');' ...
'pophelp(''biosig2eeglabevent.m'');' ...
'pophelp(''blockave.m'');' ...
'pophelp(''cart2topo.m'');' ...
'pophelp(''cbar.m'');' ...
'pophelp(''celltomat.m'');' ...
'pophelp(''chancenter.m'');' ...
'pophelp(''changeunits.m'');' ...
'pophelp(''compvar.m'');' ...
'pophelp(''condstat.m'');' ...
'pophelp(''convertlocs.m'');' ...
'pophelp(''copyaxis.m'');' ...
'pophelp(''coregister.m'');' ...
'pophelp(''crossf.m'');' ...
'pophelp(''dftfilt.m'');' ...
'pophelp(''eegfilt.m'');' ...
'pophelp(''eegfiltfft.m'');' ...
'pophelp(''eegplot.m'');' ...
'pophelp(''eegplot2event.m'');' ...
'pophelp(''eegplot2trial.m'');' ...
'pophelp(''eegrej.m'');' ...
'pophelp(''eegthresh.m'');' ...
'pophelp(''entropy_rej.m'');' ...
'pophelp(''env.m'');' ...
'pophelp(''envtopo.m'');' ...
'pophelp(''epoch.m'');' ...
'pophelp(''erpimage.m'');' ...
'pophelp(''eventalign.m'');' ...
'pophelp(''eventlock.m'');' ...
'pophelp(''eyelike.m'');' ...
'pophelp(''fastif.m'');' ...
'pophelp(''floatread.m'');' ...
'pophelp(''floatwrite.m'');' ...
'pophelp(''forcelocs.m'');' ...
'pophelp(''headplot.m'');' ...
'pophelp(''icaact.m'');' ...
'pophelp(''icadefs.m'');' ...
'pophelp(''icaproj.m'');' ...
'pophelp(''icavar.m'');' ...
'pophelp(''imagesctc.m'');' ...
'pophelp(''jader.m'');' ...
'pophelp(''jointprob.m'');' ...
'pophelp(''kurt.m'');' ...
'pophelp(''loadavg.m'');' ...
'pophelp(''loadcnt.m'');' ...
'pophelp(''loaddat.m'');' ...
'pophelp(''loadeeg.m'');' ...
'pophelp(''loadtxt.m'');' ...
'pophelp(''matsel.m'');' ...
'pophelp(''mattocell.m'');' ...
'pophelp(''movav.m'');' ...
'pophelp(''moveaxes.m'');' ...
'pophelp(''nan_mean.m'');' ...
'pophelp(''openbdf.m'');' ...
'pophelp(''parsetxt.m'');' ...
'pophelp(''phasecoher.m'');' ...
'pophelp(''plotchans3d.m'');' ...
'pophelp(''plotdata.m'');' ...
'pophelp(''ploterp.m'');' ...
'pophelp(''plotmesh.m'');' ...
'pophelp(''plotsphere.m'');' ...
'pophelp(''plottopo.m'');' ...
'pophelp(''posact.m'');' ...
'pophelp(''projtopo.m'');' ...
'pophelp(''qqdiagram.m'');' ...
'pophelp(''quantile.m'');' ...
'pophelp(''read_erpss.m'');' ...
'pophelp(''readbdf.m'');' ...
'pophelp(''readedf.m'');' ...
'pophelp(''readeetraklocs.m'');' ...
'pophelp(''readegi.m'');' ...
'pophelp(''readegihdr.m'');' ...
'pophelp(''readelp.m'');' ...
'pophelp(''readlocs.m'');' ...
'pophelp(''readneurodat.m'');' ...
'pophelp(''readneurolocs.m'');' ...
'pophelp(''realproba.m'');' ...
'pophelp(''rejkurt.m'');' ...
'pophelp(''rejstatepoch.m'');' ...
'pophelp(''rejtrend.m'');' ...
'pophelp(''reref.m'');' ...
'pophelp(''rmbase.m'');' ...
'pophelp(''runica.m'');' ...
'pophelp(''runica_ml.m'');' ...
'pophelp(''runica_ml2.m'');' ...
'pophelp(''runica_mlb.m'');' ...
'pophelp(''sbplot.m'');' ...
'pophelp(''sdfopen.m'');' ...
'pophelp(''sdfread.m'');' ...
'pophelp(''shuffle.m'');' ...
'pophelp(''signalstat.m'');' ...
'pophelp(''slider.m'');' ...
'pophelp(''snapread.m'');' ...
'pophelp(''sobi.m'');' ...
'pophelp(''spec.m'');' ...
'pophelp(''spectopo.m'');' ...
'pophelp(''sph2topo.m'');' ...
'pophelp(''spher.m'');' ...
'pophelp(''spherror.m'');' ...
'pophelp(''strmultiline.m'');' ...
'pophelp(''textsc.m'');' ...
'pophelp(''timef.m'');' ...
'pophelp(''timefdetails.m'');' ...
'pophelp(''timewarp.m'');' ...
'pophelp(''timtopo.m'');' ...
'pophelp(''topo2sph.m'');' ...
'pophelp(''topoplot.m'');' ...
'pophelp(''transformcoords.m'');' ...
'pophelp(''trial2eegplot.m'');' ...
'pophelp(''uigetfile2.m'');' ...
'pophelp(''uiputfile2.m'');' ...
'pophelp(''writecnt.m'');' ...
'pophelp(''writelocs.m'');' ...
};
vartext = { ...
'acsobiro.m' ...
'adjustlocs.m' ...
'angtimewarp.m' ...
'axcopy.m' ...
'binica.m' ...
'biosig2eeglabevent.m' ...
'blockave.m' ...
'cart2topo.m' ...
'cbar.m' ...
'celltomat.m' ...
'chancenter.m' ...
'changeunits.m' ...
'compvar.m' ...
'condstat.m' ...
'convertlocs.m' ...
'copyaxis.m' ...
'coregister.m' ...
'crossf.m' ...
'dftfilt.m' ...
'eegfilt.m' ...
'eegfiltfft.m' ...
'eegplot.m' ...
'eegplot2event.m' ...
'eegplot2trial.m' ...
'eegrej.m' ...
'eegthresh.m' ...
'entropy_rej.m' ...
'env.m' ...
'envtopo.m' ...
'epoch.m' ...
'erpimage.m' ...
'eventalign.m' ...
'eventlock.m' ...
'eyelike.m' ...
'fastif.m' ...
'floatread.m' ...
'floatwrite.m' ...
'forcelocs.m' ...
'headplot.m' ...
'icaact.m' ...
'icadefs.m' ...
'icaproj.m' ...
'icavar.m' ...
'imagesctc.m' ...
'jader.m' ...
'jointprob.m' ...
'kurt.m' ...
'loadavg.m' ...
'loadcnt.m' ...
'loaddat.m' ...
'loadeeg.m' ...
'loadtxt.m' ...
'matsel.m' ...
'mattocell.m' ...
'movav.m' ...
'moveaxes.m' ...
'nan_mean.m' ...
'openbdf.m' ...
'parsetxt.m' ...
'phasecoher.m' ...
'plotchans3d.m' ...
'plotdata.m' ...
'ploterp.m' ...
'plotmesh.m' ...
'plotsphere.m' ...
'plottopo.m' ...
'posact.m' ...
'projtopo.m' ...
'qqdiagram.m' ...
'quantile.m' ...
'read_erpss.m' ...
'readbdf.m' ...
'readedf.m' ...
'readeetraklocs.m' ...
'readegi.m' ...
'readegihdr.m' ...
'readelp.m' ...
'readlocs.m' ...
'readneurodat.m' ...
'readneurolocs.m' ...
'realproba.m' ...
'rejkurt.m' ...
'rejstatepoch.m' ...
'rejtrend.m' ...
'reref.m' ...
'rmbase.m' ...
'runica.m' ...
'runica_ml.m' ...
'runica_ml2.m' ...
'runica_mlb.m' ...
'sbplot.m' ...
'sdfopen.m' ...
'sdfread.m' ...
'shuffle.m' ...
'signalstat.m' ...
'slider.m' ...
'snapread.m' ...
'sobi.m' ...
'spec.m' ...
'spectopo.m' ...
'sph2topo.m' ...
'spher.m' ...
'spherror.m' ...
'strmultiline.m' ...
'textsc.m' ...
'timef.m' ...
'timefdetails.m' ...
'timewarp.m' ...
'timtopo.m' ...
'topo2sph.m' ...
'topoplot.m' ...
'transformcoords.m' ...
'trial2eegplot.m' ...
'uigetfile2.m' ...
'uiputfile2.m' ...
'writecnt.m' ...
'writelocs.m' ...
};
textgui( vartext, command,'fontsize', 15, 'fontname', 'times', 'linesperpage', 18, 'title',strvcat( 'Signal processing functions', '(Click on blue text for help)'));
icadefs; set(gcf, 'COLOR', BACKCOLOR);h = findobj('parent', gcf, 'style', 'slider');set(h, 'backgroundcolor', GUIBACKCOLOR);return;


%DILATE  Dilates an image by using a specific structuring element
%
%     dst = cv.dilate(src)
%     dst = cv.dilate(src, 'OptionName',optionValue, ...)
%
% ## Input
% * __src__ input image; the number of channels can be arbitrary, but the
%   depth should be one of `uint8`, `uint16`, `int16`, `single` or `double`.
%
% ## Output
% * __dst__ output image of the same size and type as `src`.
%
% ## Options
% * __Element__ Structuring element used for dilation. By default, a 3x3
%   rectangular structuring element is used `ones(3)`. Kernel can be created
%   using cv.getStructuringElement
% * __Anchor__ Position of the anchor within the element. The default value
%   [-1, -1] means that the anchor is at the element center.
% * __Iterations__ Number of times dilation is applied. default 1
% * __BorderType__ Border mode used to extrapolate pixels outside of the
%   image. See cv.copyMakeBorder. default 'Constant'
% * __BorderValue__ Border value in case of a constant border. The default
%   value has a special meaning which gets automatically translated to the
%   minimum value of the image class type (`intmin(class(img))` for integer
%   types and `realmin(class(img))` for floating-point types). See
%   cv.morphologyDefaultBorderValue
%
% The function dilates the source image using the specified structuring
% element that determines the shape of a pixel neighborhood over which the
% maximum is taken:
%
%     dst(x,y) = max_{(xp,yp): Element(xp,yp)!=0} src(x+xp, y+yp)
%
% Dilation can be applied several (`Iterations`) times. In case of
% multi-channel images, each channel is processed independently.
%
% See also: cv.erode, cv.morphologyEx, cv.getStructuringElement, imdilate
%


function [Gain, errMsg] = bst_duneuro(cfg)
% BST_DUNEURO: Call Duneuro to compute a FEM solution for Brainstorm.
%
% USAGE:  [Gain, errMsg] = bst_duneuro(cfg)

% @=============================================================================
% 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: Takfarinas Medani, Juan Garcia-Prieto, 2019-2021
%          Francois Tadel 2020-2023

% Initialize returned values
Gain = [];
% Install/load duneuro plugin
[isInstalled, errMsg, PlugDesc] = bst_plugin('Install', 'duneuro', cfg.Interactive);
if ~isInstalled
    return;
end
bst_plugin('SetProgressLogo', 'duneuro');
% Get DUNEuro executable
DuneuroExe = bst_fullfile(PlugDesc.Path, PlugDesc.SubFolder, 'bin', ['bst_duneuro_meeg_', bst_get('OsType')]);
if ispc
    DuneuroExe = [DuneuroExe, '.exe'];
else
    DuneuroExe = [DuneuroExe, '.app'];
end

%% ===== SENSORS =====
% Select modality for DUNEuro
isEeg  = strcmpi(cfg.EEGMethod, 'duneuro')  && ~isempty(cfg.iEeg);
isMeg  = strcmpi(cfg.MEGMethod, 'duneuro')  && ~isempty(cfg.iMeg);
isEcog = strcmpi(cfg.ECOGMethod, 'duneuro') && ~isempty(cfg.iEcog);
isSeeg = strcmpi(cfg.SEEGMethod, 'duneuro') && ~isempty(cfg.iSeeg);

% Get the modality
if ((isEeg || isEcog || isSeeg) && isMeg)
    dnModality = 'meeg';
elseif (isEeg || isEcog || isSeeg)
    dnModality = 'eeg';
elseif  isMeg
    dnModality = 'meg'; % from DUNEuro side, EEG, sEEG, ECOG  uses the same process
else
    errMsg = 'No valid modality available.';
    return;
end

% Get EEG positions
% Combined modalities for EEG/sEEG/EcoG as EEG
if (isEeg || isEcog || isSeeg)
    cfg.iEeg = [cfg.iEeg, cfg.iSeeg, cfg.iEcog];
    EegLoc = cat(2, cfg.Channel(cfg.iEeg).Loc);
end

% Get MEG positions/orientations
if isMeg
    MegChannels = [];
    for iChan = 1:length(cfg.iMeg)
        sChan = cfg.Channel(cfg.iMeg(iChan));
        for iInteg = 1:size(sChan.Loc, 2)
            MegChannels = [MegChannels; iChan, sChan.Loc(:,iInteg)', sChan.Orient(:,iInteg)', sChan.Weight(iInteg)];
        end
    end
    % In the case where the MEG integration points are used
    if cfg.UseIntegrationPoint == 0
        % loop over the integration Points
        % chan_loc = figure_3d('GetChannelPositions', cfg, cfg.iMeg); % <= this function is not sufficient, we need also the weights.
        MegChannelsTemp = [];
        for iChan = 1 : length(cfg.iMeg)
            group = MegChannels(MegChannels(:,1) == iChan,:);
            groupPositive = group(group(:,end)>0,:);
            groupNegative = group(group(:,end)<0,:);
            if ~isempty(groupPositive)
                %equivalentPositionPostive = sum(repmat(abs(groupPositive(:,end)),[1 3])  .* groupPositive(:,2:4));
                equivalentPositionPostive = mean(groupPositive(:,2:4));
                MegChannelsTemp = [MegChannelsTemp; iChan  equivalentPositionPostive groupPositive(1,5:7)  sum(groupPositive(:,end))];
            end
            if ~isempty(groupNegative)
                %equivalentPositionNegative = sum(repmat(abs(groupNegative(:,end)),[1 3])  .* groupNegative(:,2:4));
                equivalentPositionNegative = mean(groupNegative(:,2:4));
                MegChannelsTemp = [MegChannelsTemp; iChan  equivalentPositionNegative groupNegative(1,5:7)  sum(groupNegative(:,end))];
            end
        end
        MegChannels = MegChannelsTemp;
    end
end

%% ===== HEAD MODEL =====
% Load FEM mesh
FemMat = load(cfg.FemFile);
% Get mesh type
switch size(FemMat.Elements,2)
    case 4,  ElementType = 'tetrahedron';
    case 8,  ElementType = 'hexahedron';
end
% Remove unselected tissue from the head model (MEG only), this could be also done for sEEG later
if strcmp(dnModality, 'meg')
    % Remove the elements corresponding to the unselected tissues
    iRemove = find(~ismember(FemMat.Tissue, find(cfg.FemSelect)));
    if ~isempty(iRemove)
        FemMat = fem_remove_elem(FemMat, iRemove);
    end
elseif strcmp(dnModality,'meeg') && (sum(cfg.FemSelect) ~= length(unique(FemMat.Tissue)))
    errMsg = 'Reduced head model cannot be used when computing MEG+EEG simultaneously.';
    return;
end
% Hexa mesh: detect whether the geometry was adapted
if strcmpi(ElementType, 'hexahedron')
    GeometryAdapted = [];
    % Detect using the options of the Brainstorm process that created the file
    if isfield(FemMat, 'History') && ~isempty(FemMat.History) && ~isempty(strfind([FemMat.History{:,3}], 'NodeShift'))
        strOptions = [FemMat.History{:,3}];
        iTag = strfind(strOptions, 'NodeShift');
        val = sscanf(strOptions(iTag:end), 'NodeShift=%f');
        if ~isempty(val)
            GeometryAdapted = (val > 0);
        end
    end
    % Otherwise, try to guess based on the geometry
    if isempty(GeometryAdapted)
        % Compute the distance between the first two nodes of each element
        dist = sqrt(sum([FemMat.Vertices(FemMat.Elements(:,1),1) - FemMat.Vertices(FemMat.Elements(:,2),1), ...
         FemMat.Vertices(FemMat.Elements(:,2),2) - FemMat.Vertices(FemMat.Elements(:,2),2), ...
         FemMat.Vertices(FemMat.Elements(:,2),3) - FemMat.Vertices(FemMat.Elements(:,2),3)] .^ 2, 2));
        % If the distance is not constant: then the geomtry is adapted
        GeometryAdapted = (max(abs(dist - dist(1))) > 1e-9);
    end
    % Copy value in DUNEuro options
    if ~isempty(GeometryAdapted)
        cfg.GeometryAdapted = GeometryAdapted;
        disp(['DUNEURO> Detected parameter: GeometryAdapted=', bool2str(GeometryAdapted)]);
    end
end
% Isotropic
if (cfg.Isotropic)
    % Isotropic without tensor
    if ~cfg.UseTensor
        if strcmp(ElementType,'hexahedron')
            MeshFile = 'head_model.dgf';
        else
            MeshFile = 'head_model.msh';
        end
    % Isotropic with tensor
    else
        if strcmp(ElementType,'hexahedron')
            errMsg = 'Using the tensor model with hexahedral mesh is not supported for now.';
            return;
        end
        MeshFile = 'head_model.geo';
    end
% Anisotropic (with tensor)
else
    if strcmp(ElementType,'hexahedron')
        errMsg = 'Using the anisotropy model with hexahedral mesh is not supported for now.';
        return;
    end
    MeshFile = 'head_model.geo';
    cfg.UseTensor = true;
end

% Get temp folder
TmpDir = bst_get('BrainstormTmpDir', 0, 'duneuro');
% Display message
bst_progress('text', 'DUNEuro: Writing temporary files...');
disp(['DUNEURO> Writing temporary files to: ' TmpDir]);

% Write mesh model
MeshFile = fullfile(TmpDir, MeshFile);
out_fem(FemMat, MeshFile);

%% ====== SOURCE SPACE =====
% Source space type
switch (cfg.HeadModelType)
    case 'volume'
        % TODO or keep it as it's now....
    case 'surface'
        bst_progress('text', 'DUNEuro: Fixing source space...');
        % Read cortex file
        sCortex = bst_memory('LoadSurface', cfg.CortexFile);
        cfg.GridLoc = sCortex.Vertices;
        % Shrink the cortex surface by XX mm
        if (cfg.SrcShrink > 0)
            % Get spherical coordinates of the surface normals
            [azimuth, elevation] = cart2sph(sCortex.VertNormals(:,1), sCortex.VertNormals(:,2), sCortex.VertNormals(:,3));
            % Find components to shrink the surface in the three dimensions
            depth = cfg.SrcShrink ./ 1000 .* [cos(elevation) .* cos(azimuth), cos(elevation) .* sin(azimuth), sin(elevation)];
            % Apply to the cortex surface
            cfg.GridLoc = cfg.GridLoc - depth;
        end
        % Force all the dipoles within the GM layer
        iGM = find(panel_duneuro('CheckType', FemMat.TissueLabels, 'gray'), 1);
        iWM = find(panel_duneuro('CheckType', FemMat.TissueLabels, 'white'), 1);
        if cfg.SrcForceInGM && ~isempty(iGM)
            % Install/load iso2mesh plugin
            [isInstalled, errMsg] = bst_plugin('Install', 'iso2mesh', cfg.Interactive);
            if ~isInstalled
                % Delete the temporary files
                file_delete(TmpDir, 1, 1);
                return;
            end
            % Extract GM vertices and elements
            [gmVert, gmElem] = removeisolatednode(FemMat.Vertices, FemMat.Elements(FemMat.Tissue == iGM,:));
            % Compute the centroid of the GM elements
            nElem = size(gmElem, 1);
            nMesh = size(gmElem, 2);
            ElemCenter = zeros(nElem, 3);
            for i = 1:3
                ElemCenter(:,i) = sum(reshape(gmVert(gmElem,i), nElem, nMesh)')' / nMesh;
            end

            % Extract GM envelope
            envFaces = volface(FemMat.Elements(FemMat.Tissue <= iGM,:));
            [envVert, envFaces] = removeisolatednode(FemMat.Vertices, envFaces);
            % Find the dipoles outside of the GM envelope
            iVertOut = find(~inpolyhedron(envFaces, envVert, cfg.GridLoc));
            if ~isempty(iVertOut)
                disp(['DUNEURO> Warning: ' num2str(length(iVertOut)) ' dipole(s) outside of the GM.']);
            end

            % If there is a white matter layer: find the dipoles inside the WM and move them outside to the GM
            if ~isempty(iWM)
                % Extract GM envelope
                wmFaces = volface(FemMat.Elements(FemMat.Tissue == iWM,:));
                [wmVert, wmFaces] = removeisolatednode(FemMat.Vertices, wmFaces);
                % Find the dipoles outside of the GM envelope
                iVertWM = find(inpolyhedron(wmFaces, wmVert, cfg.GridLoc));
                if ~isempty(iVertWM)
                    disp(['DUNEURO> Warning: ' num2str(length(iVertWM)) ' dipole(s) inside the WM.']);
                    iVertOut = union(iVertOut, iVertWM);
                end
            end

            % Move each vertex towards the centroid of the closest GM element
            % view_surface_matrix(cfg.GridLoc, sCortex.Faces)
            for i = 1:length(iVertOut)
                bst_progress('text', sprintf('DUNEuro: Fixing dipole %d/%d...', i, length(iVertOut)));
                % Find the closest GM centroid
                iTarget = dsearchn(ElemCenter, cfg.GridLoc(iVertOut(i),:));
                targetFaces = volface(gmElem(iTarget,:));

                % OPTION #1: Replace the vertex position directly with the centroid.
                % => Problem: might project multiple vertices on the same centroid...
                % cfg.GridLoc(iVertOut ,:) = ElemCenter(iTarget,:);

                % OPTION #2: Gradually move the vertex towards the center of the centroid, until it is located inside the element
                % Move the vertex towards the center until it is inside the element
                %nFix = 10;
                %for iFix = 1:nFix
                %    tmpVert = (nFix - iFix)/nFix * cfg.GridLoc(iVertOut(i),:) + iFix/nFix * ElemCenter(iTarget,:);
                %   if inpolyhedron(targetFaces, gmVert, tmpVert)
                %        distMove = sqrt(sum((cfg.GridLoc(iVertOut(i),:) - tmpVert) .^ 2)) * 1000;
                %        disp(sprintf('DUNEURO> Dipole #%d moved inside the GM (%1.2fmm)', iVertOut(i), distMove));
                %        cfg.GridLoc(iVertOut(i),:) = tmpVert;
                %        break;
                %    end
                %end

                % OPTION #3: move the vertex towards the centroid of the element, and then place the final dipole
                % in the symetric point to the center, as the image of the computed vertex
                % x-----o-----x'
                % ^      ^      ^____ : x' the image of x, or the final dipole position
                % |       |_________ : o is the center of the elem, and middle of [x,x']
                % |_____________ : the point inside the element determined by the tmpVert in the following equation
                nFix = 10; % divid into 10 segments
                iFix = 7; % ratio of the distance tmpVert from the centroid
                tmpVert = (nFix - iFix)/nFix * cfg.GridLoc(iVertOut(i),:) + iFix/nFix * ElemCenter(iTarget,:);
                distPoint = ElemCenter(iTarget,:) - tmpVert;
                newPoint = ElemCenter(iTarget,:) + (distPoint);
                distMove = sqrt(sum((cfg.GridLoc(iVertOut(i),:) - newPoint) .^ 2)) * 1000;
                % use the image/symeric point if it's inside GM
                if inpolyhedron(targetFaces, gmVert, newPoint)
                    disp(sprintf('DUNEURO> iDipole %d/%d : Dipole #%d moved inside the GM (%1.2fmm) (option 3 :as image)',i,length(iVertOut), iVertOut(i), distMove));
                    cfg.GridLoc(iVertOut(i),:) = newPoint;
                elseif inpolyhedron(targetFaces, gmVert, tmpVert) % use the original point if it's inside GM
                    disp(sprintf('DUNEURO> iDipole %d/%d : Warning Dipole #%d moved outside the GM (%1.2fmm) (option 3 :as image)', i,length(iVertOut),iVertOut(i), distMove));
                    % use the original distance unstead of the image
                    distMove = sqrt(sum((cfg.GridLoc(iVertOut(i),:) - tmpVert) .^ 2)) * 1000;
                    disp(sprintf('DUNEURO> iDipole %d/%d : Correction 1: Dipole #%d moved inside the GM (%1.2fmm) (option 3: not image)', i,length(iVertOut),iVertOut(i), distMove));
                    cfg.GridLoc(iVertOut(i),:) = tmpVert;
                else % Use the option 2 defined by Francois
                    disp(sprintf('DUNEURO> iDipole %d/%d : Warning Dipole #%d moved outside the GM (%1.2fmm) (option 3 :as image)', i,length(iVertOut),iVertOut(i), distMove));
                    nFix = 20; % with 10 it's not working for some extrem case, then I upgrade it to 20
                    for iFix = 1: nFix
                       tmpVert = (nFix - iFix)/nFix * cfg.GridLoc(iVertOut(i),:) + iFix/nFix * ElemCenter(iTarget,:);
                      if inpolyhedron(targetFaces, gmVert, tmpVert)
                           distMove = sqrt(sum((cfg.GridLoc(iVertOut(i),:) - tmpVert) .^ 2)) * 1000;
                           disp(sprintf('DUNEURO> iDipole %d/%d : Correction 2: Dipole #%d moved inside the GM (%1.2fmm) (option2)', i,length(iVertOut),iVertOut(i), distMove));
                           cfg.GridLoc(iVertOut(i),:) = tmpVert;
                           break;
                       end
                    end
                end
            end
            % view_surface_matrix(cfg.GridLoc, sCortex.Faces)

%             %%%% =============================================
%             % Now similar process for the WM with an extra and unexpedted step ...
%             if ~isempty(iWM)
%                 disp('Checking the the WM ...');
%                 %Extract WM surface
%                 wm_tetra = FemMat.Elements(FemMat.Tissue <= iWM,:);
%                 wm_face = volface(wm_tetra);
%                 [nwmf, ewmf] = removeisolatednode(FemMat.Vertices,wm_face);
%                 % check if any dipoles is inside the WM
%                 wMfv.vertices = nwmf;
%                 wMfv.faces = ewmf;
%                 tic
%                 wMin = inpolyhedron(wMfv, sCortex.Vertices);
%                 wMindex_in = find(wMin);
%                 disp(['There are ' num2str(sum(wMin)) ' dipoles inside the WM']);
%                 disp('Moving these dipoles to the GM tissues ...');
%                 twm = toc;
%                 if ~isempty(wMindex_in)
%                     % 1- move the dipole from inside the WM to the GM surface
%                     % ==> this is for testing, when we use the centroide
%                     % directely, some dipole remains within the WM ...
%                     GMcentroide = 0; % just for testing, to use directely the GM centroides
%                     if GMcentroide == 1
%                         k = dsearchn(elem_centroide,sCortex.Vertices(wMindex_in,:));
%                         NewVertices(wMindex_in ,:) = ElemCenter(k,:);
%                         wMoutFinal = inpolyhedron(wMfv, NewVertices);
%                         disp(['Now, there are ' num2str(sum(wMoutFinal)) ' dipoles inside  the WM']);
%                     else
%                         k = dsearchn(gMfv.vertices,sCortex.Vertices(wMindex_in,:));
%                         NewVertices(wMindex_in ,:) = gMfv.vertices(k,:);
%                         wMoutFinal = inpolyhedron(wMfv, NewVertices);
%                         disp(['There are ' num2str(sum(wMoutFinal)) ' dipoles inside  the WM']);
%                         disp(['These dipoles are moved to the GM outer surface nodes']);
%                         disp(['Moving these dipoles to the nearest centroide of the GM element...']);
%                         % Now, we move these same dipole from the surface to the
%                         % nearest centroide ==> to be sure it's inside the GM
%                         % ==> fill partially the FEM condition
%                         % move again  to the centoide
%                         k = dsearchn(ElemCenter,NewVertices(wMindex_in,:));
%                         NewVertices(wMindex_in ,:) = ElemCenter(k,:);
%                         % just to check
%                         wMoutFinal = inpolyhedron(wMfv, NewVertices);
%                         disp(['Now, there are ' num2str(sum(wMoutFinal)) ' dipoles inside  the WM']);
%                         disp(['All the dipoles are moved to nearest centroid of the GM']);
%                     end
%                 end
%             end

        end
    case 'mixed'
        % TODO : not used ?
end

%% ===== SOURCE MODEL =====
bst_progress('text', 'DUNEuro: Writing temporary files...');
% Write the source/dipole file
DipoleFile = fullfile(TmpDir, 'dipole_model.txt');
% Unconstrained orientation for each dipole. ie the output file have 3*N dipoles (3 directions for each)
%     [x1,y1,z1, 1, 0, 0,;
%      x1,y1,z1, 0, 1, 0;
%      x1,y1,z1, 0, 0, 1;
%      x2,y2,z2, 1, 0, 0;
%      .... ];
dipoles = [kron(cfg.GridLoc, ones(3,1)), kron(ones(size(cfg.GridLoc,1), 1), eye(3))];
fid = fopen(DipoleFile, 'wt+');
fprintf(fid, '%d %d %d %d %d %d \n', dipoles');
fclose(fid);

%% ===== SENSOR MODEL =====
% Write the EEG electrode file
ElecFile = 'electrode_model.txt';
if isEeg || isEcog || isSeeg
    fid = fopen(fullfile(TmpDir, ElecFile), 'wt+');
    fprintf(fid, '%d %d %d  \n', EegLoc);
    fclose(fid);
end
% Write the MEG sensors file
CoilFile = fullfile(TmpDir, 'coil_model.txt');
ProjFile = fullfile(TmpDir, 'projection_model.txt');
if isMeg
    % Write coil file
    CoilsLoc = MegChannels(:,2:4);
    fid = fopen(CoilFile, 'wt+');
    fprintf(fid, '%d %d %d  \n', CoilsLoc');
    fclose(fid);
    % Write projection file
    CoilsOrient = MegChannels(:,5:7);
    fid = fopen(ProjFile, 'wt+');
    fprintf(fid, '%d %d %d  \n', CoilsOrient');
    fclose(fid);
end

%% ===== CONDUCTIVITY MODEL =====
% Isotropic without tensor
if ~cfg.UseTensor
    CondFile = fullfile(TmpDir, 'conductivity_model.con');
    fid = fopen(CondFile, 'w');
    fprintf(fid, '%d\t', cfg.FemCond);
    fclose(fid);
% With tensor (isotropic or anisotropic)
else
    CondFile = fullfile(TmpDir, 'conductivity_model.knw');
    % Transformation matrix  and tensor mapping on each direction
    CondTensor = zeros(length(FemMat.Elements),6) ;
    for ind =1 : length(FemMat.Elements)
        temp0 = reshape(FemMat.Tensors(ind,:),3,[]);
        T1 = temp0(:,1:3); % get the 3 eigen vectors
        l =  diag(temp0(:,4)); % get the eigen value as 3x3
        temp = T1 * l * T1'; % reconstruct the tensors
        CondTensor(ind,:) = [temp(1) temp(5) temp(9) temp(4) temp(8) temp(7)]; % this is the right order
    end
    % write the tensors
    out_fem_knw(FemMat, CondTensor, CondFile);
end

%% ===== WRITE MINI FILE =====
% Open the mini file
IniFile = fullfile(TmpDir, 'duneuro_minifile.mini');
fid = fopen(IniFile, 'wt+');
% General setting
fprintf(fid, '__name = %s\n\n', IniFile);
if strcmp(cfg.SolverType, 'cg')
    fprintf(fid, 'type = %s\n', cfg.FemType);
end
fprintf(fid, 'element_type = %s\n', ElementType);
fprintf(fid, 'solver_type = %s\n', cfg.SolverType);
fprintf(fid, 'geometry_adapted = %s\n', bool2str(cfg.GeometryAdapted));
fprintf(fid, 'tolerance = %d\n', cfg.Tolerance);
% [electrodes]
if isEcog || isSeeg
    % Instead of selecting the electrode on the outer surface,
    % uses the nearest FEM node as the electrode location
    cfg.ElecType = 'closest_subentity_center';
end
if strcmp(dnModality, 'eeg') || strcmp(dnModality, 'meeg')
    fprintf(fid, '[electrodes]\n');
    fprintf(fid, 'filename = %s\n', fullfile(TmpDir, ElecFile));
    fprintf(fid, 'type = %s\n', cfg.ElecType);
    fprintf(fid, 'codims = %s\n', '3');
end
% [meg]
if strcmp(dnModality, 'meg') || strcmp(dnModality, 'meeg')
    fprintf(fid, '[meg]\n');
    fprintf(fid, 'intorderadd = %d\n', cfg.MegIntorderadd);
    fprintf(fid, 'type = %s\n', cfg.MegType);
    fprintf(fid, 'cache.enable = %s\n',bool2str(cfg.EnableCacheMemory) );
    % [coils]
    fprintf(fid, '[coils]\n');
    fprintf(fid, 'filename = %s\n', CoilFile);
    % [projections]
    fprintf(fid, '[projections]\n');
    fprintf(fid, 'filename = %s\n', ProjFile);
end
% [dipoles]
fprintf(fid, '[dipoles]\n');
fprintf(fid, 'filename = %s\n', DipoleFile);
% [volume_conductor.grid]
fprintf(fid, '[volume_conductor.grid]\n');
fprintf(fid, 'filename = %s\n', MeshFile);
% [volume_conductor.tensors]
fprintf(fid, '[volume_conductor.tensors]\n');
fprintf(fid, 'filename = %s\n', CondFile);
% [solver]
fprintf(fid, '[solver]\n');
fprintf(fid, 'solver_type = %s\n', cfg.SolvSolverType);
fprintf(fid, 'preconditioner_type = %s\n', cfg.SolvPrecond);
if strcmp(cfg.SolverType, 'cg')
    fprintf(fid, 'cg_smoother_type = %s\n', cfg.SolvSmootherType);
end
fprintf(fid, 'intorderadd = %d\n', cfg.SolvIntorderadd);
% Discontinuous Galerkin
if strcmp(cfg.SolverType, 'dg')
    fprintf(fid, 'dg_smoother_type = %s\n', cfg.DgSmootherType);
    fprintf(fid, 'scheme = %s\n', cfg.DgScheme);
    fprintf(fid, 'penalty = %d\n', cfg.DgPenalty);
    fprintf(fid, 'edge_norm_type = %s\n', cfg.DgEdgeNormType);
    fprintf(fid, 'weights = %s\n', bool2str(cfg.DgWeights));
    fprintf(fid, 'reduction = %s\n', bool2str(cfg.DgReduction));
end
% [solution]
fprintf(fid, '[solution]\n');
fprintf(fid, 'post_process = %s\n', bool2str(cfg.SolPostProcess)); % true/false
fprintf(fid, 'subtract_mean = %s\n', bool2str(cfg.SolSubstractMean)); % boolean
% [solution.solver]
fprintf(fid, '[solution.solver]\n');
fprintf(fid, 'reduction = %d\n', cfg.SolSolverReduction);
% [solution.source_model]
fprintf(fid, '[solution.source_model]\n');
fprintf(fid, 'type = %s\n', cfg.SrcModel);
fprintf(fid, 'intorderadd = %d\n', cfg.SrcIntorderadd);
fprintf(fid, 'intorderadd_lb = %d\n', cfg.SrcIntorderadd_lb);
fprintf(fid, 'numberOfMoments = %d\n', cfg.SrcNbMoments);
fprintf(fid, 'referenceLength = %d\n', cfg.SrcRefLen);
fprintf(fid, 'weightingExponent = %d\n', cfg.SrcWeightExp);
fprintf(fid, 'relaxationFactor = %e\n', 10^(-cfg.SrcRelaxFactor));
fprintf(fid, 'mixedMoments = %s\n', bool2str(cfg.SrcMixedMoments));
fprintf(fid, 'restrict = %s\n', bool2str(cfg.SrcRestrict));
fprintf(fid, 'initialization = %s\n', cfg.SrcInit);
% [brainstorm]
fprintf(fid, '[brainstorm]\n');
fprintf(fid, 'modality = %s\n', dnModality);
fprintf(fid, 'output_folder = %s\n', [TmpDir, filesep]);
fprintf(fid, 'save_eeg_transfer_file = %s\n', bool2str(cfg.BstSaveTransfer));
fprintf(fid, 'save_meg_transfer_file = %s\n', bool2str(cfg.BstSaveTransfer));
fprintf(fid, 'save_meeg_transfer_file = %s\n', bool2str(cfg.BstSaveTransfer));
fprintf(fid, 'eeg_transfer_filename = %s\n', cfg.BstEegTransferFile);
fprintf(fid, 'meg_transfer_filename = %s\n', cfg.BstMegTransferFile);
fprintf(fid, 'eeg_leadfield_filename = %s\n', cfg.BstEegLfFile);
fprintf(fid, 'meg_leadfield_filename = %s\n', cfg.BstMegLfFile);
% Close file
fclose(fid);

%% ===== RUN DUNEURO ======
% Assemble command line
callStr = ['"' DuneuroExe '"' ' ' '"' IniFile '"'];
bst_progress('text', 'DUNEuro: Computing leadfield...');
disp(['DUNEURO> System call: ' callStr]);
tic;
% Call DUNEuro
status = system(callStr)
if (status ~= 0)
    errMsg = 'Error during the DUNEuro computation, see logs in the command window.';
    return;
end
disp(['DUNEURO> FEM computation completed in: ' num2str(toc) 's']);

%% ===== READ LEADFIELD ======
bst_progress('text', 'DUNEuro: Reading leadfield...');
% EEG
if (isEeg || isEcog || isSeeg)
    GainEeg = in_duneuro_bin(fullfile(TmpDir, cfg.BstEegLfFile))';
end

%MEG
if isMeg
    GainMeg = in_duneuro_bin(fullfile(TmpDir, cfg.BstMegLfFile))';

    % === POST-PROCESS MEG LEADFIELD ===
    % Compute the total magnetic field
    dipoles_pos_orie = [kron(cfg.GridLoc,ones(3,1)), kron(ones(length(cfg.GridLoc),1), eye(3))];

    % a- Compute the MEG Primary Magnetic B-field analytically (formula of Sarvas)
    dip_pos = dipoles_pos_orie(:,1:3);
    dip_mom = dipoles_pos_orie(:,4:6);
    Bp = zeros(size(MegChannels,1), size(dip_pos,1));
    for i = 1:size(CoilsLoc,1)
        for j = 1 : size(dip_pos,1)
            R = CoilsLoc(i,:);
            R_0 = dip_pos(j,:);
            A = R - R_0;
            a = norm(A);
            aa = A./(a^3);
            BpHelp = cross(dip_mom(j,:),aa);
            Bp(i,j) = BpHelp * CoilsOrient(i, :)'; % projection of the primary B-field along the coil orientations
        end
    end

    % b- The total magnetic field B = Bp + Bs;
    %  full B-field
    Bs = GainMeg;
    mu = 4*pi*1e-4; % check the value of the units maybe it needs to be mu = 4*pi*1e-7
    Bfull = (mu/(4*pi)) * (Bp - Bs);

    % c- Apply the weight :
    [channelIndex] = unique(MegChannels(:,1));
    nbChannel = length(channelIndex);
    weighted_B = zeros(nbChannel,size(Bfull,2));
    for iCh = 1 : nbChannel
        communChannel = find(iCh==MegChannels(:,1));
        BcommunChannel = Bfull(communChannel(:),:);
        WcommunChannel =  MegChannels(communChannel(:), 8: end);
        weighted_B(iCh,:) = sum (BcommunChannel.*WcommunChannel,1);
    end
    GainMeg = weighted_B;
end

% Fill the unused channels with NaN
Gain = NaN * zeros(length(cfg.Channel), 3 * length(cfg.GridLoc));
if isMeg
    Gain(cfg.iMeg,:) = GainMeg;
    % scaling the MEG Gain matrix
    Gain(cfg.iMeg,:) = GainMeg/1000;
end
if (isEeg || isEcog || isSeeg)
    Gain(cfg.iEeg,:) = GainEeg;
end

% Delete the temporary files
file_delete(TmpDir, 1, 1);

%% ===== SAVE TRANSFER MATRIX ======
disp('DUNEURO> TODO: Save transferOut.dat to database.')

% Remove logo
bst_plugin('SetProgressLogo', []);

end

%% =================================================================================
%  === SUPPORT FUNCTIONS  =========================================================
%  =================================================================================

%% ===== BOOL => STR =====
function str = bool2str(bool)
    if bool
        str = 'true';
    else
        str = 'false';
    end
end


clear;
addpath(genpath('../'));

output_dir = './CECLM_res_menpo/';
if(~exist(output_dir, 'dir'))
    mkdir(output_dir)
end
%% select database and load bb initializations
db_root = 'E:\datasets\300VW\300VW_Dataset_2015_12_14/';
bb_root = './300VW_dets_mtcnn/';
extra_dir = 'E:\datasets\300VW\300VW_Dataset_2015_12_14\extra';
[ vid_locs, bboxes, gts_all, invalid_frames ] = CollectTestData(db_root, bb_root, extra_dir);

%% loading the patch experts and the PDM
[patches, pdm, clmParams, early_term_params] = Load_CECLM_menpo();
multi_view = true;

%% Select video
for i=1:numel(vid_locs)

    vid = VideoReader(vid_locs{i});

    bounding_boxes = bboxes{i};

    n_frames = size(bounding_boxes,1);
    preds = zeros(68,2,n_frames);
    for f=1:n_frames
        input_image = readFrame(vid);

        %% Initialize from detected bounding boxes every 30 frames
        if (mod(f-1, 30) == 0)
            ind = min(f, size(bounding_boxes,1));
            bb = bounding_boxes(ind, :);
            % If no face detected use the closest detected BB
            if(bb(3) == 0)
               ind_next = ind + find(bounding_boxes(ind+1:end,3), 1);
               if(isempty(bounding_boxes(ind_next)) || bounding_boxes(ind_next,3)==0)
                   ind_next = find(bounding_boxes(1:ind,3), 1, 'last');
               end
               bb = bounding_boxes(ind_next, :);
            end
            bb(3) = bb(1) + bb(3);
            bb(4) = bb(2) + bb(4);
            reset = true;
        else
            reset = false;
        end

        % have a multi-view version for initialization, otherwise use
        % previous shape
        if(reset && multi_view)
            clmParams.window_size = [25,25; 23,23; 21,21; 21,21];
            clmParams.numPatchIters = 4;
            clmParams.startScale = 1;

            views = [0,0,0; 0,-30,0; 0,30,0; 0,-55,0; 0,55,0; 0,0,30; 0,0,-30; 0,-90,0; 0,90,0; 0,-70,40; 0,70,-40];
            views = views * pi/180;

            [shape,g_param,l_param,lhood,lmark_lhood,view_used] =...
                Fitting_from_bb_multi_hyp(input_image, [], bb, pdm, patches, clmParams, views, early_term_params);

        else
            clmParams.window_size = [23,23; 21,21; 19,19; 17,17];
            clmParams.numPatchIters = 3;
            clmParams.startScale = 2;
            [shape,g_param,l_param,lhood,lmark_lhood,view_used] = Fitting_from_bb(input_image, [], bb, pdm, patches, clmParams, 'gparam', g_param, 'lparam', l_param);
        end

        preds(:,:,f) = shape;

        %% plot the result
        imshow(input_image);
        hold on;
        plot(shape(:,1), shape(:,2), '.r');
%         rectangle('Position', [bb(2), bb(1), bb(4), bb(3)]);
        hold off;
        drawnow expose

    end

    %% Grab the ground truth
    gt_landmarks = gts_all{i};

    % Remove unreliable frames
    if(~isempty(invalid_frames{i}))
        preds(:,:,int32(invalid_frames{i}))=[];
    end

    if(size(gt_landmarks,3) ~= size(preds,3))
        fprintf('something went wrong with vid %d\n', i);
    end

    [vid_name,~,~] = fileparts(vid_locs{i});
    [~,vid_name,~] = fileparts(vid_name);
    save([output_dir, '/', vid_name], 'preds', 'gt_landmarks');
end


classdef PTKGetLeftLungROI < PTKPlugin
    % PTKGetLeftLungROI. Plugin for finding the left lung region of interest.
    %
    %     This is a plugin for the Pulmonary Toolkit. Plugins can be run using
    %     the gui, or through the interfaces provided by the Pulmonary Toolkit.
    %     See PTKPlugin.m for more information on how to run plugins.
    %
    %     Plugins should not be run directly from your code.
    %
    %     PTKGetLeftLungROI runs the library function PTKLeftAndRightLungs to
    %     segment the left and right lungs, and uses this to determine a region
    %     of interest for the left lung only.
    %
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2012.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    properties
        ButtonText = 'Left Lung <BR>ROI'
        ToolTip = 'Change the context to display the left lung'
        Category = 'Context'

        AllowResultsToBeCached = true
        AlwaysRunPlugin = false
        PluginType = 'ReplaceImage'
        HidePluginInDisplay = false
        FlattenPreviewImage = false
        PTKVersion = '1'
        ButtonWidth = 6
        ButtonHeight = 2
        GeneratePreview = true
        Visibility = 'Developer'

        MemoryCachePolicy = 'Permanent'
        DiskCachePolicy = 'Permanent'
    end

    methods (Static)
        function results = RunPlugin(dataset, reporting)
            roi = dataset.GetResult('PTKLungROI');
            left_and_right_lung_mask = dataset.GetResult('PTKLeftAndRightLungs');
            results = PTKGetLeftLungROIFromLeftAndRightLungs(roi, left_and_right_lung_mask, reporting);
        end
    end
end


% Set up axis and view

% Axis
ax = gca;
axis equal;
ax.NextPlot = 'add';
ax.Clipping = 'off';
ax.Box = 'off';
xlabel('X'); ylabel('Y'); zlabel('Z');
grid off;
if ~showAxis
	axis off;
end

% View
cameratoolbar('Show') % show camera toolbar
cameratoolbar('SetCoordSys', 'y')
camup(ground_plane_normal);
campos(C(:, 68));
if showAxis
    camva(50);
else
	camva(30);
end


% Script make_example_data
% Creates example data for uniQC-demos based on the ME-data in Heunis et al.
%
%  make_example_data
%
% NOTE: This script has to be run only once, in order to prepare some
%       example files for all uniqc-demos from a publicly available imaging
%       dataset.
%
% INSTRUCTIONS:
% 1. Please download the following data
%   (we only need subject sub-001 and sub-021) separately at
%    https://dataverse.nl/dataverse/rt-me-fmri.
%
%   from
%   Heunis, Stephan, 2020, "rt-me-fMRI: A task and resting state dataset for
%   real-time, multi-echo fMRI methods development and validation",
%   https://doi.org/10.34894/R1TNL8, DataverseNL, V1.
%
% 2. Unzip the downloaded subject folders
%
% 3. Update the paths in section "User Input" below, i.e.
%       a) where the downloaded, unzipped data resides (parent folder of sub-001)
%       b) where the generated uniqc example data files shall be stored
%          (for future use)
%
% 4. Run this script
%

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

clear; close all; clc;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% User Inputs
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% file path to the Heunis-example data (we only need sub-001)
% PLEASE MODIFY TO YOUR PATH
sourceDataPath = 'path/to/downloaded/me_sh_data';

% where do want to store the example data for uniQC - this is also the path
% you will be asked for when running the examples
% PLEASE MODIFY TO YOUR PATH
exampleDataPath = '/path/to/save/uniQC-examples';

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Load and plot source data
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
m = MrImage(fullfile(sourceDataPath, 'sub-001', 'func', ...
    'sub-001_task-fingerTapping_echo-2_bold.nii'));
m.plot();

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create small EPI data sets
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% nifti/rest/meanfmri.nii - just the mean of a times series (3D image)
meanfmri = m.mean();
meanfmri.parameters.save.path = fullfile(exampleDataPath, 'nifti', 'rest');
meanfmri.parameters.save.fileName = 'meanfmri.nii';
meanfmri.save();

% nifti/rest/fmri_short.nii - a short section of the fMRI time series to
% speed up processing in the demos
fmri_short = m.select('t', 1:15);
fmri_short.parameters.save.path = fullfile(exampleDataPath, 'nifti', 'rest');
fmri_short.parameters.save.fileName = 'fmri_short.nii';
fmri_short.save();

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Save anatomy
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
s = MrImage(fullfile(sourceDataPath, 'sub-001', 'anat', 'sub-001_T1w.nii'));
s.parameters.save.path = fullfile(exampleDataPath, 'nifti', 'rest');
s.parameters.save.fileName = 'struct.nii';
s.save();

% uniQC creates an extra mat file to keep track of any dimensions not saved
% in the nifti file, we delete it here to simulate the use case that we
% usually start only with a nifti
delete(fullfile(exampleDataPath, 'nifti', 'rest', '*.mat'));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create 5D file
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% nifti/5D/y_5d_deformation_field.nii
% this could for example be on of the deformation fields estimated during
% segmentation (the large samplingDistance makes this a quick and dirty
% version)
segmentInput = meanfmri.remove_dims.copyobj();
segmentInput.parameters.save.keepCreatedFiles = 'processed';
segmentInput.parameters.save.fileName = '5D.nii';
segmentInput.parameters.save.path = fullfile(exampleDataPath, 'nifti', '5D');

[~, ~, deformationFields] = segmentInput.remove_dims.segment(...
    'samplingDistance', 20, 'tissueTypes', {'GM'});
movefile(deformationFields{1}.get_filename, ...
    fullfile(exampleDataPath, 'nifti', '5D', 'y_5d_deformation_field.nii'));
delete(fullfile(exampleDataPath, 'nifti', '5D', 'bias*'));
delete(fullfile(exampleDataPath, 'nifti', '5D', 'tissue*'));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create split file
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% now load the complete multi-echo data set
meFileNames = dir(fullfile(sourceDataPath, 'sub-001', 'func', 'sub-001_task-fingerTapping_echo-*_bold.nii'));
for nFiles = 1:numel(meFileNames)
    % load files into temporary array
    meTmp{nFiles} = MrImage(fullfile(sourceDataPath, 'sub-001', 'func',...
        meFileNames(nFiles).name));
    % get echo time from json data
    metaInfo = jsondecode(fileread(fullfile(sourceDataPath, 'sub-001', 'func', ...
        regexprep(meFileNames(nFiles).name, '.nii', '.json'))));
    meTmp{nFiles}.dimInfo.add_dims('TE', ...
        'samplingPoints', metaInfo.EchoTime*1000, 'units', 'ms');
end

% combine into 5D image
me = meTmp{1}.combine(meTmp);

% save using the uniQC convention
% nift/split/subset/fmri_***.nii
meSplit = me.select('t', [1, 7, 8], 'TE', [1, 2]);
meSplit.parameters.save.path = fullfile(exampleDataPath, 'nifti', 'split', 'subset');
meSplit.parameters.save.fileName = 'fmri.nii';
meSplit.save('splitDims', {'t', 'TE'});
delete(fullfile(exampleDataPath,'nifti', 'split', 'subset', '*.mat'));

% nifti/data_multi_echo/multi_echo.nii
me.parameters.save.path = fullfile(exampleDataPath, 'nifti', 'data_multi_echo');
me.parameters.save.fileName = 'multi_echo.nii';
me.save();
delete(fullfile(exampleDataPath, 'nifti', 'data_multi_echo', '*.mat'));

% save without uniQC conventions
% /nifti/split/split_residual_images/Res.nii'
for n = 1:5
    currentM = me.select('t', n, 'TE', 1).remove_dims();
    currentM.parameters.save.path = fullfile(exampleDataPath, 'nifti', 'split', 'split_residual_images');
    currentM.parameters.save.fileName = ['Res_', num2str(n, '%04.0f'), '.nii'];
    currentM.save();
end
delete(fullfile(exampleDataPath, 'nifti', 'split', 'split_residual_images', '*.mat'));

% save with uniQC dimInfo
% /nifti/split/with_dim_info/MrImage.nii'
meSplitWith = me.select('t', [1, 7, 8]);
meSplitWith.parameters.save.path = fullfile(exampleDataPath, 'nifti', 'split', 'with_dim_info');
meSplitWith.parameters.save.fileName = 'MrImage.nii';
meSplitWith.save();

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create data for first level
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% /nifti/data_first_level/
% structural image
s.parameters.save.path = fullfile(exampleDataPath, 'nifti', 'data_first_level');
s.parameters.save.fileName = 'anatomy.nii';
s.save();

% third echo as functional image
fl = me.select('TE', 3).remove_dims();
fl.parameters.save.path = fullfile(exampleDataPath, 'nifti', 'data_first_level');
fl.parameters.save.fileName = 'single_echo.nii';
fl.save();
delete(fullfile(exampleDataPath, 'nifti', 'data_first_level', '*.mat'));


function varargout = cvx_values( varargin )
varargout = cvx_value( varargin );

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function y = vec(x)
    y = x(:);
end


function title = p10_title ( )

%*****************************************************************************80
%
%% P10_TITLE returns a title for problem 10.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    09 February 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, string TITLE, a title for the problem.
%
  title = 'The cosine combo';

  return
end


classdef SoITransitionUpIntTermCause < AbstractIntegrationTerminationCause
    %SoITransitionUpIntTermCause Summary of this class goes here
    %   Detailed explanation goes here

    properties
        fromBody KSPTOT_BodyInfo
        toBody KSPTOT_BodyInfo
        celBodyData
    end

    methods
        function obj = SoITransitionUpIntTermCause(fromBody, toBody, celBodyData)
            obj.fromBody = fromBody;
            obj.toBody = toBody;
            obj.celBodyData = celBodyData;
        end

        function tf = shouldRestartIntegration(obj)
            tf = true;
        end

        function newStateLogEntry = getRestartInitialState(obj, stateLogEntry)
            bodyInfo = stateLogEntry.centralBody;
            ut = stateLogEntry.time;
            rVect = reshape(stateLogEntry.position,1,3);
            vVect = reshape(stateLogEntry.velocity,1,3);

            [rVectUp, vVectUp] = convertRVVectOnUpwardsSoITransition(bodyInfo, obj.celBodyData, ut, rVect, vVect);

            dcm = stateLogEntry.attitude.dcm;

            newStateLogEntry = stateLogEntry.deepCopy();
            newStateLogEntry.position = rVectUp;
            newStateLogEntry.velocity = vVectUp;
            newStateLogEntry.centralBody = obj.toBody;
            newStateLogEntry.steeringModel = stateLogEntry.steeringModel.deepCopy();

            if(newStateLogEntry.steeringModel.requiresReInitAfterSoIChange())
                newStateLogEntry.steeringModel.setContinuityTerms(true, true, true);
                newStateLogEntry.steeringModel.setConstsFromDcmAndContinuitySettings(dcm, ut, rVectUp, vVectUp, obj.toBody);
                newStateLogEntry.steeringModel.setT0(ut);
            end

            newStateLogEntry.lvState.clearCachedConnEnginesTanks();
        end
    end
end


 function out = subsref(ob, args)
%function out = subsref(ob, args)
%	evaluate call of the form ob.ref or ob(ref,:)
%	fix: needs a lot of work to handle ordered subsets!

%
%	enable handling of multiple subscripts, e.g., ob.field()
%
if length(args) > 1
	arg = args(1);
else
	arg = args;
end

%
%	ob.?
%
if arg.type == '.'
	out = getfield(struct(ob), arg.subs);

%
%	ob(?)
%
elseif arg.type == '()'
	subs = arg.subs;

	if ~isempty(ob.index1) | ~isempty(ob.index2)
		error 'subscripts after subscripts perhaps not done'
	end

	%
	%	G(:,:)
	%
	if length(subs) == 2 & subs{1} == ':' & subs{2} == ':'
		out = ob(:,[1:ob.dims(2)]);

	%
	%	Gt(:,ii) - for ordered subsets
	%
	elseif length(subs) == 2 & subs{1} == ':' ...
		& ndims(subs{2}) == 2 & ob.is_transpose
		nb = ob.nb;
		ii = subs{2};
		ia = 1 + round((ii(1,:) - 1) / nb);
		if any(ii(:) ~= col(outer_sum(1:nb,(ia-1)*nb)))
			error 'bad subref'
		end
		ob.ia_start = ia(1) - 1;
		ob.ia_inc = ia(2) - ia(1);
		if any(diff(ia) ~= ob.ia_inc)
			error 'nonuniform angles'
		end
		ob.is_subset = true;
		out = ob;

	%
	%	G(:,j)
	%
	elseif length(subs) == 2 & subs{1} == ':'
		jj = col(subs{2});
		if islogical(jj)
			if length(jj) ~= ob.dims(2)
				error 'bad column logical length'
			end
		elseif any(jj < 1 | jj > ob.dims(2))
			error 'bad column index'
		end

		%
		%	handle G(:,mask(:)) as a special case
		%
		if length(jj) == ob.nx*ob.ny & all(jj == ob.mask(:)) ...
				& ~ob.is_masked & ~ob.is_transpose
			ob.is_masked = true;
			ob.dims(2) = sum(ob.mask(:));
			out = ob;

		else
			out = zeros(ob.dims(1),length(jj));
			for nn=1:length(jj)
				x = zeros(ob.dims(2),1);
				x(jj(nn)) = 1;
				out(:,nn) = ob * x;
			end
			out = sparse(out);
		end

	%
	%	G(i,:)
	%
	elseif length(subs) == 2 & subs{2} == ':'
		ii = col(subs{1});
		if islogical(ii)
			if length(ii) ~= ob.dims(1)
				error 'bad row logical length'
			end
		elseif any(ii < 1 | ii > ob.dims(1))
			error(sprintf('bad row index %s', int2str(ii)))
		end

		out = zeros(ob.dims(2), length(ii));
		for nn=1:length(ii)
			y = zeros(ob.dims(1), 1);
			y(ii(nn)) = 1;
			out(:,nn) = ob' * y;
		end
		out = sparse(out);

	else
		arg.subs
		error todo
	end

else
	error(sprintf('type %s notdone', arg.type))
end

if length(args) > 1
	out = subsref(out, args(2:end));
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Released under the MIT License.
% If you use this code, please cite the following paper:
% Mahmoud Afifi, Abdelrahman Abdelhamed, Abdullah Abuolaim, Abhijith
% Punnappurath, and Michael S Brown. CIE XYZ Net: Unprocessing Images for
% Low-Level Computer Vision Tasks. arXiv preprint, 2020.
%
% Author: Mahmoud Afifi | Email: mafifi@eecs.yorku.ca, m.3afifi@gmail.com
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function image = inRead(fileName)

image = im2double(imread(fileName));

end


function ind_cell=run_movie(Y, A, C, Cn, min_max, Coor, ctr,  Ncell, tskip, save_avi, avi_name, S)
%% play movies together with selected neurons' calcium traces. It can be used for checking results
%% Input:
%
%% output

%% Author: Pengcheng Zhou, Carnegie Mellon University, 2016
%% parameters
[d1,d2] = size(Cn);
if ismatrix(Y); Y=reshape(Y, d1, d2, []); T=size(Y, 3); else T=size(Y, ndims(Y)); end
if ~exist('save_avi', 'var');    save_avi=false;
elseif ~exist('avi_name', 'var')
    k=0; avi_name = sprintf('example_movie_%d', k);
    while exist(avi_name, file);  k=k+1; avi_name = sprintf('example_movie_%d', k); end
end
if ~exist('tskip', 'var')||isempty(tskip);    tskip=1; end
if ~exist('Ncell', 'var')||isempty(Ncell);    Ncell=5; end
if ~exist('Coor', 'var')||isempty(Coor); figure; Coor=plot_contours(A, Cn, 0.9, 0); close; end
if ~exist('ctr', 'var')|| isempty(ctr); ctr=com(A, d1, d2); end
if ~exist('min_max', 'var') || (isempty(min_max));
    temp = Y(:, :, randi(T, min(100, T), 1));
    min_max = quantile(temp(:), [0.2, 0.9999]);
    min_max(1) = max(min_max(1), 0);
end
ncol = 3;
%% select neurons by clicking the image.
figure('position', [100, 500, 1800, 420]);

% display correlation image for selecting neurons
subplot(Ncell, ncol, 1:ncol:(ncol*Ncell));
imagesc(Cn, [0, 1]);hold on;
axis equal; axis off;
ind_cell = zeros(Ncell,1);
m = 1;
while true
    subplot(Ncell, ncol, 1:ncol:(Ncell*ncol));
    drawnow;
    [x, y] = ginput(1);
    h_dot = plot(x, y, '*m');
    dx = x-ctr(:, 2);
    dy = y-ctr(:, 1);
    dist_xy = sqrt(dx.^2+dy.^2);
    [~, ind_cell(m)] = min(dist_xy);
    temp = Coor{ind_cell(m)};
    h_con = plot(temp(1, 4:end), temp(2, 4:end), 'r');
    h_txt = text(ctr(ind_cell(m), 2), ctr(ind_cell(m), 1), num2str(m), 'fontsize', 10, 'fontweight', 'bold');
    disp(ctr(ind_cell(m), :));
    subplot(Ncell, ncol, ncol*(m-1)+[2, ncol]);cla;
    ylabel(num2str(m));
    plot(C(ind_cell(m), :)); hold on;
    ylabel(sprintf('ID %d', ind_cell(m)));
    if (exist('S', 'var'))&&  (~isempty(S))
        tmp = find(S(ind_cell(m), :)>0);
        plot(tmp, S(ind_cell(m), tmp), '*g');
    end
    set(gca, 'xticklabel', []); axis tight;
    set(gca, 'yticklabel', []);
    xlim([1, T]);

    temp = input('keep the neuron? (y/n)   ', 's');
    if strcmp(temp, 'n')
        delete(h_dot); delete(h_con); delete(h_txt);
        continue;
    else
        ylabel(num2str(m));
        m =  m+1;
    end

    if m>Ncell; break; end
end
subplot(Ncell, ncol, ncol*Ncell+[-1,0]);
set(gca, 'xticklabel', get(gca, 'xtick'));
xlabel('Frame');

%% play movies
% create avi file if sve_avi==true
if save_avi
    avi_file = VideoWriter(avi_name);
    open(avi_file);
end
% draw selected neurons' contours and latex them
subplot(Ncell,ncol,1:ncol:(ncol*Ncell)); cla;  hold on; axis equal; axis off;
for m=1:Ncell
    temp = Coor{ind_cell(m)};
    plot(temp(1, 4:end), temp(2, 4:end), 'r');
    text(ctr(ind_cell(m), 2)+3*randn(1), ctr(ind_cell(m), 1)+3, num2str(m), 'fontsize', ...
        15, 'fontweight', 'bold', 'color', 'g');
end
% play movie
for t=1:tskip:T
    subplot(Ncell,ncol,1:ncol:(ncol*Ncell));
    h_img = imagesc(Y(:, :, t), min_max);
%     colormap gray;
    hold on; axis equal; axis off;
    set(gca, 'children', flipud(get(gca, 'children')));

    % shift the red line indicating the current frame
    for m=1:Ncell
        subplot(Ncell, ncol, ncol*(m-1)+[2, ncol]);
        eval(sprintf('aa%d=plot([t,t], get(gca, ''ylim''), ''r''); ',m));
    end

    % save frame
    if save_avi
        frame = getframe(gcf);
        writeVideo(avi_file, frame);
    end
    drawnow();

    % remove red lines
    for m=1:Ncell
        eval(sprintf('delete(aa%d); ', m));
    end
    delete(h_img);
end
subplot(Ncell,ncol,1:ncol:(ncol*Ncell)); hold on; axis equal; axis off;
imagesc(Cn, [0, 1]);
set(gca, 'children', flipud(get(gca, 'children')));

if save_avi;  close(avi_file);end


function [fh yMin, yMax] = tapas_uniqc_plot_abs_image(Y,iDynSli,fh, yMin, yMax, ...
    colorMap, colorBar)
%simple plotting routine for one dynamic within a 3D-dataset
%
%   [fh yMin, yMax] = tapas_uniqc_plot_image_diagnostics(Y,iDynSli,fh, yMin, yMax)
%
% IN
%   Y           [nVoxelX nVoxelY, nSlices, nVolumes] real valued data matrix
%               OR
%               [nSamples, nCoils, nDynSlis] real-valued data matrix
%   iDynSli     index of which dynamic/slice shall be plotted
%   fh          figure handle to be plotted into; if empty or missing, new figure is
%               created
%   yMin        [nPlots,1] min value in ylim, one for each set of coils, if
%               empty or missing, newly created from data limits
%   yMax        [nPlots,1] max value in ylim, one for each set of coils, if
%               empty or missing, newly created from data limits
%
% OUT
%   fh          figure handle plotted into
%   yMin        [nPlots,2] min value in ylim, one for each set of coils,
%               abs and phase
%   yMax        [nPlots,2] max value in ylim, one for each set of coils
%               abs and phase
% EXAMPLE
%   [fh yMin, yMax] = tapas_uniqc_plot_image_diagnostics(Y,iDynSli,fh, yMin, yMax, coilPlots)
%
%   See also plotTrajDiagnostics guiTrajDiagnostics

% Author: Lars Kasper
% Created: 2013-01-06
% Copyright 2013 Institute for Biomedical Engineering, ETH/Uni Zurich.

if nargin < 3 || isempty(fh)
    fh = figure('Name','Video of ImageDiagnostics', 'WindowStyle', 'normal');
else
    figure(fh);
end

% determine plot limits
if nargin < 5 || isempty(yMin)
    validIndices = find(~isinf(Y) & ~isnan(Y));
    yMin = min(Y(validIndices));
    yMax = max(Y(validIndices));
end

if nargin < 6
    colorMap = 'gray';
end

if nargin < 7
    colorBar = 'off';
end

doPlotColorBar = strcmpi(colorBar, 'on');

% plot abs data always
imagesc(Y(:,:,iDynSli));
%colormap gray; axis image;
colormap(colorMap); axis image;

caxis([yMin, yMax]);

if doPlotColorBar
    colorbar;
end

stringTitle = sprintf('abs, iDynSli = %d', iDynSli);
if exist('suptitle', 'builtin')
    suptitle(stringTitle);
else
    title(stringTitle);
end

end


function [ExcelID,WorkbookID,SheetNum,shapeID,imageHandleList]=excelReport(view,comment,ExcelID,WorkbookID,SheetNum,imSize,startPos)
%
% [ExcelID,WorkbookID,SheetNum,shapeID,imageHandleList]= ...
%                 excelReport(comment,ExcelID,WorkbookID,SheetNum,imSize,startPos)
%
% PURPOSE: Automates the generation of an Excel spreadsheet with
% Screenshots of the current datatype / scan
% Assumes that mrmesh has been invoked to visualize the data

mrGlobals;

meshTypes={'Folded','Relaxed'};
if (~exist('imSize'))
imSize=[200 200];
end
if (~exist('startPos'))
startPos=[20 20];
end

% Script to generate screenshots
% Saves in current dir:

viewList={'back','left','right','bottom','top'};
viewVectors={[pi -pi/2 0],[pi 0 0],[0 0 pi],[pi/2 -pi/2 0],[-pi/2 -pi/2 0]};
view=getSelectedVolume;

idList = viewGet(view,'allwindowids');
thisID=view.meshNum3d;
% Set up an excel worksheet to accept the data
% If no handle has been passed, we initiate a new instance of excel

if (~exist('ExcelID','var'))
    ExcelID = actxserver('Excel.Application');
end

if (isempty(ExcelID))
    ExcelID = actxserver('Excel.Application');
end
disp(ExcelID)

set(ExcelID, 'Visible', 1);

if (~exist('WorkbookID','var'))
         Workbooks = ExcelID.Workbooks;
         WorkbookID = invoke(Workbooks, 'Add');
end
if (isempty(WorkbookID))
         Workbooks = ExcelID.Workbooks;
         WorkbookID = invoke(Workbooks, 'Add');
end

if (~exist('SheetNum','var'))
    SheetNum=1;

end

         Sheets = ExcelID.ActiveWorkBook.Sheets;
         SheetID = get(Sheets, 'Item', SheetNum);

        invoke(SheetID, 'Activate');

ActivesheetID = ExcelID.Activesheet;
shapeID=WorkbookID.ActiveSheet.Shapes

for thisView=1:length(viewList);
    cam.actor=0;
    cam.rotation=rotationMatrix3d(viewVectors{thisView})
    mrMesh('localhost',thisID,'set',cam)
    date_string=datestr(now,30);
    dt=dataTYPES(view.curDataType).name;
    scanNum=int2str(getCurScan(view));
    %mt=meshTypes{v.meshNum3d};
    filename=fullfile(pwd,[dt,'_',scanNum,'_',date_string,'_',viewList{thisView},'.bmp'])
    c.filename=fullfile(pwd,'test.bmp');

    pause(2);
    [a,b,result]=mrMesh('localhost',thisID,'screenshot',c)
    pause(5);
    disp(thisView)

     a=invoke(shapeID,'AddPicture',c.filename,1,1,startPos(1),startPos(2),imSize(1),imSize(2));
%    set(a,'AlternativeText',[comment,' : ',filename]);

    startPos(1)=startPos(1)+imSize(1);
    imageHandleList(thisView)=a;

end

% We can also copy and paste figures from matlab to the excel spreadsheet.

% To copy a figure from matlab try : print -dmeta