classdef (Hidden = true) XILDebugger < handle
    %XILDEBUGGER provides utilities for debugging with SIL/PIL
    %
    %   This is an undocumented class. Its methods and properties are
    %   likely to change without warning from one release to the next.
    %
    %   You must make a subclass of XILDEBUGGER that either launches the SIL/PIL
    %   application inside an IDE with debug capabilities, or launches the
    %   application and then attaches a debug utility to the process. 
    % 
    %   You must implement the following methods:
    %
    %   XILDebugger abstract methods:
    %
    %       LAUNCHPROCESS - called when a target application must be started. In
    %                       this class it is an abstract method. When you make a
    %                       subclass of XILDEBUGGER, you must provide an
    %                       implementation of this method that starts the
    %                       application and the debug utility.
    %
    %
    %   See also RTW.PIL.RTIOSTREAMAPPLICATIONFRAMEWORK,
    %   CODER.MYPIL.LAUNCHER, RTW.CONNECTIVITY.LAUNCHER
    
    %   Copyright 2014-2021 The MathWorks, Inc.
    
    properties (SetAccess = 'private', GetAccess = 'private')
        SILDebuggingInfo;
        TopContext;
        ClientInterface;
        RaisedNotificationID;
        Breakpoints;
        ComponentArgs;
    end
    
    methods
        % constructor
        function this = XILDebugger(componentArgs, topContext, silDebuggingInfo)            
            narginchk(3,3)      
            this.TopContext = topContext;
            this.ComponentArgs = componentArgs;
            this.ClientInterface = componentArgs.getClientInterface;            
            this.SILDebuggingInfo = silDebuggingInfo;      
        end
        
        function delete(this)
            % close notification message if we raised one
            this.closeRaisedNotification();
        end
    end
    
    methods (Abstract)
        
        launchProcess(this)
        %LAUNCHPROCESS - called to launch the debug utility with the
        %SIL/PIL process
        
    end
    
    methods (Access = 'protected')
        
        function componentArgs = getComponentArgs(this)
            componentArgs = this.ComponentArgs;
        end
        
        function breakpoints = getBreakpointsInfo(this)
            breakpoints = this.SILDebuggingInfo.BreakpointsInfo;
        end
        
        function notifyUserDebugSessionReady(this)
            if ispc
                % display a different message when stepper is enabled.
                if this.ClientInterface.isPauseTimesEnabled(this.TopContext)
                    notificationID = 'PIL:pil:SILDebuggingNotificationStepperMsg';
                    notificationMsg = DAStudio.message(notificationID);
                else
                    notificationID = 'PIL:pil:SILDebuggingNotificationMsg';
                    notificationMsg = DAStudio.message(notificationID,DAStudio.message('PIL:pil:MSVCName'));
                end
            elseif ismac
                notificationID = 'PIL:pil:SILDebuggingNotificationMsg';
                notificationMsg = DAStudio.message(notificationID,DAStudio.message('PIL:pil:LLDBName'));
            else
                % Must be Linux
                notificationID = 'PIL:pil:SILDebuggingNotificationMsg';
                notificationMsg = DAStudio.message(notificationID,DAStudio.message('PIL:pil:DataDisplayDebuggerName'));     
            end
            
            this.RaisedNotificationID = notificationID;
            this.deliverNotification(notificationID, notificationMsg);
        end
        
        % true if simulation stepper is enabled using pause time
        function status = isSimulationStepperEnabled(this)
            status = this.ClientInterface.isPauseTimesEnabled(this.TopContext);
        end                
        
        % call clientInterface to deliver notifications
        function deliverNotification(this, notificationID,notificationMsg)
            this.ClientInterface.deliverNotification(this.TopContext, notificationID, notificationMsg);
        end
    end
    
    methods (Hidden, Access = 'private')
        function closeRaisedNotification(this)
            if ~isempty(this.RaisedNotificationID)
                this.ClientInterface.closeNotification(this.TopContext, this.RaisedNotificationID);
            end
        end
    end
    
    methods (Static)
        function breakpoints = createBreakpointsInfo(needClassPrefix, compInterface, className)
            % We deliberately don't add all the entry point functions, but
            % we make sure we break on the allocation / initialization
            % functions to give the user a chance to set more breakpoints
            % where needed.
            fcnsToProcess = [compInterface.AllocationFunction, ...
                             compInterface.InitializeFunctions, ...                             
                             compInterface.OutputFunctions];
                         
            breakpoint = struct('FunctionName','',...
                                 'FileName','');
            breakpoints = repmat(breakpoint, 1, length(fcnsToProcess));
                        
            for i = 1:length(fcnsToProcess)
                if needClassPrefix
                    % For C++ code generation, when the code interface packaging is
                    % set to C++ class the prototype in codeInfo is missing the
                    % class name prefix.
                    if isempty(className)
                        owner = rtw.pil.OwnerAccessor.getOwner(fcnsToProcess(i));                        
                        className = owner.Type.Identifier;
                    end
                    breakpoints(i).FunctionName = [className, '::', fcnsToProcess(i).Prototype.Name];
                else
                    % prototype is complete
                    breakpoints(i).FunctionName = fcnsToProcess(i).Prototype.Name;
                end
                breakpoints(i).FileName = fcnsToProcess(i).Prototype.SourceFile;
            end
            
            if ~isempty(getenv('XIL_DEBUG_BP'))
                % add additional breakpoints for internal use
                if isempty(breakpoints)
                    srcFile = dir(['**' filesep compInterface.HeaderFile '.c*']);
                    [~, ~, srcExt] = fileparts(srcFile.name);
                else
                    [~, ~, srcExt] = fileparts(breakpoints(1).FileName);
                end
                breakpoints(end+1) = struct( 'FunctionName', 'xilGetHostToTargetData', ...
                    'FileName', ['xil_interface' srcExt]);
            end
        end
    end
          
end
