function [signal,H] = sload(FILENAME,varargin)
% SLOAD loads signal data of various data formats
% [signal,header] = sload(FILENAME)
% [signal,header] = sload(FILENAME,CHAN)
%       read selected channels in list CHAN
%       CHAN=0 [default], reads all channels
%
% [signal,header] = sload(dir('f*.emg') ... )
% [signal,header] = sload('f*.emg' ...)
%  	loads all files 'f*.emg'
%
% [signal,header] = sload(..., PropertyName1,PropertyValue1,...)
%  	PropertyName(s)		PropertyValue
%	'UCAL'			-		data uncalibrated (not scaled)
%	'OVERFLOWDETECTION'	'On'		[default]
%				'Off'		no overflow detection
%	'OUTPUT'		'single'	single precision data [default: 'double']
%	'NUMBER_OF_NAN_IN_BREAK'   N		inserts N NaN's between two concatanated segments
%						default: N=100
%	'SampleRate'		Fs		target sampling rate (supports resampling)
%	'EOG_CORRECTION'	'On'		uses two-channel regression analysis - if possible
%				'Off'		no correction of EOG artifacts [default]
%       'CNT32', '32bit'			force CNT 32bit format
%
% The list of supported formats is available here:
% http://pub.ist.ac.at/~schloegl/biosig/TESTED
%
%    SLOAD loads all the data (of the selected channels)
%    at once. In case of large data files, This can be
%    a problem. Instead, You can use
%       HDR = sopen(FILENAME,'r');
%       [s,HDR]=sread(HDR,duration_segment1);
%       [s,HDR]=sread(HDR,duration_segment2);
%       ....
%       [s,HDR]=sread(HDR,duration_segmentM);
%       HDR = sclose(HDR);
%
% see also: SVIEW, SOPEN, SREAD, SCLOSE, SAVE2BKR, TLOAD
%
% In order to increase the speed, install mexSLOAD.mex from biosig4c++
%
% Reference(s):

%	$Id: sload.m 2656 2011-03-18 22:30:26Z schloegl $
%	Copyright (C) 1997-2007,2008,2009,2010,2011 by Alois Schloegl
%    	This is part of the BIOSIG-toolbox http://biosig.sf.net/

%
%    BioSig is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    BioSig is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with BioSig.  If not, see <http://www.gnu.org/licenses/>.

if length(varargin)<2;
	MODE = '';
else
	MODE = varargin{2};
end;

CHAN = 0;
STATE.CNT32 = 0;
STATE.UCAL = 0;
STATE.OVERFLOWDETECTION = 1;
STATE.NUMBER_OF_NAN_IN_BREAK = 100;
STATE.EOG_CORRECTION = 0 ;
STATE.OUTPUT = 'double';

Fs = NaN;
k = 1;
while (k<=length(varargin))
	if isnumeric(varargin{k})
		if (k==1), CHAN=varargin{k}; end;
	elseif ~isempty(strfind(varargin{k},'UCAL'))
		MODE = varargin{k};
		if strcmpi(varargin{k+1},'on');
			STATE.UCAL = 1;
			k=k+1;
		elseif strcmpi(varargin{k+1},'off');
			STATE.UCAL = 0;
			k=k+1;
		else
			STATE.UCAL = 1;
		end;
	elseif ~isempty(strfind(varargin{k},'OVERFLOWDETECTION:OFF'))
		MODE = varargin{k};
		STATE.OVERFLOWDETECTION = 0;
	elseif strcmpi(varargin{k},'OVERFLOWDETECTION')
		if strcmpi(varargin{k+1},'on');
			STATE.OVERFLOWDETECTION = 1;
		elseif strcmpi(varargin{k+1},'off');
			STATE.OVERFLOWDETECTION = 0;
		elseif isnumeric(varargin{k+1})
			STATE.OVERFLOWDETECTION = varargin{k+1};
		end;
		k=k+1;
	elseif ~isempty(strfind(varargin{k},'OUTPUT:SINGLE'))
		MODE = varargin{k};
		STATE.OUTPUT = 'single';
	elseif strcmpi(varargin{k},'OUTPUT')
		STATE.OUTPUT = varargin{k+1};
	elseif strcmpi(varargin{k},'EOG_CORRECTION')
		if strcmpi(varargin{k+1},'on');
			STATE.EOG_CORRECTION = 1;
		elseif strcmpi(varargin{k+1},'off');
			STATE.EOG_CORRECTION = 0;
		elseif isnumeric(varargin{k+1})
			STATE.EOG_CORRECTION = varargin{k+1};
		end;
		k=k+1;
	elseif strcmpi(varargin{k},'Number_of_nan_in_break')
		STATE.NUMBER_OF_NAN_IN_BREAK = varargin{k+1};
		k=k+1;
	elseif strcmpi(varargin{k},'SampleRate')
		Fs = varargin{k+1};
		k=k+1;
	elseif strcmpi(varargin{k},'CNT32') || strcmpi(varargin{k},'32bit')
		STATE.CNT32 = 1;
	elseif exist(varargin{k},'file') && (k==1)
	        CHAN = varargin{k};
	end;
	k=k+1;
end;

%%% resolve wildcards %%%
if ischar(FILENAME)
if any(FILENAME=='*')
        p = fileparts(FILENAME);
        f = dir(FILENAME);
        EOGix = zeros(1,length(f));
        for k = 1:length(f);
                f(k).name = fullfile(p,f(k).name);
                [p,g,e]   = fileparts(f(k).name);
                lg = length(g);
                if (lg>2) && strcmp(upper(g(lg+(-2:0))),'EOG')
                        EOGix(k) = 1;
                end
        end;
        FILENAME={f([find(EOGix),find(~EOGix)]).name};
end;
end;

if nargout>0,
	signal = [];
end;

if (iscell(FILENAME) && (length(FILENAME)==1)),
	FILENAME = FILENAME{k};
end;

if ((iscell(FILENAME) || isstruct(FILENAME))),
	signal = [];
	for k = 1:length(FILENAME),
		if iscell(FILENAME(k))
			f = FILENAME{k};
		else
			f = FILENAME(k);
		end

                [s,h] = sload(f,varargin{:});
		if k==1,
			H = h;
			signal = s;
			H.SegLen = [0,size(s,1)];
                        %H.EVENT.POS = [H.EVENT.POS; 0];
                        %H.EVENT.TYP = [H.EVENT.TYP; hex2dec('7ffe')];
                        %if isfield(H.EVENT,'CHN');
                        %        H.EVENT.CHN = [H.EVENT.CHN; 0];
                        %end;
                        %if isfield(H.EVENT,'DUR');
                        %        H.EVENT.DUR = [H.EVENT.DUR; size(s,1)];
                        %end;
                        %if isfield(H.EVENT,'Desc');	% TFM-Excel-Beat-to-Beat
                        %        H.EVENT.Desc = [H.EVENT.Desc; {'New Segment'}; h.EVENT.Desc];
                        %end;
		else
			H.FILE(k) = h.FILE;
                        H.T0(k,1:6) = h.T0;
			if ~isnan(h.SampleRate) && (H.SampleRate ~= h.SampleRate),
				fprintf(2,'Warning SLOAD: sampling rates of multiple files differ %i!=%i.\n',H.SampleRate, h.SampleRate);
			end;
                        if size(s,2)==size(signal,2), %(H.NS == h.NS)
				signal = [signal; repmat(NaN,STATE.NUMBER_OF_NAN_IN_BREAK,size(s,2)); s];
				H.SegLen = [H.SegLen,size(signal,1)];
			else
				error('ERROR SLOAD: incompatible channel numbers %i!=%i of multiple files\n',H.NS,h.NS);
			end;

                        if ~isempty(h.EVENT.POS),
                                H.EVENT.POS = [H.EVENT.POS; size(signal,1); h.EVENT.POS+size(signal,1)-size(s,1)];
                                H.EVENT.TYP = [H.EVENT.TYP; hex2dec('7ffe'); h.EVENT.TYP];
                                if isfield(H.EVENT,'CHN');
        	                	H.EVENT.CHN = [H.EVENT.CHN; 0; h.EVENT.CHN];
                                end;
                                if isfield(H.EVENT,'DUR');
      					H.EVENT.DUR = [H.EVENT.DUR; size(s,1); h.EVENT.DUR];
                                end;
                                if isfield(H.EVENT,'Desc');	% TFM-Excel-Beat-to-Beat
                                        H.EVENT.Desc = [H.EVENT.Desc; {'New Segment'}; h.EVENT.Desc];
                                end;
                        end;

                        if ~isequal(H.Label,h.Label) || ~isequal(H.PhysDimCode,h.PhysDimCode)
				warning('Labels and PhysDim of multiple files differ!\n');
                                for k2 = 1:length(H.InChanSelect),
                                	k1 = H.InChanSelect(k2);
                                        if ~strcmp(H.Label{k1},h.Label{k1}) || (H.PhysDimCode(k1)~=h.PhysDimCode(k1)),
                                                warning('#%02i:  %s | %s | (%i)%s | (%i)%s\n',k1, H.Label{k1},h.Label{k1}, H.PhysDimCode(k1),H.PhysDim{k1},h.PhysDimCode(k1),h.PhysDim{k1});
                                        end;
                                end;
                        end;
                        if ~isequal(H.Calib,h.Calib) && H.FLAG.UCAL,
				error('SLOAD: concatanating uncalibrated data with different scaling factors does not make sense!');
                        end;

                        if isfield(H,'TriggerOffset'),
                                if H.TriggerOffset ~= h.TriggerOffset,
                                        fprintf(2,'Warning SLOAD: Triggeroffset %f does not fit.%f \n',H.TriggerOffset,h.TriggerOffset);
                                end;
                        end;

                        if isfield(h,'TRIG'),
                                if ~isfield(H,'TRIG'),
                                        H.TRIG = [];
                                end;
                                H.TRIG = [H.TRIG(:); h.TRIG(:)+size(signal,1)-size(s,1)];
                        else h.TRIG=[];
                        end;

                        if isfield(h,'Classlabel'),
				if isfield(h,'ArtifactSelection'),
                                	if (any(h.ArtifactSelection>1) || (length(h.ArtifactSelection) < length(h.Classlabel)))
                                        	sel = zeros(size(h.Classlabel));
                                                sel(h.ArtifactSelection) = 1;
                                        else
                                                sel = h.ArtifactSelection(:);
                                        end;
                                else
                                	h.ArtifactSelection = repmat(logical(0),length(h.Classlabel),1);
                                end;

                                if isfield(H,'ArtifactSelection')
                                	H.ArtifactSelection = [H.ArtifactSelection; h.ArtifactSelection(:)];
                                else
                                	H.ArtifactSelection = [repmat(logical(0),length(H.Classlabel),1); h.ArtifactSelection(:)];
                                end;
                                H.Classlabel = [H.Classlabel(:); h.Classlabel(:)];

                        elseif isfield(H,'Classlabel') && (length(h.TRIG)>0),
			        warning(sprintf('field classlabel missing in %s. This might corrupt the resulting classlabels!',h.FileName));

                        end;
			clear s;
                end;
	end;

	% fprintf(1,'SLOAD: data segments are concatenated with NaNs in between.\n');
	return;
end;
%%% end of multi-file section

%%%% start of single file section
%%%%%%%%%% --------- EOG CORRECTION -------------- %%%%%%
if STATE.EOG_CORRECTION,
try
        h = get_regress_eog(FILENAME,'REG fft16');
catch,
	fprintf(H.FILE.stderr,'Error: SLOAD (EOG_CORRECTION): %s\n',lasterr);
	H.FLAG.EOG_CORRECTION = 0;
end;
end;

