classdef SisoToolViewer < viewgui.ltiviewer
%viewgui.SisoToolViewer class
%   viewgui.SisoToolViewer extends viewgui.ltiviewer.
%

%    viewgui.SisoToolViewer properties:
%       AvailableViews - All available view types with the recipe for creating them. 
%       EventManager - Event coordinator (@framemgr object) 
%       Figure - Property is of type 'MATLAB array'  
%       Preferences - Viewer preferences. 
%       StyleManager - Style manager. 
%       Views - Vector of all @plot handles. 
%       HG - Property is of type 'MATLAB array'  (read only) 
%       PlotCells - Property is of type 'MATLAB array'  
%       Systems - resppack.ltisource object created from an @lti object. 
%       Styles - Styles attached to each system object 
%       LastLsimPlot - Handle to last lsim @simplot 
%       LastInitialPlot - Handle to last initial @simplot 
%       Parent - Handle of SISO Tool database. 
%       RealTimeData - Property is of type 'MATLAB array'  
%       RealTimeEnable - Enables dynamic response update during mouse edits of the compensators. 
%       SystemInfo - Struct array defining the available loop transfers 
%
%    viewgui.SisoToolViewer methods:
%       createResponse -  Creates one response per system for a given plot.
%       createView -  Creates one of the built-in LTI plots.
%       getContents -  Queries current configuration of the SISO Tool LTI Viewer.
%       layoutstatus -  positions the status bar and text in the figure window.
%       linkMenu -  Links Analysis menu to particular View.
%       moveFinish -  Clean up after mouse edit.
%       moveInit -  Initializes dynamic update.
%       printsetup - sets the visibility specified in 'value' to the HG objects in
%       refreshgain -  Refreshes plot during dynamic edit of compensator gain.
%       refreshpz -  Refreshes plot during dynamic edit of compensator gain.
%       setContents -  Configures the SISO Tool LTI Viewer.
%       setSystems -  Configures the SISO Tool Viewer to show a particular list of loop
%       updateMultiModelMenus -  updates the multimodel menus for the SISO Tool LTI
%       updateNotifications -  updates the notifications for he SISO Tool LTI


properties (SetObservable)
    %PARENT Handle of SISO Tool database.
    Parent = [];
    %REALTIMEDATA Property is of type 'MATLAB array' 
    RealTimeData = [];
    %REALTIMEENABLE Enables dynamic response update during mouse edits of the compensators.
    RealTimeEnable = 'on';
    %SYSTEMINFO Struct array defining the available loop transfers
    SystemInfo = [];
