function  [lsac, rsac] = saccades( samples, samplerate, maxmag, Overshoot_Time, MINDUR, VFAC , isInTrial, blinks )
%-------------------------------------------------------------------
%
%  FUNCTION saccades.m
%
%   Detects all the saccades in an eye movement recording
%
%   Distinctive Features of Saccadic Intrusions and Microsaccades in
%   Progressive Supranuclear Palsy. Otero-Millan, Leigh, Serra, Troncoso,
%   Macknik, Martinez-Conde. Journal of Neuroscience (under review).
%
%-------------------------------------------------------------------
%
%  INPUT:
%
%  samples(:,1)     timestamps of the recording
%  samples(:,2)     horizontal position of the left eye   
%  samples(:,3)     vertical position of the left eye  
%  samples(:,4)     horizontal position of the right eye     
%  samples(:,5)     vertical position of the right eye  
%  samplerate       samplerate of the recording
%  maxmag           maximum magnitude of the (micro)saccades (default 5 deg)
%  Overshoot_Time   minimum interval between saccades (default 20 ms)
%  MINDUR           minimum duration of saccades (default 3 samples)
%  VFAC             threshold factor used by Engbert and Kliegl's algorithm (default 6)
%  isInTrial        binary vector indicating for each sample if it
%                   belons to a trial (1) or not (0)
%  blinks           binary vector indicating for each sample if it
%                   belons to a blink (1) or not (0)
%
%  OUTPUT:
%
%  lsac(:,1)    indices of the start of all saccades in the left eye
%  lsac(:,2)    indices of the end of all saccades in the left eye
%  rsac(:,1)    indices of the start of all saccades in the right eye
%  rsac(:,2)    indices of the end of all saccades in the right eye   
%
%---------------------------------------------------------------------


%% - Set Defaults
if ( ~exist('maxmag', 'var') || isempty( maxmag ) )
    maxmag = 5;
end
if ( ~exist('Overshoot_Time', 'var') || isempty( Overshoot_Time ) )
    Overshoot_Time = 20;
end
if ( ~exist('MINDUR', 'var') || isempty( MINDUR ) )
    MINDUR = 3;
end
if ( ~exist('VFAC', 'var') || isempty( VFAC ) )
    VFAC = 6;
end
if ( ~exist('isInTrial', 'var') || isempty( isInTrial ) )
    isInTrial = ones(size(samples(:,1)));
end
if ( ~exist('blinks', 'var') || isempty( blinks ) )
    blinks = zeros(size(samples(:,1)));
end


LEFT =  ~any(isnan(samples(:,2)));
RIGHT = ~any(isnan(samples(:,4)));
lsac = [];
rsac = [];

%% - Basic saccade detection
if ( LEFT )
    left_eyedat = zeros(size(samples,1), 4);
    v = vecvel( samples(:,[2,3]), samplerate, 2 );
    left_eyedat( :, [1 2] ) = samples(:,[2,3]);
    left_eyedat( :, [3 4] ) = v;
    
    [lsac] = findSaccades( left_eyedat, isInTrial, blinks, MINDUR, VFAC );
end
if ( RIGHT )
    right_eyedat = zeros(size(samples,1), 4);
    v = vecvel( samples(:,[4 5]), samplerate, 2 );
    right_eyedat( :, [1 2] ) = samples(:,[4,5]);
    right_eyedat( :, [3 4] ) = v;
    
    [rsac] = findSaccades( right_eyedat, isInTrial, blinks, MINDUR, VFAC );
end

%% - Remove monoculars (overlap threshold)
if ( LEFT && RIGHT )
    [left_monoculars_idx right_monoculars_idx]  = findMonoculars( lsac, rsac );
    lsac(left_monoculars_idx,:)    = [];
    rsac(right_monoculars_idx,:)  = [];
end

%% - Remove overshoots
if ( LEFT && RIGHT )
    % if monoculars have been removed we can find overshoots  binocularly
    [overshoots_idx] = findOvershoots( lsac, rsac, samplerate,  Overshoot_Time );
    lsac(overshoots_idx,:)   = [];
    rsac(overshoots_idx,:)  = [];
    left_overshoots_idx = overshoots_idx;
    right_overshoots_idx = overshoots_idx;
elseif ( LEFT )
    [left_overshoots_idx] = findOvershoots( lsac, [], samplerate, Overshoot_Time );
    lsac(left_overshoots_idx,:)   = [];
elseif ( RIGHT )
    [right_overshoots_idx] = findOvershoots( [], rsac, samplerate, Overshoot_Time );
    rsac(right_overshoots_idx,:)  = [];
end
if ( LEFT )
    if ( ~isempty( left_overshoots_idx ) )
        for i=1:size(lsac,1)
            ovidx = find(left_overshoots_idx(:,1)>=lsac(i,2) & left_overshoots_idx(:,1)<=lsac(i,2)+ Overshoot_Time);
            if ( ~isempty(ovidx) )
                lsac(i,2) = left_overshoots_idx(ovidx(end),2);
            end
        end
    end
end
if ( RIGHT )
    if ( ~isempty( right_overshoots_idx  ) )
        for i=1:size(rsac,1)
            ovidx = find(right_overshoots_idx(:,1)>=rsac(i,2) & right_overshoots_idx(:,1)<=rsac(i,2)+ Overshoot_Time);
            if ( ~isempty(ovidx) )
                rsac(i,2) = right_overshoots_idx(ovidx(end),2);
            end
        end
    end
end

