function spm_eeg_prep_ui(callback)
% User interface for spm_eeg_prep function performing several tasks
% for preparation of converted MEEG data for further analysis
% FORMAT spm_eeg_prep_ui(callback)
%__________________________________________________________________________

% Vladimir Litvak
% Copyright (C) 2008-2022 Wellcome Centre for Human Neuroimaging


spm('Pointer','Watch');

if ~nargin, callback = 'CreateMenu'; end

eval(callback);

spm('Pointer','Arrow');

end

%==========================================================================
% function CreateMenu
%==========================================================================
function CreateMenu

spm('FnBanner', 'spm_eeg_prep_ui');
Finter = spm('FnUIsetup', 'M/EEG prepare', 0);

%-Draw top level menu
% ====== File ===================================
FileMenu = uimenu(Finter,'Label','File',...
    'Tag','EEGprepUI',...
    'HandleVisibility','on');

FileOpenMenu = uimenu(FileMenu, ...
    'Label','Open',...
    'Separator','off',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Accelerator','O',...
    'Callback', 'spm_eeg_prep_ui(''FileOpenCB'')');

FileHeaderMenu = uimenu(FileMenu, ...
    'Label','Load header',...
    'Separator','off',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Accelerator','H',...
    'Callback', 'spm_eeg_prep_ui(''FileHeaderCB'')');

FileImportMenu = uimenu(FileMenu, ...
    'Label','Import from workspace',...
    'Separator','off',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Accelerator','I',...
    'Callback', 'spm_eeg_prep_ui(''FileImportCB'')');

FileSaveMenu = uimenu(FileMenu, ...
    'Label','Save',...
    'Separator','off',...
    'Tag','EEGprepUI',...
    'Enable','off',...
    'HandleVisibility', 'on',...
    'Accelerator','S',...
    'Callback', 'spm_eeg_prep_ui(''FileSaveCB'')');

FileExitMenu = uimenu(FileMenu, ...
    'Label','Quit',...
    'Separator','on',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Accelerator','Q',...
    'Callback', 'spm_eeg_prep_ui(''FileExitCB'')');

% ====== Batch inputs ======================================

