classdef Extension < matlab.mixin.Heterogeneous & handle
    %Extension   Define the Extension class.
    
    %   Copyright 2012 The MathWorks, Inc.
    
    properties (SetAccess = 'private')
        
        %Application Handle to the extensible application object
        %
        %   See also extmgr.Extension
        Application;
        
        %Configuration Handle to the configuration object
        %
        %   See also extmgr.Extension
        Configuration;
        
        %Registration Handle to the registration object
        %
        %   See also extmgr.Extension
        Registration;
    end
    
    properties (SetAccess = private, Hidden, Dependent)
        Config;
        Register;
    end
    
    properties (Access = protected)
        UIMgrGUI;
        PropertyListener;
    end
    
    methods
        function this = Extension(hApplication, hRegistration, hConfiguration)
            %Extension   Construct the Extension class.
            
            this.Application   = hApplication;
            this.Registration  = hRegistration;
            this.Configuration = hConfiguration;
            
            this.PropertyListener = event.listener(hConfiguration.pPropertySet, ...
                'PropertyChanged', @this.localPropertyChanged);
        end
        
        function convertObsoleteProps(~)
            % NO OP
        end
        
        function propValue = getPropertyValue(this, propName)
            %GETPROPVALUE Get the propValue.
            
            pSet = this.Configuration.pPropertySet;
            propValue = getValue(pSet, propName);
        end
        
        function setPropertyValue(this, varargin)
            %SETPROPVALUE Set the PropValue.
            %   setPropValue(H, PROP1, VALUE1) Set the property specified by PROP1 to
            %   value specified by VALUE1.  Multiple P/V pairs can be specified.
            %   propertyChanged will only fire for the last pair.
            
            % Disable the listener so that it doesn't fire for every single property.
            this.PropertyListener.Enabled = false;
            
            % If we were passed a trailing input, it is the 'noResponse' flag.
            noResponse = false;
            if rem(nargin, 2) == 0
                noResponse = varargin{end};
                varargin(end) = [];
            end
            
            propSet = this.Configuration.PropertySet;
            
            % Loop over and set all of the properties except the last one.
            for indx = 1:2:length(varargin)-2
                setValue(propSet, varargin{indx}, varargin{indx+1});
            end
            
            % If we want a response, turn the listener back on so that setting the last
            % property will fire it.
            if ~noResponse
                this.PropertyListener.Enabled = true;
            end
            
            % Set the last property.
            setValue(propSet, varargin{end-1}, varargin{end});
            
            % If we do not want a response, turn the listener back on here so that we
            % return the object back to its normal state.
            if noResponse
                this.PropertyListener.Enabled = true;
            end
        end
        
        function updatePropertySet(~, ~)
            % NO OP
        end
        
        function config = get.Config(this)
            config = this.Configuration;
        end
        
        function register = get.Register(this)
            register = this.Registration;
        end
    end
    
    methods (Sealed)
        function disp(this)
            if numel(this) == 1
                s.Type = this.Config.Type;
                s.Name = this.Config.Name;
                
                disp(s);
                
                disp@handle(this);
                
                disp(this.Configuration.PropertySet);

            else
                cwSize = get(0, 'CommandWindowSize');
                for indx = 1:numel(this)-1
                    
                    disp(this(indx));
                    
                    % Separate each extension display by a line.
                    fprintf([repmat('-', 1, cwSize(1)) '\n']);
                end
                disp(this(end));
            end
        end
        
        function b = eq(this, that)
            b = eq@handle(this, that);
        end
        
        function b = ne(this, that)
            b = ne@handle(this, that);
        end
    end
    
    methods (Static)
        function hPropSet = getPropertySet()
            hPropSet = [];
        end
        
        function obj = getDefaultObject
            obj = extmgr.Extension([], [], []);
        end
    end
    
    methods (Access = protected)
        function hInstaller = createGUI(~)
            hInstaller = [];
        end
        
        function hProp = findProperty(this, propName)
            hProp = findProperty(this.Configuration.PropertySet, propName);
        end
    end
    
    methods (Hidden)
        
        function disable(~); end
        function enable(~);  end
        
        function localPropertyChanged(this, ~, ev)
            propertyChanged(this, ev);
        end
        
        function disableExtension(this, varargin)
            disable(this);
            uninstallGUI(this, varargin{:})
        end
        
        function enableExtension(this, varargin)
            installGUI(this, varargin{:});
            enable(this);
        end
        
        function varargout = evalPropertyValue(this, property)
            
            string_value = getPropertyValue(this, property);
            [varargout{1:nargout}] = uiservices.evaluate(string_value);
        end
        
        function optionsDialogTitle = getOptionsDialogTitle(this, hSystem)
            hRegister = this.Registration;
            type = hRegister.Type;
            optionsDialogTitle = sprintf('%s - %s %s', ...
                hSystem.findType(type).Label, ...
                hRegister.Label, ...
                getString(message('Spcuilib:scopes:TitleOptDlg')));
        end
        
        function propsSchema = getPropsSchema(~, ~)
            propsSchema = [];
        end
        
        function h = findProp(this, tag)
            h = this.Configuration.PropertySet.findProperty(tag);
        end
        
        function installGUI(this, allowRender)
            %INSTALLGUI If extension has a GUI, install and render it now.
            %   INSTALLGUI(H) installs the GUI aspects of the extension as specified by
            %   the uimgr.Installer object returned from createGUI.
            %
            %   INSTALLGUI(H, false) installs the GUI but suppresses rendering until a
            %   later time.
            
            % If we have already created the GUI simply return early
            hUIMgr = getGUI(this.Application);
            
            % Return early if the application does not support a UIMgr-based GUI.
            if isempty(hUIMgr)
                return;
            end
            
            if isempty(this.UIMgrGUI)
                hGUI = createGUI(this);
                
                % If the extension does not wish to add GUI components, return early.
                if isempty(hGUI)
                    return;
                end
                this.UIMgrGUI = hGUI;
                
                install(hGUI, hUIMgr);
            end
            
            % Must call render() manually after install() method.  This provides
            % efficiency during startup phases (e.g., for our case when allowRender =
            % false).
            if nargin < 2 || allowRender
                render(hUIMgr);
            end
        end
        
        function onOptionsDialogClose(~, ~)
            % NO OP
        end
        
        function [success, msg] = postOptionsDialogApply(~, ~)
            %POSTOPTIONSDIALOGAPPLY Called as the post action to the OptionsDialog
            
            success = true;
            msg = '';
        end
        
        function propertyChanged(~, ~)
            % NO OP
        end
        
        function uninstallGUI(this, allowRender)
            %UNINSTALLGUI If extension has a GUI, uninstall it now.
            %   UNINSTALLGUI(H) uninstall the GUI if it exists.
            
            hInstall = this.UIMgrGUI;
            if ~isempty(hInstall)
                
                hUIMgr = getGUI(this.Application);
                uninstall(hInstall,hUIMgr);  % un-renders as well
                this.UIMgrGUI = [];
                
                if nargin < 2 || allowRender
                    render(hUIMgr);
                end
            end
        end
        
        function updatePropertyDb(this, varargin)
            updatePropertySet(this, varargin{:});
        end
        
        function varargout = validate(~, ~)
            %VALIDATE Returns true if this object is valid
            
            if nargout
                varargout = {true, MException.empty};
            end
        end
    end
end

% [EOF]