%% - Apply the maximum magnitude threshold
if ( LEFT && RIGHT )
    lmags = sqrt(lsac(:,6).^2+lsac(:,7).^2);
    rmags = sqrt(rsac(:,6).^2+rsac(:,7).^2);
    usac_idx = find( lmags > maxmag | rmags > maxmag );
    lsac(usac_idx,:)   = [];
    rsac(usac_idx,:)  = [];
elseif ( LEFT )
    lmags = sqrt(lsac(:,6).^2+lsac(:,7).^2);
    [lusac_idx] = find( lmags > maxmag );
    lsac(lusac_idx,:)   = [];
elseif ( RIGHT )
    rmags = sqrt(rsac(:,6).^2+rsac(:,7).^2);
    [rusac_idx] = find( rmags > maxmag );
    rsac(rusac_idx,:)  = [];
end

end


%% findSaccades
function sac = findSaccades( eyedat, isInTrial, blinks, MINDUR, VFAC )

trial_begin_idx = find(diff([0;isInTrial])>0);
trial_end_idx   = find(diff([isInTrial;0])<0);

num_sac = 0;
% for each trial process the data
for iTrial = 1:length(trial_begin_idx)
    trial = trial_begin_idx(iTrial);
    trial_end = trial_end_idx(iTrial);
    xy = eyedat(trial:trial_end,1:2);
    v = eyedat(trial:trial_end,3:4);
    
    [sac] = microsacc(xy, v ,VFAC,MINDUR, blinks(trial:trial_end));

    if (~isempty( sac ) )
        sac(:,1:2) = sac(:,1:2) + trial-1;
        total_sac(num_sac+1:num_sac+size(sac,1),:) = sac;
        num_sac = num_sac + size(sac,1);
    end
end
sac = total_sac(1:num_sac,:);

%-- remove usacc in intertrials and in blinks just in case
good_samples = isInTrial & ~ blinks;
if ( ~isempty( sac ) )
    
    is_good = zeros(size(sac,1),1);
    for i=1:length(is_good)
        is_good(i) = sum( ~good_samples( sac(i,1):sac(i,2)) ) == 0;
    end
    
    sac = sac( is_good==1, :);
    
end

end


%% RemoveMonoculars
function [left_monoculars_idx right_monoculars_idx] = findMonoculars( lsac, rsac )
ls = lsac(:,1);
le = lsac(:,2);
rs = rsac(:,1);
re = rsac(:,2);

binocs2 = [];
ss2 = [];
% for each microsaccade in the left eye
for i=1:length(ls)
    e = ones(size(re))*le(i);
    s = ones(size(rs))*ls(i);
    row = max(min( re, e) - max(rs, s),0);
    % row has the overlap of this left microsaccade with all the right
    % microsaccades
    if( sum(row) > 0 )
        % we get the right microsaccade that overlaps more with the
        % left one and we check that the left one is also the one that
        % overlaps more with the right one.
        [v, max_index_r] = max( row );
        e = ones(size(le))*re(max_index_r);
        s = ones(size(ls))*rs(max_index_r);
        col = max( min( le, e) - max(ls, s), 0 );
        [v, max_index_l] = max( col );
        if (max_index_l == i )
            binocs2(end+1, : ) = [max_index_l;max_index_r];
            ss2(end+1) = v;
        end
    end
end
left_monoculars_idx = setdiff(1:length(lsac),binocs2(:,1));
right_monoculars_idx = setdiff(1:length(rsac),binocs2(:,2));
end


%% findOvershoots
function overshoots_idx = findOvershoots( lsac, rsac, samplerate, Overshoot_Time )
MIN_ISI = Overshoot_Time*samplerate/1000;
if ( ~isempty(lsac) && ~isempty( rsac) )
    lmags = sqrt(lsac(:,6).^2+lsac(:,7).^2);
    rmags = sqrt(rsac(:,6).^2+rsac(:,7).^2);
elseif( ~isempty(lsac) )
    lmags = sqrt(lsac(:,6).^2+lsac(:,7).^2);
    rmags = lmags;
    rsac = lsac;
elseif (~isempty(rsac) )
    rmags = sqrt(rsac(:,6).^2+rsac(:,7).^2);
    lmags = rmags;
    lsac = rsac;
end

mean_mag = mean([lmags rmags],2);
bad_index =[];
too_close_group = [];
for j = 1:length(mean_mag(:,1))
    % finds the microsaccades that are too close to the current
    % one
    too_close_idx = find(...
        (lsac(:,1) - lsac(j,2) <= MIN_ISI & ...
        lsac(:,1) - lsac(j,2) > 0) | ...
        (rsac(:,1) - rsac(j,2) <= MIN_ISI & ...
        rsac(:,1) - rsac(j,2) > 0) );
    
    if ( ~isempty(too_close_idx) )
        % if there is any microsaccade too close
        if( isempty(too_close_group) )
            % if there is no current group, start a new one
            too_close_group =  union(j,too_close_idx);
        else
            % if there is already a group, add these
            % microsaccades to the group
            too_close_group =  union(too_close_group,too_close_idx);
        end
    else
        % if the current microsaccade is not too close and
        % there is a current group, deal with it
        if ( ~isempty(too_close_group) )
            % keep only the largest microsaccade
            [max_mag,max_idx] = max(mean_mag(too_close_group));
            bad_index = union(bad_index, too_close_group(setdiff(1:length(too_close_group),max_idx)));
            
            % initialize the current group
            too_close_group = [];
        end
    end
end
overshoots_idx = bad_index;
end