BatchInputsMenu = uimenu(Finter,'Label','Batch inputs',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

BInputsChannelsMenu = uimenu(BatchInputsMenu, 'Label', 'Channel selection',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''ChannelsCB'')');

BInputsTrialsMenu = uimenu(BatchInputsMenu, 'Label', 'Trial definition',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''TrialsCB'')');

BInputsEventsBIDSMenu = uimenu(BatchInputsMenu, ...
    'Label','Load events from BIDS',...
    'Separator','off',...
    'Enable','off',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Callback', 'spm_eeg_prep_ui(''EventsBIDSCB'')');

BInputsEventsMenu = uimenu(BatchInputsMenu, 'Label', 'Event list',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''EventsCB'')');

BInputsMontageMenu = uimenu(BatchInputsMenu, 'Label', 'Montage',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

BInputsSortCondMenu = uimenu(BatchInputsMenu, 'Label', 'Sort conditions',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''SortCondCB'')');

MontageCustomMenu = uimenu(BInputsMontageMenu, 'Label', 'Custom Montage',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''MontageCB'')');

MontageRereferenceMenu = uimenu(BInputsMontageMenu, 'Label', 'Re-reference',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''RereferenceCB'')');

MontageROIMenu = uimenu(BInputsMontageMenu, 'Label', 'ROI',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''roiCB'')');

% ====== Channel types ===============================

ChanTypeMenu = uimenu(Finter,'Label','Channel types',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

chanTypes = {'EEG', 'EOG', 'ECG', 'EMG', 'LFP', 'PHYS', 'ILAM', 'SRC', 'Other'};

for i = 1:length(chanTypes)
    CTypesMenu(i) = uimenu(ChanTypeMenu, 'Label', chanTypes{i},...
        'Tag','EEGprepUI',...
        'Enable', 'on', ...
        'HandleVisibility','on',...
        'Callback', 'spm_eeg_prep_ui(''ChanTypeCB'')');
end

CTypesRef2MEGMenu = uimenu(ChanTypeMenu, 'Label', 'MEGREF=>MEG',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Separator', 'on',...
    'Callback', 'spm_eeg_prep_ui(''MEGChanTypeCB'')');

CTypesDefaultMenu = uimenu(ChanTypeMenu, 'Label', 'Default',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on',...
    'Callback', 'spm_eeg_prep_ui(''ChanTypeDefaultCB'')');

CTypesBIDSMenu = uimenu(ChanTypeMenu, 'Label', 'From BIDS',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...   
    'Callback', 'spm_eeg_prep_ui(''ChanTypeBIDSCB'')');

CTypesReviewMenu = uimenu(ChanTypeMenu, 'Label', 'Review',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''ChanTypeCB'')');

% ====== Sensors ===================================

Coor3DMenu = uimenu(Finter,'Label','Sensors',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

LoadMEGSensMenu = uimenu(Coor3DMenu, 'Label', 'Load MEG sensors',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''LoadMEGSensCB'')');

HeadshapeMenu = uimenu(Coor3DMenu, 'Label', 'Load MEG Fiducials/Headshape',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''HeadshapeCB'')');

LoadEEGSensMenu = uimenu(Coor3DMenu, 'Label', 'Load EEG sensors',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'Separator', 'on', ...
    'HandleVisibility','on');

LoadEEGSensTemplateMenu = uimenu(LoadEEGSensMenu, 'Label', 'Assign default',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''LoadEEGSensTemplateCB'')');

LoadEEGSensMatMenu = uimenu(LoadEEGSensMenu, 'Label', 'From *.mat file',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''LoadEEGSensCB'')');

LoadEEGSensOtherMenu = uimenu(LoadEEGSensMenu, 'Label', 'Convert locations file',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''LoadEEGSensCB'')');

ReferenceMenu = uimenu(Coor3DMenu, 'Label', 'Define EEG referencing',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on');

ReferenceSelectMenu = uimenu(ReferenceMenu, 'Label', 'Select reference sensors',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''ReferenceCB'')');

ReferenceMontageMenu = uimenu(ReferenceMenu, 'Label', 'Specify montage matrix',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''ReferenceCB'')');

CoregisterMenu = uimenu(Coor3DMenu, 'Label', 'Coregister',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''CoregisterCB'')');

% ====== 2D projection ===================================

Coor2DMenu = uimenu(Finter, 'Label','2D projection',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

EditMEGMenu = uimenu(Coor2DMenu, 'Label', 'Edit existing MEG',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''EditExistingCoor2DCB'')');

EditEEGMenu = uimenu(Coor2DMenu, 'Label', 'Edit existing EEG',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''EditExistingCoor2DCB'')');

LoadTemplateMenu = uimenu(Coor2DMenu, 'Label', 'Load template',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''LoadTemplateCB'')');

SaveTemplateMenu = uimenu(Coor2DMenu, 'Label', 'Save template',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''SaveTemplateCB'')');

Project3DEEGMenu = uimenu(Coor2DMenu, 'Label', 'Project 3D (EEG)',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''Project3DCB'')');

Project3DMEGMenu = uimenu(Coor2DMenu, 'Label', 'Project 3D (MEG)',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''Project3DCB'')');

AddCoor2DMenu = uimenu(Coor2DMenu, 'Label', 'Add sensor',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''AddCoor2DCB'')');

DeleteCoor2DMenu = uimenu(Coor2DMenu, 'Label', 'Delete sensor',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''DeleteCoor2DCB'')');

UndoMoveCoor2DMenu = uimenu(Coor2DMenu, 'Label', 'Undo move',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''UndoMoveCoor2DCB'')');

ApplyCoor2DMenu = uimenu(Coor2DMenu, 'Label', 'Apply',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''ApplyCoor2DCB'')');

Clear2DMenu = uimenu(Coor2DMenu, 'Label', 'Clear',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''Clear2DCB'')');
end

%==========================================================================
% function FileOpenCB
%==========================================================================
function FileOpenCB

D = spm_eeg_load;
setD(D);

update_menu;

end

%==========================================================================
% function FileHeaderCB
%==========================================================================
function FileHeaderCB

S = [];
S.mode = 'header';
[S.dataset, sts] = spm_select(1, '.*', 'Select M/EEG data file');
if ~sts, return; end
D = spm_eeg_convert(S);
setD(D);

update_menu;

end

%==========================================================================
% function FileImportCB
%==========================================================================
function FileImportCB

var = evalin('base', 'whos');
var = {var(ismember({var.class}, {'single', 'double'})).name};

[selection, ok]= listdlg('ListString', var, 'SelectionMode', 'single' ,'Name', 'Select variable' , 'ListSize', [400 300]);

if ok
    data = full(double(evalin('base', var{selection})));
    dim = size(data);
    ndim = length(dim);
    
    if length(dim)>3
        error('Data of up to 3 dimensions are supported.');
    end
    
    prompt = sprintf('%d|', dim);
    prompt(end) = [];
    
    chandim = spm_input('Number of channels:', 1, prompt, 1:length(dim));
    
    nchan = dim(chandim);
    
    otherdim = setdiff(1:ndim, chandim);
    
    if ndim == 3        
        prompt = sprintf('%d|', dim(otherdim));
        prompt(end) = [];
        trialdim = spm_input('Number of trials:','+1', prompt, otherdim);
        
        ntrials = dim(trialdim);
    else
        trialdim = [];
        ntrials  = 1;
    end
        
    sampledim = setdiff(1:ndim, [trialdim, chandim]);
    nsamples  = dim(sampledim);
    
    data    = permute(data, [chandim, sampledim, trialdim]);
    
    fs      = spm_input('Sampling rate (Hz):', '+1','r');
    t_onset = spm_input('Time axis onset (ms):', '+1','r');
    
    fname   = spm_input('Output file name:', '+1','s');
    
    time    = (0:(nsamples-1))/fs - 1e-3*t_onset;
    
    for i = 1:ntrials
        ftdata.trial{i} = data(:, :, i);
        ftdata.time{i}  = time;
    end
    
    for i = 1:nchan
        ftdata.label{i, 1} = sprintf('Ch%d', i); 
    end
    
    D = spm_eeg_ft2spm(ftdata, fname);
    
    spm_eeg_review(D);
    spm_eeg_review_callbacks('edit', 'prep');    
end
end
%==========================================================================
% function FileSaveCB
%==========================================================================
function FileSaveCB

D = getD;
if ~isempty(D)
    D.save;
end

update_menu;

end

%==========================================================================
% function FileExitCB
%==========================================================================
function FileExitCB

spm_figure('Clear','Interactive');
spm('FigName','M/EEG prepare: done');

end

%==========================================================================
% function ChannelsCB
%==========================================================================
function ChannelsCB

D = getD;

[selection, ok]= listdlg('ListString', D.chanlabels, 'SelectionMode', 'multiple' ,'Name', 'Select channels' , 'ListSize', [400 300]);
if ~ok, return; end

[chanfilename, chanpathname] = uiputfile( ...
    {'*.mat', 'MATLAB File (*.mat)'}, 'Save channel selection as');

label = D.chanlabels(selection);

save(fullfile(chanpathname, chanfilename), 'label', spm_get_defaults('mat.format'));

end

%==========================================================================
% function TrialsCB
%==========================================================================
function TrialsCB

D = getD;

S = [];
S.D = D;
S.save = 1;
spm_eeg_definetrial(S);

CreateMenu;

setD(D);

update_menu;

end

%==========================================================================
% function EventsBIDSCB
%==========================================================================
function EventsBIDSCB

D = getD;

S = [];
S.task = 'loadbidsevents';
S.D = D;
[S.filename, sts] = spm_select(1, '.*_events.tsv$', 'Select BIDS tsv file');

if sts
    S.replace = spm_input('Replace existing','1','replace|add',[1 0], 1);
end

D = spm_eeg_prep(S);

setD(D);

update_menu;

end

%==========================================================================
% function EventsCB
%==========================================================================
function EventsCB

D = getD;

ev = D.events;

if iscell(ev)
    sev = ev{1};
    for i = 2:numel(ev)
        sev = spm_cat_struct(sev, ev{i});
    end
else
    sev = ev;
end

selected = spm_eeg_select_event_ui(sev);

events = [];
for i = 1:size(selected, 1)
    events(i).type  = selected{i, 1};
    events(i).value = selected{i, 2};
end

if ~isempty(events)
    [filename, pathname] = uiputfile('*.mat', 'Save event list as');
    
    if ~isequal(filename, 0)
        save(fullfile(pathname, filename), 'events', spm_get_defaults('mat.format'));
    end
end
end

%==========================================================================
% function MontageCB
%==========================================================================
function MontageCB

D = getD;

label = D.chanlabels(D.indchantype({'EEG', 'EOG', 'ECG', 'EMG'}));

montage = [];
montage.labelorg = label;
montage.labelnew = label;
montage.tra = eye(numel(label));

spm_eeg_montage_ui(montage);

end

%==========================================================================
% function RereferenceCB
%==========================================================================
function RereferenceCB

D = getD;

% Get indices for just EEG channels and remove any bad channels
%--------------------------------------------------------------------------
eegchan  = D.indchantype('EEG');
goodind  = D.indchantype('EEG', 'GOOD');

if isempty(goodind)
    error('No good EEG channels.')
end

%-Get reference channel indices
%--------------------------------------------------------------------------
[selection, ok]= listdlg('ListString', D.chanlabels(goodind), 'SelectionMode', 'multiple' ,...
    'Name', 'Select reference channels' , 'ListSize', [400 300]);
if ~ok
    return;
end

refind  = goodind(selection);
badind  = D.indchantype('EEG', 'BAD');

goodind = find(ismember(eegchan, goodind));
badind  = find(ismember(eegchan,  badind));
refind  = find(ismember(eegchan,  refind));

tra                 = eye(length(eegchan));
tra(goodind,refind) = tra(goodind,refind) - 1/length(refind);
tra(badind, refind) = tra(badind,refind)  - 1/length(refind);

montage          = [];
montage.labelorg = D.chanlabels(eegchan);
montage.labelnew = D.chanlabels(eegchan);
montage.tra      = tra;

[filename, pathname] = uiputfile('*.mat', 'Save montage as');

if ~isequal(filename, 0)
    save(fullfile(pathname, filename), 'montage', spm_get_defaults('mat.format'));
end

end

%==========================================================================
% function roiCB
%==========================================================================
function roiCB

D = getD;

[modality, chanind]  = spm_eeg_modality_ui(D, 0, 1);

chanind = setdiff(chanind, D.badchannels);

if isempty(chanind)
    error(['No good ' modality ' channels.']);
end

montage = [];
montage.labelorg = D.chanlabels(chanind)';
montage.labelnew = {};
montage.tra      = [];
while 1
    roilabel = spm_input('ROI Label:', '+1', 's');
    
    [selection, ok]= listdlg('ListString', D.chanlabels(chanind), 'SelectionMode', 'multiple' ,...
        'Name', 'Select reference channels' , 'ListSize', [400 300]);
    if ok
       montage.labelnew{end+1, 1} = roilabel;
       tra = zeros(1, length(chanind));
       tra(selection) = 1;
       tra = tra./sum(tra);
       montage.tra = [montage.tra; tra];        
    end
    
    if spm_input('Add another?','+1','yes|no',[0, 1], 0);
        break;
    end
end
    
[filename, pathname] = uiputfile('*.mat', 'Save montage as');

if ~isequal(filename, 0)
    save(fullfile(pathname, filename), 'montage', spm_get_defaults('mat.format'));
end

update_menu;

end

%==========================================================================
% function SortCondCB
%==========================================================================
function SortCondCB

D = getD;

S   = [];
S.task = 'sortconditions';
S.D = D;
oldcondlist = D.condlist;
S.condlist = cell(size(oldcondlist));
for i = 1:D.nconditions
    str = sprintf('%s|', oldcondlist{:});
    str = str(1:(end-1));
    
    ind = spm_input(['Select condition ' num2str(i)], 1, 'm', str, 1:numel(oldcondlist));
    S.condlist(i) = oldcondlist(ind);
    oldcondlist(ind) = [];
end

[filename, pathname] = uiputfile('*.mat', 'Save conditions list as');

if ~isequal(filename, 0)
    condlist = S.condlist;
    
    save(fullfile(pathname, filename), 'condlist', spm_get_defaults('mat.format'));
end

D = spm_eeg_prep(S);

setD(D);
update_menu;

end

%==========================================================================
% function ChanTypeCB
%==========================================================================
function ChanTypeCB

type = get(gcbo, 'Label');

D = getD;

if ~isempty(D)
    chanlist ={};
    for i = 1:D.nchannels
        if strncmp(D.chantype(i), 'MEG', 3) || strncmp(D.chantype(i), 'REF', 3)
            chanlist{i} = [num2str(i) '    Label:    ' D.chanlabels(i) '    Type:    ' D.chantype(i) , ' (nonmodifiable)'];
        else
            chanlist{i} = [num2str(i) '    Label:    ' D.chanlabels(i) '    Type:    ' D.chantype(i)];
        end
        
        chanlist{i} = [chanlist{i}{:}];
    end
    
    if strcmpi(type, 'review')
        listdlg('ListString', chanlist, 'SelectionMode', 'single', 'Name', 'Review channels', 'ListSize', [400 300]);
        return
    else
        
        [selection,ok]= listdlg('ListString', chanlist, 'SelectionMode', 'multiple',...
            'InitialValue', strmatch(type, D.chantype) ,'Name', ['Set type to ' type], 'ListSize', [400 300]);
        
        selection(strmatch('MEG', chantype(D, selection))) = [];
        
        if ok && ~isempty(selection)
            S.task = 'settype';
            S.D = D;
            S.ind = selection;
            S.type = type;
            D = spm_eeg_prep(S);
            setD(D);
        end
    end
end

update_menu;

end
%==========================================================================
% function MEGChanTypeCB
%==========================================================================
function MEGChanTypeCB

S = [];
S.D = getD;
S.task = 'settype';

switch get(gcbo, 'Label')
    case 'MEGREF=>MEG'
        dictionary = {
            'REFMAG',   'MEGMAG';
            'REFGRAD',  'MEGGRAD';
            };
        
        ind = spm_match_str(S.D.chantype, dictionary(:,1));
        
        grad = S.D.sensors('meg');
        if ~isempty(grad)
            % Under some montages only subset of the reference sensors are
            % in the grad
            [junk, sel] = intersect(S.D.chanlabels(ind), grad.label);
            ind = ind(sel);
        end
        
        S.ind = ind;
        
        [sel1, sel2] = spm_match_str(S.D.chantype(S.ind), dictionary(:, 1));
        
        S.type = dictionary(sel2, 2);
        
        D = spm_eeg_prep(S);
end

setD(D);
update_menu;

end
%==========================================================================
% function ChanTypeDefaultCB
%==========================================================================
function ChanTypeDefaultCB

S.D    = getD;
S.task = 'defaulttype';
D      = spm_eeg_prep(S);

setD(D);
update_menu;

end

%==========================================================================
% function ChanTypeBIDSCB
%==========================================================================
function ChanTypeBIDSCB

S = [];
S.D    = getD;
S.task = 'bidschantype';
[S.filename, sts] = spm_select(1, '.*_channels.tsv$', 'Select BIDS tsv file');

if sts
    D      = spm_eeg_prep(S);
    
    setD(D);
    update_menu;
end

end

%==========================================================================
% function LoadEEGSensTemplateCB
%==========================================================================
function LoadEEGSensTemplateCB

S.D    = getD;
S.task = 'defaulteegsens';

if strcmp(S.D.modality(1, 0), 'Multimodal')
    fid = fiducials(S.D);
    if ~isempty(fid)
        lblfid = fid.fid.label;
        
        S.regfid = match_fiducials({'nas'; 'lpa'; 'rpa'}, lblfid);
        S.regfid(:, 2) = {'spmnas'; 'spmlpa'; 'spmrpa'};
    else
        warndlg(strvcat('Could not match EEG fiducials for multimodal dataset.', ...
            '           EEG coregistration might fail.'));
    end
end

D = spm_eeg_prep(S);
setD(D);
update_menu;

end
%==========================================================================
% function LoadEEGSensCB
%==========================================================================
function LoadMEGSensCB

D = getD;

S = [];
S.task = 'loadmegsens';
S.D = D;
[S.source,sts] = spm_select(1, '\.*', 'Select a raw MEG data file');
if ~sts, return; end

D = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function LoadEEGSensCB
%==========================================================================
function LoadEEGSensCB

D = getD;

switch get(gcbo, 'Label')
    case 'From *.mat file'
        [S.sensfile, sts] = spm_select(1,'mat','Select EEG sensors file');
        if ~sts, return, end
        S.source = 'mat';
        [S.headshapefile, sts] = spm_select(1,'mat','Select EEG fiducials file');
        if ~sts, return, end
        S.fidlabel = spm_input('Fiducial labels:', '+1', 's', 'nas lpa rpa');
    case 'Convert locations file'
        [S.sensfile, sts] = spm_select(1, '.*', 'Select locations file');
        if ~sts, return, end
        S.source = 'locfile';
end

if strcmp(D.modality(1, 0), 'Multimodal')
    if ~isempty(D.fiducials)
        S.regfid = {};
        if strcmp(S.source, 'mat')
            fidlabel = S.fidlabel;
            lblshape = {};
            fidnum = 0;
            while ~all(isspace(fidlabel))
                fidnum = fidnum+1;
                [lblshape{fidnum},fidlabel] = strtok(fidlabel);
            end
            if (fidnum < 3)
                error('At least 3 labeled fiducials are necessary');
            end
        else
            shape = ft_read_headshape(S.sensfile);
            lblshape = shape.fid.label;
        end
        
        fid = fiducials(D);
        lblfid = fid.fid.label;
        
        S.regfid = match_fiducials(lblshape, lblfid);
    else
        warndlg(strvcat('Could not match EEG fiducials for multimodal dataset.', ...
            '           EEG coregistration might fail.'));
    end
end

S.D    = D;
S.task = 'loadeegsens';
D      = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function HeadshapeCB
%==========================================================================
function HeadshapeCB

S = [];
S.D = getD;
S.task = 'headshape';

[S.headshapefile, sts] = spm_select(1, '.*', 'Select fiducials/headshape file');
if ~sts, return, end
S.source = 'convert';

shape = ft_read_headshape(S.headshapefile);
lblshape = shape.fid.label;

fid = fiducials(S.D);

if ~isempty(fid)
    lblfid = fid.fid.label;
    S.regfid = match_fiducials(lblshape, lblfid);
end

D = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function CoregisterCB
%==========================================================================
function ReferenceCB

D = getD;

S = [];
S.D = D;
S.task = 'sens2chan';
switch get(gcbo, 'Label')
    case 'Select reference sensors'
        elec = D.sensors('EEG');
        
        [refind, ok]= listdlg('ListString', elec.label, 'SelectionMode', 'multiple' ,'Name', 'Select reference electrodes' , 'ListSize', [400 300]);
        if ~ok
            return;
        end
        
        S.refelec = elec.label(refind);
    case 'Specify montage matrix'
        
        sens  = D.sensors('EEG');
        label = D.chanlabels(D.indchantype('EEG'));
        
        [sel1, sel2] = spm_match_str(label, sens.label);
        
        montage = [];
        montage.labelorg = sens.label;
        montage.labelnew = label;
        montage.tra = zeros(numel(label), numel(sens.label));
        montage.tra(sub2ind(size(montage.tra), sel1, sel2)) = 1;
        
        S.montage = spm_eeg_montage_ui(montage);
end

D = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function CoregisterCB
%==========================================================================
function CoregisterCB

S = [];
S.D = getD;
S.task = 'coregister';

D = spm_eeg_prep(S);

% Bring the menu back
spm_eeg_prep_ui;

setD(D);

update_menu;

end
%==========================================================================
% function EditExistingCoor2DCB
%==========================================================================
function EditExistingCoor2DCB

D = getD;

switch get(gcbo, 'Label')
    case 'Edit existing MEG'
        xy = D.coor2D('MEG');
        label = D.chanlabels(strmatch('MEG', D.chantype));
    case 'Edit existing EEG'
        xy = D.coor2D('EEG');
        label = D.chanlabels(strmatch('EEG', D.chantype, 'exact'));
end

plot_sensors2D(xy, label);

update_menu;

end
%==========================================================================
% function LoadTemplateCB
%==========================================================================
function LoadTemplateCB

[sensorfile, sts] = spm_select(1, 'mat', 'Select sensor template file', ...
    [], fullfile(spm('dir'), 'EEGtemplates'));
if ~sts, return, end

template = load(sensorfile);

if isfield(template, 'Cnames') && isfield(template, 'Cpos')
    plot_sensors2D(template.Cpos, template.Cnames);
end

update_menu;

end
%==========================================================================
% function SaveTemplateCB
%==========================================================================
function SaveTemplateCB

handles=getHandles;

Cnames = handles.label;
Cpos = handles.xy;
Rxy = 1.5;
Nchannels = length(Cnames);

[filename, pathname] = uiputfile('*.mat', 'Save channel template as');

save(fullfile(pathname, filename), 'Cnames', 'Cpos', 'Rxy', 'Nchannels', spm_get_defaults('mat.format'));

end
%==========================================================================
% function Project3DCB
%==========================================================================
function Project3DCB

D = getD;

switch get(gcbo, 'Label')
    case 'Project 3D (EEG)'
        modality = 'EEG';
        chanind  = D.indchantype('EEG');
    case 'Project 3D (MEG)'
        modality = 'MEG';
        chanind  = D.indchantype('MEGANY');
end

if ~isfield(D, 'val')
    D.val = 1;
end

if isfield(D, 'inv') && isfield(D.inv{D.val}, 'datareg')
    datareg = D.inv{D.val}.datareg;
    ind     = strmatch(modality, {datareg(:).modality}, 'exact');
    sens    = datareg(ind).sensors;
else
    sens    = D.sensors(modality);
end

[xy, label] = spm_eeg_project3D(sens, modality);

[sel1, sel2] = spm_match_str(D.chanlabels(chanind), label);

plot_sensors2D(xy(:, sel2), label(sel2));

update_menu;

end
%==========================================================================
% function AddCoor2DCB
%==========================================================================
function AddCoor2DCB

newlabel = spm_input('Label?', '+1', 's');

if isempty(newlabel)
    return;
end

coord = spm_input('Coordinates [x y]', '+1', 'r', '0.5 0.5', 2);

handles = getHandles;

if ~isfield(handles, 'xy')
    handles.xy = [];
end

if ~isfield(handles, 'xy')
    handles.xy = [];
end

if ~isfield(handles, 'label')
    handles.label = {};
end

plot_sensors2D([handles.xy coord(:)], ...
    [handles.label newlabel]);

update_menu;

end
%==========================================================================
% function ApplyCoor2DCB
%==========================================================================
function ApplyCoor2DCB

handles = getHandles;
D = getD;

S = [];
S.task = 'setcoor2d';
S.D = D;
S.xy = handles.xy;
S.label = handles.label;

D = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function update_menu
%==========================================================================
function update_menu

Finter = spm_figure('GetWin','Interactive');
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'File'), 'Enable', 'on');

IsEEG = 'off';
IsMEG = 'off';
IsEpochable = 'off';
HasEvents   = 'off';
IsEpoched   = 'off';
HasPlanar   = 'off';
HasSensors  = 'off';
HasSensorsEEG = 'off';
ReferenceSelectable = 'off';
HasSensorsMEG = 'off';
HasChannelsMEGREF = 'off';
HasFiducials = 'off';
HasDefaultLocs = 'off';
HasHistory = 'off';
if isa(get(Finter, 'UserData'), 'meeg')
    Dloaded = 'on';
    
    D = getD;
    
    if ~isempty(D.indchantype('EEG'))
        IsEEG = 'on';
    end
    
    if ~isempty(D.indchantype('MEG'));
        IsMEG = 'on';
    end
    
    if ~isempty(D.events)
        HasEvents = 'on';
    end
    
    if isequal(D.type, 'continuous') && ~isempty(D.events);
        IsEpochable = 'on';
    end
    
    if ~isequal(D.type, 'continuous')
        IsEpoched = 'on';
    end
    
    
    [res, list] = modality(D, 1, 1);
    if ismember('MEGPLANAR', list);
        HasPlanar = 'on';
    end
    
    if ~isempty(D.indchantype('REF'));
        HasChannelsMEGREF = 'on';
    end
    
    if ~isempty(D.sensors('EEG')) || ~isempty(D.sensors('MEG'))
        HasSensors = 'on';
    end
    
    if ~isempty(D.sensors('EEG'))
        HasSensorsEEG = 'on';
        
        elec = D.sensors('EEG');
        if ~isfield(elec, 'tra') &&  (size(elec.elecpos, 1) == numel(elec.label))
            ReferenceSelectable = 'on';
        end
    end
    
    if  ~isempty(D.sensors('MEG'))
        HasSensorsMEG = 'on';
    end
    
    if  ~isempty(D.fiducials)
        HasFiducials = 'on';
    end
    
    template_sfp = dir(fullfile(spm('dir'), 'EEGtemplates', '*.sfp'));
    template_sfp = {template_sfp.name};
    ind = strmatch([ft_senstype(D.chanlabels(D.indchantype('EEG'))) '.sfp'], template_sfp, 'exact');
    
    if ~isempty(ind) || ft_senstype(D.chanlabels(D.indchantype('EEG')), 'ext1020')
        HasDefaultLocs = 'on';
    end
    
    if  ~isempty(D.history)
        HasHistory = 'on';
    end
    
else
    Dloaded = 'off';
end

handles = getHandles;

IsTemplate = 'off';
IsSelected = 'off';
IsMoved = 'off';
if ~isempty(handles)
    if isfield(handles, 'xy') && size(handles.xy, 1)>0
        IsTemplate = 'on';
    end
    
    if isfield(handles, 'labelSelected') && ~isempty(handles.labelSelected)
        IsSelected = 'on';
    end
    
    if isfield(handles, 'lastMoved')
        isMoved = 'on';
    end
end

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Save'), 'Enable', 'on');
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Load events from BIDS'), 'Enable', Dloaded);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Batch inputs'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Trial definition'), 'Enable', IsEpochable);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Event list'), 'Enable', HasEvents);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Montage'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Re-reference'), 'Enable', IsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'ROI'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Sort conditions'), 'Enable', IsEpoched);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Channel types'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Sensors'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', '2D projection'), 'Enable', Dloaded);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'MEGREF=>MEG'), 'Enable', HasChannelsMEGREF);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Assign default'), 'Enable', HasDefaultLocs);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Load EEG sensors'), 'Enable', IsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Load MEG sensors'), 'Enable', IsMEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Load MEG Fiducials/Headshape'), 'Enable', HasSensorsMEG);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Headshape'), 'Enable', HasSensorsMEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Define EEG referencing'), 'Enable', HasSensorsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Select reference sensors'), 'Enable', ReferenceSelectable);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Coregister'), 'Enable', HasSensors);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Edit existing EEG'), 'Enable', IsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Edit existing MEG'), 'Enable', IsMEG);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Project 3D (EEG)'), 'Enable', HasSensorsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Project 3D (MEG)'), 'Enable', HasSensorsMEG);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Delete sensor'), 'Enable', IsSelected);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Undo move'), 'Enable', IsMoved);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Apply'), 'Enable', IsTemplate);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Clear'), 'Enable', IsTemplate);

