% POP_CHANCORESP - define correspondences between two channel locations structures
%                    (EEG.chanlocs) automatically (by matching channel labels) 
%                    else using a user input gui.
% Usage:
%   >> [chanlist1 chanlist2] = pop_chancoresp(chanstruct1, chanstruc2, 'key', 'val', ...); 
%
% Inputs:
%   chanstruct1     - first (new) channel locations structure (EEG.chanlocs). 
%                      For details, >> help readlocs
%   chanstruct2     - second (reference) chanlocs structure.
%
% Optional parameters:
%   'gui'           - ['on'|'off'] display gui or not ('on' -> yes)
%   'autoselect'    - ['none'|'fiducials'|'all'] automatically pair channels
%   'chaninfo1'     - EEG.chaninfo structure for first (new) EEG.chanlocs
%   'chaninfo2'     - EEG.chaninfo structure for second (reference) EEG.chanlocs
%   'chanlist1'     - [integer] selected channel to pair in the graphic interface
%                     for the first channel structure. This requires the input 
%                     'chanlist2' below.
%   'chanlist2'     - [integer] selected channel to pair in the graphic interface
%                     for the first channel structure. This requires the input 
%                     requires the input 'chanlist1' that must be of the same length.
% Output:
%   chanlist1       - [int vector] indices of selected channels from first (new) EEG.chanlocs
%   chanlist2       - [int vector] selected channels from second (reference) EEG.chanlocs
%
% Author: Arnaud Delorme, CNL / Salk Institute, 2005

% Copyright (C) 2005 Arnaud Delorme, Salk Institute, arno@salk.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.

