classdef ServerFunctionInfo < rtw.pil.SimulinkCSFunctionInfo
    %SERVERFUNCTIONINFO: representation for server function (in
    % Client/Server semantics)
    
    %   Copyright 2014-2022 The MathWorks, Inc.
    
    properties(GetAccess = public, SetAccess = private)
        IsInvokedInModelTaskOnly;
    end

    properties (Dependent, SetAccess = private, GetAccess = public)
        IsExported;             % True if either exported scoped, or global
    end
        
    methods (Access = {?rtw.pil.CSFunctionInfoFactory})
        % Only allow construction through the factory class
        function this = ServerFunctionInfo(codeInfoData, operationName, rtmTypeIdentifier, slFunctionInfo, tid, isAUTOSAR, isLegacyAutosarOperation)
            
            % validate inputs
            assert(rtw.connectivity.CodeInfoUtils.isa(codeInfoData, 'FunctionInterface'), ...
                'codeInfoData is not the expected type');
            % need to check coder.descriptor.FunctionInterface
            % call superclass constructor
            this@rtw.pil.SimulinkCSFunctionInfo(codeInfoData, operationName, rtmTypeIdentifier, slFunctionInfo, tid, isAUTOSAR, isLegacyAutosarOperation);
            
            % Initialize TargetFunctionName
            this.TargetFunctionName = this.getCodeInfoData.Prototype.Name;
            
            % Simulink Functions that are periodic don't count as
            % separate periodic tasks. They can't be invoked
            % outside of the model task i.e. they are simply part
            % of the implementation. Also skip any Simulink Functions with
            % no timing information.
            this.IsInvokedInModelTaskOnly = ...
                isempty(this.getCodeInfoData.Timing) || ...
                strcmp(this.getCodeInfoData.Timing.TimingMode, 'PERIODIC');
        end
    end
    
    methods
        function hasChild = hasChildCaller(this)
            % return true if the server function has a child caller
            hasChild = numel(this.ChildCallers) > 0;
        end
        
        function uniqueCallers = getChildCallersHierarchy(this, allServers)           
            % return an array containing all the child callers below this
            % Simulink function
            otherServers = setdiff(allServers, this);
            calledFcns = rtw.pil.ServerFunctionInfo.getChildCallersFromServer(this, otherServers);
            % remove duplicates
            [~, uniqueIdx] = unique(calledFcns, 'stable');
            uniqueCallers = calledFcns(uniqueIdx);
        end
        
        function [sid, argTypePrefix, argNamePrefix, argConstructor] = getArgDataInfo(this) %#ok<MANU>
            sid = '';
            argTypePrefix = 'server';
            argNamePrefix = 'xsd_svr';
            argConstructor = @rtw.pil.ServerFunctionArgData;
        end

        function isExported = get.IsExported(this)
            % If a Server Function is only invoked in a model task,
            % we do not need to treat it as exported, even if it is
            % "Global".  Otherwise, we need to export ScopedExport and
            % Global Server Functions.
            %
            % However, ScopedExport functions do not propagate more than
            % one level up the model hierarchy, therefore we should not
            % treat any ScopedExport function that has a non-empty
            % FullPathToFunction as exported, since we are not re-exporting
            % it.
            isExported = ~this.IsInvokedInModelTaskOnly && ...
                this.SlFunctionVisibility.isExported() && ...
                (~this.IsScoped || isempty(this.FullPathToFunction) || this.CompPortIndex >= 0);
        end
    end


    methods (Static, Access = 'private')
        function calledFcns = getChildCallersFromServer(parent, allServers)
            % return an array containing all the child callers below a
            % parent Simulink function 
            
            % get the child callers in the parent
            calledFcns = parent.ChildCallers;
            
            % recursively search for child callers inside each parent's
            % child caller
            for idx = 1:length(calledFcns)
                currChild = calledFcns{idx};
                currParent = [];
                for fcnIdx = 1:length(allServers)
                    currFcn = allServers(fcnIdx);
                    if strcmp(currChild, currFcn.getSlFunctionName)
                        currParent = currFcn;
                        break;
                    end
                end
                if ~isempty(currParent)
                    otherServers = setdiff(allServers, currParent);
                    calledFcns = [calledFcns ,...
                        rtw.pil.ServerFunctionInfo.getChildCallersFromServer(currParent, otherServers)]; %#ok<AGROW>
                end
            end
        end
    end
end

