classdef ValueStrategy < fxptds.RangeAggregation.PrecisionExtractionStrategy
    %% VALUESTRATEGY class
    % This class covers a couple edge cases that currently do not have out of the box
    % instrumentation such as constants and delays. These block are not instrumented
    % but we can retrieve their actual values from the block mask. Then we use
    % a utility to return bin data from the actual values. Ideally these blocks
    % should have out of the box instrumentation and no extra support should be needed
    
    % Copyright 2018-2020 The MathWorks, Inc.
    
    properties(SetAccess = private, Hidden)
        instrumenter = fxptds.PrecisionInstrumentation.ObservedPrecisionInstrumenter();
    end
    
    methods
        function [data, isWholeNumber] = getData(this, member)
            % try to get observed precision for the member using values
            [data, isWholeNumber] = this.getObservedPrecisionForMember(member);
            
            % if data is empty then call next strategy
            if isempty(data)
                [data, isWholeNumber] = this.nextStrategy.getData(member);
            end
        end
    end
    
    methods(Hidden)
        function [data, isWholeNumber] = getObservedPrecisionForMember(this, member)
            % get values for the member
            values = double(this.getValues(member));
            data = [];
            isWholeNumber = [];
            % if the values are not empty turn them into bin data
            if ~isempty(values)
                data = fxpHistogram.HistogramUtil.getPrecisionHistogramData(values);
                isWholeNumber = all(~(this.instrumenter.getPrecision(values)));
            end
        end
        
        function values = getValues(~, member)
            values = [];
            
            blkObj = member.getUniqueIdentifier.getObject;
            if isa(blkObj, 'Simulink.Constant')
                % yes, 4 unecessary outputs, its not weird, its wabi
                % sabi
                [~,~,~,~, values] = SimulinkFixedPoint.slfxpprivate('evalNumericParameterRange',blkObj, blkObj.Value);
                
            end
            
            if isa(blkObj, 'Simulink.Delay')
                % if the block is a delay we need to see if the initial
                % conditions are being set in the dialog or come from
                % the input port. If they come from the dialog, we need
                % to read these values and calculate the block's
                % observed precision. If the initial conditions come
                % from the input port, we need to 'fake' observed
                % precision since the rest of the group will absorbe
                % the reponsibility of calculating the actual values
                values = 1;
                if strcmp(blkObj.InitialConditionSource, 'Dialog')
                    [~,~,~,~, values] = SimulinkFixedPoint.slfxpprivate('evalNumericParameterRange',blkObj, blkObj.InitialCondition);
                end
            end
            
            if isa(blkObj, 'Simulink.Outport')
                % if the block is an output port, we need to 'fake' the
                % observed precision and let the rest of the group
                % decide if the group has observed precision
                values = 1;
            end
            
            if isa(blkObj, 'Simulink.Gain') && isequal(member.getUniqueIdentifier.getElementName, 'Gain')
                % if the block is gain we can check the gain parameter
                % NOTE: every gain will correspond to two results: one for 
                % output, one for parameter. Checking that the element name 
                % is 'Gain' make sure that we are only checking the parameter
                % and not the output
                [~,~,~,~, values] = SimulinkFixedPoint.slfxpprivate('evalNumericParameterRange',blkObj, blkObj.Gain);
            end
        end
        
    end
end

% LocalWords:  slobject