delete(setdiff(findobj(Finter), [Finter; findobj(Finter,'Tag','EEGprepUI')]));

if strcmp(Dloaded, 'on') && isfield(D,'PSD') && D.PSD == 1
    try
        hc = get(Finter,'children');
        hc = findobj(hc,'flat','type','uimenu');
        hc = findobj(hc,'flat','label','File');
        delete(hc)
    end
    uicontrol(Finter,...
        'style','pushbutton','string','OK',...
        'callback','spm_eeg_review_callbacks(''get'',''prep'')',...
        'tooltipstring','Send changes to ''SPM Graphics'' window',...
        'BusyAction','cancel',...
        'Interruptible','off',...
        'Tag','EEGprepUI');
end


figure(Finter);

end
%==========================================================================
% function getD
%==========================================================================
function D = getD

Finter = spm_figure('GetWin','Interactive');
D = get(Finter, 'UserData');
if ~isa(D, 'meeg')
    D = [];
end

end
%==========================================================================
% function setD
%==========================================================================
function setD(D)
Finter = spm_figure('GetWin','Interactive');
set(Finter, 'UserData', D);

end
%==========================================================================
% function getHandles
%==========================================================================
function handles = getHandles
Fgraph = spm_figure('GetWin','Graphics');
handles = get(Fgraph, 'UserData');

