classdef (Abstract, Hidden = true) ComponentInterfaceArtifacts < handle
%COMPONENTINTERFACEARTIFACTS defines the ComponentInterfaceArtifacts to be
% implemented by specific subclasses
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.
    
%   Copyright 2020-2021 The MathWorks, Inc.
    
    properties (Hidden = true, Access = protected)
        TopModelOrMLFunctionName
        CodeDir
        InTheLoopType
        BinfoFullFileName
        TMWInternalArtifactsPath
        CodeName
        InfoStruct
    end
    
    properties (Constant)
        BinfoTop = 'binfo.mat'
        BinfoRef = 'binfo_mdlref.mat'
        CompileInfoTop = 'CompileInfo.xml'
        CompileInfoRef = 'ModelRefCompileInfo.xml'
    end
    
    methods(Static)
        function infoStruct = createLocatorAndGetInfoStruct(codeDir, inTheLoopType, usingXrelResourcesDir, varargin)

            locator = coder.connectivity.ComponentInterfaceArtifacts.createLocator(codeDir, inTheLoopType, usingXrelResourcesDir, varargin{:});
            infoStruct = locator.getInfoStruct();
        end
            
        function locator = createLocator(codeDir, inTheLoopType, usingXrelResourcesDir, topModelOrMLFunctionName, options)
            % factory method to create the required instance of
            % ComponentInterfaceArtifacts
            
            arguments
                codeDir char {mustBeNonzeroLengthText}
                inTheLoopType (1,1) rtw.pil.InTheLoopType
                usingXrelResourcesDir (1,1) logical = false
                topModelOrMLFunctionName char = ''
                % by default, do not wrap exception if using resources dir
                options.WrapException logical = ~usingXrelResourcesDir;
            end
            
            if inTheLoopType == rtw.pil.InTheLoopType.RLSSubsystem
                locator = coder.connectivity.RLSInterfaceArtifacts(codeDir, inTheLoopType, topModelOrMLFunctionName);
            elseif inTheLoopType == rtw.pil.InTheLoopType.MATLABFcn
                locator = coder.connectivity.MATLABInterfaceArtifacts(codeDir, inTheLoopType, topModelOrMLFunctionName);
            elseif coder.connectivity.XILSubsystemUtils.isAtomicSubsystem(topModelOrMLFunctionName)
                locator = coder.connectivity.AtomicSubsystemInterfaceArtifacts(codeDir, inTheLoopType, topModelOrMLFunctionName);
            elseif usingXrelResourcesDir
                locator = coder.connectivity.XRelBlockInterfaceArtifacts(codeDir, inTheLoopType, topModelOrMLFunctionName);
            elseif inTheLoopType == rtw.pil.InTheLoopType.Block
                locator = coder.connectivity.SILPILBlockInterfaceArtifacts(codeDir, inTheLoopType, topModelOrMLFunctionName);
            else
                locator = coder.connectivity.ComponentAndSubComponentArtifactsWithMarker(codeDir, inTheLoopType, topModelOrMLFunctionName);
            end 
            
            % initialize instance by loading bInfo
            if options.WrapException
                locator.initializeWrapException();
            else
                locator.initialize();
            end
        end   
    end
      
    methods(Abstract, Access = protected)
         initalizeInternalArtifactsLocation(this);
    end
    
    methods(Access = public)
        function codeName = getCodeName(this)
            codeName = this.CodeName;
        end
        
        function path = getTMWInternalArtifactsPath(this)
            path = this.TMWInternalArtifactsPath;
        end
        
        function infoStruct = getInfoStruct(this)
            infoStruct = this.InfoStruct;
        end
        
        function modelName = getRootModel(this)
            % use the CodeName by default
            modelName = this.CodeName;
        end
        
        function componentPath = getFullComponentPath(this)
            % use the CodeName by default
            componentPath = this.CodeName;
        end
    end
    
    methods(Access = protected)
        function this = ComponentInterfaceArtifacts(codeDir, inTheLoopType, topModelOrMLFunctionName)
            % constructor
            this.InTheLoopType = inTheLoopType;
            this.CodeDir = codeDir;
            this.TopModelOrMLFunctionName = topModelOrMLFunctionName;
        end
        
        function loadInfoStruct(this)
            % check for an up to date marker file to make sure binfo.mat
            % is up to date with all the latest fields
            fileGenCfg = Simulink.fileGenControl('getConfig');
            rootFolder = fileGenCfg.CodeGenFolder;
            markerFile = fullfile(rootFolder, Simulink.filegen.CodeGenFolderStructure.ModelSpecific.MarkerFile);
            coder.internal.folders.MarkerFile.check(markerFile);
             
            % loadPostBuild
            action = 'loadPostBuild';
            % load binfo
            minfo_or_binfo = 'binfo';
            
            binfoMATFile = this.BinfoFullFileName;
            
            mdlRefTgtType = 'NONE';
            if this.InTheLoopType == rtw.pil.InTheLoopType.ModelBlock
                binfoMATFile = strrep(binfoMATFile, ...
                    coder.connectivity.ComponentInterfaceArtifacts.BinfoTop, ...
                    coder.connectivity.ComponentInterfaceArtifacts.BinfoRef);  
                mdlRefTgtType = 'RTW';
            end
            
            % load the config set
            loadConfigSet = true;
            this.InfoStruct = coder.internal.infoMATFileMgr(action, ...
                minfo_or_binfo, ...
                this.getModelToLoadBinfo(), ...
                mdlRefTgtType, ...
                binfoMATFile, ...
                loadConfigSet);
        end

        function name = getModelToLoadBinfo(this)
            % this is the model that generated binfo.mat, use the CodeName 
            % by default
            name = this.CodeName;
        end
        
        function initialize(this)
            % initialize properties and load bInfo
            this.initalizeInternalArtifactsLocation();
            this.loadInfoStruct();
        end
    end
    
    methods(Access = private)
        function initializeWrapException(this)
            try
                this.initialize();
            catch origE
                % construct standard error with origE as cause
                [newId, newMessage] = rtw.pil.ProductInfo.message('pilverification', 'ErrorGeneratedCodeIncomplete');
                newException = MException(newId, ...
                    newMessage);
                newException = newException.addCause(origE);
                throw(newException);
            end
        end
    end
end
