classdef WindowsHostDebuggerManager < rtw.connectivity.HostDebuggerManager
    %WINDOWSHOSTDEBUGGERMANAGER utility class to create an instance of
    % a Windows host debugger
    %
    %   This is an undocumented class. Its methods and properties are
    %   likely to change without warning from one release to the next.
    
    %   Copyright 2020 The MathWorks, Inc.
    
    properties(Access = private)
        DebuggerHandle function_handle;
    end
        
    methods 
        function this = WindowsHostDebuggerManager()
            compilerID = {'MSVC', 'MSSDK', 'INTELC'};
            compilerVersionPairs = {...
                {'Microsoft Visual C++ (MSVC)', '9.0'}, ...
                {'Microsoft Windows SDK', '7.1'}, ...
                {'Intel Parallel Studio XE', '17.0'}};
            this = this@rtw.connectivity.HostDebuggerManager(compilerID, compilerVersionPairs);
        end
    end
    
    methods(Access = protected)
        function populateToolchainInfo(this, defaultComp)
            
            % Get the MSVC compatible compiler (this does not include
            % the Express versions)
            msvcComp = coder.make.internal.getMexCompilerInfo('msvcbuildcompatible');
            
            % If a full version of MSVC is being used
            if ~isempty(msvcComp) && this.isSupportedVersion('MSVC', msvcComp.comp.Version)
                this.ToolchainInfo.IDEPath = fullfile(msvcComp.comp.Location, ...
                    'Common7', 'IDE', 'devenv.exe');
                % set the IDEVersion to MSVC version
                this.ToolchainInfo.IDEVersion = msvcComp.comp.Version;
                if strcmp(msvcComp.compStr, defaultComp.compStr) 
                    % If MSVC compiler was used, use it for SIL debugging
                    this.populateBasicToolchainInfo(msvcComp);
                    this.ToolchainInfo.CompilerVersion = msvcComp.comp.Version;
                    this.DebuggerHandle = @rtw.connectivity.MSVCSILDebugger;
                    
                elseif strncmp(defaultComp.comp.ShortName,'INTELC',6) ...
                        && this.isSupportedVersion('INTELC', defaultComp.comp.Version)
                    % If the INTEL compiler (>=17.0) is being used in
                    % association to MSVC IDE
                    this.ToolchainInfo.CompilerVersion = defaultComp.comp.Version;
                    this.populateBasicToolchainInfo(defaultComp);
                    this.DebuggerHandle = @rtw.connectivity.INTELMSVCSILDebugger;
                end
            elseif strncmp(defaultComp.comp.ShortName,'MSSDK',5) && ...
                    this.isSupportedVersion('MSSDK', defaultComp.comp.Version)
                % The MS SDK (>=7.1) is used, which is now supported with
                % MSVC Express 2013
                this.ToolchainInfo.CompilerVersion = defaultComp.comp.Version;
                this.populateBasicToolchainInfo(defaultComp);
                this.DebuggerHandle = @rtw.connectivity.MSSDKSILDebugger;
            end
        end
        
        
        function checkDebuggerIsAvailable(this)            
            if strncmp(this.ToolchainInfo.ID,'MSSDK',5)
                % check for COM interface to Express
                try
                    svrHandle = actxserver('WDExpress.DTE');
                    svrHandle.Quit;
                    svrHandle.delete;
                catch COMException
                    if strcmpi(COMException.identifier,'MATLAB:COM:InvalidProgid')
                        DAStudio.error('PIL:pil:SILDebuggingMSVCExpressNotFound')
                    else
                        rethrow(COMException)
                    end
                end
            else
                % error if the MSVC IDE executable does not exist (i.e.
                % SDK installations won't have the IDE)
                if ~exist(this.ToolchainInfo.IDEPath, 'file')
                    DAStudio.error('PIL:pil:MSVCIDENotFound', this.ToolchainInfo.IDEPath);
                end
            end
        end
        
        function debugger = createDebugger(this, componentArgs, topContext, silDebuggingInfo)
            assert(~isempty(this.DebuggerHandle), 'No debugger has been selected');
            debugger = this.DebuggerHandle(componentArgs, ...
                topContext, ...
                silDebuggingInfo, ...
                this.ToolchainInfo.IDEVersion);
        end
    end
    
end