classdef (Hidden = true) AnnotateManager < handle
% Singleton class to manage all open execution profiling informers.

%   Copyright 2010-2022 The MathWorks, Inc.
    
    properties(Access=private)
        Map_SimulationComponentToAnnotate;
        Map_AnnotationType;
    end

    methods(Access = private)
        function this = AnnotateManager()
            this.Map_SimulationComponentToAnnotate = containers.Map('KeyType', 'char', 'ValueType', 'any');
            this.Map_AnnotationType = containers.Map('KeyType', 'char', 'ValueType', 'any');
        end
    end
    
    methods(Access = public)
        function lSimulationComponents = getSimulationComponentsWithInformer(this)
            lSimulationComponents = this.Map_SimulationComponentToAnnotate.keys;
        end

        function informer = getInformerForSimulationComponent(this, lSimulationComponent)
            informer = this.Map_SimulationComponentToAnnotate(lSimulationComponent);
        end

        function showProfilingResults(this, executionProfile, varargin)
            % When the execution time object becomes available, call this 
            % method to create a new informer and highlight the associated 
            % models
            
            % check if the informer must be created
            openInformer = ~coder.profile.private.featureOn('IntegratedResults') || ...
                ~executionProfile.isSILPILSimulation();
        
            % Get the Simulation Component that the new data is associated with
            lSimulationComponent = executionProfile.SimulationComponent;
            
            % close already existing annotation objects
            if this.Map_SimulationComponentToAnnotate.isKey(lSimulationComponent)
                coder.profile.AnnotateManager.tearDownInformer(lSimulationComponent);
            end

            % Create the new informer/highlight annotation
            models = coder.profile.ModelAnnotationData.getModelsToAnnotate(executionProfile);
            hilite = coder.profile.ProfileHilighter(executionProfile, models, ~openInformer);
            if openInformer
                informer = coder.profile.Informer(executionProfile, models, varargin{:});
            else
                informer = coder.profile.DockedInformer(executionProfile, models, varargin{:});
            end
            hilite.setSuccessor(informer);
            
            if informer.isAnyModelOpenInEditor()
                % annotate/highlight profiling
                coder.profile.AnnotateManager.refreshData(hilite, models, varargin{:});
            
                % store managers
                this.Map_SimulationComponentToAnnotate(lSimulationComponent) = hilite;
                if ~openInformer
                   this.Map_AnnotationType(lSimulationComponent) = models;
                end
            
                % Enable callbacks for model load etc
                exectime_profiling_callback('activate');
            end
        end 
        
        function processCallback(this, modelH, method)
            
            modelName = get_param(modelH, 'Name');
            
            %  Check if starting a new simulation
            if any(strcmp(method, {'init', 'close', 'forceClose'}))
                % Identify simulation components within this model, which have an open informer
                lSimulationComponentsAll = getSimulationComponentsWithInformer(this);
                
                % Extract model names from block specifications
                modelsForSimulationComponents = ...
                    coder.profile.private.getTopModelFromBlock(lSimulationComponentsAll);
                
                matchIdx = strcmp(modelsForSimulationComponents, modelName);
                lSimulationComponents = lSimulationComponentsAll(matchIdx);
                for i=1:length(lSimulationComponents)
                    coder.profile.AnnotateManager.tearDownInformer(lSimulationComponents{i});
                end
            end
            
            if strcmp(method, 'postLoad')
                % Notify informers in case they need to apply highlighting
                % for this model
                keys = this.Map_SimulationComponentToAnnotate.keys;
                for i = 1:length(keys)
                    key = keys{i};
                    informer = this.Map_SimulationComponentToAnnotate(key);
                    if informer.isvalid()
                        coder.profile.AnnotateManager.refreshData(informer, {modelName});
                    else
                        this.Map_SimulationComponentToAnnotate.remove(key);
                        if inst.Map_AnnotationType.isKey(key)
                            inst.Map_AnnotationType.remove(key);
                        end
                    end
                end
            end
            
        end
    end
    
    methods(Static=true, Access=protected)
        function refreshData(hilite, models, varargin)
            for i = 1:length(models)
                model = models{i};
                if ~any(strcmp(model, hilite.Models))
                    continue;
                end
                if bdIsLoaded(model)
                    for idx = 1:hilite.ExecutionTimeObj.getModuleNumber()
                        execProfObj = hilite.ExecutionTimeObj.getModule(idx);
                        lModelAnnotationData = coder.profile.ModelAnnotationData(model, execProfObj, varargin{:});
                        [itemHandles, modelHasDeletedBlocks] = lModelAnnotationData.getItemsToHighlight();
                        if modelHasDeletedBlocks
                            MSLDiagnostic('CoderProfile:ExecutionTimeInformer:etiBlockNotFound', model).reportAsWarning;
                        end
                        for j = 1:length(itemHandles)
                            itemHandle = itemHandles(j);
                            hilite.refreshItem(itemHandle, lModelAnnotationData, varargin);
                        end
                    end
                end
            end
        end
    end
    
    methods(Static=true, Access=public)
        
        function obj = getAnnotateManagerInstance
            % Return the singleton object
            persistent singletonObj
            
            if ~isa(singletonObj, 'coder.profile.AnnotateManager')
                singletonObj = coder.profile.AnnotateManager();
            end
            
            obj = singletonObj;
        end     
        
        function tearDownInformer(lSimulationComponent)
            % Tear down informer associated with this top model simulation
            inst = coder.profile.AnnotateManager.getAnnotateManagerInstance;
            if inst.Map_SimulationComponentToAnnotate.isKey(lSimulationComponent)
                % Deleting the reference to an informer invokes its delete method
                % (and this closes the informer window)
                t = inst.Map_SimulationComponentToAnnotate(lSimulationComponent);
                if ~isempty(t) && t.isvalid()
                    t.destroy();
                end
                inst.Map_SimulationComponentToAnnotate.remove(lSimulationComponent);
                if inst.Map_AnnotationType.isKey(lSimulationComponent)
                    inst.Map_AnnotationType.remove(lSimulationComponent);
                end
            end 
            if isempty(inst.Map_SimulationComponentToAnnotate.keys)
                % Switch off model callbacks
                exectime_profiling_callback('deActivate');
            end
        end

        function tearDownIntegratedView(model)
            inst = coder.profile.AnnotateManager.getAnnotateManagerInstance;
            lSimulationComponents = inst.Map_AnnotationType.keys;
            for lSimulationComponent = lSimulationComponents
                lCmp = lSimulationComponent{1};
                lmodels = inst.Map_AnnotationType(lCmp);
                if any(strcmp(model, lmodels))
                    coder.profile.AnnotateManager.tearDownInformer(lCmp);
                end
            end
        end

        function res = isInformerVisible(lSimulationComponent)
            inst = coder.profile.AnnotateManager.getAnnotateManagerInstance;
            res = inst.Map_SimulationComponentToAnnotate.isKey(lSimulationComponent);
        end
    end    
end