end


    methods  % constructor block
        function this = SisoToolViewer(sisodb)
        % Constructor for @SisoToolViewer class.
        
        %   Author(s): P. Gahinet
        
                this.Parent           = sisodb;
        LoopData = sisodb.LoopData;
        
        % REVISIT: eliminate when can call parent constructor
        initialize(this)
        toolbar(this)
        
        % Figure customization
        ViewFig = this.Figure;
        set(this.Figure,'Renderer','Zbuffer')
        set(ViewFig,...
           'Name',getString(message('Control:viewer:lblLTIViewerFor',LoopData.Name)),...
           'CloseRequestFcn',{@LocalHide this});
        
        StatusCheckBox = uicontrol('Parent',ViewFig,'Style','checkbox',...
            'String',getString(message('Control:viewer:strRealTimeUpdate')),...
            'TooltipString','Status Bar','Value',strcmpi('on',this.RealTimeEnable),...
            'Callback',{@LocalRealTime this},...
            'Units','character','Position',[0 0.45 23 1.5],'Units','pixels');
        HG = this.HG;
        HG.StatusCheckBox = StatusCheckBox;
        this.HG = HG;
        
        % Add Listener to Track the SisoToolViewer's RealTimeEnable Property
        set(this.HG.StatusCheckBox,'UserData',event.proplistener(this,this.findprop('RealTimeEnable'),...
             'PostSet',@(h,evt) LocalViewRealTime(h,evt,this.HG.StatusCheckBox)));
        
        % Install CS help
        ctrlcshelp(ViewFig,sisodb.Figure);
        
        % Customize menus
        FigMenus = this.HG.FigureMenu;
        set([FigMenus.FileMenu.Import,FigMenus.FileMenu.Export,...
                FigMenus.EditMenu.PlotConfigurations,FigMenus.EditMenu.RefreshSystems,...
                FigMenus.EditMenu.DeleteSystems],'Visible','off')
        set(FigMenus.FileMenu.Close,'Callback', {@LocalHide this})
        set(FigMenus.EditMenu.LineStyles,'Separator','off')
        
        % Remove undesirable plot types
        availViews = this.AvailableViews;
        [~,ia,ib] = intersect({availViews.Alias},{'bodemag','initial','lsim','iopzmap','sigma'});
        availViews(ia) = [];
        this.AvailableViews = availViews;
        
        % Install listeners (needed below)
        addlisteners(this)
        
        % Install listeners for communication between SISO Tool and Viewer
        L = [handle.listener(LoopData,'ObjectBeingDestroyed',{@LocalClose this});...
              handle.listener(LoopData,LoopData.findprop('LoopView'),...
              'PropertyPostSet',{@LocalUpdateLoopViews this});...
               handle.listener(sisodb.Preferences,sisodb.Preferences.findprop('PadeOrder'),...
              'PropertyPostSet',{@LocalUpdate this});...
              handle.listener(LoopData,'LoopDataChanged',{@LocalUpdate this});...
              handle.listener(LoopData,'MoveGain',{@LocalMoveGain this});...
              handle.listener(LoopData,'MovePZ',{@LocalMovePZ this});...
              handle.listener(LoopData,'ConfigChanged',{@LocalUpdatePlotNotifications this});...
              event.listener(this,'ConfigurationChanged',@(h,evt) LocalUpdatePlotNotifications(h,evt,this)); ... 
              handle.listener(LoopData,LoopData.findprop('Name'),...
              'PropertyPostSet',{@LocalSystemNameCB this})];
        set(L,'CallbackTarget',LoopData)
        this.addlisteners(L)
        
        
        L2 = addlistener(this.Figure,'Visible',...
            'PostSet',@(es,ed) LocalUpdate(LoopData,ed,this));
        this.addlisteners(L2)
        
        % Create one data source per loop transfer available for
        % current configuration
        this.setSystems(LoopData)
        
        end  % SisoToolViewer
        
        
        %%%%%%%%%%%%%%%%%%%%
        %%% SystemNameCB %%%
        %%%%%%%%%%%%%%%%%%%%

    end  % constructor block

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

        function set.RealTimeEnable(obj,value)
            % DataType = 'on/off'
        validatestring(value,{'on','off'},'','RealTimeEnable')
        obj.RealTimeEnable = value;
        end

    end   % set and get functions 

    methods  % public methods
    createResponse(this,View,Systems,Styles,varargin)
    View = createView(this,PlotType,varargin)
    Contents = getContents(this)
    layoutstatus(this,varargin)
    linkMenu(this,MenuIndex,View,idx)
    moveFinish(this)
    [RealTimeData,idxSys] = moveInit(this)
    printsetup(this,prop,value)
    refreshgain(this,LoopData)
    refreshpz(this,LoopData)
    setContents(this,Contents)
    setSystems(this,LoopData)
    updateMultiModelMenus(this,Views)
    updateNotifications(this,Views)
end  % public methods 


    methods (Hidden) % possibly private or hidden
    Constraints = findconstr(this)
end  % possibly private or hidden 

end  % classdef

function LocalSystemNameCB(LoopData,~, this)
% Update figure title
set(this.Figure,'Name',getString(message('Control:viewer:lblLTIViewerFor',LoopData.Name)));
end  % LocalSystemNameCB


%%%%%%%%%%%%%%%%%
% LocalRealTime %
%%%%%%%%%%%%%%%%%
function LocalRealTime(hSrc,~,this)
% Callback of the check box. Updates the RealTimeEnable property of the
% object.
if get(hSrc,'Value')
    this.RealTimeEnable = 'on';
else
    this.RealTimeEnable = 'off';
end
end  % LocalRealTime