end
%==========================================================================
% function setHandles
%==========================================================================
function setHandles(handles)
Fgraph = spm_figure('GetWin','Graphics');
set(Fgraph, 'UserData', handles);

end
%==========================================================================
% function plot_sensors2D
%==========================================================================
function plot_sensors2D(xy, label)

Fgraph = spm_figure('GetWin','Graphics');

spm_clf(Fgraph);

handles = [];

if ~isempty(xy)
    if size(xy, 1) ~= 2
        xy = xy';
    end
    
    
    xy(xy < 0.05) = 0.05;
    xy(xy > 0.95) = 0.95;
    
    
    handles.h_lbl=text(xy(1,:), xy(2, :),strvcat(label),...
        'FontSize', 9,...
        'Color','r',...
        'FontWeight','bold');
    
    set(handles.h_lbl, 'ButtonDownFcn', 'spm_eeg_prep_ui(''LabelClickCB'')');
    
    hold on
    
    handles.h_el = [];
    for i=1:size(xy, 2)
        handles.h_el(i) = plot(xy(1,i), xy(2,i), 'or');
    end
    
    set(handles.h_el,'MarkerFaceColor','r','MarkerSize', 2,'MarkerEdgeColor','k');
    
    handles.TemplateFrame = ...
        plot([0.05 0.05 0.95 0.95 0.05], [0.05 0.95 0.95 0.05 0.05], 'k-');
    
    axis off;
    
