classdef CodeInfoData < rtw.pil.DataInterface
%CODEINFODATA - Represents CodeInfo data to be
%               transferred between host and target.              

%   Copyright 2010-2020 The MathWorks, Inc.

    properties(Constant)        
        IsProtocolData=false;
    end
        
    properties (SetAccess = 'private', GetAccess = 'private')        
        % owning CodeInfo Data Interface type
        CodeInfoDataInterfaceType;
        % owning CodeInfo Data Interface index
        CodeInfoDataInterfaceIdx;
        
        TypeDataElements;
        CodeInfoUtils;
        
        IsBitFieldMember;
    end
    
    methods (Access = 'protected')
        function this = CodeInfoData(codeInfoDataInterface, ...
                                     codeInfoDataInterfaceType, ...
                                     codeInfoDataInterfaceIdx, ...
                                     clientInterface, ...
                                     codeInfoUtils)
            % validate inputs
            rtw.connectivity.Utils.validateArg(codeInfoDataInterfaceType, ...
                                               'char');
            rtw.connectivity.Utils.validateArg(clientInterface, ...
                                               'coder.connectivity.ClientInterface');                                           
            rtw.connectivity.Utils.validateArg(codeInfoUtils, ...
                                               'rtw.connectivity.CodeInfoUtils');

            assert(rtw.connectivity.CodeInfoUtils.isa(codeInfoDataInterface, ...
                'DataInterface'), sprintf('Unexpected DataInterface class: %s', class(codeInfoDataInterface)));
                                           
            switch codeInfoDataInterfaceType
                case {'input', ...
                      'inputsize', ...
                      'output', ...
                      'outputsize', ...
                      'parameter', ...
                      'dsread', ...
                      'dswrite', ...
                      'sharedLocalDataStore', ...
                      'internal', ...
                      'siglog', ...
                      'statelog', ...
                      'serverFcnInputArg',...
                      'serverFcnOutputArg',...
                      'serverFcnInputOutputArg',...
                      'callerFcnInputArg',...
                      'callerFcnOutputArg',...
                      'callerFcnInputOutputArg',...
                      'queuedFcnInputArg','queuedFcnOutputArg', ...
                      'unspecified', ...
                      'constructorArg'}
                    % valid types
                otherwise
                    assert(false, 'Unknown dataInterfaceType: %s', codeInfoDataInterfaceType);
            end
            rtw.connectivity.Utils.validateArg(codeInfoDataInterfaceIdx, ...
                                               'double');                           
            % call super class constructor                                                 
            this@rtw.pil.DataInterface(codeInfoDataInterface, clientInterface);                                           
               
            this.CodeInfoDataInterfaceType = codeInfoDataInterfaceType;
            this.CodeInfoDataInterfaceIdx = codeInfoDataInterfaceIdx;
            this.CodeInfoUtils = codeInfoUtils;
            this.DimsPreserved = this.needToPreserveDimensions;            
                    
            this.initIsBitFieldMember;
            this.initNeedsIOTargetTransform;
            
            % make sure VarDimIndex assumptions hold
            if strcmp(codeInfoDataInterfaceType, 'inputsize') || ...
                    strcmp(codeInfoDataInterfaceType, 'outputsize')
                dataElements = this.getTargetDataElements;
                for dataElementIdx=1:length(dataElements)
                    dataElement = dataElements(dataElementIdx);
                    assert(dataElement.getNumInstances == 1, ...
                        'inputsize and outputsize dataElements must be single instance');    
                end
            end                        
        end                              
        
    end
    
    methods        
        function type = getType(this)
            % use CodeType if this data interface supports preservation of
            % dimensions
            useCodeType = this.DimsPreserved;
            type = this.getTypeInternal(useCodeType);
        end  
        
        % override getHostType to support CodeInfo type separation
        function type = getHostType(this)
            type = this.CodeInfoUtils.getDataInterfaceHostType(this.getRTWDataInterface, ...
                this.getType);
        end               
        
        function targetBaseTypeIdentifier = getTargetBaseTypeIdentifier(this, dataElement)
            targetBaseTypeIdentifier = dataElement.getType.Identifier;
            if isempty(targetBaseTypeIdentifier)
                assert(this.IsBitFieldMember, 'Type identifier must only be empty for bit field members.');                
                % bit fields are declared as uint_T
                targetBaseTypeIdentifier = 'uint_T';
            end
        end                        
        
        % write a PILIOData table entry
        function writePILIODataTableEntry(this, dataElement, tableName, writer, targetInterface)
            
            targetBaseTypeIdentifier = this.getTargetBaseTypeIdentifier(dataElement);     
            
            codeInfoUtils = targetInterface.getCodeInfoUtils;
            targetBaseTypeIdentifier = codeInfoUtils.getReplacedTypeIdentifier(targetBaseTypeIdentifier);            
            if dataElement.hasSymbolicDimensions
                storageSize = ['(', this.getSymbolicNumElements(dataElement), ')'];
            else
                storageSize = this.getSymbolicNumElements(dataElement);
            end
            
            if dataElement.isActive
                if ~dataElement.isGetSetViaPointer
                    % get assignment info
                    [dataType, identifier] = this.getAssignmentDataInfo(dataElement);
                    expression = dataElement.getAssignmentDataElementExpression(identifier, 0);
                    dataAddress = ['&' expression];
                    % sizeof operator will return the size in bytes or words for
                    % word addressable machines
                    memUnitLength = [storageSize ' * sizeof(' dataType ')'];
                else
                    this.writeAddress(dataElement, writer, targetInterface);
                    dataAddress = 'dataAddress';
                    % sizeof operator will return the size in bytes or words for
                    % word addressable machines
                    memUnitLength = [storageSize ' * sizeof(' targetBaseTypeIdentifier ')'];
                end
                writer.writeLine([tableName '[tableIdx].memUnitLength = ' memUnitLength ';']);
                writer.writeLine([tableName '[tableIdx++].address = (MemUnit_T *) ' dataAddress ';']);
            end
        end
                        
        % get a string representation via CodeInfoUtils
        function string = getString(this, silPILWrapperUtils)
            codeInfoUtils = silPILWrapperUtils.getCodeInfoUtils;            
            string = codeInfoUtils.getDataInterfaceString(this.getRTWDataInterface, ...
                                                          this.CodeInfoDataInterfaceType);
        end                
                                        
        % return associated CodeInfo Data Interface type
        function dataInterfaceType = getDataInterfaceType(this)
            dataInterfaceType = this.CodeInfoDataInterfaceType;
        end
                
        % return associated CodeInfo Data Interface index
        function dataInterfaceIdx = getDataInterfaceIdx(this)
            assert(~strcmp(this.CodeInfoDataInterfaceType, 'unspecified'), ...
                   'No data interface idx for unspecified CodeInfoData');
            dataInterfaceIdx = this.CodeInfoDataInterfaceIdx;
        end                                                                                         
        
        % get assignment info relating to I/O dataInterfaces        
        function [dataType, identifier] = getAssignmentDataInfo(this, dataElement)
            assert(~dataElement.isGetSetViaPointer, ...
                   'getAssignmentDataInfo should only be called for assignment data!');            
            % Note: "validate" already asserts that assignment data cannot be
            % complex.
            %
            % always use the same datatype that Simulink (host) uses to 
            % represent and transfer the data            
            dtypeId = dataElement.getDtypeId;
            dataType = rtw.connectivity.TypeUtils.builtinToHostCType(dtypeId);            
            cDataIdx = dataElement.getIndex;
            % use a robust id name            
            diIdx = this.getDataInterfaceIdx;
            dataInterface = dataElement.getDataInterface;
            if isa(dataInterface, 'rtw.pil.FunctionArgData')
                % prefix the identifier with the simulink function name 
                % so it is clear who owns this argument
                diType = dataInterface.getFunctionInfo.getCodeInfoData.Prototype.Name;
            else
                diType = this.CodeInfoDataInterfaceType;
            end
            identifier = [diType '_' int2str(diIdx) '_cData_' int2str(cDataIdx) '_nonaddr_io'];               
        end                
                               
                
        % write assignments from the data transfer variable to the target
        % variable.
        function writeInputAssignment(this, dataElement, writer, targetInterface)
            isInputAssignment = true;
            this.writeDataTransferAssignment(dataElement, ...
                writer, ...
                targetInterface, ...
                isInputAssignment);
                end
        % write assignments from the target variable to the data transfer
        % variable.  Note: This method gets overloaded in rtw.pil.LoggingData.
        function writeOutputAssignment(this, dataElement, writer, targetInterface)
            isInputAssignment = false;
            this.writeDataTransferAssignment(dataElement, ...
                writer, ...
                targetInterface, ...
                isInputAssignment);
                end

        function isBitFieldMember = isBitFieldMember(this)            
            isBitFieldMember = this.IsBitFieldMember;
        end
        
        function isStructOptimizationAllowed = isStructOptimizationAllowed(this)
            dataInterfaceType = this.getDataInterfaceType;
            if strcmp(dataInterfaceType, 'inputsize') || strcmp(dataInterfaceType, 'outputsize')
                isStructOptimizationAllowed = false;
            else
                isStructOptimizationAllowed = true;
            end
        end
        
        function mf0 = getMF0ModelInstance(this)
            % Expose the MF0 model instance
            mf0 = this.CodeInfoUtils.getMF0ModelInstance;
        end
    end               
    
    methods (Access = 'protected')                          
       function expression = getReadExpression(this, dataElement, index, targetInterface)
          dataInterface = this.getRTWDataInterface;
          implementation = dataInterface.Implementation;
          expression = targetInterface.getReadExpression(implementation, ...
                                                            dataElement, ...
                                                            index);
       end
                
       function expression = getWriteExpression(this, dataElement, index, newValue, targetInterface)
           dataInterface = this.getRTWDataInterface;
           implementation = dataInterface.Implementation;
           expression = targetInterface.getWriteExpression(implementation, ...
               dataElement, index, newValue);
       end
       
       function writeAddress(this, dataElement, writer, targetInterface)           
           dataInterface = this.getRTWDataInterface;
           
           if this.CodeInfoUtils.isa(dataInterface.Implementation, 'TypedCollection')
               implementationList = this.CodeInfoUtils.getArray(dataInterface.Implementation.Elements);
               %Check nothing has changed with the use of
               %RTW.TypeCollections
               assert(numel(implementationList)==2,'Expect RTW.TypedCollections to only have 2 elements');
               dataElementList = dataElement.getDataInterface.getTargetDataElements;
               %Only ever going to be 2 storage implementations from a
               %typed collection and last one is always size. Last Data
               %Element will also be size.
               if dataElement==dataElementList(end)
                   elementIndex = 2;
               else
                   elementIndex = 1;
               end
               implementation = implementationList(elementIndex);
           else
               implementation = dataInterface.Implementation;
               if this.NeedsIOTargetTransform
                   if ~isempty(dataElement.ReplacementVar)
                       %Switch to replacement var
                       implementation = dataElement.ReplacementVar;
                       dataElement = rtw.pil.DataElement(dataElement.getDataInterface, ...
                           implementation.Type, ...
                           1, ...
                           1, ...
                           []);
                   else
                       implementation = rtw.connectivity.CodeInfoUtils.getTransferImplementation(implementation);
                   end
               end
           end
                  
           lAddressExpression = targetInterface.getAddressExpression(implementation, dataElement, dataElement.getFirstElementIndices);
           writer.wLine('void * dataAddress = (void *) %s;', lAddressExpression);
       end          
    end      
    
    methods (Access = 'private')
        function initIsBitFieldMember(this)
           this.IsBitFieldMember = rtw.connectivity.CodeInfoTypeUtils.isBitFieldMember(this.getRTWDataInterface);
        end

        function initNeedsIOTargetTransform(this)
            interfaceImplementation = this.getRTWDataInterfaceImplementation();
            if this.CodeInfoUtils.isa(interfaceImplementation,'TypedCollection')
                implementations = this.CodeInfoUtils.getArray(interfaceImplementation.Elements);
            else
                implementations = interfaceImplementation;
            end
            for idx=1:numel(implementations)
                impl = implementations(idx);
                if ~isempty(impl) && ~isempty(impl.DynamicMemory) && ...
                        (~isempty(impl.DynamicMemory.Setter) || ~isempty(impl.DynamicMemory.Getter)) && ...
                        slfeature('AccessFunctionMode')==0
                    
                    this.NeedsIOTargetTransform = true;
                elseif rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(impl)
                    this.NeedsIOTargetTransform = true;
                end
            end
            
        end
        
        function [type, isUsingCodeType] = getTypeInternal(this, useCodeType)
            % Use Implementation Type / CodeType, depending on whether ND 
            % preservation is active.       
            %
            % isUsingCodeType captures whether CodeType is actually available and used
            implementation = this.getRTWDataInterface.Implementation;
            storageImplementation = rtw.connectivity.CodeInfoTypeUtils.getStorageImplementation(implementation);
            isUsingCodeType = useCodeType;
            
            if isempty(storageImplementation)
                %If implementation is empty return empty
                type = [];
                return;
            end
            
            type = repmat(storageImplementation(1).Type, size(storageImplementation));
            % default to false
            isUsingCodeType = zeros(size(storageImplementation), 'logical');
            for i = 1:length(storageImplementation)
                if useCodeType 
                    if isempty(storageImplementation(i).CodeType)
                        % missing CodeType implementation, drop back to
                        % Type
                        storageType = storageImplementation(i).Type;
                    else
                        isUsingCodeType(i) = true;
                        storageType = storageImplementation(i).CodeType;
                    end
                else
                    storageType = storageImplementation(i).Type;                    
                end
                assert(~isempty(storageType), 'storageType must never be empty!');
                type(i) = storageType;
            end
            % reduce isUsingCodeType to a scalar
            assert(all(isUsingCodeType) || ~any(isUsingCodeType), ...
                'Inconsistent use of CodeType across storage implementations');
            isUsingCodeType = all(isUsingCodeType);            
        end
        
        % Determine whether ND index processing is required for this data
        % interface.
        %
        % Once all data interface types support CodeType then we can always
        % honor the dimensions it specifies.   Until then, we must flatten
        % other dimensions.
        function needToPreserveDimensions = needToPreserveDimensions(this) 
            % assume CodeType is not used
            isUsingCodeType = false;
            
            isAUTOSAR = strcmpi(this.CodeInfoUtils.ConfigInterface.getParam('AutosarCompliant'), ...
                                        'on');

            if this.getClientInterface.useCodeType
                useCodeType = false;
                switch this.CodeInfoDataInterfaceType
                    case 'parameter'
                        useCodeType = true;
                    case {'input', ...
                          'output', ...
                          'dsread', ...
                          'dswrite', ...
                          'sharedLocalDataStore'}
                        if ~isAUTOSAR
                            useCodeType = true;
                        end
                    case 'siglog'
                        if ~isAUTOSAR
                            useCodeType = ...
                                coder.internal.connectivity.featureOn('UseMF0CodeDescriptorLogging');
                        end
                    otherwise
                        % Cases not supported yet e.g. serverFcnInputArg
                        useCodeType = false;
                end            
                % always try to use CodeType but if it is missing
                % (e.g. g1748948), drop back to not using it and
                % assume the target data is flat
                if useCodeType
                    [~, isUsingCodeType] = this.getTypeInternal(useCodeType);
                end
            end
            % a valid CodeType for this data interface 
            % means we honor its dimensions
            needToPreserveDimensions = isUsingCodeType;
        end
        
        % If isInputAssignment is true, then write code for assignment from
        % data transfer variable to target variable, otherwise write code
        % for assignment from target variable to data transfer variable
        function writeDataTransferAssignment(this, dataElement, writer, targetInterface, isInputAssignment)
            if ~dataElement.isGetSetViaPointer
                % get assignment data info
                [dataType, identifier] = this.getAssignmentDataInfo(dataElement);
                % open any required loops
                dataElement.writeStructArrayHeader(writer);
                [elementIndexVariables, linearIndexVariable] = ...
                        dataElement.writeElementLoopHeader(writer);

                dataExpression = dataElement.getAssignmentDataElementExpression(identifier, linearIndexVariable);                
                
                if isInputAssignment
                    targetBaseTypeIdentifier = dataElement.getDataInterface.getTargetBaseTypeIdentifier(dataElement);
                    % apply type replacement
                    targetBaseTypeIdentifier = ...
                        this.CodeInfoUtils.getReplacedTypeIdentifier(targetBaseTypeIdentifier);
                    newValue = sprintf('( %s ) %s', targetBaseTypeIdentifier, dataExpression);
                    writeExpression = this.getWriteExpression(dataElement, ...
                        elementIndexVariables, ...
                        newValue, ...
                        targetInterface);
                    writer.wLine('%s', writeExpression);
                else
                    readExpression = this.getReadExpression(dataElement, elementIndexVariables, targetInterface);
                    writer.wLine('%s = ( %s ) %s;', dataExpression, dataType, readExpression);
                end
                
                % close any required loops
                dataElement.writeElementLoopTrailer(writer);
                dataElement.writeStructArrayTrailer(writer);
            elseif this.NeedsIOTargetTransform
                %Element needs to be transformed on the target before
                %transmission. Access the transfer variable rather than the
                %normal implementation.
                interfaceImplementation = dataElement.getDataInterface.getRTWDataInterfaceImplementation();
                if rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(interfaceImplementation)
                    %Skip other types of processing.
                    %Return early, Opaque region transfer scheduled elsewhere.
                    return;
                end
                dynamicMem = interfaceImplementation.DynamicMemory;
                overrideCallExpression = false;
                
                %Until g1777186 actioned, need to determine the correct
                %variable name to pass to get/set function calls
                if this.CodeInfoUtils.isa(interfaceImplementation, 'BasicAccessFunctionExpression')
                    %Access functions take the Static Variable name
                    varArg = dynamicMem.StaticVariable.getExpression;
                    %For now, also need to cope with the indexed matrix
                    %case. If an index variable is defined, then rather
                    %than setting the whole matrix, the interface access
                    %function must be called within a loop. Define that
                    %loop
                    indexVar = dynamicMem.IndexVariable;
                    if ~isempty(indexVar)
                        indexVarStr = indexVar.getExpression();
                        arraySize = prod(this.CodeInfoUtils.getArray(dynamicMem.StaticVariable.Type.Dimensions));
                        writer.wBlockStart('for (%s=0; %s<%d; %s++)', indexVarStr, indexVarStr, arraySize, indexVarStr);
                    end
                elseif ((rtw.connectivity.CodeInfoUtils.isa(interfaceImplementation,'StructExpression') || ...
                        rtw.connectivity.CodeInfoUtils.isa(interfaceImplementation,'Variable')) && ...
                        (rtw.connectivity.CodeInfoTypeUtils.isaType(interfaceImplementation.Type, 'Struct')) && ...
                        ~rtw.connectivity.CodeInfoTypeUtils.isaType(interfaceImplementation.Type, 'Class'))
                    if strcmp(dataElement.SimulinkType,'char')
                        varArg = interfaceImplementation.getExpression();
                        %Switch to replacement variable and construct
                        %element index
                        replacementVar = dataElement.ReplacementVar;
                        varIdentifier = replacementVar.Identifier;
                        elemData = dataElement.getDataInterface.StaticVarMap(varIdentifier);
                        varArg = [varArg elemData{1}];
                        overrideCallExpression = true;
                    else
                        return; %return early not special element
                    end
                else
                    %Other instances take the original interface variable.
                    varArg = interfaceImplementation.getExpression();
                end
                
                if isInputAssignment
                    if overrideCallExpression
                        writer.wLine('%s;',dynamicMem.getNthSetterCallExpression(varArg, elemData{2}));
                    else
                        writer.wLine('%s;',dynamicMem.getSetterCallExpression(varArg));
                    end
                else
                    if overrideCallExpression
                        writer.wLine('%s;',dynamicMem.getNthGetterCallExpression(varArg, elemData{2}));
                    else
                        writer.wLine('%s;',dynamicMem.getGetterCallExpression(varArg));
                    end
                end
                if this.CodeInfoUtils.isa(interfaceImplementation, 'BasicAccessFunctionExpression')
                    if ~isempty(indexVar)
                        %Close the assignment loop
                        writer.wBlockEnd(); %For index var loop
                    end
                end
            end
        end
    end        
       
    methods (Static)
        % helper function to create a rtw.pil.DataElements and 
        % rtw.pil.CodeInfoData corresponding to
        % the given CodeInfo Data Interfaces (target type)
        function [dataElements, codeInfoDataArray] = createTargetDataElements(silPILWrapperUtils, ...
                                                                        selectedIndices, ...
                                                                        dataInterfaceType)
            useHostDataElements = false;                                                  
            [dataElements, codeInfoDataArray] = ...
                rtw.pil.CodeInfoData.createDataElementsInternal(silPILWrapperUtils, ...
                    selectedIndices, ...
                    dataInterfaceType, ...
                    useHostDataElements);                                                                                                                                                                                                      
        end
        
        % helper function to create a rtw.pil.DataElements and 
        % rtw.pil.CodeInfoData corresponding to
        % the given CodeInfo Data Interfaces (host type)
        function [dataElements, codeInfoDataArray] = createHostDataElements(silPILWrapperUtils, ...
                                                                        selectedIndices, ...
                                                                        dataInterfaceType)
            useHostDataElements = true;                                                  
            [dataElements, codeInfoDataArray] = ...
                rtw.pil.CodeInfoData.createDataElementsInternal(silPILWrapperUtils, ...
                    selectedIndices, ...
                    dataInterfaceType, ...
                    useHostDataElements);
        end
        
        % helper function to create a rtw.pil.CodeInfoData corresponding to
        % the given CodeInfo Data Interfaces
        function codeInfoDataArray = createDataInterfaces(silPILWrapperUtils, ...
                                              selectedIndices, ...
                                              dataInterfaceType, ...
                                              filterInactiveInterfaces)                                          
            if nargin < 4
                % by default, filter inactive interfaces so they are
                % ignored; this is useful for excluding some interfaces for
                % data transfer for inline variants
                filterInactiveInterfaces = true; 
            end
            
            codeInfoDataArray = rtw.pil.CodeInfoData.empty;
            
            codeInfo = silPILWrapperUtils.getCodeInfo;
            codeInfoUtils = silPILWrapperUtils.getCodeInfoUtils;
            clientInterface = silPILWrapperUtils.getSILPILInterface.getClientInterface;
            indicesCount = length(selectedIndices);
            dataInterfaces = repmat(codeInfoUtils.getEmptyInterface('DataInterface'),indicesCount ,1);
            interfaceIdx = zeros(indicesCount,1);
            for idx = 1:indicesCount
                diIdx = selectedIndices(idx);
                interfaceIdx(idx) = diIdx;
                switch dataInterfaceType
                    case 'input'
                        dataInterfaces(idx) = codeInfo.Inports(diIdx);
                    case 'inputsize'
                        dataInterfaces(idx) = codeInfoUtils.getInportSizes(diIdx);
                    case 'output'
                        dataInterfaces(idx) = codeInfo.Outports(diIdx);                        
                    case 'outputsize'
                        dataInterfaces(idx) = codeInfoUtils.getOutportSizes(diIdx);                        
                    case 'parameter'
                        dataInterfaces(idx) = codeInfo.Parameters(diIdx);                        
                    case 'dsread'
                        dataInterfaces(idx) = codeInfoUtils.getReaderSharedDataStores(diIdx);
                    case 'dswrite'
                        dataInterfaces(idx) = codeInfoUtils.getWriterSharedDataStores(diIdx);
                    case 'sharedLocalDataStore'
                        dataInterfaces(idx) = codeInfoUtils.getSharedLocalDataStores(diIdx);
                    case 'internal'
                        dataInterfaces(idx) = codeInfo.InternalData(diIdx);
                    case {'serverFcnInputArg', 'serverFcnOutputArg'}
                        serverFunctionsInfo = codeInfoUtils.getServerFunctionsInfo;
                        serverFunctionInfo = serverFunctionsInfo(diIdx);
                        % create the data elements
                        serverFunctionInfo.createDataElementsForIOArgs(silPILWrapperUtils);
                        if strcmp(dataInterfaceType, 'serverFcnInputArg')
                            % includes inout args
                            serverArgsDataInterfaces = serverFunctionInfo.getInputArgsDataInterfaces;
                        else
                            % includes inout args
                            serverArgsDataInterfaces = serverFunctionInfo.getOutputArgsDataInterfaces;
                        end
                        codeInfoDataArray = [codeInfoDataArray serverArgsDataInterfaces]; %#ok<AGROW>
                        continue;                    
                    case 'constructorArg'
                        dataInterfaces(idx) = codeInfo.ConstructorFunction.ActualArgs(diIdx);
                    otherwise
                        assert(false, 'Unknown dataInterfaceType: %s', dataInterfaceType);
                end
            end
            %Sort dataInterfaces, so we have With Opaque regions first and
            %then without.
            plainInterfaces = codeInfoUtils.getEmptyInterface('DataInterface');
            plainIndex = [];
            opaqueInterfaces = codeInfoUtils.getEmptyInterface('DataInterface');
            opaqueIndex = [];
            for idx = 1:length(dataInterfaces)
                if ~isempty(dataInterfaces(idx).Implementation) && ...
                        rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(dataInterfaces(idx).Implementation)
                    
                    opaqueInterfaces(end+1) = dataInterfaces(idx); %#ok<AGROW>
                    opaqueIndex(end+1) = interfaceIdx(idx); %#ok<AGROW>
                else
                    plainInterfaces(end+1) = dataInterfaces(idx); %#ok<AGROW>
                    plainIndex(end+1) = interfaceIdx(idx); %#ok<AGROW>
                end
            end
            dataInterfaces = [opaqueInterfaces plainInterfaces];
            interfaceIdx = [opaqueIndex plainIndex];
            for idx = 1:length(dataInterfaces)
                dataInterface = dataInterfaces(idx);
                if rtw.pil.CodeInfoData.okToCreateCodeInfoData(...
                        dataInterface, filterInactiveInterfaces, codeInfoUtils)
                    diIdx = interfaceIdx(idx);
                    % check cache before creating new CodeInfoData
                    key = [dataInterfaceType '_' int2str(diIdx)];
                    if silPILWrapperUtils.CodeInfoDataCache.isKey(key)
                        codeInfoData = silPILWrapperUtils.CodeInfoDataCache(key);                        
                    else                                    
                        codeInfoData = rtw.pil.CodeInfoData(dataInterface, ...
                                                dataInterfaceType, ...
                                                diIdx, ...
                                                clientInterface, ...
                                                codeInfoUtils);
                        silPILWrapperUtils.CodeInfoDataCache(key) = codeInfoData;
                    end
                    codeInfoDataArray = [codeInfoDataArray codeInfoData]; %#ok<AGROW>                    
                end
            end
        end
        
        % helper function to create DataElements corresponding to
        % the given CodeInfo Data Interfaces
        function dataElements = createUnspecifiedDataElements(dataInterfaces, ...
                silPILWrapperUtils)            
            codeInfoUtils = silPILWrapperUtils.getCodeInfoUtils;
            clientInterface = silPILWrapperUtils.getSILPILInterface.getClientInterface;            
            dataElements = rtw.pil.DataElement.empty;
            for i=1:length(dataInterfaces)
                dataInterface = dataInterfaces(i);    
                codeInfoData = rtw.pil.CodeInfoData(dataInterface, ...
                                                'unspecified', ...
                                                -1, ...
                                                clientInterface, ...
                                                codeInfoUtils);
                dataElements = [dataElements codeInfoData.getTargetDataElements]; %#ok<AGROW>
            end            
        end
        
        
        function isActive = isInterfaceActive(interface)
            %Special workaround for Queued Message interfaces. These
            %are created with faked CodeInfoData structure that doesn't
            %fit any of the normal classes. So this is a structure with
            %a special field as validation.
            isMessage = isstruct(interface) && ...
                isfield(interface,'IsQueuedMessageInterface') && ...
                interface.IsQueuedMessageInterface;
            
            
            interfaceClass = rtw.connectivity.CodeInfoUtils.isa(interface, 'FunctionInterface') || ...
                rtw.connectivity.CodeInfoUtils.isa(interface, 'DataInterface') || ...
                rtw.connectivity.CodeInfoUtils.isa(interface, 'AutosarClientCall') || isMessage;
            assert(interfaceClass, ...
                'interface is not an instance of the expected classes');
            
            vInfoObj = interface.VariantInfo;
            isActive = true;
            if ~isempty(vInfoObj)
                % if interface has a variant object, check whether it's
                % active. If it's not active, then the interface will be
                % ignored.
                isActive = rtw.connectivity.CodeInfoUtils.isActiveVariant(vInfoObj);
            end
        end
    end  
    
    methods (Static, Access = private)
        function [dataElements, codeInfoDataArray] = createDataElementsInternal(silPILWrapperUtils, ...
                selectedIndices, ...
                dataInterfaceType, ...
                useHostDataElements)
            % create CodeInfoData
            codeInfoDataArray = rtw.pil.CodeInfoData.createDataInterfaces(silPILWrapperUtils, ...
                selectedIndices, ...
                dataInterfaceType);
            % get full set of DataElements
            dataElements = rtw.pil.DataElement.empty;
            for codeInfoData = codeInfoDataArray
                if useHostDataElements
                    currDataElements = codeInfoData.getHostDataElements;
                else
                    currDataElements = codeInfoData.getTargetDataElements;
                end
                dataElements = [dataElements currDataElements]; %#ok<AGROW>
            end
        end

        function okToCreate = okToCreateCodeInfoData(dataInterface, filterInactiveInterfaces, codeInfoUtils)
            % return a logical flag to signal dataInterfaces for which
            % CodeInfoData needs to be created
            okToCreate = ...
                ~codeInfoUtils.isDataInterfaceConstant(dataInterface) && ... % Constant DataInterfaces are normally inlined in the
                                                                         ... % generated code and not interfaces, so don't add them
                (~filterInactiveInterfaces || rtw.pil.CodeInfoData.isInterfaceActive(dataInterface)) && ... % Do not add inactive interfaces
                                                                                                        ... % unless required (filterInactiveInterfaces)
                ~codeInfoUtils.isLUTorBPwithDataFromInport(dataInterface) && ... % Do not add interfaces for LookupTables or Breakpoints
                                                                             ... % that take their data from the block inport
                codeInfoUtils.isDataInterfaceAccessible(dataInterface); % Do not add interfaces that cannot be accessed (e.g. private data)
        end
        
        function elements = getActiveAndAccessibleElements(dataElements)
            % return only active and accessible dataElements
            numElements = numel(dataElements);
            activeIdx = true(1, numElements);
            for idx = 1:numElements
                activeIdx(idx) = dataElements(idx).isActive && dataElements(idx).isAccessible;
            end
            elements = dataElements(activeIdx);
        end
    end
end
