classdef (Hidden) TimingInterfaceFlags
    % TIMINGINTERFACEFLAGS
    %   Flags related to timing properties of the model interface
    %   that cannot be derived directly from CodeInfo/CodeDescriptor
    
    %   Copyright 2018-2020 The MathWorks, Inc.
    
    properties (SetAccess = immutable, GetAccess = public)
        IsConstant(1,1) logical;
        IsPeriodicTriggered(1,1) logical;
        IsTriggered(1,1) logical;
        IsFcnCallTriggered(1,1) logical;
        HasContinuousFundamentalSampleTime(1,1);
        HasPortlessSimulinkFunctionPortGroups(1,1);
        HasInternalModelWideEventTs(1,1);
        HasResetFunction(1,1) logical;
        ExposesITPorts(1,1) logical;
    end
    
    methods
        function obj = TimingInterfaceFlags(lIsConstant, ...
                lIsPeriodicTriggered, ...
                lIsTriggered, ...
                lIsFcnCallTriggered, ...
                lHasContinuousFundamentalSampleTime, ...
                lHasPortlessSimulinkFunctionPortGroups, ...
                lHasInternalModelWideEventTs, ...
                lHasResetFunction, ...
                lExposesITPorts)
            % TIMINGINTERFACEFLAGS
            obj.IsConstant = lIsConstant;
            obj.IsPeriodicTriggered = lIsPeriodicTriggered;
            obj.IsTriggered = lIsTriggered;
            obj.IsFcnCallTriggered = lIsFcnCallTriggered;
            obj.HasContinuousFundamentalSampleTime = lHasContinuousFundamentalSampleTime;
            obj.HasPortlessSimulinkFunctionPortGroups = lHasPortlessSimulinkFunctionPortGroups;
            obj.HasInternalModelWideEventTs = lHasInternalModelWideEventTs;
            obj.HasResetFunction = lHasResetFunction;
            obj.ExposesITPorts = lExposesITPorts;
        end
    end
    
    methods (Static)
        function obj = withDefaultValues()
            % DEFAULT VALUES
            %   Construct with default values for SIL/PIL block or MATLAB
            %   Coder.
            lIsConstant = false;
            lIsPeriodicTriggered = false;
            lIsTriggered = false;
            lIsFcnCallTriggered = false;
            lHasContinuousFundamentalSampleTime = false;
            lHasPortlessSimulinkFunctionPortGroups = false;
            lHasInternalModelWideEventTs = false;
            lHasResetFunction = false;
            lExposesITPorts = false;
            obj = rtw.connectivity.TimingInterfaceFlags( ...
                lIsConstant, ...
                lIsPeriodicTriggered, ...
                lIsTriggered, ...
                lIsFcnCallTriggered, ...
                lHasContinuousFundamentalSampleTime, ...
                lHasPortlessSimulinkFunctionPortGroups, ...
                lHasInternalModelWideEventTs, ...
                lHasResetFunction, ...
                lExposesITPorts);
        end
        
        function obj = fromMF0(mf0Contents, lHasResetFunction, lExposesITPorts)
            % Need to know whether IsConstant is true/false, and
            % that needs to come from mf0, because we cannot
            % distinguish between "empty" periodic step function and one
            % with side effects from codeInfo.
            lIsConstant = ~isempty(mf0Contents.isRefModelConstant) && mf0Contents.isRefModelConstant;
            [lIsFcnCallTriggered, lIsTriggered, sampleTimeType] = ...
                rtw.connectivity.TimingInterfaceFlags.getRootTriggerProps(mf0Contents);
            lIsPeriodicTriggered = ~lIsFcnCallTriggered && strcmp(sampleTimeType, 'triggered');
            
            % Get whether using ContinuousFundamentalSampleTime before
            % computing usePortBasedSampleTimes.
            lHasContinuousFundamentalSampleTime = ...
                (mf0Contents.fundamentalSampleTimePeriod == 0);
            
            lHasPortlessSimulinkFunctionPortGroups = ...
                rtw.connectivity.TimingInterfaceFlags.computeHasPortlessSimulinkFunctionPortGroups(mf0Contents);

            lHasInternalModelWideEventTs = mf0Contents.hasInternalModelWideEventTs;

            obj = rtw.connectivity.TimingInterfaceFlags( ...
                lIsConstant, ...
                lIsPeriodicTriggered, ...
                lIsTriggered, ...
                lIsFcnCallTriggered, ...
                lHasContinuousFundamentalSampleTime, ...
                lHasPortlessSimulinkFunctionPortGroups, ...
                lHasInternalModelWideEventTs, ...
                lHasResetFunction, ...
                lExposesITPorts);
        end
    end
    
    methods (Static, Access = private)
        function [isFcnCall, isTriggered, sampleTimeType] = getRootTriggerProps(mf0)
            % retrieve required information from binfo
            triggerType = mf0.triggerType;
            
            % rtw.pil.Trigger type exposes the "TriggerType" enum via MCOS
            % using a uint32 base class.
            isFcnCall = (triggerType == rtw.pil.TriggerType.FUNCTION_CALL);
            isTriggered = (triggerType ~= rtw.pil.TriggerType.UNDEFINED_TRIGGER);
            sampleTimeType = mf0.triggerTsType;
        end
        
        function hasPortlessSimulinkFunctionPortGroups = computeHasPortlessSimulinkFunctionPortGroups(mf0)
            % A port-less simulink function port group is a function call
            % port group for a simulink function that has no associated
            % data input ports or data output ports.
            hasPortlessSimulinkFunctionPortGroups = false; 
            %
            % Only one is necessary to set the flag true.
            for kGrp = 1:mf0.fcnCallPortGroups.Size
                lGrp = mf0.fcnCallPortGroups.at(kGrp);
                hasPortlessSimulinkFunctionPortGroups = ...
                    lGrp.isSimulinkFunction && ...
                    lGrp.dataInputPorts.Size == 0 && ...
                    lGrp.dataOutputPorts.Size == 0;
                if hasPortlessSimulinkFunctionPortGroups
                    break;
                end
            end
        end
    end
end