end

handles.xy = xy;
handles.label = label(:)';

setHandles(handles);

update_menu;

end
%==========================================================================
% function DeleteCoor2DCB
%==========================================================================
function DeleteCoor2DCB

handles = getHandles;
graph = spm_figure('GetWin','Graphics');

if isfield(handles, 'labelSelected') && ~isempty(handles.labelSelected)
    set(graph, 'WindowButtonDownFcn', '');
    
    label=get(handles.labelSelected, 'String');
    ind=strmatch(label, handles.label, 'exact');
    
    delete([handles.labelSelected handles.pointSelected]);
    
    handles.xy(:, ind)=[];
    handles.label(ind) = [];
    
    plot_sensors2D(handles.xy, handles.label)
end

end
%==========================================================================
% function UndoMoveCoor2DCB
%==========================================================================
function UndoMoveCoor2DCB

handles = getHandles;

if isfield(handles, 'lastMoved')
    label = get(handles.lastMoved(end).label, 'String');
    ind = strmatch(label, handles.label, 'exact');
    handles.xy(:, ind) = handles.lastMoved(end).coords(:);
    
    set(handles.lastMoved(end).point, 'XData', handles.lastMoved(end).coords(1));
    set(handles.lastMoved(end).point, 'YData', handles.lastMoved(end).coords(2));
    set(handles.lastMoved(end).label, 'Position', handles.lastMoved(end).coords);
    
    if length(handles.lastMoved)>1
        handles.lastMoved = handles.lastMoved(1:(end-1));
    else
        handles = rmfield(handles, 'lastMoved');
    end
    
    setHandles(handles);
    update_menu;