function [chanlistout1, chanlistout2, thirdout, outfourth] = pop_chancoresp(chans1, chans2, varargin); 
    
    if nargin < 2
        help pop_chancoresp;
        return;
    end
    chanlistout1 = [];
    chanlistout2 = [];
    
    % process sub command
    % -------------------
    if ischar(chans1) 
       if strcmpi(chans1, 'pair')
           [chanlistout1, chanlistout2, thirdout, outfourth] = pair(chans2, varargin{:});
       elseif strcmpi(chans1, 'unpair')
           [chanlistout1, chanlistout2, thirdout, outfourth] = unpair(chans2, varargin{:});
       elseif strcmpi(chans1, 'clear')
           [chanlistout1, chanlistout2, thirdout, outfourth] = clearchans(chans2, varargin{:});
       elseif strcmpi(chans1, 'auto')
           [chanlistout1, chanlistout2, thirdout, outfourth] = autoselect(chans2, varargin{:});
       end
       return;
    end
    
    g = finputcheck(varargin, { 'autoselect'     'string'  {'none';'fiducials';'all'}   'all';
                                'chanlist1'      'integer' [1 Inf] []; 
                                'chanlist2'      'integer' [1 Inf] []; 
                                'chaninfo1'      ''        []      struct([]); 
                                'chaninfo2'      ''        []      struct([]); 
                                'gui'            'string'  { 'on';'off' }  'on' } );
    if ischar(g), error(g); end
    g.chanstruct1 = chans1;
    g.chanstruct2 = chans2;
    if length(g.chanlist1) ~= length(g.chanlist2)
        error('input arguments ''chanlist1'' and ''chanlist2'' must have the same length');
    end
    
    % decode different input formats
    % ------------------------------
    if isstruct(chans1)
        if isfield(chans1, 'label') % fieldtrip
            chanstr1 = chans1.label;
            chanstr2 = chans2.label;
        else % EEGLAB
            chanstr1 = { chans1.labels };
            chanstr2 = { chans2.labels };
        end
    else % only channel labels
        chanstr1 = chans1;
        chanstr2 = chans2;
    end
    
    % convert selection to integer
    % ----------------------------
    if isempty(g.chanlist1)
        if strcmpi(g.autoselect, 'fiducials')
            % find fiducials in both channel location strustures
            % --------------------------------------------------
            naz1 = strmatch('nz', lower( chanstr1 ), 'exact'); if isempty(naz1), naz1 = strmatch('nasion', lower( chanstr1 ), 'exact'); end; if isempty(naz1), naz1 = strmatch('fidnz', lower( chanstr1 ), 'exact'); end
            naz2 = strmatch('nz', lower( chanstr2 ), 'exact'); if isempty(naz2), naz2 = strmatch('nasion', lower( chanstr2 ), 'exact'); end; if isempty(naz2), naz2 = strmatch('fidnz', lower( chanstr2 ), 'exact'); end
            lpa1 = strmatch('lpa', lower( chanstr1 ), 'exact'); if isempty(lpa1), lpa1 = strmatch('left',  lower( chanstr1 ), 'exact'); end; if isempty(lpa1), lpa1 = strmatch('fidt10', lower( chanstr1 ), 'exact'); end
            lpa2 = strmatch('lpa', lower( chanstr2 ), 'exact'); if isempty(lpa2), lpa2 = strmatch('left',  lower( chanstr2 ), 'exact'); end; if isempty(lpa2), lpa2 = strmatch('fidt10', lower( chanstr2 ), 'exact'); end
            rpa1 = strmatch('rpa', lower( chanstr1 ), 'exact'); if isempty(rpa1), rpa1 = strmatch('right', lower( chanstr1 ), 'exact'); end; if isempty(rpa1), rpa1 = strmatch('fidt9', lower( chanstr1 ), 'exact'); end
            rpa2 = strmatch('rpa', lower( chanstr2 ), 'exact'); if isempty(rpa2), rpa2 = strmatch('right', lower( chanstr2 ), 'exact'); end; if isempty(rpa2), rpa2 = strmatch('fidt9', lower( chanstr2 ), 'exact'); end
            g.chanlist1 = [ naz1 lpa1 rpa1 ];
            g.chanlist2 = [ naz2 lpa2 rpa2 ];
            if length(g.chanlist1) ~= length(g.chanlist2) || length(g.chanlist1) == 0
                disp('Warning: could not find fiducials in at least one of the channel location structure');
                g.chanlist1 = [];
                g.chanlist2 = [];
            end
        elseif strcmpi(g.autoselect, 'all')
            % find common channels in both channel location strustures
            % --------------------------------------------------------
            chanstr2low = lower( chanstr2 );
            chanstr1low = lower( chanstr1 );
            for index = 1:length( chanstr1 )
                ind = strmatch(chanstr1low{index}, chanstr2low, 'exact' );
                if ~isempty(ind)
                    g.chanlist1(end+1) = index;
                    g.chanlist2(end+1) = ind;
                end
            end
        end
    end
    
    % plot
    % ----
    if strcmpi(g.gui, 'off')
        chanlistout1 = g.chanlist1;
        chanlistout2 = g.chanlist2;
        return;
    end
    try,  g.promptstring;  catch, g.promptstring = ''; end
    try,  g.selectionmode; catch, g.selectionmode = 'multiple'; end
    try,  g.listsize;      catch, g.listsize = []; end
    try,  g.initialvalue;  catch, g.initialvalue = []; end
    try,  g.name;          catch, g.name = ''; end
	g.chanstr1 = chanstr1;
	g.chanstr2 = chanstr2;

    fig = figure('visible', 'off');
    set(fig, 'name', 'Pair channels - pop_chancoresp');
    
    % make text for list
    % ------------------
    [ g.newchanstr1 g.newchanstr2 ] = makelisttext( chanstr1, chanstr2, g.chanlist1, g.chanlist2);
    
    % callback for paring and unpairing
    % ---------------------------------
    cb_pair = [ 'tmpdat = get(gcbf, ''userdata'');' ...
                'tmpval1 = get(findobj(gcbf, ''tag'', ''list1''), ''value'');' ...
                'tmpval2 = get(findobj(gcbf, ''tag'', ''list2''), ''value'');' ...
                '[tmpdat.newchanstr1{tmpval1}, tmpdat.newchanstr2{tmpval2}, tmpdat.chanlist1, tmpdat.chanlist2] = pop_chancoresp(''pair'', tmpval1, tmpval2, tmpdat.chanstr1, tmpdat.chanstr2, tmpdat.chanlist1, tmpdat.chanlist2, tmpdat.newchanstr1{tmpval1}, tmpdat.newchanstr2{tmpval2});' ...
                'set(findobj(gcbf, ''tag'', ''list1''), ''string'', tmpdat.newchanstr1);' ...
                'set(findobj(gcbf, ''tag'', ''list2''), ''string'', tmpdat.newchanstr2);' ...
                'set(gcbf, ''userdata'', tmpdat);' ...
                'clear tmpdat tmpval1 tmpval2;' ];
    cb_unpair = [ 'tmpdat = get(gcbf, ''userdata'');' ...
                'tmpval1 = get(findobj(gcbf, ''tag'', ''list1''), ''value'');' ...
                'tmpval2 = get(findobj(gcbf, ''tag'', ''list2''), ''value'');' ...
                '[tmpdat.newchanstr1{tmpval1}, tmpdat.newchanstr2{tmpval2}, tmpdat.chanlist1, tmpdat.chanlist2] = pop_chancoresp(''unpair'', tmpval1, tmpval2, tmpdat.chanstr1, tmpdat.chanstr2, tmpdat.chanlist1, tmpdat.chanlist2, tmpdat.newchanstr1{tmpval1}, tmpdat.newchanstr2{tmpval2});' ...
                'set(findobj(gcbf, ''tag'', ''list1''), ''string'', tmpdat.newchanstr1);' ...
                'set(findobj(gcbf, ''tag'', ''list2''), ''string'', tmpdat.newchanstr2);' ...
                'set(gcbf, ''userdata'', tmpdat);' ...
                'clear tmpdat tmpval1 tmpval2;' ];
    cb_plot1 = [ 'tmpdat = get(gcbf, ''userdata'');' ...
                 'figure; topoplot([], tmpdat.chanstruct1, ''style'', ''blank'', ''drawaxis'', ''on'', ' ...
                 '''electrodes'', ''labelpoint'', ''chaninfo'', tmpdat.chaninfo1, ''headrad'', 0);' ];
    cb_plot2 = [ 'tmpdat = get(gcbf, ''userdata'');' ...
                 'figure; topoplot([], tmpdat.chanstruct2, ''style'', ''blank'', ''drawaxis'', ''on'', ' ...
                 '''electrodes'', ''labelpoint'', ''chaninfo'', tmpdat.chaninfo2, ''headrad'', 0);' ];
    cb_list1 = [ 'tmpdat = get(gcbf, ''userdata'');' ...
                'tmpval = get(findobj(gcbf, ''tag'', ''list1''), ''value'');' ...
                'tmppos = find(tmpdat.chanlist1 == tmpval);' ...
                'if ~isempty(tmppos), set(findobj(gcbf, ''tag'', ''list2''), ''value'', tmpdat.chanlist2(tmppos)); end;' ...
                'clear tmpdat tmpval tmppos;' ];
    cb_list2 = [ 'tmpdat = get(gcbf, ''userdata'');' ...
                'tmpval = get(findobj(gcbf, ''tag'', ''list2''), ''value'');' ...
                'tmppos = find(tmpdat.chanlist2 == tmpval);' ...
                'if ~isempty(tmppos), set(findobj(gcbf, ''tag'', ''list1''), ''value'', tmpdat.chanlist1(tmppos)); end;' ...
                'clear tmpdat tmpval tmppos;' ];
    cb_clear = [ 'tmpdat = get(gcbf, ''userdata'');' ...
                 '[tmpdat.newchanstr1, tmpdat.newchanstr2, tmpdat.chanlist1, tmpdat.chanlist2] = pop_chancoresp(''clear'', tmpdat.chanstr1, tmpdat.chanstr2);' ...
                 'set(gcbf, ''userdata'', tmpdat);' ...
                 'set(findobj(gcbf, ''tag'', ''list1''), ''string'', tmpdat.newchanstr1);' ...
                 'set(findobj(gcbf, ''tag'', ''list2''), ''string'', tmpdat.newchanstr2);' ...
                 'set(gcbf, ''userdata'', tmpdat);' ...
                 'clear tmpdat;' ];
    cb_auto  = [ 'tmpdat = get(gcbf, ''userdata'');' ...
                 '[tmpdat.newchanstr1, tmpdat.newchanstr2, tmpdat.chanlist1, tmpdat.chanlist2] = pop_chancoresp(''auto'', tmpdat.chanstr1, tmpdat.chanstr2, tmpdat.newchanstr1, tmpdat.newchanstr2, tmpdat.chanlist1, tmpdat.chanlist2);' ...
                 'set(gcbf, ''userdata'', tmpdat);' ...
                 'set(findobj(gcbf, ''tag'', ''list1''), ''string'', tmpdat.newchanstr1);' ...
                 'set(findobj(gcbf, ''tag'', ''list2''), ''string'', tmpdat.newchanstr2);' ...
                 'set(gcbf, ''userdata'', tmpdat);' ...
                 'clear tmpdat;' ];
    geometry = {1 [1 1] [1 1] [1 1] [1 1] [1 1]};
    geomvert = [ 1 1 min(max(length(chanstr1),length(chanstr2)), 10) 1 1 1];
    listui = { ...
              { 'Style', 'text', 'string', 'Select corresponding channels to pair in the lists below' } ...
              { 'Style', 'pushbutton', 'string', 'Plot new montage', 'callback', cb_plot1 }  ...
              { 'Style', 'pushbutton', 'string', 'Plot ref montage', 'callback', cb_plot2 }  ...
              { 'Style', 'listbox', 'tag', 'list1', 'string', g.newchanstr1, 'value', 1, 'min', 1, 'max', 2, 'callback', cb_list1 } ...
              { 'Style', 'listbox', 'tag', 'list2', 'string', g.newchanstr2, 'value', 1, 'min', 1, 'max', 2, 'callback', cb_list2 } ...
              { 'Style', 'pushbutton', 'string', 'Pair channels'  , 'callback', cb_pair } ...     
              { 'Style', 'pushbutton', 'string', 'Clear this pair', 'callback', cb_unpair } ...     
              { 'Style', 'pushbutton', 'string', 'Clear all pairs' , 'callback', cb_clear } ...     
              { 'Style', 'pushbutton', 'string', 'Auto select', 'callback', cb_auto } ...     
              { 'Style', 'pushbutton', 'string', 'Cancel', 'callback', 'close(gcbf);' }  ...
              { 'Style', 'pushbutton', 'string', 'Ok'    , 'tag', 'ok', 'callback', ['set(gcbo, ''userdata'', ''ok'');'] } };

    [tmp, tmp2, allobj] = supergui( fig, geometry, geomvert, listui{:} );
    set(fig, 'userdata', g);
    
    % decode output
    % -------------
    okbut = findobj( 'parent', fig, 'tag', 'ok');
    figure(fig);
    drawnow;
    waitfor( okbut, 'userdata');
    try
        tmpdat = get(fig, 'userdata');
        chanlistout1 = tmpdat.chanlist1;
        chanlistout2 = tmpdat.chanlist2;
        close(fig);
        drawnow;
    catch
    end

