function [output_clean, output, input, h] = ...
    infer_spikes_LFP(rS, FS_SPIKE, rL, FS_LFP, fs_out, input_type, ...
                     verbose, unbiased)
%function [output_clean, output, input, h] = ...
%    infer_spikes_LFP(rS, FS_SPIKE, rL, FS_LFP, fs_out, input_type, ...
%                     verbose, unbiased)
%
% Version 1.1
%
% INPUTS:
% rS: vector of the raw spikes signal
% FS_SPIKE: sampling rate of spike signal
% rL: vector of the raw LFP signal
% FS_LFP: sampling rate of the LFP signal
% fs_out: resampling frequency for output. default = 300 Hz;
% input_type: string (default = 'sua2 4.0')
%   possible values:
%   - 'mua1' = MUA using the SQUARE/SQRT method (Stark et al. 2008)
%   - 'mua2' = MUA using the ABS method (Kayser et al. 2007)
%   - 'sua1 threshold' = SUA with the definition of spikes which uses peak
%   - 'sua2 threshold' = SUA with the def. of spikes which uses derivative
%     (threshold = how many standard deviations the peak/derivative
%     must be to count as an SUA event.)
% unbiased: if == 1 use the 20 *rotating cross-covariance. default 1.
%           (if 1, data must be at least 20*0.5 sec=10 sec duration).
% verbose: if == 1 plot results. default = 0
%
% OUPUTS:
% output_clean: LFP without spikes
% output: resampled raw LFP
% input: signal used as "spikes": SUA or MUA
% trials_start: used to create the position of the stimulus
%
% Authors:  Nicolas Malaval, Stephen David.  2009
% Supplementary material for "Decoupling action potential bias from
% cortical local field potentials"
%
% Revision history: 
% 2009-11-04: 1.0 Original release
% 2010-05-21: 1.1 Corrected strsep bug (thanks to Weimin Zheng)
%
MUA_FILT_ORDER = 500; %filter order for MUA
%FS_SPIKE = 25000;   %sampling rate of the spikes signal
%FS_LFP = 3125;      %sampling rate of the LFP signal

if nargin<5
    fs_out = 300;
end
if nargin<6
    input_type = 'sua2 4.0';
end
if nargin<7
    verbose = 0;
end
if nargin<8
    unbiased = 1;
end

FILTER_DURATION=0.5; %seconds
FILTER_LEN=FILTER_DURATION.*fs_out;

% parse input_type
% return input_number which corresponds to the number mode of the input
% and threshold if SUA
try
    input_type_array = strsep(input_type);
    input_type_var1 = input_type_array(1);
    input_type_var1 = input_type_var1{1};
    
    switch input_type_var1
        case 'mua1'
            input_number = 1;
        case 'mua2'
            input_number = 2;
        case 'sua1'
            input_number = 11;
        otherwise
            input_number = 12;
    end
    
    if input_number > 10
        threshold = input_type_array(2);
        threshold = threshold{1};
    end
    
catch
    error('error parsing input type');
end

% downsample raw LFP signal
output = resample(rL, fs_out, FS_LFP);

% extract the appropriate input (spike) signal
std_rS=std(rS);
switch input_number
     
   % MUA SQUARE/SQRT method
 case 1
  rSmua = rS;
  length_rSmua = length(rSmua);
  
  f_bp = firls(MUA_FILT_ORDER,[0 450/25000 500/25000 3000/25000 ...
                    3500/25000 1],[0 0 1 1 0 0]);
  rSmua = conv(rSmua, f_bp);
  rSmua = rSmua(round(MUA_FILT_ORDER/2)+1:round(MUA_FILT_ORDER/2)+length_rSmua);
  
  rSmua = rSmua.^2;
  rSmua = sqrt(abs(rSmua));
  
  input = resample(rSmua, fs_out, FS_SPIKE);
  
  
  % MUA ABS method
 case 2
  rSmua = rS;
  length_rSmua = length(rSmua);
  
  f_bp = firls(MUA_FILT_ORDER,[0 450/25000 500/25000 3000/25000 3500/25000 1],[0 0 1 1 0 0]);
  rSmua = conv(rSmua, f_bp);
  rSmua = rSmua(round(MUA_FILT_ORDER/2)+1:round(MUA_FILT_ORDER/2)+length_rSmua);
  
  rSmua = abs(rSmua);
  
  input = resample(rSmua, fs_out, FS_SPIKE);
  
  
  % SUA peak height method
 case 11
  rSshort = rS;
  spike_pos2 = (-rSshort>(threshold*std_rS)).*(-rSshort);
  spike_pos2 = [0;diff(spike_pos2)];
  spike_pos = zeros(length(rSshort),1);
  for i=1:length(spike_pos)-1
     if (spike_pos2(i+1)<0) && (spike_pos2(i)>0)
        spike_pos(i) = -rSshort(i);
     end
  end
  spike_pos = spike_pos / mean(spike_pos(spike_pos>0));
  input = zeros(round(length(spike_pos)*fs_out/FS_SPIKE),1);
  for i=1:length(spike_pos)
     if spike_pos(i) > 0
        input(max(1,floor(i*fs_out/FS_SPIKE))) = input(max(1,floor(i*fs_out/FS_SPIKE))) + spike_pos(i);
     end
  end
  
  
  % SUA derivative method
 case 12
  spike_pos = [0;diff(-rS>(threshold*std_rS))];
  spike_pos = spike_pos > 0;
  input = zeros(round(length(spike_pos)*fs_out/FS_SPIKE),1);
  for i=1:length(spike_pos)
     if spike_pos(i) > 0
        input(max(1,floor(i*fs_out/FS_SPIKE))) = input(max(1,floor(i*fs_out/FS_SPIKE))) + spike_pos(i);
     end
  end
  
 otherwise
  error('invalid input mode number');
  
