classdef ModelManager < matlab.mixin.SetGet & matlab.mixin.Copyable
    %modelpack.ModelManager class
    %    modelpack.ModelManager properties:
    %
    %    modelpack.ModelManager methods:
    %       addModel - Adds the MODEL of given NAME to the list of models managed by the
    %       deleteModel - delete a model stored in the model manager
    %       removeModel - Removes the MODEL from the list of models managed by the

    % Copyright 2000-2023 The MathWorks, Inc.

    properties (Access=protected, Transient, SetObservable)
        %MODELS Property is of type 'mxArray'
        Models = [];
        %MODELLISTENERS Property is of type 'handle vector'
        ModelListeners = [];
        %LISTENERS Property is of type 'handle vector'
        Listeners = [];
    end

    methods(Access = protected)
        function this = ModelManager()
            % MODELMANAGER Constructor for the singleton Model Manager class.

            
        end  % ModelManager

    end

    methods(Static = true, Access = private) 
        function obj = getInstance_

            persistent theInstance
            if isempty(theInstance)
                theInstance = modelpack.ModelManager;
            end
            obj = theInstance;
        end
    end
    methods(Static = true)
        function obj = addModel(model,name)

            inst = modelpack.ModelManager.getInstance_;
            obj = addModel_(inst,model,name);
        end

        function removeModel(model)

            inst = modelpack.ModelManager.getInstance_;
            removeModel_(inst,model);
        end
    end


    methods
        function set.ModelListeners(obj,value)
            % DataType = 'handle vector'
            validateattributes(value,{'handle'}, {'vector'},'','ModelListeners')
            obj.ModelListeners = value;
        end

        function set.Listeners(obj,value)
            % DataType = 'handle vector'
            validateattributes(value,{'handle'}, {'vector'},'','Listeners')
            obj.Listeners = value;
        end

    end   % set and get functions

    methods(Access = protected)
        function h = addModel_(this, model, name)
            % ADDMODEL Adds the MODEL of given NAME to the list of models managed by the
            % ModelManager.  If the MODEL already exists, returns the handle to it
            % instead.

            if nargin < 3, name = model.getName; end

            newModel = true;
            if ~isempty(this.Models)
                hModels  = [this.Models.hModel];
                idxModel = strcmp(hModels.getName,name);
                newModel = ~any(idxModel);
            end

            if newModel
                % Store the handle of the new model object.
                this.Models = [this.Models; struct('hModel',model,'refCount',1)];
                h = model;
                %Add delete listener for the model object
                this.ModelListeners = [this.ModelListeners; ...
                    addlistener(h,'ObjectBeingDestroyed', @(hSrc,hData) localDeleteModel(this,hSrc))];
            else
                % Return model handle as it already exists and bump reference count
                h = hModels(idxModel);
                this.Models(idxModel).refCount = this.Models(idxModel).refCount + 1;
            end
        end

        function removeModel_(this, model)
            % REMOVEMODEL Removes the MODEL from the list of models managed by the
            % ModelManager.

            hasModel = false;
            if ~isempty(this.Models)
                idx      = [this.Models.hModel] == model;
                hasModel = any(idx);
            end

            if hasModel
                % Decrement the reference counter for this model
                this.Models(idx).refCount = this.Models(idx).refCount - 1;
                if this.Models(idx).refCount < 1
                    %Lost last reference to the model, delete it from the model manager.
                    delete(model)
                end
            end
        end
    end

    methods  % public methods

        function deleteModel(this,hModel)
            % DELETEMODEL delete a model stored in the model manager
            %

            hasModel = false;
            if ~isempty(this.Models)
                idx      = [this.Models.hModel] == hModel;
                hasModel = any(idx);
            end

            if hasModel
                %Remove model from the model manager.
                this.Models(idx) = [];

                %Remove any listeners to this model
                hSrc                     = [this.ModelListeners.Source];
                hSrc                     = [hSrc{:}];
                idx                      = hSrc == hModel;
                this.ModelListeners(idx) = [];

                %Delete the model
                delete(hModel)
            end
        end  % deleteModel
    end  % public methods

end  % classdef

% Manager is being destroyed.
function localDeleteModel(this,hSrc)
%Model is being deleted outside of model manager, possibly by a client.
%Delete the model from the model manager

deleteModel(this,hSrc)
end

% LocalWords:  MODELLISTENERS ADDMODEL REMOVEMODEL DELETEMODEL
