classdef HardwareInterface < handle
    % HardwareInterface - Class that covers all hardware specific
    % callbacks.

    %   Copyright 2017-2024 The MathWorks, Inc.
    properties(Access = protected)

        supportedVersions   = {'v112', 'v230', 'v130', 'v130', 'v104', 'v151', 'v4_00_01_00'};
        controlSUITELocation = ''
        supportedCLAmathVersion = 'v4_00_01_00';
        customCLAmathVersion = '';
    end

    properties
        Toolchain
        ReqTpPkg
        TpPkg
        TokenName
        toolInfoHandle
        ToolchainInstallDir
        DSSNames
        DSSTokens
        DSSInstallDir
        RootDirFound
        deviceFilePaths
        thirdparty
        TargetFolder
        ToolName
        TargetName
    end

    methods
        %%%%%%%%%%%% End of container maps%%%%%%%%%%%%%%%%%%

        function ProcessorStatusMapObj = getProcessorValuepair(~, workflowobj)
            ProcessorStatusMapObj = workflowobj.ProcessorStatusMapObj;
            ProcessorControlSUTIEMapObj = workflowobj.ProcessorControlSUTIEMapObj;
            ProcessorC2000WareMapObj = workflowobj.ProcessorC2000WareMapObj;
            CCSProcessorCCXMLMapObj = workflowobj.CCSProcessorCCXMLMapObj;

            processorlist = ProcessorStatusMapObj.keys;
            ccspath = workflowobj.CCSInstallLocation;
            controlsuitepath = workflowobj.ControlSUITEInstallLocation;
            c2000warepath = workflowobj.C2000WareLocation;

            for i =1:numel(processorlist)
                % Set to false, then check for these above processors
                ProcessorStatusMapObj(processorlist{i}) = false;
                if ispc
                    versionfile_evaluate = fullfile(ccspath, 'ccs_base', 'common', 'targetdb', 'devices', CCSProcessorCCXMLMapObj(processorlist{i}));
                    if ~isempty(ccspath) && contains(ccspath, 'ccsv4', 'IgnoreCase', true)
                        versionfile_evaluate = fullfile(ccspath, 'common', 'targetdb', 'Devices', CCSProcessorCCXMLMapObj(processorlist{i}));
                    end
                    isControlSUITEProc = intersect(workflowobj.controlSUITEProcessors, processorlist{i});
                    isC2000WareProc = intersect(workflowobj.C2000WareProcessors, processorlist{i});
                    if ~isempty(isControlSUITEProc)
                        path_evaluate = fullfile(controlsuitepath, ProcessorControlSUTIEMapObj(processorlist{i}));
                        if 7 == exist(path_evaluate, 'dir')
                            % set container map to true, if target folder is
                            % found
                            if (2 == exist(versionfile_evaluate, 'file'))
                                ProcessorStatusMapObj(processorlist{i}) = true;
                            end
                        else
                            % do nothing
                        end
                    elseif ~isempty(isC2000WareProc)
                        path_evaluate = fullfile(c2000warepath, ProcessorC2000WareMapObj(processorlist{i}));
                        if 7 == exist(path_evaluate, 'dir') % is C2000ware exist
                            % set container map to true, if target folder is
                            % found
                            if (2 == exist(versionfile_evaluate, 'file'))
                                ProcessorStatusMapObj(processorlist{i}) = true;
                            end
                        else
                            % do nothing
                        end
                    elseif contains(processorlist{i}, 'TI F28044')
                        % For F28044 - remove this if we are not supporting
                        % this processor
                        path_evaluate = workflowobj.F28044Location;
                        if 7 == exist(path_evaluate, 'dir')
                            % set container map to true, if xml is found in CCS
                            if (2 == exist(versionfile_evaluate, 'file'))
                                ProcessorStatusMapObj(processorlist{i}) = true;
                            end
                        end
                    else
                        if (2 == exist(versionfile_evaluate, 'file'))
                            ProcessorStatusMapObj(processorlist{i}) = true;
                        end
                    end
                else
                    if ismember(processorlist{i},workflowobj.supportedProcessorsLinux)
                        ProcessorStatusMapObj(processorlist{i}) = true;
                    end
                end
            end
        end

        function ControlSUITESupportedProcessors = findControlSUITESupportedProcessors(~, workflowobj)
            ControlSUITESupportedProcessors = intersect(workflowobj.SelectedProcessors, workflowobj.controlSUITEProcessors);
        end

        function C2000WareSupportedProcessors = findC2000WareSupportedProcessors(~, workflowobj)
            C2000WareSupportedProcessors = intersect(workflowobj.SelectedProcessors, workflowobj.C2000WareProcessors);
        end

        function ThirdPartyToolsRegistration(obj, workflowobj)
            targetFolder = codertarget.target.getTargetFolder(obj.TargetFolder);
            fileName = codertarget.target.getThirdPartyToolsRegistrationFileName(targetFolder);
            try
                obj.toolInfoHandle = codertarget.thirdpartytools.ThirdPartyToolInfo(fileName, false);
            catch me %#ok<NASGU>
                obj.toolInfoHandle = codertarget.thirdpartytools.ThirdPartyToolInfo();
                obj.toolInfoHandle.setDefinitionFileName(fileName);
            end
            obj.toolInfoHandle.setName(obj.ToolName);
            obj.toolInfoHandle.setTargetName(obj.TargetName);

            % Code generation tools tokens
            obj.TpPkg = obj.createThirdPartyStruct();
            obj.TpPkg.Name = 'Texas Instruments CCS with C2000 Code Generation Tools';
            obj.TpPkg.RootDir = workflowobj.CGTLocation;
            obj.TokenName{1} = 'CCSINSTALLDIR'; % this name shall change
            % DSS Scripting tool tokens
            obj.TpPkg(2) = obj.createThirdPartyStruct();
            obj.TpPkg(2).Name = 'Debug Server Scripting for Texas Instruments CCS';
            obj.TpPkg(2).RootDir = workflowobj.DSSInstallDir;
            obj.TokenName{2} = 'CCSSCRIPTINGDIR';
            % ControlSUITE tokens
            obj.TpPkg(3) = obj.createThirdPartyStruct();
            obj.TpPkg(3).Name = 'Texas Instruments controlSUITE';
            obj.TpPkg(3).RootDir = workflowobj.ControlSUITEInstallLocation;
            obj.TokenName{3} = 'CONTROLSUITEINSTALLDIR';
            % C2000Ware tokens
            obj.TpPkg(4) = obj.createThirdPartyStruct();
            obj.TpPkg(4).Name = 'Texas Instruments C2000Ware';
            obj.TpPkg(4).RootDir = workflowobj.C2000WareLocation;
            obj.TokenName{4} = 'C2000WAREINSTALLDIR';
            % ARM Code generation tools tokens
            if (workflowobj.isConcerto || ~isempty(intersect({'TI F2838x'}, workflowobj.SelectedProcessors)))
                obj.TpPkg(5) = obj.createThirdPartyStruct();
                obj.TpPkg(5).Name = 'Texas Instruments CCS with ARM Code Generation Tools';
                obj.TpPkg(5).RootDir = workflowobj.ARMCGTLocation;
                obj.TokenName{5} = 'CCSARMINSTALLDIR';
            end

            for i = 1:length(obj.TpPkg)
                if isempty(obj.TpPkg(i).RootDir)
                    for j = 1:numel(obj.toolInfoHandle.ThirdPartyTools)
                        if isequal(obj.TpPkg(i).Name, obj.toolInfoHandle.ThirdPartyTools{j}{1}.ToolName)
                            obj.TpPkg(i).RootDir = obj.toolInfoHandle.ThirdPartyTools{j}{1}.RootFolder;
                        end
                    end
                end
            end

            supportedCSProcessors = workflowobj.SelectedSUITEProcessors;
            currentCSDir = workflowobj.ControlSUITEInstallLocation;

            if ~isequal(numel(supportedCSProcessors), 0)
                for idx = 1:numel(supportedCSProcessors)
                    out = regexp(supportedCSProcessors{idx},' ','split');
                    isMatching = intersect({'TI Concerto F28M35x', 'TI Concerto F28M36x'}, supportedCSProcessors{idx});
                    if isempty(isMatching)
                        processorname = lower(out{end});
                    else
                        processorname = out{end};
                    end
                    obj.deviceFilePaths(idx).Token         = ['DSP', processorname(2:end) ,'_INSTALLDIR'];
                    obj.deviceFilePaths(idx).RootFolder = fullfile(currentCSDir, workflowobj.controlSUTIEAvailVersionMapObj(supportedCSProcessors{idx}));
                    % get only processor name ex: F2802x, F2803x
                    obj.deviceFilePaths(idx).Name = [processorname, ' C/C++ Source/Header Files'];
                end
                % Add CLAmath
                if strcmp(workflowobj.BaseCode, 'TIC2000_BLOCKSET')
                    % dont add CLA for SOC
                else
                    isMatching = intersect({'TI Piccolo F2803x', ...
                        'TI Piccolo F2805x', 'TI Piccolo F2806x'}, supportedCSProcessors);
                    if ~isempty(isMatching)
                        idx = idx +1;
                        obj.deviceFilePaths(idx).Token         = 'CLAMATHLIB_INSTALLDIR';
                        obj.deviceFilePaths(idx).RootFolder = fullfile(currentCSDir, workflowobj.controlSUTIEAvailVersionMapObj('CLAmath'));
                        obj.deviceFilePaths(idx).Name = 'CLAmath Library Files';
                    end
                end
            else
                idx = 0;
            end
            % Add F28044 inside the device path
            if any(contains(workflowobj.SelectedProcessors, 'TI F28044'))
                idx = idx +1;
                obj.deviceFilePaths(idx).Token         = 'DSP2804x_INSTALLDIR';
                obj.deviceFilePaths(idx).RootFolder = workflowobj.F28044Location;
                obj.deviceFilePaths(idx).Name = 'f2804x C/C++ Source/Header Files';
            end
            % Add F280x inside the device path
            if any(contains(workflowobj.SelectedProcessors, 'TI F280x'))
                idx = idx +1;
                obj.deviceFilePaths(idx).Token         = 'DSP280x_INSTALLDIR';
                obj.deviceFilePaths(idx).RootFolder = workflowobj.F280xLocation;
                obj.deviceFilePaths(idx).Name = 'f280x C/C++ Source/Header Files';
            end
            % Add F281x inside the device path
            if any(contains(workflowobj.SelectedProcessors, 'TI F281x'))
                idx = idx +1;
                obj.deviceFilePaths(idx).Token         = 'DSP281x_INSTALLDIR';
                obj.deviceFilePaths(idx).RootFolder = workflowobj.F281xLocation;
                obj.deviceFilePaths(idx).Name = 'f281x C/C++ Source/Header Files';
            end
        end

        function registerTPTOkens(obj, workflowobj)
            % Add CGT  toolchain
            obj.toolInfoHandle.addTool('ToolName', obj.TpPkg(1).Name, ...
                'Category', 'toolchain', ...
                'TokenName', obj.TokenName{1}, ...
                'RootFolder', obj.TpPkg(1).RootDir);

            % Add Scripting
            obj.toolInfoHandle.addTool('ToolName', obj.TpPkg(2).Name, ...
                'Category', 'scripting', ...
                'TokenName', obj.TokenName{2}, ...
                'RootFolder', obj.TpPkg(2).RootDir);

            % Add controlsuite
            obj.toolInfoHandle.addTool('ToolName', obj.TpPkg(3).Name, ...
                'Category', 'other', ...
                'TokenName', obj.TokenName{3}, ...
                'RootFolder', obj.TpPkg(3).RootDir);

            % Add C2000Ware
            obj.toolInfoHandle.addTool('ToolName', obj.TpPkg(4).Name, ...
                'Category', 'other', ...
                'TokenName', obj.TokenName{4}, ...
                'RootFolder', obj.TpPkg(4).RootDir);

            % Add ARM CGT toolchain
            if (workflowobj.isConcerto || ~isempty(intersect({'TI F2838x'}, workflowobj.SelectedProcessors)))
                obj.toolInfoHandle.addTool('ToolName', obj.TpPkg(5).Name, ...
                    'Category', 'toolchain', ...
                    'TokenName', obj.TokenName{5}, ...
                    'RootFolder', obj.TpPkg(5).RootDir);
            end

            if ~isempty(obj.deviceFilePaths)
                for i = 1:numel(obj.deviceFilePaths)
                    if exist(obj.deviceFilePaths(i).RootFolder, 'dir')
                        obj.toolInfoHandle.addTool('ToolName', obj.deviceFilePaths(i).Name, ...
                            'Category', 'other', ...
                            'TokenName', obj.deviceFilePaths(i).Token, ...
                            'RootFolder', obj.deviceFilePaths(i).RootFolder);
                    end
                end
            end

            fileName = obj.toolInfoHandle.DefinitionFileName;
            [status, attrib] = fileattrib(fileName);
            if status && ~attrib.UserWrite
                [status, message, messageid] = fileattrib(fileName, '+w');
                if ~status
                    error(messageid, [message strrep(fileName, '\', '/')]);
                end
            end
            % Register toolinfo
            obj.toolInfoHandle.register();
        end

        function outStruct = createThirdPartyStruct(~)
            outStruct = struct('FileName', '', 'DestDir', '', 'Installer', '', ...
                'Archive', '', 'Name', '', 'Version', '1.0', 'Url', '', 'DownloadUrl', '', ...
                'LicenseUrl', '', 'DownloadDir', '', 'InstallDir', '', 'IsInstalled', 0, ...
                'IsDownloaded', 0, 'RootDir', '');
        end

        % this function has to be refactored
        function installDir = getInstallationFolder(obj, tpPkgName)
            installDir = '';
            for i=1:length(obj.ReqTpPkg)
                if isequal(obj.ReqTpPkg(i).Name, tpPkgName)
                    if ~isempty(obj.TpPkg(i).RootDir)
                        % User has alreadyspecified instalation
                        installDir = obj.TpPkg(i).RootDir;
                    else
                        % Use default value
                        installDir = obj.ReqTpPkg(i).RootDir;
                    end
                    break;
                end
            end

        end

        function CLAversion = getCLADefaultVersion(obj)
            CLAversion = obj.supportedCLAmathVersion;
        end

        function CLAversion = getCLACustomVersion(obj)
            CLAversion = obj.customCLAmathVersion;
        end

        function setCLACustomVersion(obj, version)
            obj.customCLAmathVersion = version;
        end

        function out = splitDirName(~, dir)
            if ispc
                out = regexp(dir,'\','split');
            else
                out = regexp(dir,'/','split');
            end
        end

        function [isValidCGT, CGTVersion] = validateTIC28xCGT(~, installDir)
            CGTVersion = '';
            if exist(installDir, 'dir') ~= 7
                isValidCGT = 0; % Not a valid directory
            else
                % Add check for version match.
                readMeFileDoesNotExists = ~(exist(fullfile(installDir, 'README.txt'), 'file') == 2 || ...
                    exist(fullfile(installDir, 'README.html'), 'file') == 2);
                if readMeFileDoesNotExists
                    isValidCGT = -1; % Not a valid parent CGT directory
                elseif ~contains(installDir, strcat('ti-cgt-c2000_', char(message('TIC2000:hwsetup:TICGTVersion').string)), 'IgnoreCase', true)
                    % Read version from parent folder name
                    ver = regexpi(installDir, '\d+(\.\d+)+', 'match');%get version number
                    if ~isempty(ver)
                        CGTVersion = ver{1};
                    end
                    isValidCGT = -2; % Different version of CGT
                else
                    CGTVersion = char(message('TIC2000:hwsetup:TICGTVersion').string);
                    isValidCGT = 1; % Valid CGT
                end
            end
        end

        function [isValidCGT, CGTVersion] = validateTIARMCGT(~, installDir)
            CGTVersion = '';
            if exist(installDir, 'dir') ~= 7
                isValidCGT = 0; % Not a valid directory
            else
                % Add check for version match.
                readMeFileDoesNotExists = ~(exist(fullfile(installDir, 'README.txt'), 'file') == 2 || ...
                    exist(fullfile(installDir, 'README.html'), 'file') == 2);
                if readMeFileDoesNotExists
                    isValidCGT = -1; % Not a valid parent CGT directory
                elseif ~contains(installDir, strcat('ti-cgt-arm_', char(message('TIC2000:hwsetup:ARMCGTVersion').string)), 'IgnoreCase', true)
                    % Read version from parent folder name
                    ver = regexpi(installDir, '\d+(\.\d+)+', 'match');%get version number
                    if ~isempty(ver)
                        CGTVersion = ver{1};
                    end
                    isValidCGT = -2; % Different CGT version
                else
                    CGTVersion = char(message('TIC2000:hwsetup:ARMCGTVersion').string);
                    isValidCGT = 1; % valid CGT
                end
            end
        end

    end
    methods (Static)

        function out = getInstance()
            out = '';
            if ispc
                out = codertarget.tic2000.hwsetup.WindowsHardwareModule();
            elseif ismac
                %out = codertarget.tic2000.hwsetup.MACHardwareModule();
            else
                out = codertarget.tic2000.hwsetup.LinuxHardwareModule();
            end

        end
    end

    methods(Abstract)
        [out1, out2] = getControlSUITEInfo(obj);
        [out1, out2] = getCCSInfo(obj);
        [out1, out2] = getC2000WareInfo(obj);
        [out1, out2] = getF28044Details(obj);
        [out1, out2] = getF280xDetails(obj);
        [out1, out2] = getF281xDetails(obj);
        out = getControlSUITEVersion(obj, location);
    end

end
