% POP_IMPORTERPLAB - import ERPLAB event list file and bin file into
%                      EEGLAB event structure for use in STUDY processing
%
% Usage:
%   >>  OUTEEG = pop_sample( INEEG, file1, file2);
%
% Inputs:
%   INEEG   - input EEG dataset
%   file1   - ERPLAB event list text file
%   file2   - ERPLAB bin file
%   ncbins  - [0|1] import all bins including non-contrast bins. Default is
%             0 (no).
%    
% Outputs:
%   OUTEEG  - output dataset

% Copyright (C) 2016 Arnaud Delorme
%
% 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 [EEG, com] = pop_importerplab( EEG, file1, file2, ncbins)

% the command output is a hidden output that does not have to
% be described in the header

com = ''; % this initialization ensure that the function will return something
          % if the user press the cancel button            

% display help if not enough arguments
% ------------------------------------
if nargin < 1
	help pop_importerplab;
	return;
end
if nargin < 3
    ncbins = false;
end

% pop up window
% -------------
if nargin < 3
    promptstr    = { strvcat('Time-locking event type(s) ([]=all):', ...
                    'Select ''Edit > Event values'' to see type values.'), ...
                    'Epoch limits [start, end] in seconds:', ... 
                    'Name for the new dataset:', ... 
					'Out-of-bounds EEG rejection limits ([min max], []=none):'  };

    commandload1 = [ '[filename, filepath] = uigetfile(''*'', ''Select an event list text file'');' ...
        'if filename ~=0,' ...
        '   set(findobj(''parent'', gcbf, ''tag'', ''file1''), ''string'', [ filepath filename ]);' ...
        'end;' ...
        'clear filename filepath;' ];
    commandload2 = [ '[filename, filepath] = uigetfile(''*'', ''Select an bin text file'');' ...
        'if filename ~=0,' ...
        '   set(findobj(''parent'', gcbf, ''tag'', ''file2''), ''string'', [ filepath filename ]);' ...
        'end;' ...
        'clear filename filepath;' ];
   
    geometry = { [2 2 0.5] [2 2 0.5] [1] [1] };
    uilist = { { 'style' 'text'       'string' 'Select an event list text file ' } ...
              { 'style' 'edit'       'string' '' 'tag' 'file1' } ...
              { 'style' 'pushbutton' 'string' '...' 'callback' commandload1 } ...
              { 'style' 'text'       'string' 'Select a bin text file ' } ...
              { 'style' 'edit'       'string' '' 'tag' 'file2' } ...
              { 'style' 'pushbutton' 'string' '...' 'callback' commandload2 } ...
              {} ...
              { 'style' 'checkbox'   'string' 'Import all bins including non-contrast bins' 'tag' 'ncbins' } };
    [tmp1, tmp2, tmp3, results] = inputgui( geometry, uilist, 'pophelp(''pop_importerplab'');', 'Import ERPLAB info files -- pop_importerplab()' );

    file1  = results.file1;
    file2  = results.file2;
    ncbins = results.ncbins;
end
if isempty(file1), error(sprintf('Empty file %s', file1)); end
if isempty(file2), error(sprintf('Empty file %s', file2)); end
if ~exist(file1), error(sprintf('Could not find file %s', file1)); end
if ~exist(file2), error(sprintf('Could not find file %s', file2)); end

dataFile1 = loadtxt(file1);
dataFile2 = loadtxt(file2, 'delim', 9);

% decode bin file
% ---------------
bininfo = [];
metabin = [];
bCount = 1;
mCount = 1;
for iLine = 1:size(dataFile2,1)
    begPar = find(dataFile2{iLine} == '(');
    endPar = find(dataFile2{iLine} == ')');
    indEq  = find(dataFile2{iLine} == '=');
    labelInd = strfind( dataFile2{iLine}, 'label');
    if length(indEq) < 1
        error(sprintf('Could not find equal for line %d in file %s', iLine, file2));
    end
    label    = dataFile2{iLine}(labelInd+5:end);
    bin      = recodebin(dataFile2{iLine}(1:indEq(1)-1));
    if isempty(labelInd)
        error(sprintf('Could not find "label" for line %d in file %s', iLine, file2));
    end

    if length(begPar) < 1 || length(endPar) < 1 || begPar(1) > labelInd
        binrange = deblank(dataFile2{iLine}(indEq+1:labelInd-1));
        indMinus = find(binrange == '-');
        if length(indEq) < 1
            error(sprintf('Could not find minus in bin range for line %d in file %s', iLine, file2));
        end
        bin1 = recodebin(binrange(1:indMinus-1));
        bin2 = recodebin(binrange(indMinus+1:end));
        metabin(mCount).bin   = bin;
        metabin(mCount).binnum = str2num(bin(4:end));
        metabin(mCount).label = label;
        metabin(mCount).binrange = { bin1 bin2 };
        mCount = mCount+1;
    else
        bininfo(bCount).bin    = bin;
        bininfo(bCount).binnum = str2num(bin(4:end));
        bininfo(bCount).label  = label;
        bininfo(bCount).eventrange = str2num(dataFile2{iLine}(begPar(1)+1:endPar(1)-1));
        bCount = bCount+1;
    end