end

end
%==========================================================================
% function LabelClickCB
%==========================================================================
function LabelClickCB

handles=getHandles;
Fgraph = spm_figure('GetWin','Graphics');

if isfield(handles, 'labelSelected') && ~isempty(handles.labelSelected)
    if handles.labelSelected == gcbo
        set(handles.labelSelected, 'Color', 'r');
        set(handles.pointSelected,'MarkerFaceColor', 'r');
        set(Fgraph, 'WindowButtonDownFcn', '');
    else
        handles.pointSelected=[];
        handles.labelSelected=[];
    end
else
    set(Fgraph, 'WindowButtonDownFcn', 'spm_eeg_prep_ui(''LabelMoveCB'')');
    
    coords = get(gcbo, 'Position');
    
    handles.labelSelected=gcbo;
    handles.pointSelected=findobj(gca, 'Type', 'line',...
        'XData', coords(1), 'YData', coords(2));
    
    set(handles.labelSelected, 'Color', 'g');
    set(handles.pointSelected,'MarkerFaceColor', 'g');
end

setHandles(handles);

update_menu;

end
%==========================================================================
% function LabelMoveCB
%==========================================================================
function LabelMoveCB

handles = getHandles;
Fgraph = spm_figure('GetWin','Graphics');

coords=mean(get(gca, 'CurrentPoint'));

