classdef TappedDelay < matlab.System
%TappedDelay Delay a signal and output all the delay versions
%   DELAY = hdl.TappedDelay returns a System object, DELAY, to delay the
%   input by a specified number of samples.
%
%   DELAY = hdl.TappedDelay('PropertyName', PropertyValue, ...) returns a
%   delay System object, DELAY, with each specified property set to the
%   specified value.
%
%   DELAY = hdl.TappedDelay(LEN, 'PropertyName', PropertyValue, ...)
%   returns a delay System object, DELAY, with the NumberOfDelays property
%   set to LEN and other specified properties set to the specified values.
%
%   Step method syntax:
%
%   Y = step(DELAY, X) adds delay to input X to return output Y. Each
%   column of X is treated as an independent channel of input.
%
%   System objects may be called directly like a function instead of using
%   the step method. For example, y = step(obj, x) and y = obj(x) are
%   equivalent.
%
%   TappedDelay methods:
%
%   step     - See above description for use of this method
%   release  - Allow property value and input characteristics changes
%   clone    - Create delay object with same property values
%   isLocked - Locked status (logical)
%   reset    - Reset delay states
%
%   TappedDelay properties:
%
%   NumberOfDelays              - Amount of delay
%   TapLength                   - Length of each delay element
%   InitialConditions           - Initial output of System object
%   OutputOrder                 - Order of output values
%   CurrentInputInOutput        - Include current intput in the output
%
%   % EXAMPLE: Delay input by five samples
%   delay = hdl.TappedDelay(5);
%   for ii=1:5
%       delay(ii);
%   end
%   y = delay(1); % Output is [5 4 3 2 1]'
%
%   See also hdl.RAM, hdl.Delay

%   Copyright 2011-2020 The MathWorks, Inc.

%#codegen

    properties (Nontunable)
        %NumberOfDelays Amount of delay
        %   Specify amount of delay to apply to the input signal. The value
        %   of this property must be a scalar non-negative integer.
        %   The default value of this property is 1.
        NumberOfDelays (1,1) {mustBePositive, mustBeInteger, mustBeNumeric} = 4
        %TapLength Length of each delay element
        TapLength (1,1) {mustBePositive, mustBeInteger, mustBeNumeric} = 1
        %InitialConditions Initial output of Delay
        %   Specify the initial output of the TappedDelay System object.
        %   This property must be set to a scalar MATLAB built-in numeric
        %   data type. The default value of this property is 0.
        InitialConditions (1,1) {mustBeNumericOrLogical} = 0
        %OutputOrder Order of output values
        %   Specify the output order of the TappedDelay System object as one
        %   of ['Oldest' | 'Newest']. The default is 'Oldest'.
        OutputOrder = 'Oldest'
    end
    properties (Nontunable, Logical)    
        %CurrentInputInOutput Include current intput in the output
        %   Specify the initial output of the Delay System object. This
        %   property must be set to a scalar MATLAB built-in numeric data
        %   type. The default value of this property is 0.
        CurrentInputInOutput = false
    end

    properties(Constant, Hidden)
        OutputOrderSet = matlab.system.StringSet( {'Oldest','Newest'} );
    end
    
    properties(DiscreteState)
        pState % State to hold input values
    end

    properties(Access = private, Nontunable)
        pInitialConditions % InitialConditions cast to input data type
    end

    methods
        function obj = TappedDelay(varargin)
            setProperties(obj, nargin, varargin{:}, 'NumberOfDelays');
        end
    end

    methods(Access = protected)
        function setupImpl(obj, u)
            if obj.CurrentInputInOutput && obj.TapLength > 1
                coder.internal.error('hdlmllib:hdlmllib:UnsupportedCurrentInputWithTapLength');
            end
            obj.pInitialConditions = cast(obj.InitialConditions, 'like', u);
            obj.pState = repmat(obj.pInitialConditions, [obj.NumberOfDelays, size(u,2)]);
        end

        function y = outputImpl(obj, u)
            if obj.CurrentInputInOutput
                if strcmp(obj.OutputOrder, 'Oldest')
                    y = [flipud(obj.pState(obj.TapLength:obj.TapLength:end)); u];
                else
                    y = [u; obj.pState(obj.TapLength:obj.TapLength:end)];
                end
            else
                if strcmp(obj.OutputOrder, 'Oldest')
                    y = flipud(obj.pState(obj.TapLength:obj.TapLength:end));
                else
                    y = obj.pState(obj.TapLength:obj.TapLength:end);
                end                
            end
        end

        function updateImpl(obj,u)
            obj.pState = [u; obj.pState(1:end-1)];
        end

        function resetImpl(obj)
            obj.pState(:) = obj.pInitialConditions;
        end

        function validateInputsImpl(~, u)
            validateattributes(u, {'numeric', 'embedded.fi', 'logical'}, ...
                    {'scalar'},...
                    'hdl.TappedDelay', 'input data', 1);
        end

        function s = saveObjectImpl(obj)
            % saveObjectImpl
            % save states & properties into output structure
            % Save the public properties
            s = saveObjectImpl@matlab.System(obj);
            % Save private properties if object is locked
            if obj.isLocked
                s.pState = obj.pState;
                s.pInitialConditions = obj.pInitialConditions;
            end
        end % saveObjectImpl

        function loadObjectImpl(obj, s, ~)
            % loadObjectImpl
            % load states & properties from input structure
            fn = fieldnames(s);
            obj.setObjPropertiesFromStructure(s, fn);
            for ii = 1:numel(fn)
                obj.(fn{ii}) = s.(fn{ii});
            end
        end

    end
end
