% POP_FINDMATCHINGCOMPS - Find components with the highest 
%                absolute scalp map correlation to components provided or tagged for
%                rejection in another dataset.
%
% % Usage:
%       >> OUTEEG = pop_findmatchingcomps( INEEG, 'key', val, . . . );
%
% Inputs:
%   INEEG    - Input dataset
%
% Optional input:
%   'dataset'      - Another EEGLAB dataset. Find components in this dataset matching 
%                  those tagged for rejection in INEEG (e.g., those components
%                  marked manually in the other dataset using menu item
%                  "Tools > Reject data using ICA > Reject components by map")
%                  Else, ALLEEG index (integer) of another such dataset.
%   'corrthresh'   - [0<float<=1] minimal absolute matching correlation.
%                  Default is 0.92.
%   'matchcomps'   - [float array] scalp maps (as in icawinv) of components
%                  from another decomposition or dataset to be matched to the
%                  current EEG set decomposition.
%                  Several components may be provided as input (as different
%                  columns or different rows). Default is none.
%   'rejflag'      - [0|1] Flag to tag matching components for rejection. 
%                  Default is [0] do not tag for rejection.  
%   'nomatcherror' - ['on'|'off'] trigger error if the tagged component maps
%                  are not found.  Default is 'off'.
%
% Output:
%   OUTEEG      - Output dataset with matching artifact components tagged
%                 for rejection in OUTEEG.reject.gcompreject (if rejflag
%                 option set to 1)
%   matchic     - Indices of matching components
%   matchinput  - Indices of 'matchcomps' matching components in output
%                 mathcic. This output will be empty if option 'dataset' is
%                 used.
%
% Author: Arnaud Delorme, 2017
%
% See also: matcorr.m

% Copyright (C) 2017 Arnaud Delorme, UCSD, arno@ucsd.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

% 01-25-02 reformatted help & license -ad 

function [EEG, matchic, matchinput, com] = pop_findmatchingcomps( EEG, varargin)

com = '';
if nargin < 2
    
     uilist = { { 'style'  'text'     'string'   'Index of dataset to find matching artifact comps.' } ...
               { 'style'   'edit'     'string'    '' } ...
               { 'style'   'text'     'string'    'or scalp maps (array or Matlab variable) from which to find matching artifact comps.' } ...
               { 'style'   'edit'     'string'    '' } ...
               { 'style'   'text'     'string'    '' } ...
               { 'style'   'text'     'string'    'Minimum abs. correlation for matching components' } ...
               { 'style'   'edit'     'string'    '0.92' }           };
    uigeom = { [1.5 0.5] [1] [1] [1] [1.5 0.5] };
    result = inputgui( 'geometry', uigeom, 'uilist', uilist, 'helpcom', 'pophelp(''pop_findmatchingcomps'')', ...
                       'title', 'pop_findmatchingcomps - find matching components marked for rejection', 'mode', 'normal', 'geomvert', [1 1 2 1 1]);
    if isempty(result), return; end
    options = { 'dataset' str2num(result{1}) 'matchcomps' str2num( result{2} ) 'corrthresh' str2double( result{3} ) };
else
    options = varargin;
end

% decode input parameters
% -----------------------
g = finputcheck( options, { 'dataset'       ''         []         [];
                            'corrthresh'    'float'    [0 1]      0.92;
                            'matchcomps'    'float'    []         [];
                            'rejflag'       'float'    [0 1]      [0];
                            'verbose'       'string' { 'on' 'off' } 'on'
                            'nomatcherror'  'string' { 'on' 'off' } 'off' }, 'pop_findmatchingcomps');
if isstr(g), error(g); end

if isempty(g.dataset) && isempty(g.matchcomps)
    error('Either an ALLEG dataset index or set of component scalp maps must be provided as input.');
end

if ~isempty(g.dataset) && ~isempty(g.matchcomps)
    error('Both option can not be provided as input.');
end
outputflag = ~isempty(g.matchcomps); %Checking if input provided for later use
    
% find components in current dataset matching components tagged for rejection 
% in the other dataset
if ~isempty(g.dataset)
    if isstruct(g.dataset)
        TMPEEG = g.dataset;
    else
        global ALLEEG;
        if g.dataset < 1 || g.dataset > length(ALLEEG)
            error('Dataset index out of range');
        end
        TMPEEG = ALLEEG(g.dataset);
    end
    if isempty(TMPEEG.icawinv)
        error(sprintf('Dataset %d does not contain component maps (icawinv)',...
					g.dataset));
    end
    if sum(TMPEEG.reject.gcompreject) == 0
        error( [ 'Matching dataset does not contain components marked for rejection.' 10 ...
               'Follow menu item Tools > Reject data using ICA > Reject components by map' 10 ...
               'to tag non-brain artifact components in that dataset' ] );
    end
        
    g.matchcomps = TMPEEG.icawinv(:,find(TMPEEG.reject.gcompreject));
end

% find matching component
if size(EEG.icawinv,1) ~= size(g.matchcomps,1) && size(EEG.icawinv,1) ~= size(g.matchcomps,2)
    error( [  'The tagged components do not have the same number of' 10 ...
              'channels as those in the current dataset' ]);
end
if size(g.matchcomps, 1) ~= size(EEG.icawinv, 1)
    g.matchcomps = g.matchcomps';
end
[corr,indx] = matcorr(EEG.icawinv', g.matchcomps');
corrSelected  = corr(1:size(g.matchcomps,2)) > g.corrthresh;
matchic    = indx(corrSelected);
if outputflag, matchinput = find(corrSelected); else, matchinput = []; end
res = sprintf('Matched %d of %d tagged components using abs. correlation threshold %1.2f', sum(corrSelected), length(corrSelected), g.corrthresh);
if strcmpi(g.verbose, 'on')
    fprintf([ res '\n' ]);
end
if sum(corrSelected) < length(corrSelected) && strcmpi(g.nomatcherror, 'on')
    error([ res 10 'Not all tagged components were matched.' ]);
end
if g.rejflag
    EEG.reject.gcompreject(matchic) = 1;
end

if nargout > 1
    if ~isempty(g.dataset)
        com = sprintf('EEG = pop_findmatchcomps(EEG, ''dataset'', ALLEEG(%d), ''corrthresh'', %1.4f, ''rejflag'',%1.4f);', g.dataset, g.corrthresh,g.rejflag);
    else
        com = sprintf('EEG = pop_findmatchgcomps(EEG, %s);', vararg2str(options));
    end
end
