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-2023 The MathWorks, Inc.
    
    properties(Access = private)
	    Release char;
    end
        
    methods 
        function this = WindowsHostDebuggerManager(spkgManager)
            compilerID = {'MSVC', 'MSSDK', 'INTELC', 'MinGW'};
            compilerVersionPairs = {...
                {'Microsoft Visual C++ (MSVC)', '9.0'}, ...
                {'Microsoft Windows SDK', '7.1'}, ...
                {'Intel Parallel Studio XE', '17.0'}, ...
                {'MinGW', '6.3'}};
            this = this@rtw.connectivity.HostDebuggerManager(compilerID, compilerVersionPairs, spkgManager);
        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 MinGW is the selected compiler, use it. Otherwise,
            % prioritize MSVC over any other installed compiler.
            if strncmp(defaultComp.comp.ShortName,'mingw',5) && ...
                    this.isSupportedVersion('MinGW', defaultComp.comp.Version)

                this.ToolchainInfo.CompilerVersion = defaultComp.comp.Version;
                this.populateBasicToolchainInfo(defaultComp);
            elseif ~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;
                    
                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);
                end
                this.Release = msvcComp.comp.Version;
             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);
            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
            elseif strncmp(this.ToolchainInfo.ID,'mingw',5)

                % Check for VS Code Debug Tool SPKG installation.
                [status, details] = this.TFSPKGManager.getInstallationStatus('VSCodeDebugTool');
                if ~status

                    % If MinGW is selected, but there is no VS Code Debug
                    % Tool installed, point user to Support Package.
                    % Here we use a "tripwire", to keep track of the number
                    % of users clicking on the installation link.
                    callback = sprintf('matlab.internal.addons.launchers.showExplorer(''tripwire_sildebugging_vscodedebug'', ''identifier'', ''%s'')', details.BaseCode);

                    exception = MSLException(message('PIL:pil:SILDebuggingWithMinGW', details.PublishedName));
                    exception.addSuggestion(message('targetframework:Utilities:InstallSupportPackage', details.PublishedName).getString(), callback, 'tripwire_sildebugging_vscodedebug');
                    exception.throw();
                elseif ~this.TFSPKGManager.isInstalledAndEnabled('VSCodeDebugTool')

                    callback = sprintf('matlab.addons.enableAddon(''%s'')', details.BaseCode);

                    exception = MSLException(message('PIL:pil:SILDebuggingVSCodeNotEnabled', details.PublishedName));
                    exception.addSuggestion(message('targetframework:Utilities:EnableSupportPackage', details.PublishedName).getString(), callback, 'tripwire_sildebugging_vscodedebug');
                    exception.throw();
                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 service = getDebugTool(this)

            % Look for VS Code Debug Tool.
            if strncmp(this.ToolchainInfo.ID, 'mingw', 5) && this.TFSPKGManager.isInstalledAndEnabled('VSCodeDebugTool')

                debuggerPath = target.internal.mingw.getGDBPath();
                version = target.internal.mingw.getGDBVersion(debuggerPath);

                % If the version of MinGW GDB is less than 8.0, we cannot
                % proceed with SIL Debugging.
                if isempty(version) || (str2double(version) < 8.0)

                    callback = 'matlab.internal.addons.launchers.showExplorer(''tripwire_mingwversion_vscodedebug'', ''identifier'', ''ML_MINGW'')';

                    exception = MSLException('PIL:pil:SILDebuggingWithMinGWLatestVersion');
                    exception.addSuggestion(message('PIL:pil:InstallMinGWLatestVersion').getString(), callback, 'tripwire_mingwversion_vscodedebug');
                    exception.throw();
                end

                service = target.internal.get('ExecutionService', 'Visual Studio Code');
                return;
            end

            % Fall back to MSVC.
            releaseSpec = target.internal.WindowsDebugger.fromVersion(this.Release);
            if isempty(releaseSpec)
                exception = MSLException('PIL:pil:SILDebuggingWithoutSupportedCompiler', targetframework.internal.utilities.supportpackage.VSCodeDebugTool.PublishedName);
                exception.throw();
            end
            service = target.internal.get(releaseSpec.TargetFrameworkToolUuid{:});
        end
    end
end
