% FIXFS resamples the input time-series to have a fixed sample rate.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FIXFS resamples the input time-series to have a fixed sample rate.
%
% The new sampling grid is computed from the specified sample rate. If no
% sample rate is specified, the target is taken from a fit to the input tsdata
% object. The new sampling grid starts at the time returned from the fit
% (unless specified) and contains the same number of points or spans the
% same time as specified.
%
% CALL:        bs = fixfs(a1,a2,a3,...,pl)
%              bs = fixfs(as,pl)
%              bs = as.fixfs(pl)
%
% INPUTS:      aN   - input analysis objects
%              as   - input analysis objects array
%              pl   - input parameter list
%
% OUTPUTS:     bs   - array of analysis objects, one for each input
%
% <a href="matlab:utils.helper.displayMethodInfo('ao', 'fixfs')">Parameters Description</a>
%
% $Id: fixfs.m,v 1.40 2011/08/23 13:48:48 hewitson Exp $
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


function varargout = fixfs(varargin)

  % Check if this is a call for parameters
  if utils.helper.isinfocall(varargin{:})
    varargout{1} = getInfo(varargin{3});
    return
  end

  import utils.const.*
  utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename);
  
  % Collect input variable names
  in_names = cell(size(varargin));
  for ii = 1:nargin,in_names{ii} = inputname(ii);end

  % Collect all AOs and plists
  [as, ao_invars] = utils.helper.collect_objects(varargin(:), 'ao', in_names);
  [pl, pl_invars] = utils.helper.collect_objects(varargin(:), 'plist', in_names);

  % Decide on a deep copy or a modify
  bs = copy(as, nargout);

  % combine plists
  pl = parse(pl, getDefaultPlist());


  % Get fs
  t0s    = -1; % Please keep the -1. At the moment we don' use the t0.
  fss    = find(pl, 'fs');
  method = find(pl, 'method');
  interp = find(pl, 'interpolation');
  alias  = find(pl, 'filter');
  
  if numel(fss) ~= 1 && numel(fss) < numel(as)
    error('### Please specify either a no sample rate, a single sample rate, or one for each input time-series.');
  end

  % Get only tsdata AOs
  for jj = 1:numel(bs)
    if isa(bs(jj).data, 'tsdata')
      % record input hist
      hin = bs(jj).hist;
      bs(jj).timeshift;
      utils.helper.msg(msg.PROC1, 'fixing AO: %s', bs(jj).name);
      %------------- Fit sample rate and t0
      [ffs, ft0, unevenly] = tsdata.fitfs(bs(jj).data.getX);
      %---------------- Get target sample rate
      if numel(fss) > 1
        fs = fss(jj);
      else
        fs = fss;
      end
      if fs < 0
        utils.helper.msg(msg.PROC1, 'using sample rate from fit: %f', ffs);
        fs = ffs;
      end
      %---------------- Get target start time
      if numel(t0s) > 1
        t0 = t0s(jj);
      else
        t0 = t0s;
      end
      if t0 < 0
        utils.helper.msg(msg.PROC1, 'using start time from fit: %f', ft0);
        t0 = ft0;
      end
      if unevenly % then the fitted t0 is empty so we need to get it from the first input datum
        t0 = bs(jj).x(1);
      end
      %-------------- Compute new grid
      switch lower(method)
        case 'samples'
          N = length(bs(jj).data.y);
          t = linspace(t0, t0+(N-1)/fs, N);
        case 'time'
          Nsecs = bs(jj).data.nsecs;
          t = t0 + [0:1/fs:Nsecs-1/fs].';
        otherwise
          error('### Unknown interpolation method. Do you want to preserve data duration or number of samples?');
      end
      %-------------- Antialiasing filter
      switch lower(alias)
        case 'iir'
          utils.helper.msg(msg.PROC1, 'applying iir antialising filter');
          pl = plist('type', 'lowpass',...
            'order', 8,...
            'fs', bs(jj).data.fs,...
            'fc', 0.9*(fs/2));
          f = miir(pl);
          filtfilt(bs(jj),f);
        case 'fir'
          utils.helper.msg(msg.PROC1, 'applying fir antialising filter');
          pl = plist('type', 'lowpass',...
            'order', 64,...
            'fs', bs(jj).data.fs,...
            'fc', 0.9*(fs/2));
          f = mfir(pl);
          filter(bs(jj),f);
        case 'off'
        otherwise
          error('### Unknown filtering  method. Please choose: ''iir'', ''fir'' or ''off'' ');
      end   
      %-------------- Interpolate
      bs(jj).interp(plist('vertices', t, 'method', interp));
      % Set name
      bs(jj).name = sprintf('%s(%s)', mfilename, ao_invars{jj});
      % Add history
      bs(jj).addHistory(getInfo, pl, ao_invars(jj), hin);
      % clear errors
      bs(jj).clearErrors;
    else
      warning('!!! Skipping AO %s - it''s not a time-series AO.', ao_invars{jj});
      bs(jj) = [];
    end
  end

  % Set output
  if nargout == numel(bs)
    % List of outputs
    for ii = 1:numel(bs)
      varargout{ii} = bs(ii);
    end
  else
    % Single output
    varargout{1} = bs;
  end