coords(coords < 0.05) = 0.05;
coords(coords > 0.95) = 0.95;

set(handles.pointSelected, 'XData', coords(1));
set(handles.pointSelected, 'YData', coords(2));


set(handles.labelSelected, 'Position', coords);
set(handles.labelSelected, 'Color', 'r');
set(handles.pointSelected,'MarkerFaceColor','r','MarkerSize',2,'MarkerEdgeColor','k');
set(Fgraph, 'WindowButtonDownFcn', '');
set(Fgraph, 'WindowButtonMotionFcn', 'spm_eeg_prep_ui(''CancelMoveCB'')');


labelind=strmatch(get(handles.labelSelected, 'String'), handles.label);

if isfield(handles, 'lastMoved')
    handles.lastMoved(end+1).point = handles.pointSelected;
    handles.lastMoved(end).label = handles.labelSelected;
    handles.lastMoved(end).coords = handles.xy(:, labelind);
else
    handles.lastMoved.point = handles.pointSelected;
    handles.lastMoved.label = handles.labelSelected;
    handles.lastMoved.coords = handles.xy(:, labelind);
end

handles.xy(:, labelind) = coords(1:2)';

setHandles(handles);

update_menu;

end
%==========================================================================
% function CancelMoveCB
%==========================================================================
function CancelMoveCB

