classdef CSFunctionInfoFactory < handle
% CSFunctionInfoFactory
%
% Factory class to handle instantiation of subclasses of
% rtw.pil.CSFunctionInfo.

% Copyright 2018-2020 The MathWorks, Inc.
properties (GetAccess = private, SetAccess = immutable)
IsAutosar(1,1) logical = false;
AUTOSARClientBlockOperationNames cell = {};
RTMTypeIdentifier(1,:) char = '';
end

properties (Access = private)
% Cache of server function prototype names
ServerFunctionPrototypeNames(1,:) cell = {};
MF0ModelInstance;
end

properties (GetAccess = public, SetAccess = immutable)
% Map of CGFunctionNames to the rtw.pil.SlFunctionInfo objects
SlFunctionInfoMap;
end

properties (GetAccess = public, SetAccess = private, Dependent)
SimulinkFunctions;
end

properties (Constant, Access = private)
RTModelGraphicalName = 'RTModel';
KeySeparator = '###'
end

methods
function this = CSFunctionInfoFactory(codeInfoUtils, lAutosarClientBlockOperationNames, lMf0CompFunctions, isAutosar)
% Factory constructor
% Initialize data that will be common between all objects
% that are to be created.
%
this.AUTOSARClientBlockOperationNames = lAutosarClientBlockOperationNames;

lComponentName = codeInfoUtils.getCodeInfo.GraphicalPath;

% Build a map between a unique text string key (derived
% from the Simulink Function Name and its Full Path)
% and the object describing the information about the Simulink
% Function obtained from MF0.
lSimulinkFunctions = cell(1, lMf0CompFunctions.Size);
lUniqueKeys = cell(1, lMf0CompFunctions.Size);
for kSlFcn = 1:lMf0CompFunctions.Size
lSimulinkFunctions{kSlFcn} = rtw.pil.SlFunctionInfo(lMf0CompFunctions.at(kSlFcn), lComponentName);
lUniqueKeys{kSlFcn} = lSimulinkFunctions{kSlFcn}.UniqueKey;
end
if isempty(lSimulinkFunctions)
this.SlFunctionInfoMap = containers.Map;
else
this.SlFunctionInfoMap = containers.Map(lUniqueKeys, lSimulinkFunctions);
end

this.IsAutosar = isAutosar;

lInternalData = codeInfoUtils.getCodeInfo.InternalData;
lIsRTM = strcmp({lInternalData.GraphicalName}, this.RTModelGraphicalName);
lRTMInterfaces = lInternalData(lIsRTM);
assert(numel(lRTMInterfaces) == 0 || numel(lRTMInterfaces) == 1, 'Must be 0 or 1 RTM interfaces')
if isempty(lRTMInterfaces)
this.RTMTypeIdentifier = '';
else
lRTMBaseType = rtw.connectivity.CodeInfoTypeUtils.getBaseType(lRTMInterfaces.Implementation.Type);
this.RTMTypeIdentifier = lRTMBaseType.Identifier;
end
this.MF0ModelInstance = codeInfoUtils.getMF0ModelInstance;
end

function lServerFunctionInfo = createServerFunctionInfo(this, codeInfoOutputfunction, tid, externalKey)
% Create an instance of rtw.pil.ServerFunctionInfo
%
if nargin < 4
lKey = this.getKeyFromCodeInfoInterface(codeInfoOutputfunction);
else
lKey = externalKey;
end
lOperationName = codeInfoOutputfunction.Prototype.Name;

lIsLegacy = false; % Server function can never be a client block - legacy or otherwise!
lCurrentSimulinkFunction = this.SlFunctionInfoMap(lKey);
lServerFunctionInfo = rtw.pil.ServerFunctionInfo(codeInfoOutputfunction, ...
lOperationName, ...
this.RTMTypeIdentifier, ...
lCurrentSimulinkFunction, ...
tid, ...
this.IsAutosar, ...
lIsLegacy);

% Cache the name of the server function prototype so we know later if
% we have defined it when we create a caller function.
this.ServerFunctionPrototypeNames{end+1} = lOperationName;
end