end

% scan events
% -----------
oldEvents = [dataFile1{:,1}]; % event type numerical
eventInds = [dataFile1{:,3}];
newName   = dataFile1(:,2);
for iEvent = 1:length(EEG.event)
    type = EEG.event(iEvent).type;
    typeInd = find(type == oldEvents);
    if length(typeInd) ~= 1
        error('Could not find type in event list');
    end
    typeIndForBin = eventInds(typeInd);
    EEG.event(iEvent).newtype  = recodeeventlabel(newName{typeInd});
    EEG.event(iEvent).eventind = typeIndForBin;
end
if ncbins
    for iEvent = 1:length(EEG.event)
        typeIndForBin = EEG.event(iEvent).eventind;
        for iBin = 1:length(bininfo)
            if any(typeIndForBin == bininfo(iBin).eventrange)
                 EEG.event(iEvent).(recodelabel(bininfo(iBin).label)) = 1;
            else EEG.event(iEvent).(recodelabel(bininfo(iBin).label)) = 0;
            end
        end
    end
end

% scan metabin and transform to bin
% ---------------------------------
wb = warning('backtrace');
warning('backtrace', 'off');
for iMeta = 1:length(metabin)
    allBins = [ bininfo.binnum ];
    bin1 = str2num(metabin(iMeta).binrange{1}(4:end));
    bin2 = str2num(metabin(iMeta).binrange{2}(4:end));
    indBin1 = find(allBins == bin1);
    indBin2 = find(allBins == bin2);
    if length(indBin1) ~= 1 || length(indBin2) ~= 1
        warning([ 'Cannot calculate contrast for ' metabin(iMeta).bin ' using ' metabin(iMeta).binrange{1} ' which is already a contrast bin' ]); 
    else
        if length(indBin2) ~= 1, error([ metabin(iMeta).binrange{2} ' not found or found twice' ]); end
        if ~isempty(intersect(bininfo(indBin1).eventrange, bininfo(indBin2).eventrange))
            warning([ 'cannot calculate contrast for ' metabin(iMeta).bin ' because ' metabin(iMeta).binrange{1} ' and ' metabin(iMeta).binrange{2} ' share some common event codes' ]);
        else
            for iEvent = 1:length(EEG.event)
                type = EEG.event(iEvent).eventind;
                if any(type == bininfo(indBin1).eventrange)
                    EEG.event(iEvent).(recodelabel(metabin(iMeta).label)) = doubledeblank(bininfo(indBin1).label);
                elseif any(type == bininfo(indBin2).eventrange)
                    EEG.event(iEvent).(recodelabel(metabin(iMeta).label)) = doubledeblank(bininfo(indBin2).label);
                else
                    EEG.event(iEvent).(recodelabel(metabin(iMeta).label)) = 'none of these';
                end
            end
        end
    end
end
warning(wb);

% return the string command
% -------------------------
com = sprintf('EEG = pop_importerplab( EEG, ''%s'',''%s'', %d);', file1, file2, ncbins);

function str = recodebin(str)

str = lower(str);
str = doubledeblank(str);
if str(1) ~= 'b'
    str = [ 'bin' str ];
elseif str(2) ~= 'i'
    str = [ 'bin' str(2:end) ];
end

function str = recodeeventlabel(str)
str(find(str == '"')) = [];

function str = recodelabel(str)

str = doubledeblank(str);
str(find(str == ' ')) = '_';
str(find(str == ')')) = '_';
str(find(str == '(')) = '_';
str(find(str == '/')) = '_';
str(find(str == '-')) = '_';
str(find(str == '&')) = '_';
if ~isempty(str2num(str(1)))
    str = ['f' str];
end

function str = doubledeblank(str)

str = deblank(str(end:-1:1));
str = deblank(str(end:-1:1));