% unpair channels
% ---------------
function [ str1, str2, chanlist1, chanlist2 ] = unpair(ind1, ind2, chanstr1, chanstr2, chanlist1, chanlist2, str1, str2);
    if nargin > 4
        if isempty(find(chanlist2 == ind2)), disp('Channels not associated'); return; end
        if isempty(find(chanlist1 == ind1)), disp('Channels not associated'); return; end
    end
    
    str1 = sprintf('%2d - %3s', ind1, chanstr1{ind1});
    str2 = sprintf('%2d - %3s', ind2, chanstr2{ind2});
    if nargout > 2
        tmppos = find( chanlist1 == ind1);
        chanlist1(tmppos) = [];
        chanlist2(tmppos) = [];
    end
    
% pair channels
% -------------
function [ str1, str2, chanlist1, chanlist2 ] = pair(ind1, ind2, chanstr1, chanstr2, chanlist1, chanlist2, str1, str2);
    if nargin > 4
        if ~isempty(find(chanlist2 == ind2)), disp('Channel in second structure already associated'); return; end
        if ~isempty(find(chanlist1 == ind1)), disp('Channel in first structure already associated'); return; end
    end
    
    str1 = sprintf('%2d - %3s   -> %2d - %3s', ind1, chanstr1{ind1}, ind2, chanstr2{ind2});
    str2 = sprintf('%2d - %3s   -> %2d - %3s', ind2, chanstr2{ind2}, ind1, chanstr1{ind1});
    if nargout > 2
        chanlist1 = [ chanlist1 ind1 ];
        chanlist2 = [ chanlist2 ind2 ];
    end