function lCallerFunctionInfo = createCallerFunctionInfo(this, codeInfoCaller, tid, fcnId)
% Create an instance of rtw.pil.CallerFunctionInfo
%
lOperationName = codeInfoCaller.Prototype.Name;
lIsLegacyClientOperation = this.isLegacyAUTOSAROperation(lOperationName);
if lIsLegacyClientOperation
lCurrentSimulinkFunction = rtw.pil.SlLegacyAutosarOperationInfo(lOperationName);
lIsDefined = false;
else
lKey = this.getKeyFromCodeInfoInterface(codeInfoCaller);
lCurrentSimulinkFunction = this.SlFunctionInfoMap(lKey);
if this.IsAutosar
% check whether the server function implementation is in
% the generated code (i.e. within the SIL/PIL component).
% If not, SIL/PIL does need to provide the
% implementation for the caller function by going up to
% Simulink and requesting the service.
lIsDefined = lCurrentSimulinkFunction.IsDefined;
else
lIsDefined = ismember(codeInfoCaller.Prototype.Name, this.ServerFunctionPrototypeNames);
end
end

lCallerFunctionInfo = rtw.pil.CallerFunctionInfo(codeInfoCaller, ...
lOperationName, ...
this.RTMTypeIdentifier, ...
lCurrentSimulinkFunction, ...
tid,...
fcnId,...
lIsDefined,...
this.IsAutosar, ...
lIsLegacyClientOperation);
end

function lMessageSendFunctionInfo = createMessageSendFunctionInfo(this, fcnName, argType, timing, variantInfo, ...
tid, ...
fcnId, ...
portNumber)

% Create an instance of rtw.pil.MessageSendFunctionInfo
lIsDefined = false;
assert(this.IsAutosar, 'MessageSendFunctionInfo currently supported only for AUTOSAR workflows');

lMessageSendFunctionInfo = rtw.pil.MessageSendFunctionInfo(fcnName, ...
this.RTMTypeIdentifier, ...
argType, ...
timing, ...
variantInfo,...
tid,...
fcnId,...
lIsDefined,...
this.IsAutosar, ...
portNumber, ...
this.MF0ModelInstance);
end

function lInvalidateFunctionInfo = createInvalidateFunctionInfo(this, ...
fcnName, ...
argType, ...
timing, ...
variantInfo, ...
tid, ...
fcnId, ...
portNumber, ...
invalidateSLFcnName)

% Create an instance of rtw.pil.InvalidateFunctionInfo
lIsDefined = false;
assert(this.IsAutosar, 'InvalidateFunctionInfo only supported for AUTOSAR workflows');

lInvalidateFunctionInfo = rtw.pil.InvalidateFunctionInfo(fcnName, ...
this.RTMTypeIdentifier, ...
argType, ...
timing, ...
variantInfo,...
tid,...
fcnId,...
lIsDefined,...
this.IsAutosar, ...
portNumber, ...
this.MF0ModelInstance, ...
invalidateSLFcnName);
end

function lMessageReceiveFunctionInfo = createMessageReceiveFunctionInfo(this, fcnName, argType, timing, variantInfo, ...
tid, fcnId, portNumber, queueID)
% Create an instance of rtw.pil.MessageReceiveFunctionInfo
lIsDefined = false;
assert(this.IsAutosar, 'MessageReceiveFunctionInfo currently supported only for AUTOSAR workflows');
lMessageReceiveFunctionInfo = rtw.pil.MessageReceiveFunctionInfo(fcnName, ...
this.RTMTypeIdentifier, ...
argType, ...
timing, ...
variantInfo,...
tid,...
fcnId,...
lIsDefined,...
this.IsAutosar, ...
portNumber, ...
queueID, ...
this.MF0ModelInstance);
end

function ret = get.SimulinkFunctions(this)
ret = this.SlFunctionInfoMap.values;
end
end

methods (Static)
function ret = getKeyFromCodeInfoInterface(lCodeInfoInterface)
lSimulinkFunctionName = lCodeInfoInterface.SimulinkFunctionName;
fullPathArray = rtw.connectivity.CodeInfoUtils.getArray(lCodeInfoInterface.FullPathToSimulinkFunction);
lFullPathToSimulinkFunction = fullPathArray{1};

% Remove initial character that is used in TLC to encode
% the nature of scoped-ness
ret = rtw.pil.CSFunctionInfoFactory.getKeyFromFullPathAndFunctionName(lFullPathToSimulinkFunction(2:end), lSimulinkFunctionName);
end

function ret = getKeyFromFullPathAndFunctionName(lFullPathToSimulinkFunction, lSimulinkFunctionName)
% Get a unique key from a FullPathToSimulinkFunction and a Simulink Function
% name
ret = [lFullPathToSimulinkFunction, rtw.pil.CSFunctionInfoFactory.KeySeparator, lSimulinkFunctionName];
end
end

methods (Access = private)
function isLegacy = isLegacyAUTOSAROperation(this, operationName)
isLegacy = ismember(operationName, this.AUTOSARClientBlockOperationNames);
end
end
end