classdef HDLAppContext < dig.CustomContext

% HDLAppContext to store state information.

%   Copyright 2019-2023 The MathWorks, Inc.

    properties(SetObservable=true)
        BDListener
        CSListener
        HDLPropListener
        HDLPIListener
        isHDLPIVis
    end
    
    properties( SetAccess = protected )
        OrigTypeChain
        GenCodeContext 
        DeploymentContext
        ModelH
    end
    
    properties 
        hdlCodeView
    end

    methods (Access = public)
        function obj = HDLAppContext(app, cbinfo)
            obj@dig.CustomContext(app);
            obj.ModelH = cbinfo.model.Handle;
            obj.OrigTypeChain = obj.TypeChain;
            setupListeners(obj, obj.ModelH, cbinfo);
            obj.updateTypeChain();
        end

        % Update the TypeChain with all the manual contexts
        function doContextualRefresh(obj, ~, ~)
            obj.updateTypeChain();
        end
    end

    methods (Access = protected)
        %% Listeners
        % Setup Listeners that the toolstrip requires
        function setupListeners(obj, modelHandle, cbinfo)
            % Instantiate HDLProperty Inspector when app is opened, so we
            % can add a listener when close is clicked on it to trigger the
            % refresher correctly.
            if strcmpi(hdlfeature('HDLBlockPropertyInspector'),'on')
                studio = cbinfo.studio;
                hdlpi = studio.getComponent('GLUE2:PropertyInspector','HDL Property Inspector');
                if isempty(hdlpi)
                    if isempty(cbinfo.getSelection)
                        % canvas is selected at some level
                        blockHandle = studio.App.getCurrentSelection.handle;
                    else
                        % something is selected
                        blockHandle = cbinfo.getSelection.Handle;
                    end
                    HDLPropertyInspector.create(modelHandle,blockHandle, true);
                    hdlpi = studio.getComponent('GLUE2:PropertyInspector','HDL Property Inspector');
    
                end
            end
            hBlockDiagram = get_param(modelHandle, 'Object');
            cs_active = getActiveConfigSet(modelHandle);
            % listen for active config set event in case new CS is used
            obj.BDListener = Simulink.listener(hBlockDiagram, 'ActivateConfigSetEvent', ...
                @(bd, ed) obj.onActiveConfigChange(bd, ed));
            % listen for component change in the active config set
            obj.addHDLConfigChangeListener(cs_active);
            obj.HDLPropListener = addlistener(hBlockDiagram, 'HDLParams', 'PostSet', ...
                    @(src, data) obj.onHDLPropertyChange(src, data));
            if strcmpi(hdlfeature('HDLBlockPropertyInspector'),'on')
                % Listen for changes in HDL properties that require us to
                % change context
                obj.HDLPIListener = addlistener(hdlpi,'Closed',@(varargin)obj.setHDLPIOff(varargin));
            end
        end

        function setHDLPIOff(obj,~)
            obj.isHDLPIVis = false;
        end

        function addHDLConfigChangeListener(obj, cs_active)
            obj.CSListener = handle.listener(cs_active, 'ConfigSetComponentChanged', @(cs, ed) obj.onConfigComponentChange(cs, ed));
        end

        function onConfigComponentChange(obj, ~, ~)
            obj.updateTypeChain;
        end

        function onActiveConfigChange(obj, bd, ~)
            cs_active = getActiveConfigSet(bd);
            obj.addHDLConfigChangeListener(cs_active);
            obj.updateTypeChain;
        end

        function onHDLPropertyChange(obj, ~, ~)
            % Update type chain in case workflow changes
            obj.updateTypeChain; 
        end

        %% Update context
        function updateTypeChain( obj )
            obj.updateGenCodeContext();
            obj.updateDeploymentContext();
            if ~isempty(obj.DeploymentContext)
                obj.TypeChain = [{obj.GenCodeContext, obj.DeploymentContext}  obj.OrigTypeChain ];
            else
                obj.TypeChain = [{obj.GenCodeContext}  obj.OrigTypeChain ];
            end
        end

        % Switch between contexts based on the presence of a HDL
        % Configuration in the ConfigSet
        function updateGenCodeContext(obj)
            configSet = getActiveConfigSet( obj.ModelH );
            hasHDLConfig = ~isempty(gethdlcconfigset( configSet ));
            
            
            if hasHDLConfig
                % HDL configuration set is attached to model
                
                % Feature flag for IP core toolstrip workflow
                if ~feature_enableIPCorePerspective
                    obj.GenCodeContext = 'hdlCoderAppConfigContext';
                    return;
                end

                % Switch between code generation contexts
                % IP core generation context is enabled when the workflow is IP Core generation
                % or when feature_enableSLRTPerspective is enabled for SLRT workflows 
                modelName = get_param(obj.ModelH, 'Name');
                if strcmp(hdlget_param(modelName, 'Workflow'), 'IP Core Generation') || ...
                    (feature_enableSLRTPerspective && strcmp(hdlget_param(modelName, 'Workflow'), 'Simulink Real-Time FPGA I/O'))    
                    % IP core workflow context
                    obj.GenCodeContext = 'hdlCoderAppIPCoreContext';
                else
                    % All other workflows go to generic HDL context
                    obj.GenCodeContext = 'hdlCoderAppConfigContext';
                end
            else
                % HDL configuration set is NOT attached to model
                obj.GenCodeContext = 'hdlCoderAppNoConfigContext';
            end
        end
        % Switch between deployment contexts 
        % TODO: future release support synthesis workflows for Generic ASIC
        % FPGA 
        function updateDeploymentContext(obj)
            % Feature flag for reference design toolstrip workflow
            if ~strcmp( obj.GenCodeContext ,'hdlCoderAppIPCoreContext') || ...
                ~feature_enableDeploymentPerspective
                obj.DeploymentContext = [];
                return
            end
            
            % Switch between generic IP core and reference design
            % workflows
            modelName = get_param(obj.ModelH, 'Name');
            if isempty(hdlget_param(modelName, 'ReferenceDesign'))
                % Generic IP core context
                obj.DeploymentContext = [];
            else
                % Reference design context
                obj.DeploymentContext = 'hdlCoderAppReferenceDesignContext';
            end
        end
    end
end

% LocalWords:  SLRT