end

%--------------------------------------------------------------------------
% Get Info Object
%--------------------------------------------------------------------------
function ii = getInfo(varargin)
  if nargin == 1 && strcmpi(varargin{1}, 'None')
    sets = {};
    pl   = [];
  else
    sets = {'Default'};
    pl   = getDefaultPlist;
  end
  % Build info object
  ii = minfo(mfilename, 'ao', 'ltpda', utils.const.categories.sigproc, '$Id: fixfs.m,v 1.40 2011/08/23 13:48:48 hewitson Exp $', sets, pl);
end

%--------------------------------------------------------------------------
% Get Default Plist
%--------------------------------------------------------------------------
function plout = getDefaultPlist()
  persistent pl;  
  if exist('pl', 'var')==0 || isempty(pl)
    pl = buildplist();
  end
  plout = pl;  
end

function pl = buildplist()
  pl = plist();
  
  % Fs
  p = param({'fs', 'The target sampling frequency.'}, {1, {-1}, paramValue.OPTIONAL});
  pl.append(p);
  
  % Method
  p = param({'method','Choose if the new data should span the same time or preserve the number of samples (time/samples)'},...
    {1, {'time', 'samples'}, paramValue.SINGLE});
  pl.append(p);
  
  % Filter
  p = param({'filter','Specify options for the antialiasing filter.'},{3, {'iir', 'fir', 'off'}, paramValue.SINGLE});
  pl.append(p);
  
  % Interpolation
  pli = ao.getInfo('interp').plists;
  p = setKey(pli.params(pli.getIndexForKey('method')), 'interpolation');
  pl.append(p);
  
end

%   Parameter list:
%           'fs'   - specify the target sample rate. Either a single value
%                    for all input time-series, or a vector of values, one
%                    for each input. To take a fitted value from the data,
%                    specify a sample rate of -1.
%                    e.g.: fs = [1 2 -1 4] to work on 4 input time-series
%                    [default: take from data]
% 
%           'method' - Choose the behaviour
%                      'Time'    - new data span the same time [default]
%                      'Samples' - new data preserves number of samples
%
%           'filter' - specify if antialising filter is applied
%                      'off' - no filter applied [default]
%                      'iir' - 8th order iir filter at fc = fs/2
%                              filter is applied forward and backward (filtfilt)
%                      'fir' - 64th order fir filter at fc = fs/2
%                              filter is applied only forward 
%
%           'interpolation' - specify interpolation method as for interp method
%                             'nearest' -
%                             'linear'  -
%                             'spline'  - default
%                             'cubic'   - 