% make full channel list    
% ----------------------
function [ newchanstr1, newchanstr2 ] = makelisttext( chanstr1, chanstr2, chanlist1, chanlist2);    
    for index = 1:length(chanstr1)
        if ismember(index, chanlist1)
            pos = find(chanlist1 == index);
            newchanstr1{index} = pair( chanlist1(pos), chanlist2(pos), chanstr1, chanstr2 );
        else
            newchanstr1{index} = sprintf('%2d - %3s', index, chanstr1{index});
        end
    end
    for index = 1:length(chanstr2)
        if ismember(index, chanlist2)
            pos = find(chanlist2 == index);
            [tmp newchanstr2{index}] = pair( chanlist1(pos), chanlist2(pos), chanstr1, chanstr2 );
        else
            newchanstr2{index} = sprintf('%2d - %3s', index, chanstr2{index});
        end
    end

% clear channel pairs
% -------------------
function [ newchanstr1, newchanstr2, chanlist1, chanlist2 ] = clearchans(chanstr1, chanstr2);
    
    chanlist1 = [];
    chanlist2 = [];
    [ newchanstr1, newchanstr2 ] = makelisttext( chanstr1, chanstr2, chanlist1, chanlist2);

% autoselect channel pairs
% ------------------------
function [ newchanstr1, newchanstr2, chanlist1, chanlist2 ] = autoselect(chanstr1, chanstr2, newchanstr1, newchanstr2, chanlist1, chanlist2);
    
    % GUI for selecting pairs
    % -----------------------
    listoptions = { 'All channels (same labels)' 'Fiducials (same labels)' 'BIOSEMI -> 10-20' ...
                    'EGI -> 10-20' }; 
    listui = { { 'style' 'text' 'string' [ 'How to pair channels (click to select)' 10 ] } ...
               { 'style' 'listbox' 'string' listoptions 'value' 1 } };
    results = inputgui({ [1 1] }, listui, '', 'Auto select channel pairs', [], 'normal', 2);
    
    % decode results
    % --------------
    if isempty(results), return; end
    if results{1} == 1 % select all pairs
        [chanlist1 chanlist2] = pop_chancoresp(chanstr1, chanstr2, 'autoselect', 'all', 'gui', 'off');
    elseif results{1} == 2
        [chanlist1 chanlist2] = pop_chancoresp(chanstr1, chanstr2, 'autoselect', 'fiducials', 'gui', 'off');
    else
        disp('Not implemented yet'); return;
    end
    [ newchanstr1, newchanstr2 ] = makelisttext( chanstr1, chanstr2, chanlist1, chanlist2);
        
    
    
