% MFIR FIR filter object class constructor.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DESCRIPTION: MFIR FIR filter object class constructor.
%              Create a mfir object.
%
% CONSTRUCTORS:
%
%       f = mfir()         - creates an empty mfir object.
%       f = mfir(fi)       - creates a copy of the input mfir object, fi.
%       f = mfir(a)        - creates a mfir object based on the magnitude of
%                            the input AO/fsdata object a.
%       f = mfir(pzm)      - creates a mfir object from a pole/zero model
%       f = mfir(c,fs)     - creates an mfir object based on the vector of input
%                            coefficients c.
%                            The sample rate for which the filter is designed
%                            should be specified as well.
%       f = mfir(filename) - creates an mfir object loading the  mfir object from disk
%       f = mfir(pl)       - creates an mfir object from the description given
%                            in the parameter list.
%
%
% Parameter sets examples for plist constructor:
%
% EXAMPLE 1:   Create an order 1 highpass filter with high frequency gain 2.
%              Filter is designed for 10 Hz sampled data and has a cut-off
%              frequency of 0.2 Hz.
%
%              >> pl = plist('type', 'highpass', ...
%                            'order', 128,       ...
%                            'gain',  2.0,       ...
%                            'fs',    10,        ...
%                            'fc',    0.2);
%              >> f = mfir(pl)
%
% NOTES:
%           ** The convention used here for naming the filter coefficients is
%              the opposite to MATLAB's convention. The recursion formula
%              for this convention is
%
%              y(n) = a(1)*x(n) + a(2)*x(n-1) + ... + a(na+1)*x(n-na)
%
% <a href="matlab:utils.helper.displayMethodInfo('mfir', 'mfir')">Parameters Description</a>
%
% VERSION:     $Id: mfir.m,v 1.117 2011/08/15 12:22:57 hewitson Exp $
%
% SEE ALSO:    miir, ltpda_filter, ltpda_uoh, ltpda_uo, ltpda_obj, plist
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