end

if length(input)>length(output),
   fprintf('truncating input at %d bins to match output length\n',...
           length(output));
   input=input(1:length(output));
elseif length(output)>length(input),
   fprintf('truncating output at %d bins to match input length\n',...
           length(input));
   output=output(1:length(input));
end

if sum(input) == 0
    error('sum of input signal == 0. bad channel');
end

%
% measure the spike-LFP filter and apply it to the LFP signal
%
if unbiased
   output_clean=[];
   
   % 20 cross-covariance
   for i=0:19
      
      stop1 = floor(length(input)/20)*i;
      start2 = floor(length(input)/20)*(i+1)+1;
      
      switch i
       case 0
        input_filter = input(start2:end);
        output_filter = output(start2:end);
       case 19
        input_filter = input(1:stop1);
        output_filter = output(1:stop1);
       otherwise
        input_filter = [input(1:stop1);input(start2:end)];
        output_filter = [output(1:stop1);output(start2:end)];
      end
      
      xc = xcov(output_filter,input_filter,FILTER_LEN,'unbiased');
      ac = xcov(input_filter,input_filter,FILTER_LEN,'unbiased');
      
      fft_xc = fft(xc);
      fft_ac = fft(ac);
      
      h_fft = fft_xc./fft_ac;
      h = real(ifft(h_fft));
      h = fftshift(h) .* hann(FILTER_LEN*2+1);
      h = h - mean(h);
      
      switch i
       case 0
        out_spikes = conv(input(stop1+1:start2-1+length(h)), h);
        out_spikes = out_spikes(round(length(h)/2):length(out_spikes)-round(3*length(h)/2)+1);
        output_short = output(stop1+1:start2-1);
       case 19
        out_spikes = conv(input(stop1+1-length(h):end), h);
        out_spikes = out_spikes(round(3*length(h)/2):length(out_spikes)-round(length(h)/2)+1);
        output_short = output(stop1+1:end);
       otherwise
        out_spikes = conv(input(stop1+1-length(h):start2-1+length(h)), h);
        out_spikes = out_spikes(round(3*length(h)/2):length(out_spikes)-round(3*length(h)/2)+1);
        output_short = output(stop1+1:start2-1);
      end
      
      output_clean = cat(1, output_clean, output_short - out_spikes);
   end
   
else
   % biased
   
   xc = xcov(output,input,FILTER_LEN,'unbiased');
   ac = xcov(input,input,FILTER_LEN,'unbiased');
   
   fft_xc = fft(xc);
   fft_ac = fft(ac);
   
   h_fft = fft_xc./fft_ac;
   h = real(ifft(h_fft));
   h = fftshift(h) .* hann(FILTER_LEN*2+1);
   h = h - mean(h);
   
   out_spikes = conv(input, h);
   output_clean = output - ...
       out_spikes(round(length(h)/2):...
                  length(out_spikes)-round(length(h)/2)+1);
end

% only for debugging purposes
if ((exist('verbose','var') ~= 0) && (verbose == 1))

   figure(1)
   plot(h);
   title('spike-LFP filter');
   
   figure(2);
   clf
   plot(xcov(output,input,fs_out*2,'unbiased'));
   hold on;
   plot(xcov(input,output_clean,fs_out*2,'unbiased'),'g');
   hold off
   title('spike-LFP cross correlations');
   legend('lfp raw-spike','lfp clean-spike');

end



% function sout=strsep(s,delim,keepstr)
%
% helper function for parsing input strings
% 
function sout=strsep(s,delim,keepstr)

if iscell(s),
   sout=s;
   return
end

if ~exist('keepstr','var'),
   keepstr=0;
end

sout={};
if length(s)==0,
   return
end
if ~exist('delim','var'),
   delim=' ';
end

x1=[0 find(s==delim)];
x2=[x1(2:end) length(s)+1];

for ii=1:length(x1),
   ts=s((x1(ii)+1):(x2(ii)-1));
   if keepstr | ismember(exist(ts),[1 2 3 4 5 6]),
      sout{ii}=ts;
   elseif isempty(ts),
      sout{ii}=[];
   else
      try
         sout{ii}=evalin('caller',ts);
      catch
         sout{ii}=ts;
      end
   end
end
