function [y_corr, varargout]=f_baseline_corr(x,y,smoothwidth,bwidth,iterations,varargin)
%
% function performs baseline correction for the Raman spectra.
% The baseline at the first and last bwidth/2 data points may show some
% artificial peaks, if there are any sharp edges in the input data. To
% delet thos areas of the spectrum one can set 'cutoff' as shown in the
% Example below.
%
%[INPUT]
% x                 the ordinate (usually in wavelength or wavenumbers)
% y                 the abscissa (usually the intensity)
% smoothwidth       sets the pixel-span for spectrum smoothing
% bwidth            sets the baseline window width
% iterations        sets the number of iterations
%
%[Optional Input]
% 'cutoff'          the function deletes the first and last data points
% 'plot2D'          plots an overview of all estimated baselines and corrected spectra
% 'plot3D'          plots all corrected spectra for each iteration as a mesh
% 'check_iteration' plots the input and baseline in new figure for each iteration
% 'plotrange',[start stop]  set the x-range which should be used in the
%                           plots. If plotrange is not defined the plots are using
%                           the whole available range
%
%[Output]
% y_corr         corrected y-data (in case of 'cutoff' this variable is
%                   a Matrix with corrected data [x y]
%
%[Optional Output]
% baseline          y-data of the baseline
% alteration        array that contains information about the alteration of
%                   the spectrum after each iteration
%   
%[Example]
% spec_corr = f_baseline_corr(wavelength, intesity,6,180,5,'cutoff','plot2D','plotrange',[800 1800])
% [spec_corr baseline] = f_baseline_corr(wavelength, intesity,6,180,5,'plot3D','plot2D')
% [spec_corr baseline alteration] = f_baseline_corr(wavelength, intesity,6,180,5,'plot2D')
%
%
%[History]
% 05.02.2015 - first version by Mikhail Mazurenka,
% based on Matlab code generated by Christian Suhr
% from the script of Matthias Koch
%
% 27.02.2015 - 2nd version by Christian Suhr:
% Added multiple iterations and Baseline output
%
% 04.03.2015 - 3rd version by Christian Suhr:
% Added Plot to controll the results
%
% 11.03.2015 - 4th version by Christian Suhr:
% Changed Smoothing Kernel from rectangle to mollifier
%
% 20.03.2015 - 5th version by Christian Suhr & Matthias Koch:
% Added Spectrum-Alteration Info for each Iteration and 3D Plot
%
%*****************************************************************
% HOT-Hannoversches Zentrum für Optische Technologien
% Leibniz Universität Hannover
% Nienburger Str. 17, D-30167 Hannover, Germany
% http://www.hot.uni-hannover.de/
%*****************************************************************
%

arg_size = size(varargin,2);
set = struct('plot2D',0,'plot3D',0,'check_iteration',0,...
    'plotrange',[min(x) max(x)],...
    'cutoff',0);

for i = 1: arg_size
    if strcmp(varargin{i},'cutoff') == 1
        set.cutoff = 1;
    elseif strcmp(varargin{i},'plot2D') == 1
        set.plot2D = 1;
    elseif strcmp(varargin{i},'plot3D') == 1
        set.plot3D = 1;
    elseif strcmp(varargin{i},'check_iteration') == 1
        set.check_iteration = 1;
    elseif strcmp(varargin{i},'plotrange') == 1
        if isnumeric(varargin{i+1})
            set.plotrange = varargin{i+1};
        end
    end
end

% do some data preparation
  if isrow(x)
      x = x';
  end
  if isrow(y)
      y = y';
  end
  
% keep the Input Data and create spec which will be modified below
spec = [x y];

% to smooth the data a mollifier kernel will be used:
% mollifier kernel for spectrum
m = -1:2/smoothwidth:1;
mollify_spec = exp(-1./(1-abs(m).^2));
mollify_spec = mollify_spec ./ sum(mollify_spec);
ones = zeros(length(spec(:,2)),1) + 1;
conv_edge_spec = conv(ones,mollify_spec,'same');
% mollifier kernel for baseline
m = -1:1/bwidth:1;
mollify_base = exp(-1./(1-abs(m).^2));
mollify_base = mollify_base ./ sum(mollify_base);
ones = zeros(length(spec(:,2)),1) + 1;
conv_edge_base = conv(ones,mollify_base,'same');

% preallocation for the upcoming for-loop
q_spec = length(spec(:,2));
baseline_smooth = zeros(q_spec,iterations);
alteration = zeros(iterations);

for k = 1:iterations
    % smooth the spectrum
    ysmooth = conv(spec(:,2),mollify_spec,'same') ./ conv_edge_spec;
    baseline = zeros(q_spec,1);
    
    % look for lokal minima within bwidth
    i = 0;
    while i < (bwidth/2)
        i = i+1;
        baseline(i) = min( ysmooth(1:(i+bwidth/2)) );
    end
    i = bwidth/2;
    while i+(bwidth/2) < q_spec
        i = i+1;
        baseline(i) = min( ysmooth((i-(bwidth/2)):(i+(bwidth/2))) );
    end
    i = q_spec-bwidth/2;
    while i < q_spec
        i = i+1;
        baseline(i) = min( ysmooth((i-(bwidth/2)):q_spec) );
    end
    
    % smooth the baseline found above
    baseline_smooth(:,k) = conv(baseline,mollify_base,'same') ./ conv_edge_base;
    
    % plot the spektrum and the baseline at each iteration if requested
    if set.check_iteration == 1
        figure
        hold on
        plot(x,spec(:,2))
        plot(x,baseline)
        plot(x,baseline_smooth(:,k))
        hold off
        start = set.plotrange(1);
        stop = set.plotrange(2);
        xlim([start stop])
        box on
        legend('Input Data','baseline','smoothed baseline','Location','northwest')
        title(['Iteration No. ' num2str(k)])
    end
    
    % correct old spectrum with the smoothed baseline
    spec_old = spec(:,2);
    spec(:,2) = spec(:,2) - baseline_smooth(:,k);
    spec_new = spec(:,2);
    
    % Alteration at each iteration
    sum_spec_old = sum(abs(spec_old));
    spec_diff = abs(spec_new - spec_old);
    sum_spec_diff = sum(spec_diff);
    norm_sum_spec_diff = sum_spec_diff / sum_spec_old;
    alteration(k) = norm_sum_spec_diff;
end %for loop

% calculate the total Baseline
baseline = sum(baseline_smooth,2);

% cut the beginning and end of the spectrum if requested
if set.cutoff == 1
    cut = bwidth/0.5;
    baseline=baseline(cut:end-cut);
    y_corr(:,1)=spec((cut:end-cut),1);
    y_corr(:,2)=spec((cut:end-cut),2);
else
    y_corr = spec(:,2);
end

% define variable outputs
nOutputs = nargout-1;
varargout = cell(1,nOutputs);
if nOutputs == 1
    varargout{1} = baseline;
elseif nOutputs == 2
    varargout{1} = baseline;
    varargout{2} = alteration;
end

% plot an overview of the baselines and corrected spectra for each
% iteration in 2D or 3D if requested
if set.plot2D == 1 || set.plot3D == 1
    baseline_iteration = zeros(length(y),size(baseline_smooth,2));
    baseline_iteration(:,1) = baseline_smooth(:,1);
    for i = 2:iterations
        baseline_iteration(:,i) = baseline_iteration(:,i-1) + baseline_smooth(:,i);
    end
    
    spec_iteration = zeros(length(x),size(baseline_smooth,2));
    spec_iteration(:,1) = y - baseline_smooth(:,1);
    for i = 2:iterations
        spec_iteration(:,i) = spec_iteration(:,i-1) - baseline_smooth(:,i);
    end
    
    if set.plot2D == 1
        figure
        
        start = set.plotrange(1);
        stop = set.plotrange(2);
        
        subplot(2,1,1)
        hold on
        plot(x,y,'k')
        for i = 1:iterations
            plot(x,baseline_iteration(:,i))
        end
        hold off
        xlim([start stop])
        title('Input and Baseline (for each Iteration)')
    
        subplot(2,1,2)
        hold on
        for i = 1:iterations
            plot(x,spec_iteration(:,i))
        end
        hold off
        xlim([start stop])
        title('Corrected Spectra (for each Iteration)')
    end
    
    if set.plot3D == 1
        figure
        iteration_array = 1:iterations;
        
        start = set.plotrange(1);
        stop = set.plotrange(2);
        start_index = find(x>=start,1,'first');
        stop_index = find(x>=stop,1,'first');
        
        if iterations>3
            first_color = 3;
        else
            first_color = 1;
        end
        color_start = min(min(spec_iteration(start_index:stop_index,first_color:iterations)));
        color_stop = max(max(spec_iteration(start_index:stop_index,first_color:iterations)));
        meshc(iteration_array,x(start_index:stop_index),spec_iteration(start_index:stop_index,1:iterations))
        colormap(jet)
        caxis([color_start color_stop])
        shading interp
    end
end

end %function