classdef mfir < ltpda_filter
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                            Property definition                            %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  %---------- Public (read/write) Properties  ----------
  properties
  end
  
  %---------- Protected read-only Properties ----------
  properties (SetAccess = protected)
    gd      = []; %
  end
  
  %---------- Protected read-only Properties ----------
  properties (SetAccess = protected, Dependent = true)
    ntaps % number of coefficients in the filter
  end
  
  %---------- Private Properties ----------
  properties (GetAccess = protected, SetAccess = protected)
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                          Check property setting                           %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  methods
    function set.gd(obj, val)
      if ~isempty(val)
        if ~isnumeric(val) || ~isreal(val)
          error('### The value for the property ''gd'' must be a real number(s)');
        end
      end
      obj.gd = val;
    end
    function set.ntaps(obj, val)
      error('### Don''t set the property ''ntaps''. It is computed by length(a).');
    end
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                     Compute the Dependent properties                      %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods
    function val = get.ntaps(obj)
      val = length(obj.a);
    end
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                Constructor                                %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods
    function obj = mfir(varargin)
      
      import utils.const.*
      utils.helper.msg(msg.OMNAME, 'running %s/%s', mfilename('class'), mfilename);
      
      % Collect all mfir objects
      [fs, invars, rest] = utils.helper.collect_objects(varargin(:), 'mfir');
      
      if isempty(rest) && ~isempty(fs)
        % Do copy constructor and return
        utils.helper.msg(msg.OPROC1, 'copy constructor');
        obj = copy(fs, 1);
        for kk=1:numel(obj)
          obj(kk).addHistory(mfir.getInfo('mfir', 'None'), [], [], obj(kk).hist);
        end
        return
      end
      
      switch nargin
        case 0
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%   no inputs   %%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          utils.helper.msg(msg.OPROC1, 'empty constructor');
          obj.addHistory(mfir.getInfo('mfir', 'None'), plist(), [], []);
          
        case 1
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%   one input   %%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          
          if ischar(varargin{1})
            %%%%%%%%%%  f = mfir('foo.xml')   %%%%%%%%%%
            %%%%%%%%%%  f = mfir('foo.mat')   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from file %s', varargin{1});
            obj = fromFile(obj, plist('filename', varargin{1}));
            
          elseif isa(varargin{1}, 'ao')
            %%%%%%%%%%  f = mfir(ao-object)   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from AO %s', varargin{1}.name);
            pl = mfir.getDefaultPlist('From AO');
            pl = pset(pl, 'AO', varargin{1});
            obj = fromAO(obj, pl);
            
          elseif isstruct(varargin{1})
            %%%%%%%%%%  f = mfir(struct)   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from struct');
            obj = fromStruct(obj, varargin{1});
            
          elseif isa(varargin{1}, 'pzmodel')
            %%%%%%%%%%  f = mfir(pzmodel-object)   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from pzmodel %s', varargin{1}.name);
            obj = fromPzmodel(obj, plist('pzmodel', varargin{1}));
            
          elseif isa(varargin{1}, 'plist')
            %%%%%%%%%%  f = mfir(plist-object)   %%%%%%%%%%
            %----------- plist
            
            pl       = varargin{1};
            
            % Selection of construction method
            if pl.isparam('filename')
              utils.helper.msg(msg.OPROC1, 'constructing from file %s', pl.find('filename'));
              obj = fromFile(obj, pl);
              
            elseif pl.isparam('hostname') || pl.isparam('conn')
              utils.helper.msg(msg.OPROC1, 'constructing from repository');
              obj = obj.fromRepository(pl);
              
            elseif pl.isparam('type')
              utils.helper.msg(msg.OPROC1, 'constructing standard %s filter', pl.find('type'));
              obj = fromStandard(obj, pl);
              
            elseif pl.isparam('pzmodel')
              utils.helper.msg(msg.OPROC1, 'constructing from pzmodel object');
              obj = fromPzmodel(obj, pl);
              
            elseif pl.isparam('a')
              utils.helper.msg(msg.OPROC1, 'constructing from A/B coefficients');
              obj = fromA(obj, pl);
              
            elseif pl.isparam('AO')
              utils.helper.msg(msg.OPROC1, 'constructing from AO');
              obj = fromAO(obj, pl);
              
            elseif pl.isparam('built-in')
              utils.helper.msg(msg.OPROC1, 'constructing from built-in model');
              obj = fromModel(obj, pl);
              
            elseif pl.isparam('plist')
              %--- Construct from plist
              % if the plist is empty, we return an empty MFIR
              ipl = find(pl, 'plist');
              if nparams(ipl) == 0
                obj = mfir();
              else
                % do plist constructor
                obj = mfir(ipl);
              end
              
            else
              obj.setObjectProperties(pl);
              obj.addHistory(mfir.getInfo('mfir', 'None'), pl, [], []);
            end
            
          else
            error('### Unknown 1 argument constructor.');
          end
        case 2
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%   two input   %%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          
          if isa(varargin{1}, 'ao')
            %%%%%%%%%%  f = mfir(ao-object, plist-object)   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from AO %s', varargin{1}.name);
            obj = fromAO(obj, pset(varargin{2}, 'AO', varargin{1}));
            
          elseif (isa(varargin{1}, 'database') || isa(varargin{1}, 'mpipeline.repository.RepositoryConnection')) ...
              && isnumeric(varargin{2})
            %%%%%%%%%%  f = mfir(<database-object>, [IDs])   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'retrieve from repository');
            obj = obj.fromRepository(plist('conn', varargin{1}, 'id', varargin{2}));
            
          elseif isa(varargin{1}, 'pzmodel') && isa(varargin{2}, 'plist')
            %%%%%%%%%%  f = mfir(pzmodel-object, plist-object)   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from pzmodel %s', varargin{1}.name);
            obj = fromPzmodel(obj, combine(plist('pzmodel', varargin{1}), varargin{2}));
            
          elseif isa(varargin{1}, 'mfir') && isa(varargin{2}, 'plist') && isempty(varargin{2}.params)
            %%%%%%%%%%  f = mfir(mfir, <empty-plist>)   %%%%%%%%%%
            obj = mfir(varargin{1});
            
          elseif isnumeric(varargin{1})
            %%%%%%%%%%  f = mfir(a, fs)   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from A coefficients');
            obj = fromA(obj, plist('A', varargin{1}, 'fs', varargin{2}));
            
          elseif isa(varargin{1}, 'org.apache.xerces.dom.DeferredElementImpl') && ...
              isa(varargin{2}, 'history')
            %%%%%%%%%%   obj = mfir(DOM node, history-objects)   %%%%%%%%%%
            obj = fromDom(obj, varargin{1}, varargin{2});
            
          elseif isa(varargin{1}, 'ltpda_uoh') && isa(varargin{2}, 'plist')
            %%%%%%%%%%%   mfir(<ltpda_uoh>-object, plist-object)   %%%%%%%%%%
            % always recreate from plist
            
            % If we are trying to load from file, and the file exists, do
            % that. Otherwise, copy the input object.
            if varargin{2}.isparam('filename')
              if exist(fullfile('.', find(varargin{2}, 'filename')), 'file')==2
                obj = mfir(varargin{2});
              else
                obj = mfir(varargin{1});
              end
            else
              obj = mfir(varargin{2});
            end
          else
            error('### Unknown 2 argument constructor.');
          end
          
        otherwise
          [firs, invars, rest] = utils.helper.collect_objects(args, 'mfir');
          
          %%% Do we have a list of MFIR objects as input
          if ~isempty(firs) && isempty(rest)
            obj = mfir(firs);
          else
            error('### Unknown number of arguments.');
          end
      end
      
    end % End constructor
  end % End public methods
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                            Methods (static)                               %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  methods (Static)
    
    function mdls = getBuiltInModels(varargin)
      mdls = ltpda_uo.getBuiltInModels('mfir');
    end
    
    function out = VEROUT()
      out = '$Id: mfir.m,v 1.117 2011/08/15 12:22:57 hewitson Exp $';
    end
    
    function ii = getInfo(varargin)
      ii = utils.helper.generic_getInfo(varargin{:}, 'mfir');
    end
    
    function out = SETS()
      out = [SETS@ltpda_uoh,    ...
        {'From Standard Type'}, ...
        {'From Pzmodel'},       ...
        {'From A'},             ...
        {'From AO'}];
    end
    
    
    function plout = getDefaultPlist(set)
      persistent pl;
      persistent lastset;
      if exist('pl', 'var')==0 || isempty(pl) || ~strcmp(lastset, set)
        pl = mfir.buildplist(set);
        lastset = set;
      end
      plout = pl;
    end
    
    function out = buildplist(set)
      
      if ~utils.helper.ismember(lower(mfir.SETS), lower(set))
        error('### Unknown set [%s]', set);
      end
      
      out = plist();
      out = mfir.addGlobalKeys(out);
      out = buildplist@ltpda_uoh(out, set);
      
      switch lower(set)
        case 'from standard type'
          
          % Type
          p = param({'type','Choose the filter type.'}, {2, {'highpass', 'lowpass', 'bandpass', 'bandreject'}, paramValue.SINGLE});
          out.append(p);
          
          % Fc
          p = param({'fc','The roll-off frequency [Hz].'},  paramValue.DOUBLE_VALUE([0.1 0.4]));
          out.append(p);
          
          % Gain
          p = param({'gain','The gain of the filter.'},  paramValue.DOUBLE_VALUE(1));
          out.append(p);
          
          % Fs
          p = param({'fs','The sampling frequency to design for.'}, paramValue.DOUBLE_VALUE(1));
          out.append(p);
          
          % Order
          p = param({'order', 'The filter order.'}, paramValue.DOUBLE_VALUE(128));
          out.append(p);
          
          % Iunits
          p = param({'iunits','The input units of the filter.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
          % Ounits
          p = param({'ounits','The output units of the filter.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
        case 'from pzmodel'
          
          % Pzmodel
          p = param({'pzmodel', 'A pole/zero model to design from.'}, {1, {pzmodel}, paramValue.OPTIONAL});
          out.append(p);
          
          % Fs
          p = param({'fs','The sampling frequency to design for.'}, paramValue.EMPTY_DOUBLE);
          out.append(p);
          
          % Iunits
          p = param({'iunits','The input units of the transfer function.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
          % Ounits
          p = param({'ounits','The output units of the transfer function.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
        case 'from ao'
          
          % AO
          p = param({'AO', 'The AO object to design from.'}, {1, {ao}, paramValue.OPTIONAL});
          out.append(p);
          
          % N
          p = param({'N', 'The filter order.'}, paramValue.DOUBLE_VALUE(512));
          out.append(p);
          
          % Method
          p = param({'Method', ['The filter design method:<ul>'...
            '<li>''frequency-sampling'' - uses <a href="matlab:doc(''fir2'')">fir2()</a></li>',...
            '<li>''least-squares''      - uses <a href="matlab:doc(''firls'')">firls()</a></li>',...
            '<li>''Parks-McClellan''    - uses <a href="matlab:doc(''firpm'')">firpm()</a></li></ul>']}, {1, {'frequency-sampling', 'least-squares', 'Parks-McClellan'}, paramValue.SINGLE});
          out.append(p);
          
          % Win
          p = param({'win', 'A window to design with when using the frequency-sampling method.'}, paramValue.WINDOW);
          out.append(p);
          
          % PSLL
          p = param({'psll', 'If you specify a Kaiser window, you can also specify the PSLL.'}, paramValue.DOUBLE_VALUE(100));
          out.append(p);
          
          % Iunits
          p = param({'iunits','The input units of the transfer function.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
          % Ounits
          p = param({'ounits','The output units of the transfer function.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
        case 'from a'
          
          % A
          p = param({'a','Vector of A coefficients.'}, paramValue.EMPTY_DOUBLE);
          out.append(p);
          
          % Fs
          p = param({'fs','Sampling frequency of the filter.'}, paramValue.EMPTY_DOUBLE);
          out.append(p);
          
          % Iunits
          p = param({'iunits','The input units of the transfer function.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
          % Ounits
          p = param({'ounits','The output units of the transfer function.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
      end
    end
    
    function obj = initObjectWithSize(n,m)
      obj = mfir.newarray([n m]);
    end
    
  end % End static methods
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                         Methods (static, private)                         %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods (Static, Access=private)
    plo = parseFilterParams(pl)
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                         Methods (static, hidden)                          %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods (Static = true, Hidden = true)
    varargout = loadobj(varargin)
    varargout = update_struct(varargin);
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                              Methods (public)                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods
    varargout = char(varargin)
    varargout = copy(varargin)
    varargout = display(varargin)
    
    varargout = setGd(varargin)
  end
  
  methods (Hidden = true)
    varargout = attachToDom(varargin)
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                              Methods (protected)                          %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods (Access = protected)
    varargout = fromStruct(varargin)
    varargout = fromDom(varargin)
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                               Methods (private)                           %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods (Access = private)
    f = fromPzmodel(f, pli)
    f = fromA(f, pli)
    f = fromAO(f, pli)
    f = fromStandard(f, pli)
    
    f = mklowpass(f, pl)
    f = mkhighpass(f, pl)
    f = mkbandpass(f, pl)
    f = mkbandreject(f, pl)
  end
  
end

