classdef FunctionArgData < rtw.pil.CodeInfoData
    %FUNCTIONARGDATA: codeInfoData representation for Client/Server
    % function args
    
    %   Copyright 2014-2019 The MathWorks, Inc.
    
    properties (SetAccess = 'private', GetAccess = 'private')
        CodeArgPosition; % position of this arg in the generated code function prototype
        SlArgPosition; % position of this arg in the Simulink block function prototype
        FunctionInfo;
    end
    
    methods
        function this = FunctionArgData(codeInfoDataInterface, ...
                dataInterfaceType,...
                codeArgPosition,...
                slArgPosition,...
                functionInfo,...
                clientInterface, ...
                codeInfoData)
            
            % use argPosition as this should be unique per argument
            codeInfoDataInterfaceIdx = codeArgPosition;
            
            assert(isnumeric(codeArgPosition) &&...
                isscalar(codeArgPosition) && ~isempty(codeArgPosition),...
                'codeArgPosition has to be numeric and scalar value');
            assert(isnumeric(slArgPosition) && ~isempty(slArgPosition),...
                'slArgPosition has to be numeric');
            this@rtw.pil.CodeInfoData(codeInfoDataInterface,...
                dataInterfaceType, codeInfoDataInterfaceIdx,...
                clientInterface, codeInfoData);

            this.CodeArgPosition = codeArgPosition;
            this.SlArgPosition   = slArgPosition;
            this.FunctionInfo    = functionInfo;
        end
                
        function slArgPosition = getSlArgPosition(this)
            slArgPosition = this.SlArgPosition;
        end
                
        function codeArgPosition = getCodeArgPosition(this)
            codeArgPosition = this.CodeArgPosition;
        end
                
        function functionInfo = getFunctionInfo(this)
            functionInfo = this.FunctionInfo;
        end        
    end
    
    methods (Static, Access = 'private')
        % return a cell array of all the Caller Functions and Simulink
        % Functions that the XIL interface will define
        function allCSTgtFcnNames = getAllCSTgtFunctionNames(silPILWrapperUtils, isAUTOSAR)
            allCSTgtFcnNames = [];                        
            if isAUTOSAR
                callerFcnsInfo = silPILWrapperUtils.getCodeInfoUtils.getAllCallerFunctionsInfo;
            else
                callerFcnsInfo = silPILWrapperUtils.getCodeInfoUtils.getUndefinedCallerFunctionsInfo;
            end
            for ii=1:length(callerFcnsInfo)
                allCSTgtFcnNames{end+1} = callerFcnsInfo(ii).getTargetFunctionName; %#ok<AGROW>
            end
            svrFcnsInfo = silPILWrapperUtils.getCodeInfoUtils.getServerFunctionsInfo;
            for ii=1:length(svrFcnsInfo)
                allCSTgtFcnNames{end+1} = svrFcnsInfo(ii).getTargetFunctionName; %#ok<AGROW>
            end
            assert(length(unique(allCSTgtFcnNames)) == length(allCSTgtFcnNames), ...
                'found duplicate client/server target function names!')
        end
    end
    
    methods (Static)
        function [argsDataElements,...
                argDataInterfaces] = createDataElementsForIOArgs(...
                slFunctionInfo, ...
                silPILWrapperUtils)
            
            % validate inputs
            assert(isa(slFunctionInfo, 'rtw.pil.CSFunctionInfo'),....
                'Unexpected class "%s" when calling createDataElementsForIOArgs.',...
                class(slFunctionInfo));
            rtw.connectivity.Utils.validateArg(silPILWrapperUtils, ...
                'coder.connectivity.WrapperUtils');
            
            clientInterface = silPILWrapperUtils.getSILPILInterface.getClientInterface;
            codeInfoUtils = silPILWrapperUtils.getCodeInfoUtils;            
            allCSTgtFcnNames = rtw.pil.FunctionArgData.getAllCSTgtFunctionNames(...
                silPILWrapperUtils, slFunctionInfo.getIsAUTOSAR);            
            isLegacyAUTOSARClientBlock = slFunctionInfo.isLegacyAUTOSARClientBlock;
            
            if isLegacyAUTOSARClientBlock
                % no Simulink functions for legacy AUTOSAR client block and
                % we don't expect argument re-ordering or inout args
                inArgsIdx = 0;
                outArgsIdx = 0;                                            
            end
            
            args = slFunctionInfo.getAllCodeInfoArgs;
            argsDataElements = [];
            argDataInterfaces = [];
            tgtFcnName = slFunctionInfo.getTargetFunctionName;
            argNameIdx = find(strcmp(tgtFcnName, allCSTgtFcnNames));
            assert(isscalar(argNameIdx), 'could not match client/server target function name correctly!');
            for codeInfoArgIdx = 1:length(args)
                
                arg = args(codeInfoArgIdx);
                codeArgPosition = slFunctionInfo.getCodeArgPosition(arg);
                if slFunctionInfo.isArgMultiInstanceRTM(codeInfoArgIdx, arg)
                    % Skip the first argument for multi instance simulink
                    % functions if it's the RTM
                    continue;
                end
                
                if arg.Type.isPointer && ...
                        ~isempty(arg.Type.BaseType) && arg.Type.BaseType.isVoid
                    % VoidType's BaseType contains the actual type. Note
                    % that void* was introduced with NvM read/write
                    % support. If VoidType's BaseType is a matrix, this
                    % DOES NOT mean pointer to matrix (double pointer). For
                    % this exception, the actual type is just matrix type.
                    actualBaseT = arg.Type.BaseType.BaseType;
                    assert(~isempty(actualBaseT));
                    if actualBaseT.isMatrix
                        arg.Type = actualBaseT;
                    else
                        arg.Type.BaseType = actualBaseT;
                    end
                    assert(~isempty(arg.Type));
                end
                
                % "g1051905 (item 7)"
                % ERT treats output arguments as pointer types while
                % AUTOSAR treats them as non-pointer types, although the
                % generated function interface is exactly the same. This
                % should be fixed in codeInfo, but for now naturalize the
                % ERT case, so the TargetInterface.m code can work for both
                % cases.
                if arg.Type.isPointer && any(strcmp(arg.IOType,...
                        {'OUTPUT', 'INPUT_OUTPUT'}))
                    arg.Type = arg.Type.BaseType;
                end
                
                [sid, argTypePrefix, argNamePrefix, argConstructor] = slFunctionInfo.getArgDataInfo();                                              
                
                % create RTW.DataInterface
                argName = [argNamePrefix '_' num2str(argNameIdx) '_'...
                    slFunctionInfo.getCodeInfoArgumentName(arg)];
                implementationOwner = ''; % XIL needs to define this
                % create a Variable
                mF0Model = codeInfoUtils.getMF0ModelInstance;
                if isempty(mF0Model)
                    argImplemenation = RTW.Variable;
                    argImplemenation.Owner = implementationOwner;
                    if slFunctionInfo.isQueued
                        argDataInterface.DataInterface = RTW.MessageDataInterface(sid, argName,...
                            argImplemenation, slFunctionInfo.getCodeInfoData.Timing);
                        argDataInterface.DataInterface.IsQueued = slFunctionInfo.isQueued;
                    else
                        argDataInterface.DataInterface = RTW.DataInterface(sid, argName,...
                            argImplemenation, slFunctionInfo.getCodeInfoData.Timing);
                    end
                else
                    argImplemenation = coder.descriptor.Variable(mF0Model);
                    argImplemenation.VarOwner = implementationOwner;
                    if slFunctionInfo.isQueued
                        argDataInterface.DataInterface = coder.descriptor.MessageDataInterface(mF0Model);
                        argDataInterface.DataInterface.isQueued = slFunctionInfo.isQueued;
                    else
                        argDataInterface.DataInterface = coder.descriptor.DataInterface(mF0Model);
                    end
                    argDataInterface.DataInterface.SID = sid;
                    argDataInterface.DataInterface.GraphicalName = argName;
                    argDataInterface.DataInterface.Implementation = argImplemenation;
                    argDataInterface.DataInterface.Timing = slFunctionInfo.getCodeInfoData.Timing;
                end
                argImplemenation.Type = arg.Type;
                argImplemenation.Identifier = argName;
                argDataInterface.CodeArgPosition = codeArgPosition;               
                if argImplemenation.Type.isPointer
                    % Simulink Function FPC allows scalar inputs to be passed in
                    % as pointer
                    argDataInterface.DataInterface.Type = argImplemenation.Type.BaseType;                    
                end
                functionDataInterface = slFunctionInfo.getCodeInfoData;
                if ~rtw.pil.CodeInfoData.isInterfaceActive(functionDataInterface)
                    % If the Simulink Function is not active assign its
                    % VariantInfo to the argDataInterface
                    argDataInterface.DataInterface.VariantInfo = functionDataInterface.VariantInfo;
                end
                                
                % set argType
                switch (arg.IOType)
                    case 'INPUT'
                        argType = [argTypePrefix 'FcnInputArg'];
                        if isLegacyAUTOSARClientBlock
                            inArgsIdx = inArgsIdx + 1;
                            slArgPosition = inArgsIdx;
                        end
                    case 'OUTPUT'
                        argType = [argTypePrefix 'FcnOutputArg'];
                        if isLegacyAUTOSARClientBlock
                            outArgsIdx = outArgsIdx + 1;
                            slArgPosition = outArgsIdx;
                        end
                    case 'INPUT_OUTPUT'
                        argType = [argTypePrefix 'FcnInputOutputArg'];
                    otherwise
                        assert(false, 'Unexpected arg.IOType %s', argType);
                end
                
                % get the position for this argument in the Simulink block
                % prototype
                if ~isLegacyAUTOSARClientBlock
                    % Get back the position of this argument in the 
                    % Simulink block prototype from codeInfo.
                    % 
                    % For inout args, the GraphicalIndex property of
                    % the Argument object is a vector of indices of the 
                    % position in the right and left hand sides of the
                    % prototype respectively.
                    
                    % Convert from 0 to 1-based indexing - note
                    % slArgPosition may be a scalar or 2 element vector.
                    graphicalIdx = rtw.connectivity.CodeInfoUtils.getArray(arg.GraphicalIndex);
                    assert(numel(graphicalIdx) == 1 || numel(graphicalIdx) == 2, ...
                        'Unexpected number of elements in GraphicalIndex');
                    slArgPosition = graphicalIdx + 1;
                end
                
                % create DataElement                
                codeInfoData = argConstructor(argDataInterface.DataInterface, ...
                    argType,...
                    codeArgPosition,...
                    slArgPosition,...
                    slFunctionInfo,...
                    clientInterface, ...
                    codeInfoUtils);
                argDataInterface.CodeInfoData = codeInfoData;
                argsDataElements = [argsDataElements codeInfoData.getTargetDataElements]; %#ok<AGROW>
                argDataInterfaces = [argDataInterfaces argDataInterface]; %#ok<AGROW>
            end
        end                
    end
end