Fgraph = spm_figure('GetWin','Graphics');
handles = getHandles;

handles.pointSelected=[];
handles.labelSelected=[];
set(Fgraph, 'WindowButtonMotionFcn', '');

setHandles(handles);

update_menu;

end
%==========================================================================
% function Clear2DCB
%==========================================================================
function Clear2DCB

plot_sensors2D([], {});

update_menu;

end
%==========================================================================
% function match_fiducials
%==========================================================================
function regfid = match_fiducials(lblshape, lblfid)

if numel(intersect(upper(lblshape), upper(lblfid))) < 3
    if numel(lblshape)<3 || numel(lblfid)<3
        warndlg('3 fiducials are required');
        return;
    else
        regfid = {};
        for i = 1:length(lblfid)
            [selection,ok]= listdlg('ListString',lblshape, 'SelectionMode', 'single',...
                'InitialValue', strmatch(upper(lblfid{i}), upper(lblshape)), ...
                'Name', ['Select matching fiducial for ' lblfid{i}], 'ListSize', [400 300]);
            
            if ~ok
                continue
            end
            
            regfid = [regfid; [lblfid(i) lblshape(selection)]];
        end
        
        if size(regfid, 1) < 3
            warndlg('3 fiducials are required to load headshape');
            return;
        end
    end
else
    [sel1, sel2] = spm_match_str(upper(lblfid), upper(lblshape));
    lblfid = lblfid(sel1);
    lblshape = lblshape(sel2);
    regfid = [lblfid(:) lblshape(:)];
end
end
