function res = spm_eeg_reduce_cva(S)
% Plugin for data reduction using PCA
% FORMAT res = spm_eeg_reduce_cva(S)
%
% S                     - input structure
% fields of S:
%    S.ncomp            - number of PCA components
%
% Output:
%  res -
%   If no input is provided the plugin returns a cfg branch for itself
%
%   If input is provided:
%      montage struct implementing projection to PCA subspace
%__________________________________________________________________________

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


if nargin == 0
    
    cvachan = cfg_branch;
    cvachan.tag = 'cvachan';
    cvachan.name = 'Channels to reduce';
    cvachan.val = {spm_cfg_eeg_channel_selector};
    
    refchan = cfg_branch;
    refchan.tag = 'refchan';
    refchan.name = 'Reference channels';
    refchan.val = {spm_cfg_eeg_channel_selector};
    
    ncomp = cfg_entry;
    ncomp.tag = 'ncomp';
    ncomp.name = 'Number of components';
    ncomp.strtype = 'n';
    ncomp.num = [1 1];
    ncomp.val = {1};
    ncomp.help = {'Number of components to retain'};
    
    
    outlabel = cfg_entry;
    outlabel.tag = 'outlabel';
    outlabel.name = 'Output channel label';
    outlabel.strtype = 's';
    outlabel.num = [1 Inf];
    outlabel.help = {'Label for the output channel(s).',...
        'Numbers are added for multiple channels.'};
    
    foi = cfg_entry;
    foi.tag = 'foi';
    foi.name = 'Frequency band of interest';
    foi.strtype = 'r';
    foi.num = [1 2];
    foi.val = {[0 Inf]};
    foi.help = {'Frequency window to optimize for'};
    
    tshiftwin = cfg_entry;
    tshiftwin.tag = 'tshiftwin';
    tshiftwin.name = 'Time shift window';
    tshiftwin.strtype = 'r';
    tshiftwin.num = [1 2];
    tshiftwin.val = {[0 0]};
    tshiftwin.help = {'Time shift window (ms). [0 0] - instantaneous'};
    
    tshiftres = cfg_entry;
    tshiftres.tag = 'tshiftres';
    tshiftres.name = 'Time shift resolution';
    tshiftres.strtype = 'r';
    tshiftres.num = [1 1];
    tshiftres.val = {5};
    tshiftres.help = {'Time shift resolution (ms)'};
    
    chanset = cfg_branch;
    chanset.tag = 'chanset';
    chanset.name = 'Set';
    chanset.val = {cvachan, refchan, ncomp, outlabel, foi, tshiftwin, tshiftres};
    
    chansets = cfg_repeat;
    chansets.tag = 'chansets';
    chansets.name = 'Channel sets';
    chansets.values = {chanset};
    chansets.num = [1 Inf];
    chansets.val = {chanset};
    
    
    cva = cfg_branch;
    cva.tag = 'cva';
    cva.name = 'CVA';
    cva.val = {chansets};
    
    res = cva;
    
    return
end

D = S.D;

nsets = numel(S.chanset);
badind = D.badchannels;

% Assuming projecting to columns
montage = [];
montage.labelorg    = D.chanlabels;
montage.labelnew    = {};
montage.chantypenew = {};
montage.tra         = zeros(0, D.nchannels);

spm('Pointer', 'Watch');drawnow;
spm_progress_bar('Init', nsets, 'Channel sets processed'); drawnow;
if nsets > 100, Ibar = floor(linspace(1, nsets,100));
else Ibar = 1:nsets; end

for i = 1:nsets
    
    spm_progress_bar('Set','ylabel','preparing data...');
    
    cvaind = setdiff(D.selectchannels(spm_cfg_eeg_channel_selector(S.chanset(i).cvachan.channels)), badind);
    refind = setdiff(D.selectchannels(spm_cfg_eeg_channel_selector(S.chanset(i).refchan.channels)), badind);
    
    if any(S.chanset(i).tshiftwin)
        tshiftind = S.chanset(i).tshiftwin(1):S.chanset(i).tshiftres:S.chanset(i).tshiftwin(2);
        tshiftind = [0 round(1e-3*D.fsample*tshiftind)];
        tshiftind = repmat(1:D.nsamples, length(tshiftind), 1)+repmat(tshiftind(:), 1, D.nsamples);
        tshiftind = tshiftind(:, all(tshiftind>0 & tshiftind<=D.nsamples));
    else
        tshiftind = repmat(1:D.nsamples, 2, 1);
    end
    
    nshift = size(tshiftind, 1)-1;
    nt     = size(tshiftind, 2);
    
    Y  = zeros(length(cvaind), nt, D.ntrials);
    Yr = zeros(length(refind)*nshift, nt, D.ntrials);
    
    for j = 1:D.ntrials
        cY  = D(cvaind, :, j);
        cYr = D(refind, :, j);
        
        if S.chanset(i).foi(1) > 0
            cY  = ft_preproc_highpassfilter(cY, D.fsample, S.chanset(i).foi(1),...
                5, 'but', 'twopass', 'reduce');
            cYr = ft_preproc_highpassfilter(cYr, D.fsample, S.chanset(i).foi(1),...
                5, 'but', 'twopass', 'reduce');
        end
        
        if isfinite(S.chanset(i).foi(2))
            cY  = ft_preproc_lowpassfilter(cY, D.fsample, S.chanset(i).foi(2),...
                5, 'but', 'twopass', 'reduce');
            cYr = ft_preproc_lowpassfilter(cYr, D.fsample, S.chanset(i).foi(2),...
                5, 'but', 'twopass', 'reduce');
        end
        
        Y(:, :, j) = cY(:, tshiftind(1, :));
        
        for k = 1:size(cYr, 1)
            Yr(((k-1)*nshift+1):k*nshift, :, j) = reshape(cYr(k, tshiftind(2:end, :)'),[], nshift)';
        end
    end
    
    Y  = reshape(Y,  size(Y, 1), []);
    Yr = reshape(Yr, size(Yr, 1), []);
    
    if D.fsample>2.5*S.chanset(i).foi(2)
        dec = floor(D.fsample/(2.5*S.chanset(i).foi(2)));
        Y   =  Y(:, 1:dec:end);
        Yr  = Yr(:, 1:dec:end);
    end
    
    spm_progress_bar('Set','ylabel','running CVA...');
    
    CVA = spm_cva(Y', Yr');
    
    ncomp = min(S.chanset(i).ncomp, size(CVA.V, 2));
    for j = 1:ncomp
        if ncomp == 1
            montage.labelnew{end+1, 1} = S.chanset(i).outlabel;
        else
            montage.labelnew{end+1, 1} = [S.chanset(i).outlabel num2str(j)];
        end
        
        montage.tra(end+1, end)  = 0;
        montage.tra(end, cvaind) = CVA.V(:, j)';
        
        montage.chantypenew{end+1}='LFP';
    end
    
    if ismember(i, Ibar)
        spm_progress_bar('Set', i); drawnow;
    end
end

spm_progress_bar('Clear');

if ~isempty(S.chanind)
    montage.labelnew = [montage.labelnew; D.chanlabels(S.chanind)'];
    I = eye(D.nchannels);
    montage.tra = [montage.tra; I(S.chanind, :)];
    montage.chantypenew = [montage.chantypenew, D.chantype(S.chanind)];
end

res = montage;
