% STD_ERSP - Compute ERSP and/or ITC transforms for ICA components 
%              or data channels of a dataset. Save results into Matlab 
%              float files. 
%
% Function description:
%              The function computes the mean ERSP or ITC for the selected 
%              dataset ICA components or data channels in the requested 
%              frequency range and time window (the two are dependent). 
%              Frequencies are equally log spaced. Options specify component 
%              numbers, desired frequency range,  time window length, 
%              frequency resolution, significance level, and wavelet
%              cycles. See >> help newtimef and >> timef details 
%
%              Two Matlab files are saved (for ERSP and ITC). These contain 
%              the ERSP|ITC image, plus the transform parameters 
%              used to compute them. Saves the computed dataset mean images 
%              in dataset-name files with extensions '.icaersp' and '.icaitc'
%              for ICA components or '.datersp', '.datitc' for data channels.
% Usage:  
%              >> [X times logfreqs ] = std_ersp(EEG, 'key', 'val', ...);
% Inputs:
%   EEG          - a loaded epoched EEG dataset structure. May be an array
%                  of such structure containing several datasets.
%
% Other inputs:
%   'trialindices' - [cell array] indices of trials for each dataset.
%                  Default is EMPTY (no trials). NEEDS TO BE SET.
%   'components' - [numeric vector] components of the EEG structure for which 
%                  activation spectrum will be computed. Note that because 
%                  computation of ERP is so fast, all components spectrum are
%                  computed and saved. Only selected component 
%                  are returned by the function to Matlab
%                  {default|[] -> all}
%   'channels'   - [cell array] channels of the EEG structure for which 
%                  activation spectrum will be computed. Note that because 
%                  computation of ERP is so fast, all channels spectrum are
%                  computed and saved. Only selected channels 
%                  are returned by the function to Matlab
%                  {default|[] -> none}
%   'recompute'  - ['on'|'off'] force recomputing data file even if it is 
%                  already on disk.
%   'rmcomps'    - [integer array] remove artifactual components (this entry
%                  is ignored when plotting components). This entry contains 
%                  the indices of the components to be removed. Default is none.
%   'interp'     - [struct] channel location structure containing electrode
%                  to interpolate ((this entry is ignored when plotting 
%                  components). Default is no interpolation.
%   'fileout'    - [string] name of the file to save on disk. The default
%                  is the same name (with a different extension) as the 
%                  dataset given as input.
%  'savetrials'  - ['on'|'off'] save single-trials ERSP. Requires a lot of disk
%                  space (dataset space on disk times 10) but allow for refined
%                  single-trial statistics. This option is obsolete as
%                  trials are now always saved.
%  'savefile'    - ['on'|'off'] save file or simply return measures.
%                  Default is to save files ('on').
%  'getparams'   - ['on'|'off'] return optional parameters for the newtimef 
%                  function (and do not compute anything). This argument is
%                  obsolete (default is 'off').
%
% ERSP optional inputs:
%   'type'       - ['ersp'|'itc'|'ersp&itc'] save ERSP, ITC, or both data 
%                  types to disk {default: 'ersp'}
%   'freqs'      - [minHz maxHz] the ERSP/ITC frequency range to compute 
%                  and return. {default: 3 to EEG sampling rate divided by 3}
%   'timelimits' - [minms maxms] time window (in ms) to compute.
%                  {default: whole input epoch}.
%   'cycles'     - [wavecycles (factor)]. If 0 -> DFT (constant window length 
%                  across frequencies).
%                  If >0 -> the number of cycles in each analysis wavelet. 
%                  If [wavecycles factor], wavelet cycles increase with 
%                  frequency, beginning at wavecyles. (0 < factor < 1) 
%                  factor = 0 -> fixed epoch length (DFT, as in FFT). 
%                  factor = 1 -> no increase (standard wavelets)
%                  {default: [0]}
%   'padratio'   - (power of 2). Multiply the number of output frequencies 
%                  by dividing their frequency spacing through 0-padding.
%                  Output frequency spacing is (low_freq/padratio).
%   'alpha'      - If in (0, 1), compute two-tailed permutation-based 
%                  probability thresholds and use these to mask the output 
%                  ERSP/ITC images {default: NaN}
%   'powbase'    - Deprecated. Note that baseline can be readjusted after 
%                  computation as single trial spectral decompositions are stored.
%
% Other optional inputs:
%   This function will take any of the NEWTIMEF optional inputs (for instance
%   to compute log-space frequencies)...
%
% Outputs:
%   X         - the masked log ERSP/ITC of the requested ICA components/channels 
%               in the selected frequency and time range. Note that for
%               optimization reasons, this parameter is now empty or 0. X
%               thus must be read from the datafile saved on disk.
%   times     - vector of time points for which the ERSPs/ITCs were computed. 
%   logfreqs  - vector of (equally log spaced) frequencies (in Hz) at which the 
%               log ERSP/ITC was evaluated. 
%   parameters - parameters given as input to the newtimef function.
%
% Files written or modified:     
%              [dataset_filename].icaersp   <-- saved component ERSPs
%              [dataset_filename].icaitc    <-- saved component ITCs
%              [dataset_filename].icatimef  <-- saved component single
%                                               trial decompositions.
%  OR for channels
%              [dataset_filename].datersp   <-- saved channel ERSPs
%              [dataset_filename].datitc    <-- saved channel ITCs
%              [dataset_filename].dattimef  <-- saved channel single
%                                               trial decompositions.
% Example: 
%            % Create mean ERSP and ITC images on disk for all comps from 
%            % dataset EEG use three-cycle wavelets (at 3 Hz) to more than 
%            % three-cycle wavelets at 50 Hz. See >> help newtimef 
%            % Return the (equally log-freq spaced, probability-masked) ERSP.
%            >> [Xersp, times, logfreqs] = std_ersp(EEG, ...
%                       'type', 'ersp', 'freqs', [3 50], 'cycles', [3 0.5]);
%
% See also: NEWTIMEF, STD_ERP, STD_SPEC, STD_TOPO, STD_PRECLUST
%
% Authors: Arnaud Delorme, Hilit Serby, SCCN, INC, UCSD, January, 2005-

% Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, October 11, 2004, arno@sccn.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.

function [X, times, logfreqs, parameters] = std_ersp(EEG, varargin)

if nargin < 1
    help std_ersp;
    return;
end

X = [];
options = {};
if length(varargin) > 1 
    if ~ischar(varargin{1})
        if length(varargin) > 0, options = { options{:} 'components' varargin{1} }; end
        if length(varargin) > 1, options = { options{:} 'freqs'      varargin{2} }; end
        if length(varargin) > 2, options = { options{:} 'timewindow' varargin{3} }; end
        if length(varargin) > 3, options = { options{:} 'cycles'     varargin{4} }; end
        if length(varargin) > 4, options = { options{:} 'padratio'   varargin{5} }; end
        if length(varargin) > 5, options = { options{:} 'alpha'      varargin{6} }; end
        if length(varargin) > 6, options = { options{:} 'type'       varargin{7} }; end
        if length(varargin) > 7, options = { options{:} 'powbase'    varargin{8} }; end
    else
        options = varargin;
    end
end

[g, timefargs] = finputcheck(options, { ...
                        'components'    'integer'               []          [];
                        'channels'      { 'cell','integer' }    { [] [] }   {};
                        'powbase'       'real'                  []          [];
                        'trialindices' { 'integer','cell' }     []          [];
                        'savetrials'    'string'      { 'on','off' }        'off';
                        'plot'        'string'      { 'on','off' }    'off';
                        'recompute'   'string'      { 'on','off' }    'off';
                        'getparams'     'string'      { 'on','off' }        'off';
                        'savefile'      'string'      { 'on','off' }        'on';
                        'parallel'      'string'      { 'on','off' }        'off';
                        'timewindow',    'real',    [], [];
                        'fileout',       'string',  [], '';
                        'timelimits'    'real'                  []          [EEG(1).xmin EEG(1).xmax]*1000;
                        'cycles'        'real'                  []          [3 .5];
                        'padratio'      'real'                  []          1;
                        'trialinfo'     'struct'                []          struct([]);
                        'freqs'         'real'                  []          [0 EEG(1).srate/2];
                        'rmcomps'       'cell'                  []          cell(1,length(EEG));
                        'interp'        'struct'                { }         struct([]);
                        'freqscale'     'string'                []         'log';
                        'alpha'         'real'                  []          NaN;
                        'baseline'      'real'                  []          0;
                        'type'          'string'      { 'ersp','itc','both','ersp&itc' }  'both'}, 'std_ersp', 'ignore');
if ischar(g), error(g); end
if isempty(g.trialindices), g.trialindices = cell(length(EEG)); end
if ~iscell(g.trialindices), g.trialindices = { g.trialindices }; end
if ~isempty(g.powbase), disp('''powbase'' parameter is no longer supported at computation time'); end

% checking input parameters
% -------------------------
if isempty(g.components) && isempty(g.channels)
    if isempty(EEG(1).icaweights)
        error('EEG.icaweights not found');
    end
    g.components = 1:size(EEG(1).icaweights,1);
    disp('Computing ERSP with default values for all components of the dataset');
end

% select ICA components or data channels
% --------------------------------------
if isempty(g.fileout), g.fileout = fullfile(EEG(1).filepath, EEG(1).filename(1:end-4)); end
if ~isempty(g.components)
    g.indices = g.components;
    prefix = 'comp';
    filenameersp   = [ g.fileout '.icaersp'  ];
    filenameitc    = [ g.fileout '.icaitc'   ];
    filenametrials = [ g.fileout '.icatimef' ];    
    if ~isempty(g.channels)
        error('Cannot compute ERSP/ITC for components and channels at the same time');
    end
elseif ~isempty(g.channels)
    if iscell(g.channels)
        if ~isempty(g.interp)
            g.indices = eeg_chaninds(g.interp, g.channels, 0);
        else
            g.indices = eeg_chaninds(EEG(1), g.channels, 0);
            for ind = 2:length(EEG)
                if ~isequal(eeg_chaninds(EEG(ind), g.channels, 0), g.indices)
                    error([ 'Channel information must be consistent when ' 10 'several datasets are merged for a specific design' ]);
                end
            end
        end
    else
        g.indices = g.channels;
    end
    prefix = 'chan';
    filenameersp   = [ g.fileout '.datersp'  ];
    filenameitc    = [ g.fileout '.datitc'   ];
    filenametrials = [ g.fileout '.dattimef' ];    
end

% Check if ERSP/ITC information found in datasets and if fits requested parameters 
% ----------------------------------------------------------------------------
if exist( filenametrials ) && strcmpi(g.recompute, 'off')
    fprintf('Use existing file for ERSP: %s; check the ''recompute checkbox'' to force recomputing.\n', filenameersp);
    return;
end

% Compute ERSP parameters
% -----------------------
parameters = { 'cycles', g.cycles, 'padratio', g.padratio, ...
               'alpha', g.alpha, 'freqscale', g.freqscale, timefargs{:} };
defaultlowfreq = 3;
[time_range] = compute_ersp_times(g.cycles,  EEG(1).srate, ...
                                 [EEG(1).xmin EEG(1).xmax]*1000 , defaultlowfreq, g.padratio); 
if time_range(1) < time_range(2) && g.freqs(1) == 0
     g.freqs(1) = defaultlowfreq; % for backward compatibility
end
parameters = { parameters{:} 'freqs' g.freqs };
if strcmpi(g.plot, 'off')
    parameters = { parameters{:} 'plotersp', 'off', 'plotitc', 'off', 'plotphase', 'off' };
end
parameters{end+1} = 'baseline';
parameters{end+1} = g.baseline;

% return parameters
% -----------------
if strcmpi(g.getparams, 'on')
    X = []; times = []; logfreqs = [];
    if strcmpi(g.savetrials, 'on')
        parameters = { parameters{:} 'savetrials', g.savetrials };
    end
    return;
end

options = {};
if ~isempty(g.rmcomps), options = { options{:} 'rmcomps' g.rmcomps }; end
if ~isempty(g.interp),  options = { options{:} 'interp' g.interp }; end
if isempty(g.channels)
     X = eeg_getdatact(EEG, 'component', g.indices, 'trialindices', g.trialindices );
else X = eeg_getdatact(EEG, 'channel'  , g.indices, 'trialindices', g.trialindices, 'rmcomps', g.rmcomps, 'interp', g.interp);
end
if size(X, 3) == 1
    error('The data is continuous for one of the dataset. ERSP can only be computed when data trials are present');
end

% frame range
% -----------
pointrange1 = round(max((g.timelimits(1)/1000-EEG(1).xmin)*EEG(1).srate, 1));
pointrange2 = round(min(((g.timelimits(2)+1000/EEG(1).srate)/1000-EEG(1).xmin)*EEG(1).srate, EEG(1).pnts));
pointrange = [pointrange1:pointrange2];

% Compute ERSP && ITC
% ------------------
allTrialsTmp   = cell(1,length(g.indices));
allTrialsTime  = cell(1,length(g.indices));
allTrialsFreqs = cell(1,length(g.indices));
eeglab_options;
usesingle = option_single;

disp('Computing time/frequency decomposition...');
parfor k = 1:length(g.indices)
    tmpparams = parameters;
    if length(g.indices) > 1
        tmpparams{end+1} = 'verbose';
        tmpparams{end+1} = 'off';
    end
    
    % Run timef() to get ERSP
    % ------------------------
    timefdata  = reshape(X(k,pointrange,:), 1, length(pointrange)*size(X,3));
    mytimes = [];
    mylogfreqs = [];
    alltfX   = [];
    if ~isempty(timefdata)
        [logersp,logitc,logbase,mytimes,mylogfreqs,logeboot,logiboot,alltfX] ...
              = newtimef( timefdata, length(pointrange), g.timelimits, EEG(1).srate, tmpparams{2:end});
        %figure; newtimef( TMP.data(32,:), EEG.pnts, [EEG.xmin EEG.xmax]*1000, EEG.srate, cycles, 'freqs', freqs);
        %figure; newtimef( timefdata, length(pointrange), g.timelimits, EEG.srate, cycles, 'freqs', freqs);
    end
    %if strcmpi(g.plot, 'on'), return; end
    if usesingle
        alltfX = single(alltfX);
    end

    allTrialsTmp{k}   = single( alltfX );
    allTrialsTime{k}  = mytimes;
    allTrialsFreqs{k} = mylogfreqs;
end
all_trials = [];
for k = 1:length(g.indices)  % for each (specified) component/channel
    all_trials = setfield( all_trials, [ prefix int2str(g.indices(k)) ], allTrialsTmp{k});
end
X = allTrialsTmp{1};

% Save ERSP into file
% -------------------
logfreqs             = allTrialsFreqs{1};
times                = allTrialsTime{1};
all_trials.freqs     = allTrialsFreqs{1};
all_trials.times     = allTrialsTime{1};
all_trials.parameters = { options{:} parameters{:} };
all_trials.datatype   = 'TIMEF';
all_trials.datafiles  = computeFullFileName( { EEG.filepath }, { EEG.filename });
all_trials.datatrials = g.trialindices;

all_trials.parameters = parameters;
if ~isempty(g.channels)
    if ~isempty(g.interp)
        all_trials.labels = { g.interp(g.indices).labels };
    elseif ~isempty(EEG(1).chanlocs)
        tmpchanlocs = EEG(1).chanlocs;
        all_trials.labels = { tmpchanlocs(g.indices).labels };
    end
end
all_trials.trialinfo = g.trialinfo;

if strcmpi(g.savefile, 'on')
    std_savedat( filenametrials , all_trials );
end

% compute full file names
% -----------------------
function res = computeFullFileName(filePaths, fileNames);
for index = 1:length(fileNames)
    res{index} = fullfile(filePaths{index}, fileNames{index});
end

