% STD_EDITSET - modify a STUDY set structure.
%
% Usage: 
%             >> [STUDY, ALLEEG] = std_editset(STUDY, ALLEEG, key1, val1, ...);  
% Inputs:
%   STUDY      - EEGLAB STUDY set
%   ALLEEG     - vector of the EEG datasets included in the STUDY structure 
%
% Optional inputs:
%   'commands' - {cell_array} change STUDY (see command description and
%                 example below.
%   'name'     - [string] specify a (mnemonic) name for the STUDY structure. 
%                {default: ''}
%   'task'     - [string] attach a description of the experimental task(s) 
%                performed by the STUDY subjects {default: ''}.  
%   'filename' - [string] filename for the STUDY set.
%   'filepath' - [string] file path (directory/folder) in which the STUDY file
%                will be saved.  
%   'addchannellabels' - ['on'|'off'] add channel labels ('1', '2', '3', ...)
%                to all datasets of a STUDY to ensure that all STUDY functions
%                will work {default: 'off' unless no dataset has channel
%                locations and then it is automatically set to on}
%   'notes'    - [string] notes about the experiment, the datasets, the STUDY, 
%                or anything else to store with the STUDY itself {default: ''}. 
%   'updatedat' - ['on'|'off'] update 'subject' 'session' 'condition' and/or
%                'group' fields of STUDY dataset(s). Default is 'on'.
%   'savedat'   - ['on'|'off'] re-save datasets
%   'inbrain'   - ['on'|'off'] select components for clustering from all STUDY 
%                 datasets with equivalent dipoles located inside the brain volume. 
%                 Dipoles are selected based on their residual variance and their 
%                 location {default: 'off'}
%   'resave'    - ['on'|'off'] save or resave STUDY {default: 'off'}
%
% Each of the 'commands' (above) is a cell array composed of any of the following: 
%   'index'     - [integer] modify/add dataset index. Note that if a
%                 dataset is added and that this leaves some indices not 
%                 populated, the dataset is automatically set to the last
%                 empty index. For instance creating a STUDY with a single
%                 dataset at index 10 will result with a STUDY with a
%                 single dataset at index 1.
%   'subject'   - [string] subject code.
%   'condition' - [string] dataset condition. 
%   'session '  - [integer] dataset session number.
%   'group'     - [string] dataset group.
%   'task'      - [string] dataset task.
%   'load'      - [filename] load dataset from specified filename. Can be 
%                 an EEGLAB dataset or any file supported by File-IO (if 
%                 it is installed from the plugin manager).
%   'dipselect' - [float<1] select components for clustering from all STUDY 
%                 datasets with dipole model residual var. below this value. 
%   'inbrain'   - ['on'|'off'] same as above. This option may also be
%                 placed in the command list (preceding the 'dipselect'
%                 option).
%   'remove'    - deprecated. Use the STD_RMDAT function instead to remove
%                 dataset.
%
% Outputs:
%   STUDY      - a new STUDY set containing some or all of the datasets in ALLEEG, 
%                plus additional information from the optional inputs above. 
%   ALLEEG     - a vector of EEG datasets included in the STUDY structure 
%
% Example: [STUDY, EEG] = std_editset(STUDY, EEG, 'commands',{{'remove',4}},'updatedat','on','rmclust','on');
%
%  See also:  POP_CREATESTUDY, STD_LOADALLEEG, POP_CLUST, POP_PRECLUST, 
%             EEG_PRECLUST, EEG_CREATEDATA
%
% Authors: Arnaud Delorme, Hilit Serby, SCCN/INC/UCSD, October , 2004-

% Copyright (C) Arnaud Delorme & Scott Makeig, SCCN/INC/UCSD, October 11, 2004, smakeig@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 [STUDY, ALLEEG] = std_editset(STUDY, ALLEEG, varargin) 

if (nargin < 3)
    help std_editset;
    return;
end

% decode input parameters
% -----------------------
g = finputcheck(varargin, { 'updatedat' 'string'  { 'on','off' }  'on';
                            'name'      'string'  { }             '';
                            'task'      'string'  { }             '';
                            'notes'     'string'  { }             '';
                            'filename'  'string'  { }             '';
                            'filepath'  'string'  { }             '';
                            'resave'    'string'  { 'on','off','info' }  'off';
                            'savedat'   'string'  { 'on','off' }  'off';
                            'addchannellabels' 'string'  { 'on','off' }  'off';
                            'rmclust'   'string'  { 'on','off' }  'on';
                            'inbrain'   'string'  { 'on','off' }  'off';
                            'commands'  'cell'    {}              {} }, 'std_editset');
if ischar(g), error(g); end

if isempty(STUDY), STUDY.history = 'STUDY = [];'; end
if ~isempty(g.name),  STUDY.name  = g.name; end
if ~isempty(g.task),  STUDY.task  = g.task; end
if ~isempty(g.notes), STUDY.notes = g.notes; end

% default addchannellabels
% ------------------------
if ~isempty(ALLEEG)
    allchanlocs = { ALLEEG.chanlocs };
    if all(cellfun( @isempty, allchanlocs))
        g.addchannellabels = 'on';
    else
        if any(cellfun( @isempty, allchanlocs))
            error( [ 'Some datasets have channel locations and some other don''t' 10 ...
                     'the STUDY is not homogeneous and cannot be created.' ]);
        end
    end
end

% make one cell array with commands
% ---------------------------------
allcoms = {};
if ~isempty(g.commands)
    if iscell(g.commands{1})
        for k = 1:length(g.commands)
            % put index field first
            indindex   = strmatch('index', lower(g.commands{k}(1:2:end)));
            if ~isempty(indindex)
                 tmpcom = { 'index' g.commands{k}{2*(indindex-1)+1+1} g.commands{k}{:} };
            else tmpcom = g.commands{k}; 
            end
            allcoms = { allcoms{:} tmpcom{:} };
        end
    else 
        allcoms = g.commands;
    end
end
g.commands = allcoms;

% add 'dipselect' command if 'inbrain' option is selected
% ---------------------------------
dipselectExists = false;
for k = 1:2:length(g.commands)
    if strcmp(g.commands{k},'dipselect')
       dipselectExists = true;
    end
end
if strcmp(g.inbrain,'on') && ~dipselectExists
    g.commands{length(g.commands)+1} = 'dipselect';
    g.commands{length(g.commands)+1} = 0.15;
end

% copy values
% -----------
if ~isfield(STUDY, 'datasetinfo')
    for realindex = 1:length(ALLEEG)
        if ~isempty(ALLEEG(realindex).data)
            [tmppath tmpfile tmpext] = fileparts(  fullfile(ALLEEG(realindex).filepath, ALLEEG(realindex).filename) );
            STUDY.datasetinfo(realindex).filepath  = tmppath;   
            STUDY.datasetinfo(realindex).filename  = [ tmpfile tmpext ];   
            STUDY.datasetinfo(realindex).subject   = ALLEEG(realindex).subject;
            STUDY.datasetinfo(realindex).session   = ALLEEG(realindex).session;
            STUDY.datasetinfo(realindex).run       = ALLEEG(realindex).run;
            STUDY.datasetinfo(realindex).condition = ALLEEG(realindex).condition;
            STUDY.datasetinfo(realindex).group     = ALLEEG(realindex).group;       
            if isfield(ALLEEG(realindex), 'task')   
                STUDY.datasetinfo(realindex).task      = ALLEEG(realindex).task;                 
            end
        else
            error('Dataset %d has no data, remove dataset and try again\n', realindex)
        end
    end
end

% execute commands
% ----------------
currentind = 1;
rmlist = [];
fullFileNames = {};
for k = 1:2:length(g.commands)
    
    switch g.commands{k}
        case 'index'
            currentind = g.commands{k+1};
        case 'subject'
            STUDY.datasetinfo(currentind).subject = g.commands{k+1};
        case 'comps'
            STUDY.datasetinfo(currentind).comps = g.commands{k+1};
        case 'condition'
            STUDY.datasetinfo(currentind).condition = g.commands{k+1};
        case 'group'
            STUDY.datasetinfo(currentind).group   = g.commands{k+1};
        case 'session' 
            STUDY.datasetinfo(currentind).session = g.commands{k+1};
        case 'run' 
            STUDY.datasetinfo(currentind).run     = g.commands{k+1};
        case 'task' 
            STUDY.datasetinfo(currentind).task    = g.commands{k+1};
        case 'remove'
            % create empty structure
            allfields = fieldnames(ALLEEG);
            tmpfields = allfields;
            tmpfields(:,2) = cell(size(tmpfields));
            tmpfields = tmpfields';
            ALLEEG(g.commands{k+1}) = struct(tmpfields{:});

            % create empty structure
            allfields = fieldnames(STUDY.datasetinfo);
            tmpfields = allfields;
            tmpfields(:,2) = cell(size(tmpfields));
            tmpfields = tmpfields';
            STUDY.datasetinfo(g.commands{k+1}) = struct(tmpfields{:});
            
            if isfield(STUDY.datasetinfo, 'index')
                STUDY.datasetinfo = rmfield(STUDY.datasetinfo, 'index');
            end
            STUDY.datasetinfo(1).index = [];
            STUDY.changrp = [];
        case 'return', return;
        case 'inbrain' 
            g.inbrain = g.commands{k+1};
        case 'dipselect'
            STUDY = std_checkset(STUDY, ALLEEG);
            rv = g.commands{k+1};
            clusters = std_findsameica(ALLEEG);
            
            for cc = 1:length(clusters)
                
                idat = 0;
                for tmpi = 1:length(clusters{cc})
                    if isfield(ALLEEG(clusters{cc}(tmpi)).dipfit, 'model')
                        idat = clusters{cc}(tmpi);
                    end
                end
                
                indleft = [];
                if rv ~= 1
                    if idat ~= 0
                        if strcmp(g.inbrain,'on')
                            fprintf('Selecting dipoles with less than %%%2.1f residual variance and removing dipoles outside brain volume in dataset ''%s''\n', ...
                                100*rv, ALLEEG(idat).setname);
                            indleft = eeg_dipselect(ALLEEG(idat), rv*100,'inbrain'); 
                        else
                           fprintf('Selecting dipoles with less than %%%2.1f residual variance in dataset ''%s''\n', ...
                                100*rv, ALLEEG(idat).setname);
                            indleft = eeg_dipselect(ALLEEG(idat), rv*100,'rv'); 
                        end
                    else
                        fprintf('No dipole information found in ''%s'' dataset, using all components\n', ALLEEG.setname)
                    end
                end
                for tmpi = 1:length(clusters{cc})                
                    STUDY.datasetinfo(clusters{cc}(tmpi)).comps = indleft;
                end
            end
            STUDY.cluster = [];
            STUDY = std_checkset(STUDY, ALLEEG); % recreate parent dataset           
           
        case 'load'
            if ~isempty(strmatch(g.commands{k+1}, fullFileNames))
                error(['Duplicate dataset %s in STUDY.' 10 ...
                    'Copy and rename the dataset if you absolutely want to use it twice'], g.commands{k+1})
            end
            fullFileNames{end+1} = g.commands{k+1};

            TMPEEG = std_loadalleeg( { g.commands{k+1} } );
            ALLEEG = eeg_store(ALLEEG, eeg_checkset(TMPEEG), currentind);
            ALLEEG(currentind).saved = 'yes';
            
            % update datasetinfo structure
            % ----------------------------
            [tmppath, tmpfile, tmpext] = fileparts( fullfile(ALLEEG(currentind).filepath, ...
                                                           ALLEEG(currentind).filename) );
            STUDY.datasetinfo(currentind).filepath  = tmppath;   
            STUDY.datasetinfo(currentind).filename  = [ tmpfile tmpext ];   
            STUDY.datasetinfo(currentind).subject   = ALLEEG(currentind).subject;
            STUDY.datasetinfo(currentind).session   = ALLEEG(currentind).session;
            STUDY.datasetinfo(currentind).run       = ALLEEG(currentind).run;
            STUDY.datasetinfo(currentind).condition = ALLEEG(currentind).condition;
            STUDY.datasetinfo(currentind).group     = ALLEEG(currentind).group;      
            STUDY.datasetinfo(currentind).index     = currentind;    
            if isfield(ALLEEG(currentind), 'task')   
                STUDY.datasetinfo(currentind).task  = ALLEEG(currentind).task;                 
            end
        otherwise
            % running custom command
            cleanname = cleanvarname(g.commands{k});
            cleanname(cleanname == ' ') = [];
            cleanname(cleanname == ',') = [];
            cleanname(cleanname == '.') = [];
            cleanname(cleanname == '-') = [];
            STUDY.datasetinfo(currentind).(cleanname) = g.commands{k+1};
    end
end

% add channel labels automatically
% -------------------------------
if strcmpi(g.addchannellabels, 'on')
    disp('Generating channel labels for all datasets...');
    for currentind = 1:length(ALLEEG)
        for ind = 1:ALLEEG(currentind).nbchan
            ALLEEG(currentind).chanlocs(ind).labels = int2str(ind);
        end
    end
    ALLEEG(currentind).saved = 'no';
    g.savedat = 'on';
end
    
% update ALLEEG structure?
% ------------------------
if strcmpi(g.updatedat, 'on')
    for currentind = 1:length(ALLEEG)
        if ~strcmpi(ALLEEG(currentind).subject,   STUDY.datasetinfo(currentind).subject)
            ALLEEG(currentind).subject          = STUDY.datasetinfo(currentind).subject;
            ALLEEG(currentind).saved            = 'no';
        end
        if ~strcmpi(ALLEEG(currentind).condition, STUDY.datasetinfo(currentind).condition)
            ALLEEG(currentind).condition        = STUDY.datasetinfo(currentind).condition;
            ALLEEG(currentind).saved            = 'no';
        end
        if ~isequal(ALLEEG(currentind).session, STUDY.datasetinfo(currentind).session)
            ALLEEG(currentind).session          = STUDY.datasetinfo(currentind).session;
            ALLEEG(currentind).saved            = 'no';
        end
        if ~isequal(ALLEEG(currentind).run, STUDY.datasetinfo(currentind).run)
            ALLEEG(currentind).run              = STUDY.datasetinfo(currentind).run;
            ALLEEG(currentind).saved            = 'no';
        end
        if ~strcmpi(char(ALLEEG(currentind).group), char(STUDY.datasetinfo(currentind).group))
            ALLEEG(currentind).group            = STUDY.datasetinfo(currentind).group;
            ALLEEG(currentind).saved            = 'no';
        end
        if isfield(STUDY.datasetinfo, 'task')
            if ~isfield(ALLEEG, 'task') || ~strcmpi(char(ALLEEG(currentind).task), char(STUDY.datasetinfo(currentind).task))
                ALLEEG(currentind).task         = STUDY.datasetinfo(currentind).task;
                ALLEEG(currentind).saved        = 'no';
            end
        end
    end
end

% remove empty datasets (cannot be done above because some empty datasets
% might not have been removed)
% ---------------------
rmindex = [];
for index = 1:length(STUDY.datasetinfo)
    if isempty(STUDY.datasetinfo(index).subject) && isempty(ALLEEG(index).nbchan)
        rmindex = [ rmindex index ];
    end
end
STUDY.datasetinfo(rmindex) = [];
ALLEEG(rmindex)            = [];
for index = 1:length(STUDY.datasetinfo)
    STUDY.datasetinfo(index).index = index;
end

% remove empty ALLEEG structures
% ------------------------------
while length(ALLEEG) > length(STUDY.datasetinfo)
   ALLEEG(end) = [];
end
%[ ALLEEG STUDY.datasetinfo ] = remove_empty(ALLEEG, STUDY.datasetinfo);

% save datasets if necessary
% --------------------------
if strcmpi(g.savedat, 'on')
    for index = 1:length(ALLEEG)
        if isempty(ALLEEG(index).filename)
            fprintf('Cannot resave ALLEEG(%d) because the dataset has no filename\n', index);
        else
            TMP = pop_saveset(ALLEEG(index), 'savemode', 'resave');
            ALLEEG = eeg_store(ALLEEG, TMP, index);
            ALLEEG(index).saved = 'yes';
        end
    end
end

% remove cluster information if necessary
% ---------------------------------------
if strcmpi(g.rmclust, 'on')
    STUDY.cluster = [];
    STUDY.cache = [];
end

% save study if necessary
% -----------------------
if ~isempty(g.commands)
    STUDY.changrp = [];
    STUDY.cluster = [];
end
[STUDY, ALLEEG] = std_checkset(STUDY, ALLEEG);
if ~isempty(g.filename)
    [STUDY.filepath, STUDY.filename, ext] = fileparts(fullfile( g.filepath, g.filename ));
    STUDY.filename = [ STUDY.filename ext ];
    g.resave = 'on';
end
if strcmpi(g.resave, 'on')
    STUDY = pop_savestudy(STUDY, ALLEEG, 'savemode', 'resave');
end

% ---------------------
% remove empty elements
% ---------------------
function [ALLEEG, datasetinfo] = remove_empty(ALLEEG, datasetinfo);

    rmindex = [];
    for index = 1:length(datasetinfo)
        if isempty(datasetinfo(index).subject) && isempty(ALLEEG(index).nbchan)
            rmindex = [ rmindex index ];
        end
    end
    datasetinfo(rmindex) = [];
    ALLEEG(rmindex)      = [];
    for index = 1:length(datasetinfo)
        datasetinfo(index).index = index;
    end
    
    % remove empty ALLEEG structures
    % ------------------------------
    while length(ALLEEG) > length(datasetinfo)
       ALLEEG(end) = [];
    end
        
    
    
    