%%%%%%%%%%%%%%%%%%%%%
% LocalViewRealTime %
%%%%%%%%%%%%%%%%%%%%%
function LocalViewRealTime(~,event,CheckBox)
% Listener Callback of the RealTimeEnable property of the
% object. Will update the CheckBox.
set(CheckBox,'Value',strcmpi('on',event.NewValue));
end  % LocalViewRealTime


%%%%%%%%%%%%%%
% LocalClose %
%%%%%%%%%%%%%%
function LocalClose(~,~,this)
% Callback when closing SISO Tool
close(this)

end  % LocalClose

%%%%%%%%%%%%%
% LocalHide %
%%%%%%%%%%%%%
function LocalHide(~,~,this)
% Callback when closing SISO Tool
set(this.Figure,'Visible','off')

end  % LocalHide


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% LocalUpdatePlotNotifications %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function LocalUpdatePlotNotifications(~,~, this)
% Update plot notifications
this.updateNotifications;
this.updateMultiModelMenus;
end  % LocalUpdatePlotNotifications



%%%%%%%%%%%%%%%%%%%%%%%%
% LocalUpdateLoopViews %
%%%%%%%%%%%%%%%%%%%%%%%%
function LocalUpdateLoopViews(LoopData,~,this)
% Update system list when configuration changes
% Get current contents
Contents = getContents(this);
VisViews = find(this.Views(ishandle(this.Views)),'Visible','on');
% Update system list
% RE: Makes all plot invisible for efficiency
this.setSystems(LoopData)
% Restore contents
nsys = length(this.SystemInfo);
for ct=1:length(VisViews)
   VisResp = Contents(ct).VisibleModels;
   set(VisViews(ct).Responses(VisResp(VisResp<=nsys)),'Visible','on')
end
% Restore plot visibility
set(VisViews,'Visible','on')

end  % LocalUpdateLoopViews

%%%%%%%%%%%%%%%
% LocalUpdate %
%%%%%%%%%%%%%%%
function LocalUpdate(LoopData,~,this)
% Update LTI Viewer.
ActiveViews = getCurrentViews(this);
if strcmp(get(this.Figure,'Visible'),'off') || isempty(ActiveViews)
   return
end

% Disable limit managers to avoid multiple limit updates
AxGrids = get(ActiveViews,{'AxesGrid'});
AxGrids = cat(1,AxGrids{:});
CurrentState = get(AxGrids,{'LimitManager'});
set(AxGrids,'LimitManager','off')   

% Refresh models
% RE: Not enough to update visible models (hidden models would be out of
%     sync when made visible
for ct=1:length(this.SystemInfo)
   src = this.Systems(ct);
   ModelInfo = this.SystemInfo(ct);  % Recipe for computing model data
   [Model,UncertainModel] = LoopData.getmodel(ModelInfo);
   %Model = LoopData.getmodel(ModelInfo); % Updated model data
   % Update ltisource model
   src.UncertainModel = UncertainModel;
   if isequal(src.Model,Model)
      % Model is up to date when releasing the mouse after dynamic edit.
      % Issue SourceChanged event to force full update in this case. 
      src.send('SourceChanged')
   else
      % If model has changed, let src.Model listener trigger the update
      src.Model = Model;
   end
end

% Refresh each view
% RE: Explicit DRAW to force update when leaving drag-edit mode
set(AxGrids,{'LimitManager'},CurrentState)   
for ax=AxGrids'
   ax.send('ViewChanged')
end
end  % LocalUpdate


%%%%%%%%%%%%%%%%%%%%%
%%% LocalMoveGain %%%
%%%%%%%%%%%%%%%%%%%%%
function LocalMoveGain(LoopData,~,this)
% Callback to dynamic gain update start/finish events
if strcmp(this.RealTimeEnable,'on') && strcmp(get(this.Figure,'Visible'),'on')
   refreshgain(this,LoopData) 
end
end  % LocalMoveGain

    

%%%%%%%%%%%%%%%%%%%
%%% LocalMovePZ %%%
%%%%%%%%%%%%%%%%%%%
function LocalMovePZ(LoopData,~,this)
% Notifies editors of MOVEPZ:init and MOVEPZ:finish events
if strcmp(this.RealTimeEnable,'on') && strcmp(get(this.Figure,'Visible'),'on')
   refreshpz(this,LoopData)
end
end  % LocalMovePZ