FlagLoaded = 0;
if exist('mexSLOAD','file')==3,
	try
		valid_rerefmx = 1;
		if ischar(CHAN)
		        HDR = sopen(CHAN,'r'); HDR=sclose(HDR);
		        if isfield(HDR.Calib),
		                ReRefMx = HDR.Calib;
		        else
        			valid_rerefmx = 0;
		        end;
		elseif all(size(CHAN)>1) || any(floor(CHAN)~=CHAN) || any(CHAN<0) || (any(CHAN==0) && (numel(CHAN)>1));
		        ReRefMx = CHAN;
	        	CHAN = find(any(CHAN,2));
		elseif all(CHAN>0) && all(floor(CHAN)==CHAN),
			[tmp,ix]= sort(CHAN);
		        ReRefMx = sparse(CHAN,1:length(CHAN),1);
		else
		        ReRefMx = [];
			valid_rerefmx=0;
		end

		if STATE.EOG_CORRECTION,
			ReRefMx = h.r0*ReRefMx;
			valid_rerefmx=1;
		end
		if STATE.OVERFLOWDETECTION,
			arg1 = 'OVERFLOWDETECTION:ON';
		else
			arg1 = 'OVERFLOWDETECTION:OFF';
		end
		if STATE.UCAL,
			arg2 = {'UCAL:ON'};
		else
			arg2 = {'UCAL:OFF'};
		end
		if STATE.CNT32
			arg2 = {arg2,'CNT32'};
		end
		if ~valid_rerefmx,
			[signal,HDR] = mexSLOAD(FILENAME,0,arg1,arg2{:});
			if isfield(HDR,'ErrNum') && (HDR.ErrNum>0),
				fprintf(1,'%s\n',HDR.ErrMsg);
				H = HDR;
				x = HDR.nonsense; 	% hack: triggers CATCH branch in TRY-CATCH-END;
			end

			if isfield(HDR.FLAG,'ROW_BASED_CHANNELS') && HDR.FLAG.ROW_BASED_CHANNELS, signal = signal.'; end;
			FlagLoaded   = isfield(HDR,'NS');
			HDR.InChanSelect = 1:HDR.NS;
		else
			InChanSelect = find(any(ReRefMx,2));
			[signal,HDR] = mexSLOAD(FILENAME,InChanSelect,arg1,arg2{2});
			if isfield(HDR,'ErrNum') && HDR.ErrNum==3,
				%% file not found - fopen failed
				H = HDR;
				return;
			end
			if isfield(HDR.FLAG,'ROW_BASED_CHANNELS') && HDR.FLAG.ROW_BASED_CHANNELS, signal = signal.'; end;
			FlagLoaded   = isfield(HDR,'NS');
			HDR.InChanSelect = InChanSelect(InChanSelect <= HDR.NS);
			signal = signal*ReRefMx(HDR.InChanSelect,:); %% can be sparse if just a single channel is loaded
			signal = full(signal);  %% make sure signal is not sparse
		end;

		HDR.T0 = datevec(HDR.T0);
		HDR.Patient.Birthday = datevec(HDR.Patient.Birthday);
		HDR.Calib = [HDR.Off(:)';diag(HDR.Cal)];
		[HDR.FILE.Path,HDR.FILE.Name,HDR.FILE.Ext] = fileparts(FILENAME);
		HDR.FileName = FILENAME;
		HDR = leadidcodexyz(HDR);
		HDR.EVENT.POS = HDR.EVENT.POS+1; % convert from 0-based to 1-based index

		H=HDR;
		H.FLAG.EOG_CORRECTION = STATE.EOG_CORRECTION;

		if isfield(HDR,'Patient') && isfield(HDR.Patient,'Weight') && isfield(HDR.Patient,'Height')
			%% Body Mass Index
			HDR.Patient.BMI = HDR.Patient.Weight * HDR.Patient.Height^-2 * 1e4;

		       	%% Body Surface Area
			% DuBois D, DuBois EF. A formula to estimate the approximate surface area if height and weight be known. Arch Intern Medicine. 1916; 17:863-71.
			% Wang Y, Moss J, Thisted R. Predictors of body surface area. J Clin Anesth. 1992; 4(1):4-10.
		       	HDR.Patient.BSA = 0.007184 * HDR.Patient.Weight^0.425 * HDR.Patient.Height^0.725;
		end;

		if strcmp(H.TYPE,'GDF');
                        % Classlabels according to
                        % http://biosig.cvs.sourceforge.net/*checkout*/biosig/biosig/doc/eventcodes.txt

                        % sort event table before extracting HDR.Classlabel and HDR.TRIG
			[H.EVENT.POS,ix] = sort(H.EVENT.POS);
			H.EVENT.TYP = H.EVENT.TYP(ix);
			if isfield(H.EVENT,'CHN')
				H.EVENT.CHN = H.EVENT.CHN(ix);
			end;
			if isfield(H.EVENT,'DUR')
				H.EVENT.DUR = H.EVENT.DUR(ix);
			end;

			if (length(H.EVENT.TYP)>0)
                                ix = (H.EVENT.TYP>hex2dec('0300')) & (H.EVENT.TYP<hex2dec('030d'));
                                ix = ix | ((H.EVENT.TYP>=hex2dec('0320')) & (H.EVENT.TYP<=hex2dec('037f')));
                                ix = ix | (H.EVENT.TYP==hex2dec('030f')); % unknown/undefined cue
                                H.Classlabel = mod(H.EVENT.TYP(ix),256);
                                H.Classlabel(H.Classlabel==15) = NaN; % unknown/undefined cue
                        end;

                        % Trigger information and Artifact Selection
                        ix = find(H.EVENT.TYP==hex2dec('0300'));
                        H.TRIG = H.EVENT.POS(ix);
                        ArtifactSelection = repmat(logical(0),length(ix),1);
                        for k = 1:length(ix),
                                ix2 = find(H.EVENT.POS(ix(k))==H.EVENT.POS);
                                if any(H.EVENT.TYP(ix2)==hex2dec('03ff'))
                                        ArtifactSelection(k) = logical(1);
                                end;
                        end;
                        if any(ArtifactSelection), % define only if necessary
                                H.ArtifactSelection = ArtifactSelection;
                        end;

			% apply channel selections to EVENT table
			if valid_rerefmx && ~isempty(H.EVENT.POS) && (isfield(H.EVENT,'CHN')),	% only if channels are selected.
				sel = (H.EVENT.CHN(:)==0);	% memory allocation, select all general events
				for k = find(~sel'),		% select channel specific elements
					sel(k) = any(H.EVENT.CHN(k)==InChanSelect);
				end;
				H.EVENT.POS = H.EVENT.POS(sel);
				H.EVENT.TYP = H.EVENT.TYP(sel);
				H.EVENT.DUR = H.EVENT.DUR(sel);	% if EVENT.CHN available, also EVENT.DUR is defined.
				H.EVENT.CHN = H.EVENT.CHN(sel);
				% assigning new channel number
				a = zeros(1,HDR.NS);
				for k = 1:length(InChanSelect),		% select channel specific elements
					a(InChanSelect(k)) = k;		% assigning to new channel number.
				end;
				ix = H.EVENT.CHN>0;
				H.EVENT.CHN(ix) = a(H.EVENT.CHN(ix));	% assigning new channel number
			end;

		elseif strcmp(H.TYPE,'BDF');
			H.BDF.ANNONS = zeros(HDR.NRec*HDR.SPR,1);
			ix  = HDR.EVENT.TYP==hex2dec('7ffe');
			ix1 = find(~ix);
			POS = HDR.EVENT.POS(ix1);
			TYP = HDR.EVENT.TYP(ix1);
			[s,ix2] = sort([POS; HDR.NRec*HDR.SPR+1]);
			for k = 1:length(s)-1,
				H.BDF.ANNONS(s(k):s(k+1)-1) = TYP(ix2(k));
			end;
			H.BDF.ANNONS(HDR.EVENT.POS(ix)) = H.BDF.ANNONS(HDR.EVENT.POS(ix)) | hex2dec('10000');

		elseif strcmp(H.TYPE,'BKR');
	                H.Classlabel = [];
	                H.TRIG = [];
		        tmp=fullfile(H.FILE.Path,[H.FILE.Name,'.mat']);
        		if ~exist(tmp,'file'),
        		        tmp=fullfile(H.FILE.Path,[H.FILE.Name,'.MAT']);
       			end
		        x = [];
        		if exist(tmp,'file'),
                		x = load('-mat',tmp);
        		end;

                        if isfield(x,'header'),
                                H.MAT  = x.header;
                                if isfield(x.header,'Setup'),
                                        if isfield(x.header.Setup,'Bits'),
                                                H.Bits = x.header.Setup.Bits;
                                                [datatyp, limits, datatypes] = gdfdatatype(H.Bits+255);
                                                % THRESHOLD for Overflow detection
                                                if ~isfield(H,'THRESHOLD')
                                                    H.THRESHOLD = repmat(limits, H.NS, 1);
                                                end;
                                        end;
                                end;
                                if isfield(x.header,'Result') && isfield(x.header.Result,'Classlabel'),
                                        H.Classlabel = x.header.Result.Classlabel;
                                end;
                                if isfield(x.header,'Paradigm')
                                        if isempty(H.Classlabel) && isfield(x.header.Paradigm,'Classlabel')
                                                H.Classlabel = x.header.Paradigm.Classlabel;
                                        end;
                                        H.BCI.Paradigm = x.header.Paradigm;
                                        if isfield(H.BCI.Paradigm,'TriggerOnset');
                                                H.TriggerOffset = H.BCI.Paradigm.TriggerOnset;
                                        elseif isfield(H.BCI.Paradigm,'TriggerTiming');
                                            %    H.BCI.Paradigm.TriggerTiming,
                                                H.TriggerOffset = H.BCI.Paradigm.TriggerTiming;
                                                fprintf(2,'Warning BKROPEN: Paradigm.TriggerOnset is unknown. Paradigm.TriggerTiming= %f ms is used instead\n',H.TriggerOffset);
                                        end;
                                end;

                                if isfield(x.header,'PhysioRec'), % R. Leeb's data
                                       H.Label = cellstr(x.header.PhysioRec);
                                end;
                                if isfield(x.header,'BKRHeader'), % R. Scherer Data
                                        if isfield(x.header.BKRHeader,'TO'),
                                                H.T0 = x.header.BKRHeader.TO;
                                        end;
                                        if isfield(x.header.BKRHeader,'Label'),
                                                H.Label = cellstr(x.header.BKRHeader.Label);
                                                ns = H.NS-length(H.Label);
                                                if ns == 1;
                                                        H.Label = strvcat(H.Label,'TRIGGER');
                                                elseif ns > 1;
                                                        H.Label = strvcat(H.Label,char(repmat('n.a.',ns,1)));
                                                end;
                                        end;
                                end;
                                if isfield(x.header,'Model'), % More
                                        if isfield(x.header.Model,'AnalogInput'),
                                                for k = 1:length(x.header.Model.AnalogInput),
                                                        H.Filter.HighPass(k) = x.header.Model.AnalogInput{k}{5};
                                                        H.Filter.LowPass(k)  = x.header.Model.AnalogInput{k}{6};
                                                        H.Filter.Notch(k)    = strcmpi(x.header.Model.AnalogInput{k}{7},'on');

                                                        H.MAT.Cal(k) = x.header.Model.AnalogInput{k}{3};
                                                end
                                        end;
                                end;
                                if ~isempty(strmatch('TRIGGER',H.Label))
                                        H.AS.TRIGCHAN = H.NS; %strmatch('TRIGGER',H.Label);
                                end;
                        end;
                        if 1; ~isfield(H,'Classlabel');
                                tmp=fullfile(H.FILE.Path,[H.FILE.Name,'.par']);
                                if ~exist(tmp,'file'),
                                    tmp=fullfile(H.FILE.Path,[H.FILE.Name,'.PAR']);
                                end
                                if exist(tmp,'file'),
                                        H.Classlabel = load(tmp);
                                end;
                        end;

                        %%% Artifact Selection files
                        tmp1=fullfile(H.FILE.Path,[H.FILE.Name,'.sel']);
                        if ~exist(tmp1,'file'),
                                tmp1=fullfile(H.FILE.Path,[H.FILE.Name,'.SEL']);
                        end
                        tmp2 = fullfile(H.FILE.Path,[H.FILE.Name,'_artifact.mat']);
                        SW   = (exist(tmp1,'file')>0) + 2*(exist(tmp2,'file')>0);
                        if SW == 0,
                        elseif SW == 1,
                                if exist('OCTAVE_VERSION','builtin')
                                        H.ArtifactSelection = load('-ascii',tmp1);
                                else
                                        H.ArtifactSelection = load(tmp1);
                                end;
                        elseif SW == 2,
                                if exist('OCTAVE_VERSION','builtin')
                                        tmp = load('-mat',tmp2);
                                else
                                        tmp = load(tmp2);
                                end;
                                H.ArtifactSelection = tmp.artifact(:);
                        elseif SW == 3,
                                fprintf(H.FILE.stderr,'Warning BKROPEN: more than one ArtifactSelection files. File %s is used.\n',tmp1);
                                if exist('OCTAVE_VERSION')>5
                                        H.ArtifactSelection = load('-ascii',tmp1);
                                else
                                        H.ArtifactSelection = load(tmp1);
                                end;
                        end;
                        if isfield(H,'ArtifactSelection'),
                                if any(H.ArtifactSelection>1) || (length(H.ArtifactSelection)<length(H.Classlabel))
                                        sel = zeros(size(H.Classlabel));
                                        sel(H.ArtifactSelection) = 1;
                                        H.ArtifactSelection = sel(:);
                                end;
                                H.ArtifactSelection = H.ArtifactSelection(:);
                        end;

                        if isfield(H.AS,'TRIGCHAN') % & isempty(H.EVENT.POS)
                                if H.AS.TRIGCHAN <= H.NS, %size(H.data,2),
                                        H.THRESHOLD(H.AS.TRIGCHAN,1:2) = [-1-2^15,2^15]; % do not apply overflow detection for Trigger channel
                                        data = mexSLOAD(H.FileName,H.AS.TRIGCHAN,'UCAL:ON','OVERFLOWDETECTION:OFF');
                                        TRIGon = gettrigger(data);
                                        %TRIGoff = gettrigger(-double(H.data(:,H.AS.TRIGCHAN)));
                                        if isfield(H,'TriggerOffset')
                                                TRIGon  = TRIGon - round(H.TriggerOffset/1000*H.SampleRate);
                                        %        TRIGoff = TRIGoff - round(H.TriggerOffset/1000*H.SampleRate);
                                        end;
                                end;
                                H.TRIG = TRIGon(:);
                                H.EVENT.POS = TRIGon(:); %[TRIGon(:); TRIGoff(:)];
                                H.EVENT.TYP = repmat(hex2dec('0300'),numel(TRIGon),1); %repmat(hex2dec('8300'),numel(TRIGoff),1)];
                        end;
                        if length(H.TRIG)~=length(H.Classlabel),
                                % hack to deal with BCI22 data
                                fprintf(2,'Warning BKROPEN: Number of triggers (%i) and number of Classlabels (%i) do not fit\n',length(H.TRIG),length(H.Classlabel));
                                H.TRIG = [];
                                H.Classlabel = [];
                                H.ArtifactSelection = [];
                        end;
			%% end of BKR

		elseif strcmp(H.TYPE,'BrainVision');
        	        try
        	        	H = bv2biosig_events(H);
       	        	catch
       	        		%warning('bv2biosig_events not executed');
       	        	end;

		elseif strcmp(H.TYPE,'EDF') && isfield(H.EVENT,'Desc');
			if length(H.EVENT.TYP)==length(H.EVENT.Desc)
	                        [H.EVENT.CodeDesc, CodeIndex, H.EVENT.TYP] = unique(H.EVENT.Desc);
	                end;

			%% end of BKR

		elseif strcmp(H.TYPE,'BrainVision');
			HDR = sopen(fullfile(H.FILE.Path,[H.FILE.Name,'.vmrk']));
			HDR = sclose(HDR);
			H.EVENT = HDR.EVENT;

		elseif strcmp(H.TYPE,'PDP');
			signal = repmat(NaN,max(HDR.EVENT.POS),HDR.NS);
                end;

		if isempty(signal);
			signal = repmat(NaN,round(max(HDR.EVENT.POS)*HDR.SampleRate/HDR.EVENT.SampleRate),HDR.NS);
			for k = 1:HDR.NS,
				ix = find(HDR.EVENT.CHN==k);
				signal(round(HDR.EVENT.POS(ix)*HDR.SampleRate/HDR.EVENT.SampleRate),k)=HDR.EVENT.DUR(ix);
			end;
                end;

	        H.CHANTYP = repmat(' ',1,H.NS);
		for k=1:H.NS,
			if     ~isempty(strfind(lower(H.Label{k}),'eeg')) 	H.CHANTYP(k) = 'E';
			elseif ~isempty(strfind(lower(H.Label{k}),'meg')) 	H.CHANTYP(k) = 'E';
			elseif ~isempty(strfind(lower(H.Label{k}),'emg')) 	H.CHANTYP(k) = 'M';
			elseif ~isempty(strfind(lower(H.Label{k}),'eog')) 	H.CHANTYP(k) = 'O';
			elseif ~isempty(strfind(lower(H.Label{k}),'ecg')) 	H.CHANTYP(k) = 'C';
			elseif ~isempty(strfind(lower(H.Label{k}),'air')) 	H.CHANTYP(k) = 'R';
			elseif ~isempty(strfind(lower(H.Label{k}),'trig')) 	H.CHANTYP(k) = 'T';
			end;
		end;

	catch
		%fprintf(1,lasterr);
		fprintf(1, 'SLOAD: mexSLOAD failed - the slower M-function is used.\n');
	end;
else
	global FLAG_HINT_mexSLOAD;
	if isempty(FLAG_HINT_mexSLOAD)
		fprintf(1, 'Hint: the performance of SLOAD can be improved with mexSLOAD.mex which is part of biosig4c++.\n');
		FLAG_HINT_mexSLOAD = 1;	 	% turn off hint
	end;
end;

if ~FlagLoaded,
	H = getfiletype(FILENAME);

	if isempty(H)
		fprintf(2,'Warning SLOAD: no file found\n');
		return;
	else
		% FILENAME can be fn.name struct, or HDR struct.
		FILENAME = H.FileName;
	end;
	H.FLAG.UCAL = STATE.UCAL;
	H.FLAG.OVERFLOWDETECTION = STATE.OVERFLOWDETECTION;
	H.FLAG.EOG_CORRECTION = STATE.EOG_CORRECTION;
	H.FLAG.OUTPUT = STATE.OUTPUT;

if strncmp(H.TYPE,'IMAGE:',5)
	[H] = iopen(H);
	if H.FILE.OPEN,
		signal = iread(H);
		H.FILE.OPEN = 0;
		fclose(H.FILE.FID);
	end;
	return;
end;

%%%%%%%%%%%%%%% --------- Load single file ------------%%%%%%%%%%%
H = sopen(H,'r',CHAN,MODE);
if 0, ~isnan(H.NS),
%------ ignore 'NaC'-channels
	NS = size(H.Calib,2);
	SelMx = speye(NS);
	ch = 1:NS; %ch(strmatch('NaC',H.Label))=[];
	if length(ch)<NS,
		fprintf(2,'Warning SLOAD: Some NaC channels have been removed %s\n',H.FileName);
	end;
	SelMx = SelMx(:,ch);
	H.Calib = H.Calib*SelMx;

	% generate correction matrix
	if H.FLAG.EOG_CORRECTION,
		H.Calib = H.Calib*h.REGRESS.r0;
		if ~isequal(H.Label(ch),h.Label)
			fprintf(2, 'Warning SLOAD (EOG correction): Channel labels in %s do not fit with arti* recording!\n',H.FileName);
		end;
	elseif STATE.EOG_CORRECTION,
		fprintf(2, 'Warning: EOG correction not supported for this file (%s)!\n',H.FileName);
	end;

	%----- generate HDR.Calib -----
	if all(size(CHAN)>1) || any(floor(CHAN)~=CHAN) || any(CHAN<0) || (any(CHAN==0) && (numel(CHAN)>1));
        	ReRefMx = CHAN;
        	CHAN = find(any(CHAN,2));
	elseif all(CHAN>0) && all(floor(CHAN)==CHAN),
		if any(diff(CHAN)<=0),
		%	fprintf(HDR.FILE.FID,'Warning SOPEN: CHAN-argument not sorted - header information like Labels might not correspond to data.\n');
		end;
        	ReRefMx = sparse(CHAN,1:length(CHAN),1,H.NS,length(CHAN));
	else %if (CHAN==0)
		ReRefMx = [];
	end
	if ~isempty(ReRefMx),
		%[size(H.Calib),size(SelMx),size(h.REGRESS.r0),size(ReRefMx)]
		ReRefMx = [ReRefMx(1:min(size(ReRefMx,1),size(H.Calib,2)),:); zeros(max(0,size(H.Calib,2)-size(ReRefMx,1)),size(ReRefMx,2))];
		H.Calib = H.Calib*ReRefMx;
	end;

	H.InChanSelect = find(any(H.Calib(2:end,:),2));
	H.Calib = H.Calib([1;1+H.InChanSelect(:)],:);
end

if 0,

elseif isfield(H,'data') && all(diag(H.Calib(2:end,1:end))==1) && ~H.FLAG.OVERFLOWDETECTION,
	% only a single copy of the data
	% important for large data sets, close to the available memory
	signal = H.data;
	H = rmfield(H,'data');
	if (CHAN>0)
		signal = signal(:,CHAN);
	end;

elseif any(strmatch(H.TYPE,{'native','TFM_EXCEL_Beat_to_Beat','EEProbe-CNT','EEProbe-AVR'}));
	[signal,H] = sread(H);
        H = sclose(H);

elseif (H.FILE.OPEN > 0)
	[signal,H] = sread(H);
        H = sclose(H);

elseif (H.FILE.OPEN > 0)
        signal = repmat(NaN,H.SPR*H.NRec,size(H.Calib,2));
	k1 = 0;
        while ~seof(H),
	        [s,H] = sread(H,100);
        	if 1,
			k2 = size(s,1);
			signal(k1+1:k1+k2,:)=s;
			k1 = k1+k2;
		else
	        	 %fprintf('%i/%i\n',stell(H),H.SPR*H.NRec);
		        signal=[signal;s];
		end;
	end;
        H = sclose(H);

elseif strncmp(H.TYPE,'EVENT',5)
        signal = H.EVENT;
        return;

elseif strncmp(H.TYPE,'ELPOS',5)
        signal = H.ELEC.XYZ;
	return;

elseif strcmp(H.TYPE,'DAQ')
	fprintf(1,'Loading a matlab DAQ data file - this can take a while.\n');
	tic;
        [signal, tmp, H.DAQ.T0, H.DAQ.events, DAQ.info] = daqread(H.FileName);
        fprintf(1,'Loading DAQ file finished after %.0f s.\n',toc);
        [H.SPR,H.NS] = size(signal);

        H.SampleRate = DAQ.info.ObjInfo.SampleRate;
        sz     = size(signal);
        if length(sz)==2, sz=[1,sz]; end;
        H.NRec = sz(1);
        H.Dur  = sz(2)/H.SampleRate;
        H.NS   = sz(3);
        H.FLAG.TRIGGERED = H.NRec>1;
        H.FLAG.UCAL = 1;

        H.PhysDim = {DAQ.info.ObjInfo.Channel.Units};
        H.DAQ   = DAQ.info.ObjInfo.Channel;

        H.Cal   = diff(cat(1,DAQ.info.ObjInfo.Channel.InputRange),[],2).*(2.^(-DAQ.info.HwInfo.Bits));
        H.Off   = cat(1,DAQ.info.ObjInfo.Channel.NativeOffset);
        H.Calib = sparse([H.Off';eye(H.NS)]*diag(H.Cal));

        if CHAN<1,
                CHAN = 1:H.NS;
        end;
        if ~H.FLAG.UCAL,
                Calib = H.Calib;	% Octave can not index sparse matrices within a struct
                signal = [ones(size(signal,1),1),signal]*Calib(:,CHAN);
        end;

elseif 0, strcmp(H.TYPE,'BIFF'),
	try,
                [H.TFM.S,H.TFM.E] = xlsread(H.FileName,'Beat-To-Beat');
	        if size(H.TFM.S,1)+1==size(H.TFM.E,1),
                        H.TFM.S = [repmat(NaN,1,size(H.TFM.S,2));H.TFM.S];
                end;
                H.TYPE = 'TFM_EXCEL_Beat_to_Beat';
	catch
		try
	                [H.TFM.S,H.TFM.E] = xlsread(H.FileName,'Beat-to-Beat');
        	        H.TYPE = 'TFM_EXCEL_Beat_to_Beat';
        	catch
			0;
        	end;
	end;

	if strcmp(H.TYPE, 'TFM_EXCEL_Beat_to_Beat');
                if ~isempty(strfind(H.TFM.E{3,1},'---'))
                        H.TFM.S(3,:) = [];
                        H.TFM.E(3,:) = [];
                end;

                H.Label   = strvcat(H.TFM.E{4,:});
                H.PhysDim = strvcat(H.TFM.E{5,:});

                H.TFM.S = H.TFM.S(6:end,:);
                H.TFM.E = H.TFM.E(6:end,:);

                ix = find(isnan(H.TFM.S(:,2)) & ~isnan(H.TFM.S(:,1)));
                H.EVENT.Desc = H.TFM.E(ix,2);
                H.EVENT.POS  = ix;

                if any(CHAN),
			H.TFM.S = H.TFM.S(:,CHAN);
			H.TFM.E = H.TFM.E(:,CHAN);
		end;
		[H.SPR,H.NS] = size(H.TFM.S);
		H.NRec = 1;
		H.THRESHOLD  = repmat([0,NaN],H.NS,1);

		signal  = H.TFM.S;
		signal(signal==0) = NaN;
        end;

elseif strcmp(H.TYPE,'MatrixMarket'),
        H.FILE.FID = fopen(H.FileName,'rt','ieee-le');

    	line = fgetl(H.FILE.FID);

	H.FLAG.Coordinate = ~isempty(strfind(line,'coordinate'));
	H.FLAG.Array 	  = ~isempty(strfind(line,'array'));

	H.FLAG.Complex = ~isempty(strfind(line,'complex'));
	H.FLAG.Real = ~isempty(strfind(line,'real'));
	H.FLAG.Integer = ~isempty(strfind(line,'integer'));
	H.FLAG.Pattern = ~isempty(strfind(line,'pattern'));

	H.FLAG.General = ~isempty(strfind(line,'general'));
	H.FLAG.Symmetric = ~isempty(strfind(line,' symmetric'));
	H.FLAG.SkewSymmetric = ~isempty(strfind(line,'skew-symmetric'));
	H.FLAG.Hermitian = ~isempty(strfind(lower(line),'hermitian'));

	while strncmp(line,'%',1)
        	line = fgetl(H.FILE.FID);
	end;

	[tmp,status] = str2double(line);
	if any(status)
		fprintf(H.FILE.stderr,'SLOAD (MM): invalid size %s\n',line);
	else
		H.MATRIX.Size = tmp;
	end;

	if length(H.MATRIX.Size)==3,
		H.Length = tmp(3);
		signal = sparse([],[],[],tmp(1),tmp(2),tmp(3));
		for k = 1:H.Length,
	        	line = fgetl(H.FILE.FID);
			[tmp,status] = str2double(line);
			if any(status)
				fprintf(H.FILE.stderr,'SLOAD (MM): invalid size %s\n',line);
			elseif length(tmp)==4,
		    		val = tmp(3) + i*tmp(4);
			elseif length(tmp)==3,
				val = tmp(3);
			elseif length(tmp)==2,
				val = 1;
			else
				fprintf(H.FILE.stderr,'SLOAD (MM): invalid size %s\n',line);
			end;

			if H.FLAG.General,
				signal(tmp(1),tmp(2)) = val;
			elseif H.FLAG.Symmetric,
				signal(tmp(1),tmp(2)) = val;
				signal(tmp(2),tmp(1)) = val;
			elseif H.FLAG.SkewSymmetric,
				signal(tmp(1),tmp(2)) = val;
				signal(tmp(2),tmp(1)) =-val;
			elseif H.FLAG.Hermitian,
				signal(tmp(1),tmp(2)) = val;
				signal(tmp(2),tmp(1)) = conj(val);
			else
				fprintf(H.FILE.stderr,'SLOAD (MM): invalid size %s\n',line);
			end;
		end;

	elseif length(H.MATRIX.Size)==2
		H.Length = prod(tmp);
		signal = zeros(H.MATRIX.Size);
		if H.FLAG.General==1,
			[IX,IY]=find(ones(H.MATRIX.Size));
		else
			[IX,IY]=find(cumsum(eye(H.MATRIX.Size)));
		end;

		for k = 1:H.Length,
	        	line = fgetl(H.FILE.FID);
			[tmp,status] = str2double(line);
			if any(status)
				error('SLOAD (MM)');
			elseif length(tmp)==2,
				val=tmp(1) + i*tmp(2);
			elseif length(tmp)==1,
				val=tmp(1);
			else
				fprintf(H.FILE.stderr,'SLOAD (MM): invalid size %s\n',line);
			end;

			signal(IX(k),IY(k)) = val;
			if H.FLAG.Symmetric,
				signal(IY(k),IX(k)) = val;
			elseif H.FLAG.SkewSymmetric,
				signal(IY(k),IX(k)) =-val;
			elseif H.FLAG.Hermitian,
				signal(IY(k),IX(k)) = conj(val);
			else
				fprintf(H.FILE.stderr,'SLOAD (MM): invalid size %s\n',line);
			end;
		end;
        end;
        fclose(H.FILE.FID);

elseif strcmp(H.TYPE,'OFF'),
	        H.FILE.FID = fopen(H.FileName,'rt','ieee-le');

                line1 = fgetl(H.FILE.FID);
                line2 = fgetl(H.FILE.FID);
		while ~feof(H.FILE.FID) && (line2(1)=='#'),
	                line2 = fgetl(H.FILE.FID);
		end;
		[tmp,status] = str2double(line2);
		if status || (size(tmp,2)~=3),
			fclose(H.FILE.FID);
			error('SOPEN (OFF)');
		else
			H.VertexCount = tmp(1);
			H.FaceCount = tmp(2);
			H.EdgeCount = tmp(3);
		end

		H.Vertex = repmat(NaN,H.VertexCount,3);
		for k = 1:H.VertexCount,
			line = '';
			while isempty(line) || strncmp(line,'#',1)
				line = fgetl(H.FILE.FID);
			end;
			len = min(length(line),min(find(line=='#')));
			tmp = str2double(line(1:len));
			H.Vertex(k,:) = tmp(1:H.ND);
		end;

%		H.Face = repmat(NaN,H.FaceCount,3);
		for k = 1:H.FaceCount,
			line = '';
			while isempty(line) || strncmp(line,'#',1)
				line = fgetl(H.FILE.FID);
			end;
			len = min(length(line),min(find(line=='#')));
			tmp = str2double(line(1:len));
			H.Ngon(k) = tmp(1);
			H.Face{k} = tmp(2:tmp(1)+1) + 1;
		end;
		if all(H.Ngon(1)==H.Ngon),
			H.Face = cat(1,H.Face{:});
		end;
                fclose(H.FILE.FID);

elseif strcmp(H.TYPE,'POLY'),
        H.FILE.FID = fopen(H.FileName,'rt','ieee-le');

	K = 0;
	while ~feof(H.FILE.FID)
        	line = fgetl(H.FILE.FID);
		if isempty(line),
		elseif line(1)=='#',
		else
			K = K + 1;

		end;
        end;
        fclose(H.FILE.FID);

elseif strcmp(H.TYPE,'SMF'),
	        H.FILE.FID = fopen(H.FileName,'rt','ieee-le');

		VertexCount = 0;
		FaceCount = 0;
		PalLen = 0;
		K = 1;
		while ~feof(H.FILE.FID)
	                line = fgetl(H.FILE.FID);
			if isempty(line)
			elseif line(1)=='#';

			elseif line(1)=='v';
				[tmp,status] = str2double(line(3:end));
				if ~any(status)
					VertexCount = VertexCount + 1 ;
					H.Vertex(VertexCount,:) = tmp;
				else
					fprintf(H.FILE.stderr,'Warning SLOAD: could not read line %i in file %s\n',K,H.FileName);
				end;

			elseif line(1)=='f';
				[tmp,status] = str2double(line(3:end));
				if ~any(status)
					FaceCount  = FaceCount + 1;
					H.Ngon(FaceCount) = length(tmp);
					H.Face{FaceCount} = tmp;
				else
					fprintf(H.FILE.stderr,'Warning SLOAD: could not read line %i in file %s\n',K,H.FileName);
				end;

			elseif line(1)=='n';
				[tmp,status] = str2double(line(3:end));
				if ~any(status)
					H.NormalVector = tmp;
				else
					fprintf(H.FILE.stderr,'Warning SLOAD: could not read line %i in file %s\n',K,H.FileName);
				end;

			elseif line(1)=='c';
				[tmp,status] = str2double(line(3:end));
				if ~any(status)
					PalLen = PalLen +1;
					H.Palette(PalLen,:)= tmp;
				else
					fprintf(H.FILE.stderr,'Warning SLOAD: could not read line %i in file %s\n',K,H.FileName);
				end;
			else

			end;
			K = K+1;
		end;
		fclose(H.FILE.FID);
		if all(H.Ngon(1)==H.Ngon),
			H.Face = cat(1,H.Face{:});
		end;

elseif strcmp(H.TYPE,'TVF 1.1A'),
        H.FILE.FID = fopen(H.FileName,'rt');

	tmp = fgetl(H.FILE.FID);
	H.TVF.Name = fgetl(H.FILE.FID);
	H.TVF.Desc = fgetl(H.FILE.FID);
	[tmp,status] = str2double(fgetl(H.FILE.FID));
	H.TVF.NTR = tmp(1);
	H.TVF.NV  = tmp(2);
	H.FLAG.CULL = ~~tmp(3);
	[tmp,status] = str2double(fgetl(H.FILE.FID));
	H.TVF.NTRC  = tmp(1);
	H.TVF.NTRM  = tmp(2);
	[tmp,status] = str2double(fgetl(H.FILE.FID));
	H.TVF.NVC   = tmp(1);
	H.TVF.NVN   = tmp(2);
	[tmp,status] = str2double(fgetl(H.FILE.FID));
	H.TVF.GlobalColor = tmp;
	[tmp,status] = str2double(fgetl(H.FILE.FID));
	H.TVF.GlobalMtrlProps = tmp;

	H.TVF.Triangles = repmat(NaN,[H.TVF.NTR,3]);
	for k = 1:H.TVF.NTR,
		[tmp, status] = str2double(fgetl(H.FILE.FID));
		H.TVF.Triangles(k,:) = tmp;
	end;
	H.TVF.TrColorSets = reshape(NaN,[H.TVF.NTRC,H.TVF.NTR]);
	for k = 1:H.TVF.NTRC,
		[tmp, status] = str2double(fgetl(H.FILE.FID));
		H.TVF.TrColorSets(k,:) = tmp;
	end;
	H.TVF.TrMtrlSets = repmat(NaN, [H.TVF.NTRM,H.TVF.NTR]);
	for k = 1:H.TVF.NTRM,
		[tmp, status] = str2double(fgetl(H.FILE.FID));
		H.TVF.TrMtrlSets(k,:) = tmp;
	end;

	H.TVF.Vertices   = repmat(NaN, [H.TVF.NV,3]);
	for k = 1:H.TVF.NV,
		[tmp, status] = str2double(fgetl(H.FILE.FID));
		H.TVF.Vertices(k,:) = tmp;
	end;
	H.TVF.VColorSets = repmat(NaN, [H.TVF.NVC,H.TVF.NV]);
	for k = 1:H.TVF.NVC,
		[tmp, status] = str2double(fgetl(H.FILE.FID));
		H.TVF.VColorSets(k,:) = tmp;
	end;
	H.TVF.VNrmlSets  = repmat(NaN, [H.TVF.NVN,3]);
	for k = 1:H.TVF.NVN,
		[tmp, status] = str2double(fgetl(H.FILE.FID));
		H.TVF.VNrmlSets(k,:) = tmp;
	end;

	fclose(H.FILE.FID);

elseif strcmp(H.TYPE,'TVF 1.1B'),
        H.FILE.FID = fopen(H.FileName,'rb',H.Endianity);

	tmp = fread(H.FILE.FID,12,'uchar');
	H.TVF.Name = char(fread(H.FILE.FID,32,'uchar'));
	H.TVF.Desc = char(fread(H.FILE.FID,80,'uchar'));
	tmp = fread(H.FILE.FID,7,'uint32');
	H.TVF.NTR = tmp(1);
	H.TVF.NV  = tmp(2);
	H.FLAG.CULL = ~~tmp(3);
	H.TVF.NTRC  = tmp(4);
	H.TVF.NTRM  = tmp(5);
	H.TVF.NVC   = tmp(6);
	H.TVF.NVN   = tmp(7);
	tmp = fread(H.FILE.FID,[4,2],'float32');
	H.TVF.GlobalColor = tmp(:,1)';
	H.TVF.GlobalMtrlProps = tmp(:,2)';

	H.TVF.Triangles   = fread(H.FILE.FID, [3,H.TVF.NTR],'uint32')';
	H.TVF.TrColorSets = fread(H.FILE.FID, [H.TVF.NTR,H.TVF.NTRC],'uint32')';
	H.TVF.TrMtrlSets  = fread(H.FILE.FID, [H.TVF.NTR,H.TVF.NTRM],'uint32')';

	H.TVF.Vertices   = fread(H.FILE.FID, [3,H.TVF.NV],'float32')';
	H.TVF.VColorSets = fread(H.FILE.FID, [H.TVF.NV,H.TVF.NVC],'uint32')';
	H.TVF.VNrmlSets  = fread(H.FILE.FID, [3,H.TVF.NVN],'float32')';

	fclose(H.FILE.FID);

elseif strcmp(H.TYPE,'VTK'),
                H.FILE.FID = fopen(H.FileName,'rt','ieee-le');

                H.VTK.version = fgetl(H.FILE.FID);
                H.VTK.Title   = fgetl(H.FILE.FID);
                H.VTK.type    = fgetl(H.FILE.FID);

		while ~feof(H.FILE.FID),
			tline = fgetl(H.FILE.FID);

			if 0,
			elseif strncmp(tline,'CELLS',5);
			elseif strncmp(tline,'CELL_DATA',9);
			elseif strncmp(tline,'DATASET',7);
		                H.VTK.DATASET = tline(8:end);
			elseif strncmp(tline,'LINES',6);
			elseif strncmp(tline,'POINTS',6);
			elseif strncmp(tline,'POINT_DATA',10);
			elseif strncmp(tline,'POLYGON',7);
			elseif strncmp(tline,'SCALARS',7);
				[t1,r]=strtok(tline);
				[dataName,r]=strtok(r);
				[dataType,r]=strtok(r);
				[numComp ,r]=strtok(r);
				if isempty(numComp), numComp=1;
				else numComp = str2double(numComp); end;
				tline = fgetl(fid);
				if strcmp(tline,'LOOKUP_TABLE');
	    				[t1,r]=strtok(tline);
	    				[tableName,r]=strtok(tline);
				else
					tline = fgetl(fid);
				end;
			end;

		end;
                fclose(H.FILE.FID);

                fprintf(H.FILE.stderr,'Warning SOPEN: VTK-format not supported, yet.\n');

elseif strcmp(H.TYPE,'unknown')
	[p,f,e]=fileparts(H.FileName);
        TYPE = upper(e);
        if 0,
        elseif strcmp(TYPE,'.RAW')
                loadraw;
        elseif strcmp(TYPE,'.RDT')
                [signal] = loadrdt(FILENAME,CHAN);
                fs = 128;
        elseif strcmp(TYPE,'.XLS')
                loadxls;
        elseif strcmp(TYPE,'.DA_')
                fprintf('Warning SLOAD: Format DA# in testing state and is not supported\n');
                loadda_;
        elseif strcmp(TYPE,'.RG64')
                [signal,H.SampleRate,H.Label,H.PhysDim,H.NS]=loadrg64(FILENAME,CHAN);
                %loadrg64;
        else
                fprintf('Error SLOAD: Unknown Data Format\n');
                signal = [];
                return;
        end;
end;

end;

%%%%%%%%%% Post-Processing %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if strcmp(H.TYPE,'CNT');
        f = fullfile(H.FILE.Path, [H.FILE.Name,'.txt']);
        if exist(f,'file'),
                fid = fopen(f,'r');
		tmp = fread(fid,inf,'uint8');
		fclose(fid);
		[tmp,v] = str2double(char(tmp'));
		if ~any(v),
            		H.Classlabel=tmp(:);
	        end;
        end
        f = fullfile(H.FILE.Path, [H.FILE.Name,'.par']);
        if exist(f,'file'),
                fid = fopen(f,'r');
		tmp = fread(fid,inf,'uint8');
		fclose(fid);
		[tmp,v] = str2double(char(tmp'));
		if ~any(v),
            		H.Classlabel=tmp(:);
	        end;
        end
        f = fullfile(H.FILE.Path, [H.FILE.Name,'.mat']);
        if exist(f,'file'),
                try,
                        tmp = load(f);
                catch
                        tmp = [];
                end
                if isfield(tmp,'classlabel') && ~isfield(H,'Classlabel')
                        H.Classlabel=tmp.classlabel(:);
                elseif isfield(tmp,'classlabel') && isfield(tmp,'header') && isfield(tmp.header,'iniFile') && strcmp(tmp.header.iniFile,'oom.ini'), %%% for OOM study only.
                        H.Classlabel=tmp.classlabel(:);
                end;
        end;
        f = fullfile(H.FILE.Path, [H.FILE.Name,'c.mat']);
        if exist(f,'file'),
                try,
                        tmp = load(f);
                catch
                        tmp = [];
                end
                if isfield(tmp,'classlabel') && ~isfield(H,'Classlabel')
                        H.Classlabel=tmp.classlabel(:);
                end;
        end;
        f = fullfile(H.FILE.Path, [H.FILE.Name,'_classlabel.mat']);
        if exist(f,'file'),
                try,
                        tmp = load(f);
                catch
                        tmp = [];
                end
                if isfield(tmp,'Classlabel') && (size(tmp.Classlabel,2)==4)
                        [x,H.Classlabel] = max(tmp.Classlabel,[],2);
                end;
                if isfield(tmp,'classlabel') && (size(tmp.classlabel,2)==4)
                        [x,H.Classlabel] = max(tmp.classlabel,[],2);
                end;
        end;

        f=fullfile(H.FILE.Path,[H.FILE.Name,'.sel']);
        if ~exist(f,'file'),
                f=fullfile(H.FILE.Path,[H.FILE.Name,'.SEL']);
        end
        if exist(f,'file'),
                fid = fopen(f,'r');
		tmp = fread(fid,inf,'uint8');
		fclose(fid);
		[tmp,v] = str2double(char(tmp'));
		if any(isnan(tmp)) || any(tmp~=ceil(tmp)) || any(tmp<0) || (any(tmp==0) && any(tmp>1))
                        fprintf(2,'Warning SLOAD(CNT): corrupted SEL-file %s\n',f);
                else
                        if ~isfield(H,'Classlabel'),
                                H.Classlabel = H.EVENT.TYP;
                        end;
                        n = length(H.Classlabel);

                        H.ArtifactSelection = zeros(n,1);
                        if all((tmp==0) | (tmp==1)) && (length(tmp)>1) && (sum(diff(sort(tmp))~=0) ~= length(tmp)-1)
                                H.ArtifactSelection = logical(tmp);
                        else
                                H.ArtifactSelection(tmp) = 1;
                        end;
                end;
        end;
end;

if ~isempty(strfind(upper(MODE),'TSD'));
        f = fullfile(H.FILE.Path, [H.FILE.Name,'.tsd']);
        if ~exist(f,'file'),
                        fprintf(2,'Warning SLOAD-TSD: file %s.tsd found\n',H.FILE(1).Name,H.FILE(1).Name);
        else
                fid = fopen(f,'rb');
                tsd = fread(fid,inf,'float');
                fclose(fid);
                nc = size(signal,1)\size(tsd,1);
                if (nc == round(nc)),
                        signal = [signal, reshape(tsd,nc,size(tsd,1)/nc)'];
                else
                        fprintf(2,'Warning SLOAD: size of %s.tsd does not fit to size of %s.bkr\n',H.FILE(1).Name,H.FILE(1).Name);
                end;
        end;
end;

if strcmp(H.TYPE,'GDF')
        if isfield(H.EVENT,'TYP')
                if isempty(H.EVENT.TYP)
                        %%%%% if possible, load Reinhold's configuration files
                        f = fullfile(H.FILE.Path, [H.FILE.Name,'.mat']);
                        if exist(f,'file'),
                                try
                                        x = load(f,'header');
                                catch;
                                        x=[];
                                end;
                                if isfield(x,'header'),
                                        if isfield(x.header,'Paradigm')
                                                fprintf(2,'Warning SLOAD (GDF): Obsolete feature is used (header information loaded from MAT-file %s).\n',H.FileName);
                                                fprintf(2,'This feature will be removed in future. Contact <a.schloegl@ieee.org> if you need this.\n ');
                                                H.BCI.Paradigm = x.header.Paradigm;
                                                if isfield(H.BCI.Paradigm,'TriggerTiming');
                                                        H.TriggerOffset = H.BCI.Paradigm.TriggerTiming;
                                                elseif isfield(H.BCI.Paradigm,'TriggerOnset');
                                                        H.TriggerOffset = H.BCI.Paradigm.TriggerOnset;
                                                end;

                                                if isfield(H,'Classlabel') && isempty(H.Classlabel),
                                                        H.Classlabel = x.header.Paradigm.Classlabel;
                                                end;
                                        end;
                                end;
                        end;
                end;
        end;

        fid=fopen(fullfile(H.FILE.Path,[H.FILE.Name,'.sel']),'r');
        if fid<0,
	        fid=fopen(fullfile(H.FILE.Path,[H.FILE.Name,'.SEL']),'r');
        end
        if fid>0,
                [tmp,c] = fread(fid,[1,inf],'uint8');
                fclose(fid);
                [tmp,v,sa] = str2double(tmp);
                if isempty(sa) || isempty(sa{1})
                        H.ArtifactSelection = repmat(0,length(H.TRIG),1);
                elseif any(isnan(tmp)) || any(tmp~=ceil(tmp)) || any(tmp<0) || (any(tmp==0) && any(tmp>1))
                        fprintf(2,'Warning SLOAD(GDF): corrupted SEL-file %s\n',fullfile(H.FILE.Path,[H.FILE.Name,'.sel']));
		else
                        H.ArtifactSelection = zeros(length(H.TRIG),1);
                        if all((tmp==0) | (tmp==1)) && (length(tmp)>1) && (sum(diff(sort(tmp))~=0) ~= length(tmp)-1)
                                H.ArtifactSelection = logical(tmp);
                        else
                                H.ArtifactSelection(tmp) = 1;
                        end;
                end;
        end;
end;

% resampling
if ~isnan(Fs) && (H.SampleRate~=Fs);
        tmp = ~mod(H.SampleRate,Fs) || ~mod(Fs,H.SampleRate);
        tmp2= ~mod(H.SampleRate,Fs*2.56);
        if tmp,
                signal = rs(signal,H.SampleRate,Fs);
                H.EVENT.POS = H.EVENT.POS/H.SampleRate*Fs;
                if isfield(H.EVENT,'DUR');
                        H.EVENT.DUR = H.EVENT.DUR/H.SampleRate*Fs;
                end;
                if isfield(H,'TRIG');
                        H.TRIG = H.TRIG/H.SampleRate*Fs;
                end;
                H.SampleRate = Fs;
        elseif tmp2,
                x = load('resample_matrix.mat');
                signal = rs(signal,x.T256100);
                if H.SampleRate*100~=Fs*256,
                        signal = rs(signal,H.SampleRate/(Fs*2.56),1);
                end;
                H.EVENT.POS = H.EVENT.POS/H.SampleRate*Fs;
                if isfield(H.EVENT,'DUR');
                        H.EVENT.DUR = H.EVENT.DUR/H.SampleRate*Fs;
                end;
                if isfield(H,'TRIG');
                        H.TRIG = H.TRIG/H.SampleRate*Fs;
                end;
                H.SampleRate = Fs;
        else
                fprintf(2,'Warning SLOAD: resampling %f Hz to %f Hz not implemented.\n',H.SampleRate,Fs);
        end;
end;

return;
ratiomissing = mean(isnan(signal));
if any(ratiomissing>.1)
	fprintf(2,'Warning SLOAD: ratio of missing samples exceeds 10%% in file %s.\n',H.FileName);
	ix = find(ratiomissing);
	fprintf(1,'#%3i:  %4.1f%%\n',[ix;ratiomissing(ix)*100])
end;


% ---------------------------------------------------------
% Copyright (c) 2017, Gurkirt Singh
% This code and is available
% under the terms of MIT License provided in LICENSE.
% Please retain this notice and LICENSE if you use
% this file (or any portion of it) in your project.
% ---------------------------------------------------------
%% This is main script to build tubes and evaluate them %%

function myI01onlineTubes()
close all
data_root = '/home/zhujiagang/realtime-action-detection';
save_root = '/home/zhujiagang/realtime-action-detection/save';
iteration_num_rgb = [120000]; % you can also evaluate on multiple iertations
iteration_num_flow = [120000]; % you can also evaluate on multiple iertations

% add subfolder to matlab paths
addpath(genpath('gentube/'));
addpath(genpath('actionpath/'));
addpath(genpath('eval/'));
addpath(genpath('utils/'));
model_type = 'CONV';

completeList = {...
    {'ucf24','01',{'rgb'},iteration_num_rgb,{'score'}},...
    {'ucf24','01',{'brox'},iteration_num_flow,{'score'}}...
    {'ucf24','01',{'fastOF'},iteration_num_flow,{'score'}}...
    };

alldopts = cell(2,1);
count = 1;
gap=3;

for setind = 1:length(completeList)
    [dataset, listid, imtypes, iteration_nums, costTypes] = enumurateList(completeList{setind});
    for ct = 1:length(costTypes)
        costtype = costTypes{ct};
        for imtind = 1:length(imtypes)
            imgType = imtypes{imtind};
            for iteration = iteration_nums
                for iouthresh=0.1
                    %% generate directory sturcture based on the options
                    dopts = initDatasetOpts(data_root,save_root,dataset,imgType,model_type,listid,iteration,iouthresh,costtype, gap);
                    if exist(dopts.detDir,'dir')
                        alldopts{count} = dopts;
                        count = count+1;
                    end
                end
            end
        end
    end
end

%% For each option type build tubes and evaluate them
for index = 1:count-1
    opts = alldopts{index};
    if exist(opts.detDir,'dir')
        fprintf('Video List %02d :: %s\nAnnotFile :: %s\nImage  Dir :: %s\nDetection Dir:: %s\nActionpath Dir:: %s\nTube Dir:: %s\n',...
            index, opts.vidList, opts.annotFile, opts.imgDir, opts.detDir, opts.actPathDir, opts.tubeDir);
        %% online bbx and prediction scores display given frame level detections
        actionPaths(opts);
    end
end

%% Function to enumrate options
function [dataset,listnum,imtypes,weights,costTypes] = enumurateList(sublist)
dataset = sublist{1}; listnum = sublist{2}; imtypes = sublist{3};
weights = sublist{4};costTypes = sublist{5};

%% Facade function for smoothing tubes and evaluating them
function results = gettubes(dopts)

numActions = length(dopts.actions);
results = zeros(300,6);
counter=1;
class_aps = cell(2,1);

annot = load(dopts.annotFile);
annot = annot.annot;
testvideos = getVideoNames(dopts.vidList);

for alpha = 3
    fprintf('alpha %03d ', alpha);
    % read action paths
    actionpaths = readALLactionPaths(dopts.vidList,dopts.actPathDir,1);
    %% perform temporal trimming
    smoothedtubes = parActionPathSmoother(actionpaths,alpha*ones(numActions,1),numActions);
    fprintf('\n');
end

results(counter:end,:) = [];
result = cell(2,1);
result{2} = class_aps;
result{1} = results;
results = result;

function videos = getVideoNames(split_file)
% -------------------------------------------------------------------------
fid = fopen(split_file,'r');
data = textscan(fid, '%s');
videos  = cell(1);
count = 0;
for i=1:length(data{1})
    filename = cell2mat(data{1}(i,1));
    count = count +1;
    videos{count} = filename;
end


function Sen = prepareForActiveInit(Sen,Obs)

if strcmp(Obs.ltype(4:6),'Pnt')
    if Obs.vis
        c = getCell(Obs.exp.e,Sen.imGrid);
        Sen.imGrid.usedCell(c(2),c(1)) = true;
    end
end

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB is free software: you can redistribute it and/or modify
%   it under the terms of the GNU General Public License as published by
%   the Free Software Foundation, either version 3 of the License, or
%   (at your option) any later version.
%
%   SLAMTB is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU General Public License for more details.
%
%   You should have received a copy of the GNU General Public License
%   along with SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


%EEGLAB group processing (STUDY) functions (studyfunc folder):
%  compute_ersp_times   - Computes the widest possible ERSP/ITC time window,...
%  neural_net           - Computes clusters using Matlab Neural Net toolbox.
%  pop_chanplot         - Graphic user interface (GUI)-based function with plotting...
%  pop_clust            - Select and run a clustering algorithm on components from an EEGLAB STUDY...
%  pop_clustedit        - Graphic user interface (GUI)-based function with editing and plotting...
%  pop_erpimparams      - Set plotting and statistics parameters for...
%  pop_erpparams        - Set plotting and statistics parameters for cluster ERP...
%  pop_erspparams       - Set plotting and statistics parameters for...
%  pop_loadstudy        - Load an existing EEGLAB STUDY set of EEG datasets plus...
%  pop_preclust         - Prepare STUDY components' location and activity measures for later clustering.
%  pop_precomp          - Precompute measures (spectrum, ERP, ERSP) for a collection of data...
%  pop_savestudy        - Save a STUDY structure to a disk file...
%  pop_specparams       - Set plotting and statistics parameters for computing...
%  pop_statparams       - Helper function for pop_erspparams, pop_erpparams, and...
%  pop_study            - Create a new STUDY set structure defining a group of related EEG datasets.
%  pop_studydesign      - Create a STUDY design structure.
%  pop_studyerp         - Create a simple design for ERP analysis...
%  robust_kmeans        - An extension of Matlab kmeans() that removes outlier...
%  std_cell2setcomps    - Convert .sets and .comps to cell array. The .sets and...
%  std_centroid         - Compute cluster centroid in EEGLAB dataset STUDY.
%  std_changroup        - Create channel groups for plotting.
%  std_chaninds         - Look up channel indices in a STUDY...
%  std_chantopo         - Plot ERP/spectral/ERSP topoplot at a specific...
%  std_checkconsist     - Create channel groups for plotting.
%  std_checkfiles       - Check all STUDY files consistency...
%  std_checkset         - Check STUDY set consistency...
%  std_clustmaxelec     - Function to find the electrode with maximum absolute projection...
%  std_clustread        - This function has been replaced by std_readdata() for...
%  std_comppol          - Inverse component polarity in a component cluster...
%  std_convertdesign    - Temporary function converting STUDY design legacy...
%  std_createclust      - Dreate a new empty cluster.  After creation, components...
%  std_dipoleclusters   - Plots clusters of ICs as colored dipoles in MRI...
%  std_dipplot          - Commandline function to plot cluster component dipoles. Dipoles for each...
%  std_editset          - Modify a STUDY set structure.
%  std_erp              - Constructs and returns channel or ICA activation ERPs for a dataset.
%  std_erpimage         - Compute ERP images and save them on disk.
%  std_erpimageplot     - Commandline function to plot cluster ERPimage or channel erpimage.
%  std_erpplot          - Command line function to plot STUDY cluster component ERPs. Either...
%  std_ersp             - Compute ERSP and/or ITC transforms for ICA components...
%  std_erspplot         - Plot STUDY cluster ERSPs. Displays either mean cluster ERSPs,...
%  std_figtitle         - Generate plotting figure titles in a cell array...
%  std_filecheck        - Check if ERSP or SPEC file contain specific parameters.
%  std_fileinfo         - Check uniform channel distribution accross datasets...
%  std_findoutlierclust - Determine whether an outlier cluster already exists...
%  std_findsameica      - Find groups of datasets with identical ICA decomposiotions...
%  std_getdataset       - Constructs and returns EEG dataset from STUDY design.
%  std_getindvar        - Get independent variables of a STUDY...
%  std_indvarmatch      - Match independent variable value in a list of values...
%  std_interp           - Interpolate, if needed, a list of named data channels...
%  std_itcplot          - Commandline function to plot cluster ITCs. Either displays mean cluster...
%  std_loadalleeg       - Constructs an ALLEEG structure, given the paths and file names...
%  std_makedesign       - Create a new or edit an existing STUDY.design by...
%  std_maketrialinfo    - Create trial information structure using the...
%  std_mergeclust       - Commandline function, to merge several clusters.
%  std_movecomp         - Move ICA component(s) from one cluster to another.
%  std_moveoutlier      - Commandline function, to reassign specified outlier component(s)...
%  std_movie            - Make movie in the frequency domain...
%  std_pac              - Compute or read PAC data (Phase Amplitude Coupling).
%  std_pacplot          - Commandline function to plot cluster PAC...
%  std_plot             - This function is outdated. Use std_plottf() to plot time/...
%  std_plotcurve        - Plot ERP or spectral traces for a STUDY component...
%  std_plottf           - Plot ERSP/ITC images a component...
%  std_preclust         - Select measures to be included in computation of a preclustering array.
%  std_precomp          - Precompute measures (ERP, spectrum, ERSP, ITC) for channels in a...
%  std_precomp_worker   - Allow dispatching ERSP to be computed in parallel...
%  std_prepare_neighbors - Prepare Fieldtrip channel neighbor structure.
%  std_propplot         - Command line function to plot component cluster...
%  std_pvaf             - Compute 'percent variance accounted for' (pvaf) by specified...
%  std_readdata         - LEGACY FUNCTION, SHOULD NOT BE USED ANYMORE. INSTEAD...
%  std_readerp          - Load ERP measures for data channels or...
%  std_readerpimage     - Load ERPimage measures for data channels or...
%  std_readersp         - Load ERSP measures for data channels or  for all...
%  std_readfile         - Read data file containing STUDY measures.
%  std_readitc          - Load ITC measures for data channels or...
%  std_readpac          - Read phase-amplitude correlation...
%  std_readspec         - Load spectrum measures for data channels or...
%  std_readspecgram     - Returns the stored mean power spectrogram for an ICA component...
%  std_readtopo         - Returns the scalp map of a specified ICA component, assumed...
%  std_readtopoclust    - Compute and return cluster component scalp maps.
%  std_rebuilddesign    - Reduild design structure when datasets have been...
%  std_rejectoutliers   - Commandline function, to reject outlier component(s) from clusters.
%  std_renameclust      - Commandline function, to rename clusters using specified (mnemonic) names.
%  std_renamestudyfiles - Rename files for design 1 if necessary. In design...
%  std_reset            - Remove all preloaded measures from STUDY...
%  std_rmalldatafields  - Remove all data fields from STUDY (before saving...
%  std_savedat          - Save measure for computed data...
%  std_selcomp          - Helper function for std_erpplot(), std_specplot()...
%  std_selectdataset    - Select datasets and trials for a given independent...
%  std_selectdesign     - Select an existing STUDY design.
%  std_selsubject       - Helper function for std_erpplot(), std_specplot()...
%  std_setcomps2cell    - Convert .sets and .comps to cell array. The .sets and...
%  std_spec             - Returns the ICA component spectra for a dataset. Updates the EEG structure...
%  std_specgram         - Returns the ICA component or channel spectrogram for a dataset.
%  std_specplot         - Plot STUDY component cluster spectra, either mean spectra...
%  std_stat             - Compute statistics for ERP/spectral traces or ERSP/ITC images...
%  std_substudy         - Create a sub-STUDY set by removing datasets, conditions, groups, or...
%  std_topo             - Uses topoplot() to get the interpolated Cartesian grid of the...
%  std_topoplot         - Command line function to plot cluster component and mean scalp maps.
%  std_uniformfiles     - Check uniform channel distribution accross data files...
%  std_uniformsetinds   - Check uniform channel distribution accross datasets...
%  toporeplot           - Re-plot a saved topoplot() output image (a square matrix)...


function ddbs = select_outcomes(ddbs)
%function ddbs = select_outcomes(ddbs)
%
%This function populates outcomes from Excel file into the ddbs structure
%
%APA, 12/24/2009
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

try
    %Get Excel File
    [fName,pName] = uigetfile('.xls','Select Excel file containing data');
    if isnumeric(fName)
        return;
    end
    xlFile = fullfile(pName,fName);
    [num,txt,raw] = xlsread(xlFile);

    columnNameRawC = {};
    indNaN = [];
    for i = 1:length(raw(1,:))
        if ~isnan(raw{1,i})
            columnNameRawC{end+1} = raw{1,i};
        else
            indNaN = [indNaN i];
        end
    end

    raw(:,indNaN) = [];

    %Get FileNames to match with DVH extraction
    [selIndFileName, OK] = listdlg('ListString',columnNameRawC,'PromptString','Select Column containing CERR fileNames');
    if OK && (length(selIndFileName) == 1)
        rowIndicesV = [];
        for j=1:size(raw,1)-1
            if ~isnan(raw{j+1,selIndFileName})
                excel_fileNameC{j} = raw{j+1,selIndFileName};
                rowIndicesV = [rowIndicesV j];
            end
        end
    else
        error('Please select ONE column and try again')
    end

    ddbs_fileNameC = {ddbs(:).fileName};
    ddbs_fileNameC = strtok(ddbs_fileNameC,'.');

    [jnk,indexMapV] = ismember(ddbs_fileNameC,excel_fileNameC);

    %replace spaces with underscores
    for i=1:size(raw,2)
        raw{1,i} = repSpaceHyp(raw{1,i});
    end

%     %removes columns with non-numeric data
%     indToRemove  = [];
%     for i=1:size(raw,2)
%         if ~isnumeric([raw{2:size(raw,1),i}]) || all(isnan([raw{2:size(raw,1),i}]))
%             indToRemove = [indToRemove i];
%         end
%     end
%     raw(:,indToRemove) = [];

    %get variables from the selection list
    [selMetricsIndV, OK] = listdlg('ListString',raw(1,:),'PromptString','Select Metrics');

    %get outcome from the selection list
    [selOutcomesIndV, OK] = listdlg('ListString',raw(1,:),'SelectionMode','single','PromptString','Select Outcome');

    %set variable fields
    for i=1:length(selMetricsIndV)
        for j = 1:length(indexMapV)
            if isnumeric(raw{indexMapV(j)+1,selMetricsIndV(i)})
                ddbs(j).(raw{1,selMetricsIndV(i)}) = raw{indexMapV(j)+1,selMetricsIndV(i)};
            else
                ddbs(j).(raw{1,selMetricsIndV(i)}) = NaN;
            end
        end
    end

    %set outcomes field
    for i=1:length(selOutcomesIndV)
        for j = 1:length(indexMapV)
            if isnumeric(raw{indexMapV(j)+1,selOutcomesIndV(i)})
                ddbs(j).outcome.(raw{1,selOutcomesIndV(i)}) = raw{indexMapV(j)+1,selOutcomesIndV(i)};
            else
                ddbs(j).outcome.(raw{1,selOutcomesIndV(i)}) = NaN;
            end
        end
    end

catch
    errordlg('There was an errror reading the DVH data. Please check the data format and try again.','DVH Import error','modal')
end

return;

% --------- supporting sub-functions
function str = repSpaceHyp(str)
indSpace = strfind(str,' ');
indDot = strfind(str,'.');
str(indDot) = [];
indOpenParan = strfind(str,'(');
indCloseParan = strfind(str,')');
indToReplace = [indSpace indOpenParan indCloseParan];
str(indToReplace) = '_';
indGreaterThan = strfind(str,'>');
indLessThan = strfind(str,'<');
str(indGreaterThan) = 'G';
str(indLessThan) = 'L';
return;


function [isBehavior] = bz_isBehavior(behavior)
% USAGE
% [isBehavior] = bz_isBehavior(behavior)
%
% INPUT
%       behavior   - struct with the following fields
%                   .timestamps
%                   .position
%                       .detectorname
%                       .detectionparms
%                       .detectioninterval
%                       .detectiondate
%
% OUTPUT
%      logical true if struct meets behavior criteria, false if otherwise
%
% written by david tingley, 2017

if isfield(behavior,'behaviorinfo') && isfield(behavior,'timestamps') && isfield(behavior,'samplingRate')% check that fields exist
     if isstruct(behavior.behaviorinfo) && isvector(behavior.timestamps) && isnumeric(behavior.samplingRate)
         % check that sub fields exist
         if isfield(behavior.behaviorinfo,'description') && isfield(behavior.behaviorinfo,'acquisitionsystem') && isfield(behavior.behaviorinfo,'substructnames')
            isBehavior = true;
         else
            warning('behavior struct seems to be missing something in .behaviorinfo')
            isBehavior = false;
         end

     else
         warning('one of the required fields for an behavior type is not formatted correctly')
         isBehavior = false;
     end
else
    warning('one of the required fields for an behavior type does not exist')
    isBehavior = false;
end


function t = islower(c)
%ISLOWER True for lowercase letters.
%
%   For a string C, ISLOWER(C) is 1 for lowercase letters and 0 otherwise.
%
%   See also: ISALNUM, ISALPHA, ISASCII, ISDIGIT, ISPRTCHR, ISUPPER,
%   ISXDIGIT.

%   Author:      Peter J. Acklam
%   Time-stamp:  2002-03-03 13:51:03 +0100
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam

   error(nargchk(1, 1, nargin));

   t = isletter(c) & (c == lower(c));


function sched = par_beginschedule(tasks,varargin)
% Begin the scheduling of some set of tasks across a pool of (possibly remote) workers.
% Id = par_beginschedule(Tasks,Options...)
%
% Returns a scheduler handle to wait for and obtain results upon completion.
%
% In:
%   Tasks : cell array of tasks; each cell can be formatted as
%           * (evaluatable) string
%           * {function_handle, arg1,arg2,arg3, ...}
%           * struct('head',function_handle, 'parts',{{arg1,arg2,arg3, ...}})
%
%   Options...: optional name-value pairs, with possible names:
%               'engine': parallelization engine to use, can be one of:
%                         'global': select the global setting (tracking.parallel.engine)
%                         'local': do all computations locally, skipping serialization
%                         'BLS': use the BCILAB Scheduler (uses the resources specified in the pool argument) (default)
%                         'Reference': local reference implementation for testing BLS (using the same task serialization mechanism)
%                         'ParallelComputingToolbox': use the Mathworks Parallel Computing Toolbox (tm); uses resources allocated via the matlabpool command or a configuration file
%
%               'pool': pool of workers to consider for the BLS scheduler (default: 'global')
%                       if 'global', the global setting (tracking.parallel.pool) will be chosen
%                       (with the BLS engine, an empty pool implies local computation)
%
%               'policy': name of the scheduling policy function for the BLS (default: 'global')
%                         if 'global', the global setting (tracking.parallel.policy) will be chosen
%
%               'pushscope' : whether to "push" the current symbol scope (see hlp_scope and hlp_resolve)
%                             over the network (default: true)
%
%               'keep': keep this scheduler alive for later re-use (default: false)
%                       if false, the scheduler will be destroyed after use, and re-created during the next run
%
% Out:
%   Id : output handle; used to collect the results
%
% See also:
%   par_endschedule, par_worker, par_schedule
%
% Example:
%   % schedule two computations across a pool of some IP:port addresses (assuming that MATLAB is running there,
%   % and is executing the par_worker() function
%   id = par_beginschedule({'sin(randn(10))','exp(randn(10))'}, 'pool',{'192.168.1.1:23547','192.168.1.2:23547','192.168.1.2:23548','192.168.1.3:23547'});
%   ... optionally do something in the meantime
%   results = par_endschedule(id);
%
% Expert note:
%  The 'keep' option is easy to use with the wrapper function par_schedule; otherwise, the following holds:
%   * if passed as true to par_beginschedule, it *must* also be passed as true to par_endschedule
%   * nested schedules are not allowed if they use the same worker pool
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2010-08-29

% Copyright (C) Christian Kothe, SCCN, 2010, christian@sccn.ucsd.edu
%
% This program is free software; you can redistribute it and/or modify it under the terms of the GNU
% General Public License as published by the Free Software Foundation; either version 2 of the
% License, or (at your option) any later version.
%
% This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
% even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
% General Public License for more details.
%
% You should have received a copy of the GNU General Public License along with this program; if not,
% write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
% USA

% read options
opts = arg_define('allow-unlisted-names',varargin, ...
    arg_norep({'tasks','Tasks'},[],[],'Tasks to execute.','type','expression'), ...
    arg({'engine','ParallelEngine'},'global',{'global','local','BLS','Reference','ParallelComputingToolbox'}, 'Parallel engine to use. This can either be one of the supported parallel engines (BLS for BCILAB Scheduler, Reference for a local reference implementation, and ParallelComputingToolbox for a PCT-based implementation), or local to skip parallelization altogether, or global to select the currently globally selected setting (in the global tracking variable).'), ...
    arg({'pool','WorkerPool'},'global',[], 'Worker pool to use. This is typically a cell array, but can also be the string ''gobal'', which stands for the currently globally set up worker pool (see global tracking variable).','type','expression'), ...
    arg({'policy','ReschedulingPolicy'},'global',[], 'Rescheduling policy. This is the name of the rescheduling policy function that controls if and when tasks are being rescheduled. If set to global, the current global setting will be used.'), ...
    arg({'verbosity','Verbosity'},'global',{'global','0','1','2','3'}, 'Verbosity level. Verbosity level for the scheduling process, or global to use the global setting.'), ...
    arg({'receiver_backlog','ReceiverBacklog'},5,uint32([0 1 7 10]), 'Max backlogged connections at receiver. Maximum number of connections that can be in the backlog of the result receiver. '), ...
    arg({'receiver_timeout','ReceiverTimeout'},1,[0 1 60 3600], 'Receiver timeout. In seconds. When a worker that has previously announced that it is ready to transmit results does not start transmitting results within this time period after pinged, the connection will be discarded and the worker has to re-connect.'), ...
    arg({'reschedule_interval','RescheduleInterval'},5,[0 1 15 3600], 'Rescheduling interval. In seconds. Time between periodic checks if jobs have to be re-scheduled.'), ...
    arg({'keep','RetainScheduler'},false,[], 'Retain scheduler instance. If true, the scheduler instance will be reused on the next call to par_beginschedule. Can be more efficient, but does not allow for nested calls to par_beginschedule.'), ...
    arg({'pushscope','PushScope'},true,[], 'Push variable scope. If true, the current variable scope (established via hlp_scope) will be pushed over the network; necessary for some features to run seamlessly.'));

% max length of java arrays that we construct (technically 2^31-1 minus some slack but playing it safe)
max_java_array_len = 2^30;

% look up global settings, if requested
if strcmp(opts.engine,'global')
    opts.engine = par_globalsetting('engine'); end
if strcmp(opts.pool,'global')
    opts.pool = par_globalsetting('pool'); end
if strcmp(opts.policy,'global')
    opts.policy = par_globalsetting('policy'); end
if strcmp(opts.verbosity,'global')
    opts.verbosity = par_globalsetting('verbosity'); end
if ischar(opts.verbosity)
    opts.verbosity = str2num(opts.verbosity); end
if isa(opts.policy,'function_handle')
    opts.policy = char(opts.policy); end
if strcmp(opts.engine,'BLS') && isempty(opts.pool)
    opts.engine = 'local'; end

% validate options
if ~iscellstr(opts.pool)
    error('The given worker pool must be a cell array of strings, but was: %s',hlp_tostring(opts.pool)); end
if ~ischar(opts.policy)
    error('The given rescheduling policy must be a string, but was: %s',hlp_tostring(opts.policy)); end
if ~exist(opts.policy,'file')
    error('The given rescheduling policy refers to a non-existing file: %s',opts.policy); end

% canonlicalize task format to {function-handle,arguments...}
for t=1:length(tasks)
    if isfield(tasks{t},{'head','parts'})
        % task given as Mathematica-style expression struct (see expressions/exp_*)
        tasks{t} = [{tasks{t}.head} tasks{t}.parts];
    elseif ischar(tasks{t})
        % task given as string
        tasks{t} = {@eval,tasks{t}};
    elseif ~(iscell(tasks{t}) && ~isempty(tasks{t}) && isa(tasks{t}{1},'function_handle'))
        % incorrect task format...
        error('Unsupported task format, please see documentation: %s',hlp_tostring(tasks{t},10000));
    end
end

% push current symbol context over the network
if opts.pushscope && ~strcmp(opts.engine,'local')
    % get the current scope
    scope = hlp_resolveall;
    % and wrap a hlp_scope() around the task
    for t=1:length(tasks)
        tasks{t} = [{@hlp_scope, scope} tasks{t}]; end
end

% create a scheduler (Java code, see dependencies/Scheduling-*)
if strcmp(opts.engine,'BLS')
    if opts.verbosity>0 %#ok<*ST2NM>
        fprintf('Creating scheduler...\n'); end
    % trim the 'pid@' part from opts.pool (the scheduler expects the 'host:port' format)
    pool = opts.pool;
    for p=1:length(pool)
        pos = pool{p}=='@';
        if any(pos)
            pool{p} = pool{p}(find(pos,1)+1:end); end
    end
    if opts.keep
        tmp = hlp_microcache('schedulers',@(varargin)Scheduler(varargin{:}),pool,opts.policy,'par_accept_results',opts.receiver_backlog,round(1000*opts.receiver_timeout),round(1000*opts.reschedule_interval),opts.verbosity,length(tasks));
    else
        tmp = Scheduler(pool,opts.policy,'par_accept_results',opts.receiver_backlog,round(1000*opts.receiver_timeout),round(1000*opts.reschedule_interval),opts.verbosity,length(tasks));
    end
    sched = struct('sched',{tmp},'finisher',{onCleanup(@()tmp.clear())});
end

% serialize tasks for network transmission (and prepend order id)
if any(strcmp(opts.engine,{'BLS','Reference'}))
    if opts.verbosity>0
        t0=tic; fprintf('Serializing %d tasks...\n', length(tasks)); end
    for t=1:length(tasks)
        tasks{t} = hlp_serialize([{t} tasks{t}]); end
    if opts.verbosity>0
        fprintf('Tasks serialized (%.1f seconds).\n', toc(t0)); end
end

% submit tasks for execution
switch opts.engine
    case 'local'
        sched = cell(1,length(tasks));
        for t=1:length(tasks)
            sched(t) = {{t,tasks{t}{1}(tasks{t}{2:end})}}; end
    case 'ParallelComputingToolbox'
        sched = {};
        parfor t=1:length(tasks)
            sched(t) = {{t,tasks{t}{1}(tasks{t}{2:end})}}; end
    case 'BLS'
        % over the scheduler
        fprintf('Converting tasks for scheduler...');
        bam = ByteArrayMaker();
        for t=1:length(tasks)
            % we need to efficiently split the task into chunks no larger than max_java_array_len
            % (since the data can easily exceed the max java array length)
            if length(tasks{t}) > max_java_array_len
                num_blocks = ceil(length(tasks{t})/max_java_array_len);
                rest_size = mod(length(tasks{t}),max_java_array_len);
                sizes = [max_java_array_len*ones(1,num_blocks-1) rest_size];
            else
                % we split into at least 2 blocks to not get confused with the gnarly
                % construction/marshaling of Java objects in MATLAB
                sizes = [floor(length(tasks{t})/2), length(tasks{t})-floor(length(tasks{t})/2)];
            end
            blocks = cell(length(sizes),1);
            [blocks{:}] = chopdeal(tasks{t},sizes);
            for b=1:length(blocks)
                blocks{b} = bam.makeByteArray(blocks{b}); end
            tasks{t} = [blocks{:}];
        end
        tasklist = java.util.ArrayList;
        for t=1:length(tasks)
            tasklist.add(tasks{t}); end
        fprintf('done.\n');
        if opts.verbosity>0
            fprintf('Submitting tasks to scheduler...\n'); end
        sched.sched.submit(tasklist);
    case 'Reference'
        % evaluate locally, but go through the same evaluation function as the BLS workers
        unencoded_results = cell(1,length(tasks)); % holds unencoded results for debugging
        for t=1:length(tasks)
            [tasks{t},unencoded_results{t}] = par_evaluate(tasks{t}); end
        % return the collected result in sched
        sched = struct('ReferenceResults',{tasks},'ReferenceResultsUnencoded',{unencoded_results});
    otherwise
        error('Unsupported parallelization engine selected: %s',hlp_tostring(opts.engine));
end


function result=run(obj,result,test_partition_index,test_partition_count)
% Run the test suite
%
% result=run(obj[,result])
%
% Inputs:
%   obj                     MoxUnitTestSuite instance with tests to be run.
%   result                  MoxUnitTestResult instance to which test
%                           results are to be reported (default: empty
%                           MoxUnitTestResult instance).
%   partition_index         } Either both or neither of these arguments
%   partition_count         } must be passed to this function. It causes
%                             a subset of all tests to be run, namely the
%                             ones indexed by:
%                                partition_index+partition_count*K
%                             for all values of K. Default values are
%                             partition_index=1 and partition_count=1,
%                             meaning that all tests are run. A use case is
%                             parallelization of test cases over multiple
%                             processes.
%
% Output:
%   result          MoxUnitTestResult containing tests results
%                   after running the tests in obj.
%
% See also: MoxUnitTestResult
%
% NNO 2015

    if nargin<2
        result=MOxUnitTestReport();
    end

    if nargin<3
        test_partition_index=1;
        test_partition_count=1;
    elseif nargin<4
            error(['With third argument provided, the fourth argument '...
                        'is required']);
    end

    ensure_integer(test_partition_index,'test_partition_index');
    ensure_integer(test_partition_count,'test_partition_count');

    if test_partition_index>test_partition_count
        error('Test index %d greater than number of partitions %d',...
                test_partition_index,test_partition_count);
    end

    n_nodes=countTestNodes(obj);
    for j=test_partition_index:test_partition_count:n_nodes
        test_case=getTestNode(obj,j);
        result=run(test_case, result);
    end

function ensure_integer(value, label)
    msg='';

    if ~isnumeric(value) || ~isscalar(value)
        msg='input must be scalar numeric';
    elseif round(value)~=value
        msg='input must be integer';
    elseif ~(value>0)
        msg='input must be positive';
    end

    if ~isempty(msg)
        error('''%s'' argument: %s',label,msg);
    end


function [chansel, trlsel, cfg] = rejectvisual_trial(cfg, data)

% SUBFUNCTION for ft_rejectvisual

% determine the initial selection of trials
ntrl = length(data.trial);
if isequal(cfg.trials, 'all') % support specification like 'all'
  cfg.trials = 1:ntrl;
elseif isempty(cfg.trials)
  cfg.trials = 1:ntrl;
elseif isnumeric(cfg.trials)
  % use the selection as it is
end
trlsel = false(1, ntrl);
trlsel(cfg.trials) = true;

% determine the initial selection of channels
nchan = length(data.label);
cfg.channel = ft_channelselection(cfg.channel, data.label); % support specification like 'all'
chansel = false(1, nchan);
chansel(match_str(data.label, cfg.channel)) = true;

% compute the sampling frequency from the first two timepoints
fsample = 1/mean(diff(data.time{1}));

% compute the offset from the time axes
offset = zeros(ntrl,1);
for i=1:ntrl
  offset(i) = time2offset(data.time{i}, fsample);
end

if (isfield(cfg, 'preproc') && ~isempty(cfg.preproc))
  ft_progress('init', cfg.feedback, 'filtering data');
  for i=1:ntrl
    ft_progress(i/ntrl, 'filtering data in trial %d of %d\n', i, ntrl);
    [data.trial{i}, label, time, cfg.preproc] = preproc(data.trial{i}, data.label, data.time{i}, cfg.preproc);
  end
  ft_progress('close');
end

% select the specified latency window from the data
% this is done AFTER the filtering to prevent edge artifacts
if ischar(cfg.latency)
  cfg.latency(1) = min(cellfun(@min, data.time));
  cfg.latency(2) = max(cellfun(@max, data.time));
end

% the info structure will be attached to the figure
% and passed around between the callback functions
info.trlop   = 1;
info.chanlop = 1;
info.quit    = 0;
info.ntrl    = ntrl;
info.nchan   = nchan;
info.data    = data;
info.cfg     = cfg;
info.offset  = offset;
info.chansel = chansel;
info.trlsel  = trlsel;

tmpcfg = [];
tmpcfg.layout = 'ordered';
tmpcfg.skipscale = 'yes';
tmpcfg.skipcomnt = 'yes';
tmpcfg.channel = data.label;
info.layout = ft_prepare_layout(tmpcfg);

minx = min(info.layout.pos(:,1) - info.layout.width/2);
maxx = max(info.layout.pos(:,1) + info.layout.width/2);
miny = min(info.layout.pos(:,2) - info.layout.height/2);
maxy = max(info.layout.pos(:,2) + info.layout.height/2);

h = create_figure([minx maxx miny maxy], info);

% Maintain focus while GUI is open
while ishandle(h)
  drawnow
  info = guidata(h);
  if info.quit == 0
    uiwait;
  else
    chansel = info.chansel;
    trlsel = info.trlsel;
    delete(h);
    break
  end
end % while ishandle(h)
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function h = create_figure(limits, info)
% Creates the GUI and plots the data for the first time.
% All additional changes are done by manipulating the properties of the
% specific graphical objects
%
% Input:
% limits - limits of the area where the trials are plotted
% info   - the struct that is attched to figure using guidata
h = open_figure(keepfields(info.cfg, {'figure', 'position', 'visible', 'renderer', 'figurename', 'title'}));
axis('off')
axis(limits);

% Set buttons and callback functions
info.ui.quit        = uicontrol(h,'units','pixels','position',[  5 5 40 18],'String','quit','Callback',@stop);
info.ui.prev        = uicontrol(h,'units','pixels','position',[ 50 5 25 18],'String','<','Callback',{@step_trial, -1});
info.ui.next        = uicontrol(h,'units','pixels','position',[ 75 5 25 18],'String','>','Callback',{@step_trial, 1});
info.ui.prev10      = uicontrol(h,'units','pixels','position',[105 5 25 18],'String','<<','Callback',{@step_trial, -10});
info.ui.next10      = uicontrol(h,'units','pixels','position',[130 5 25 18],'String','>>','Callback',{@step_trial, 10});
info.ui.exclude     = uicontrol(h,'units','pixels','position',[160 5 70 18],'String','exclude','Callback',@markexclude);
info.ui.include     = uicontrol(h,'units','pixels','position',[230 5 70 18],'String','include','Callback',@markinclude);
info.ui.excludenext = uicontrol(h,'units','pixels','position',[310 5 70 18],'String','exclude >','Callback',@markexclude_next);
info.ui.includenext = uicontrol(h,'units','pixels','position',[380 5 70 18],'String','include >','Callback',@markinclude_next);
set(h, 'WindowButtonUpFcn', @button);
set(h, 'KeyPressFcn', @key);

% Determine the y-axis limits for this channel for all included trials
[ymin, ymax] = get_ylim(info);

% Plot individual channels
hold('on')
color = 'k';
tim = info.data.time{info.trlop};
for chanindx=1:info.nchan
  dat = info.data.trial{info.trlop}(chanindx,:);
  info.h_chan(chanindx) = ft_plot_vector(tim, dat, 'hpos', info.layout.pos(chanindx,1), 'vpos', info.layout.pos(chanindx,2), 'width', info.layout.width(chanindx), 'height', info.layout.height(chanindx), 'vlim', [ymin ymax], 'box', istrue(info.cfg.box), 'color', color, 'label', info.layout.label{chanindx}, 'labelpos', 'lowerleft');
end

% Only blank excluded channels if the trial is included
if info.trlsel(info.trlop)
  set(info.h_chan(~info.chansel), 'Color', 'none');
else % If channel is excluded blank out everything
  set(info.h_chan, 'Color', 'none');
end
hold('off')

% Enable or disable buttons as appropriate
check_buttons_status(info);

% Add title
title(description_trial(info),'interpreter','none');

% Update the GUI
guidata(h, info);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function step_trial(h, event, step)
info = guidata(h);
if info.trlop == 1 && step < 0
  fprintf('At first trial\n');
elseif info.trlop == info.ntrl && step > 0
  fprintf('At last trial\n');
else
  trls = (1:info.ntrl);
  info.trlop = trls(nearest(trls, info.trlop + step));
  % Update plots
  new_plots(info);
end
guidata(h, info);
uiresume;
end

function markexclude_next(h, event)
markexclude(h, event);
step_trial(h, event, 1);
end

function markinclude_next(h, event)
markinclude(h, event);
step_trial(h, event, 1);
end

function markinclude(h, event)
info = guidata(h);
% Include trial if it's not already included
if ~info.trlsel(info.trlop)
  info.trlsel(info.trlop) = 1;
  fprintf(description_trial(info));
  title(description_trial(info));
  % Color in black all channels that are included (excluded remain blank)
  set(info.h_chan(info.chansel), 'Color', 'k');
  guidata(h,info);
end
uiresume;
end

function markexclude(h, event)
info = guidata(h);
% Exclude channel if it's not already excluded
if info.trlsel(info.trlop)
  info.trlsel(info.trlop) = 0;
  fprintf(description_trial(info));
  title(description_trial(info));
  % Blank all channels
  set(info.h_chan(info.chansel), 'Color', 'none');
  guidata(h,info);
end
uiresume;
end

function key(h, event)

switch lower(event.Key)
  case 'rightarrow'
    step_trial(h, event, 1);
  case 'leftarrow'
    step_trial(h, event, -1);
  case 'g'
    markinclude(h, event);
  case 'b'
    markexclude(h, event);
  case 'q'
    stop(h);
  otherwise
    fprintf('unknown key pressed\n');
end
end

function button(h, event)
% Find selected trial
pos = get(gca, 'CurrentPoint');
x = pos(1,1);
y = pos(1,2);
info = guidata(h);
dx = info.layout.pos(:,1) - x;
dy = info.layout.pos(:,2) - y;
dd = sqrt(dx.^2 + dy.^2);
[d, i] = min(dd);
threshold = median(sqrt(info.layout.width.^2 + info.layout.height.^2)/2);
if d<threshold && i<=info.nchan
  % Toggle
  info.chansel(i) = ~info.chansel(i); % toggle
  info.chanlop = i;
  guidata(h,info);

  % Print to command window
  fprintf(description_channel(info));

  % Update plots
  update_plots(info);

  % Update trial color
  if strcmp(info.h_chan(i).Color, 'none') % channel is toggled to include
    info.h_chan(i).Color = 'k';
  else % channel is toggled to exclude
    info.h_chan(i).Color = 'none';
  end
else
  fprintf('button clicked\n');
end
uiresume;
end

function stop(h, event)
info = guidata(h);
info.quit = 1;
guidata(h,info);
uiresume;
end

function str = description_channel(info)
if info.chansel(info.chanlop)
  str = sprintf('channel %s marked to INCLUDE\n', info.data.label{info.chanlop});
else
  str = sprintf('channel %s marked to EXCLUDE\n', info.data.label{info.chanlop});
end
end

function str = description_trial(info)
if info.trlsel(info.trlop)
  str = sprintf('trial %d marked to INCLUDE\n', info.trlop);
else
  str = sprintf('trial %d marked to EXCLUDE\n', info.trlop);
end
end

function [ymin, ymax] = get_ylim(info)
% Determines the y-axis limits for one trial for all included channels

if ~isnumeric(info.cfg.ylim)
  ymin = nan(1,info.nchan);
  ymax = nan(1,info.nchan);
  for chanindx = find(info.chansel)
    dat = info.data.trial{info.trlop}(chanindx,:);
    ymin(chanindx) = min(dat);
    ymax(chanindx) = max(dat);
  end
  ymin = min(ymin);
  ymax = max(ymax);
  if strcmp(info.cfg.ylim, 'maxabs') % handle maxabs, make y-axis center on 0
    ymax = max(abs(ymax), abs(ymin));
    ymin = -ymax;
  elseif strcmp(info.cfg.ylim, 'zeromax')
    ymin = 0;
  elseif strcmp(info.cfg.ylim, 'minzero')
    ymax = 0;
  end
else
  ymin = info.cfg.ylim(1);
  ymax = info.cfg.ylim(2);
end
end

function update_plots(info)
% Updates the trial plots after inclusion/exclusion of trials
%
% Input:
% info     - info struct with all the data attched to the GUI

[ymin, ymax] = get_ylim(info);

% Get positions and heights
v_centers = info.layout.pos(:, 2);
heights   = info.layout.height;

for chanindx = find(info.chansel)
  % Shift the vertical axis to zero
  vdat = info.data.trial{info.trlop}(chanindx, :);
  % Scale to length 1 of the new data
  vdat = vdat ./ (ymax - ymin);
  % Scale by availabe plot area
  vdat = vdat .* heights(chanindx);
  % Shift to the vertical position
  vdat = vdat + v_centers(chanindx);
  % Update
  info.h_chan(chanindx).YData = vdat;
end
end

function new_plots(info)
% Updates the trial plots after channel change
%
% Input:
% info - info struct with all the data attched to the GUI

[ymin, ymax] = get_ylim(info);

% Get positions and heights
v_centers = info.layout.pos(:, 2);
h_centers = info.layout.pos(:, 1);
heights   = info.layout.height;
widths    = info.layout.width;

% Update data of ALL channels, also excluded channels
xmin = min(info.data.time{info.trlop});
xmax = max(info.data.time{info.trlop});
for chanindx = 1:info.nchan
  % Shift the horizontal axis to zero
  hdat = info.data.time{info.trlop} - (xmin + xmax)/2;
  % Scale to length 1 of the new data
  hdat = hdat ./ (xmax - xmin);
  % Scale by availabe plot area
  hdat = hdat .* widths(chanindx);
  % Shift to the horizontal position
  hdat = hdat + h_centers(chanindx);

  % Shift the vertical axis to zero
  vdat = info.data.trial{info.trlop}(chanindx, :);
  % Scale to length 1 of the new data
  vdat = vdat ./ (ymax - ymin);
  % Scale by availabe plot area
  vdat = vdat .* heights(chanindx);
  % Shift to the vertical position
  vdat = vdat + v_centers(chanindx);
  % Update
  set(info.h_chan(chanindx), {'XData', 'YData'}, {hdat, vdat});
end

% Blank excluded channels
if info.trlsel(info.trlop) % channel is included
  set(info.h_chan(info.chansel), 'Color', 'k');
  set(info.h_chan(~info.chansel), 'Color', 'none');
else % if channel is excluded blank out everything
  set(info.h_chan, 'Color', 'none');
end

% Enable or disable buttons as appropriate
check_buttons_status(info);

% Update title
title(description_trial(info),'interpreter','none');
end

function check_buttons_status(info)
% Updates buttons status if required
%
% Input:
% info - info struct with all the data attched to the GUI

if info.trlop == 1
  info.ui.prev.Enable   = 'off';
  info.ui.prev10.Enable = 'off';
else
  info.ui.prev.Enable   = 'on';
  info.ui.prev10.Enable = 'on';
end
if info.trlop == info.ntrl
  info.ui.next.Enable   = 'off';
  info.ui.next10.Enable = 'off';
else
  info.ui.next.Enable   = 'on';
  info.ui.next10.Enable = 'on';
end
if info.trlop == info.ntrl
  info.ui.excludenext.Enable = 'off';
  info.ui.includenext.Enable = 'off';
else
  info.ui.excludenext.Enable = 'on';
  info.ui.includenext.Enable = 'on';
end
end