classdef rctMultiPlotManager < handle
   % Management of MultiPlot Graph block

   %  Copyright 2022 The MathWorks, Inc.

   % Pre-computed constants
   properties
      % MATLAB System block associated with MPGSysObj class
      MLSBlock
      % MultiPlotGraph mask
      MPGMask
      % Graph figure
      FigureHandle
      % Axes 
      AxesHandle
      % Lines
      LineHandle
      % System object from MATLAB System block
      SystemObject
   end

   properties (Access = protected)
      InputSize % input vector length
      SimCount = 0;  % counts simulations 
      StepCount = 0; % counts time steps in current simulation
      T  % current time vector
      Y  % current y data
      LOG = [];
      Colors
   end

   properties (Access = private)
      Listeners
   end

   methods
      % Constructor
      function this = rctMultiPlotManager(bh,mblk,ny,YAxisLabel)
         % Construct new instance
         this.MLSBlock = bh;
         this.MPGMask = get_param(mblk,'Handle');
         this.InputSize = ny;
         localCreateFig(this,ny,YAxisLabel,get_param(mblk,'Name'));
         mobj = get_param(mblk,'Object');
         this.Listeners = [...
            addlistener(mobj,'NameChangeEvent',@(x,y) updateFigTitle(this));...
            addlistener(mobj,'DeleteEvent',@(x,y) deleteFcn(this))];
         Colors = wavepack.colordefs('Color8');
         this.Colors = Colors(1:end-1,:); % skip white
      end

      function initializeSim(this)
         % Configures new sim
         isim = this.SimCount+1;
         this.SimCount = isim;
         % Create new trace
         ny = this.InputSize;
         COLOR = this.Colors{mod(isim-1,numel(this.Colors))+1};
         for ct=1:ny
            this.LineHandle(ct) = line(NaN,NaN,'Parent',this.AxesHandle(ct),'Color',COLOR);
         end
         % Configure based on System object settings
         setLimits(this)
         this.AxesHandle(1).Title.String = this.SystemObject.PlotTitle;
         % Reset T,Y
         this.StepCount = 0;
         this.T = zeros(1,0);
         this.Y = zeros(ny,0);
         % Make figure visible
         figure(this.FigureHandle)
      end

      function logSample(this,t,y)
         % Logs new (t,y) point
         ny = this.InputSize;
         j = this.StepCount+1;
         if j>numel(this.T)
            % Extend buffer
            this.T = [this.T zeros(1,1000)];
            this.Y = [this.Y zeros(ny,1000)];
         end
         this.T(:,j) = t;
         this.Y(:,j) = y;
         this.StepCount = j;
         if rem(j,25)==0
            t = this.T(1:j);
            for ct=1:ny
               set(this.LineHandle(ct),'XData',t,'YData',this.Y(ct,1:j))
            end
         end
      end

      function logData(this)
         % Logs data at the end of simulation
         ny = this.InputSize;
         j = this.StepCount;
         if j>0
            t = this.T(1:j);
            y = this.Y(:,1:j);
            % Make sure all data is shown
            for ct=1:ny
               set(this.LineHandle(ct),'XData',t,'YData',y(ct,:))
            end
            sig = struct('values',y.','dimensions',ny,'label','');
            S = struct('time',t.','signals',sig,'blockName',get(this.MPGMask,'Name'));
            this.LOG = [this.LOG;S];
         else
            % Model compilation can trigger a Start/Stop with no data
            % generated. Ignore such void runs
            delete(this.LineHandle)
            this.SimCount = this.SimCount-1; % to preserve color order
         end
      end

      function setLimits(this)
         % Validate and set limits
         ny = this.InputSize;
         sysobj = this.SystemObject;
         ymin = sysobj.ymin;
         if isscalar(ymin)
            ymin = repmat(ymin,[ny 1]);
         elseif numel(ymin)~=ny
            error(message('Robust:simulink:MultiPlot5'))
         end
         ymax = sysobj.ymax;
         if isscalar(ymax)
            ymax = repmat(ymax,[ny 1]);
         elseif numel(ymax)~=ny
            error(message('Robust:simulink:MultiPlot5'))
         end
         TLim = [sysobj.tmin sysobj.tmax];
         for ct=1:ny
            set(this.AxesHandle(ct),'XLim',TLim,'Ylim',[ymin(ct) ymax(ct)])
         end
      end

      function updateFigTitle(this)
         this.FigureHandle.Name = get(this.MPGMask,'Name');
      end

      function pf = isConfigured(this,ny)
         pf = (this.InputSize==ny && ishandle(this.FigureHandle));
      end

      function deleteFcn(this)
         if ishandle(this.FigureHandle)
            close(this.FigureHandle)
         end
         set_param(this.MLSBlock,'UserData',[])
         delete(this)
      end

   end

end



function localCreateFig(this,ny,YAxisLabel,BlockName)
% Create figure
left = 100; bottom = 100;
screenLoc = get(0,'ScreenSize');
if screenLoc(1) < 0
    left  = -screenLoc(1) + 100;
end
if screenLoc(2) < 0
    bottom = -screenLoc(2) + 100;
end
hfig = figure('Units',          'pixel',...
  'Position',       [left bottom  450 300],...
  'Name',           BlockName,...
  'Tag',            'MultiPlot_FIGURE',...
  'NumberTitle',    'off',...
  'IntegerHandle',  'off',...
  'Toolbar','figure');
toolbarhan = findall(hfig,'Tag','FigureToolBar');

% Load Cdata for Clear/Save/Autoscale Plot buttons
[EraserIm,SaveIm,BinocIm]=LOCALCreateImages;

% need to add icon to button, i.e. Cfig
uipushtool(toolbarhan,'Cdata',EraserIm,...
   'ClickedCallback',@(x,y) localClear(this),...
   'TooltipString','Clear Plots');
uipushtool(toolbarhan,'Cdata',SaveIm,...
   'ClickedCallback',@(x,y) localSave(this),...
   'TooltipString','Save Plot Data to Workspace');
uipushtool(toolbarhan,'Cdata',BinocIm,...
   'ClickedCallback',@(x,y) localAutoscale(this),...
   'TooltipString','Autoscale');
hax = gobjects(ny,1);
for i=1:ny
   hax(i) = subplot(ny,1,i);
   set(hax(i),'Xgrid','on','Ygrid','on');
   ylab = YAxisLabel{i};
   if isempty(ylab)
      ylabel(['Y' int2str(i) ' Signal']);
   else
      ylabel(ylab);
   end
   if i==ny
      xlabel('Time');
   end
end
set(hfig,'HandleVisibility','callback');
this.FigureHandle = hfig;
this.AxesHandle = hax;
end


function localClear(this)
% Clear plots and logged data
this.SimCount = 0;
this.LOG = [];
C = get(this.AxesHandle,{'Children'});
delete(cat(1,C{:}))
end


function localSave(this)
% Clear plots and logged data
varname = get_param(this.MLSBlock,'ExportVariable');
try
   assignin('base',varname,this.LOG);
catch  %#ok<CTCH>
   warning(message('Robust:simulink:MultiPlot1'))
end
end


function localAutoscale(this)
% Clear plots and logged data
ny = this.InputSize;
tmin = inf; tmax = -inf;
ymin = inf(ny,1); ymax = -inf(ny,1);
for j=1:ny
   ax = this.AxesHandle(j);
   C = get(ax,'Children');
   for ct=1:numel(C)
      t = C(ct).XData;  y = C(ct).YData;
      tmin = min(tmin,t(1));
      tmax = max(tmax,t(end));
      ymin(j) = min(ymin(j),min(y));
      ymax(j) = max(ymax(j),max(y));
      if ymin(j)==ymax(j)
         if ymin(j)==0
            delta = 1;
         else
            delta = abs(ymin(j))/2;
         end
         ymin(j) = ymin(j)-delta;
         ymax(j) = ymax(j)+delta;
      end
   end
   set(ax,'Xlim',[tmin tmax],'YLim',[ymin(j) ymax(j)])
end
end

function [EraserIm,SaveIm,BinocIm]=LOCALCreateImages
% Eraser Image Setup
Cfig = ones(16,16);
Cfig(:,1)   = 0;
Cfig(1,:)   = 0;
Cfig(end,:) = 0;
Cfig(:,end) = 0;
Cfig = repmat(Cfig,[1 1 3]);
%--------------Draw Eraser
Efig = Cfig;

% Eraser Outline
Efig(13,3:8,1:3) = 0;
Efig(10,3:8,1:3) = 0;
Efig(10:13,3,1:3) = 0;
Efig(10:13,8,1:3) = 0;
Efig(4,9:14,1:3) = 0;
Efig(4:7,14,1:3) = 0;
for i1=1:5
   Efig(10-i1,3+i1,1:3)=0;
   Efig(10-i1,8+i1,1:3)=0;
   Efig(13-i1,8+i1,1:3)=0;
end

% Eraser Fill
Ecolor = ones(2,4,3);
Ecolor(:,:,2) = 0.5;
Efig(11:12,4:7,1:3) = Ecolor;

Ecolor1 = ones(1,4,3);
Ecolor1(:,:,2) = 0.5;
Ecolor2 = ones(2,1,3);
Ecolor2(:,:,2) = 0.5;
for i1=1:5
   Efig(10-i1,(4:7)+i1,1:3) = Ecolor1;
   Efig((11:12)-i1,8+i1,1:3) = Ecolor2;
end

%--------------Draw Matlab Prompt
Pfig = Cfig;

% Draw Prompt Double Arrows
for i1=1:5
   Pfig(3+i1,4+i1,1:3) = 0;
   Pfig(14-i1,4+i1,1:3) = 0;
   Pfig(3+i1,8+i1,1:3) = 0;
   Pfig(14-i1,8+i1,1:3) = 0;
end

%--------------Draw Binocular Image
% This is the binocular image used in the simulink "Scope" block
% XXX-Perhaps there is any easy way to load their *.mat file? It
% is located at: matlab\toolbox\simulink\simulink\scpbarbmp.mat
Bfig(:,:,1) =[ ...
   NaN   NaN   NaN   NaN     0     0     0   NaN   NaN     0     0     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN   NaN     0     0     0   NaN   NaN     0     0     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN   NaN     0     1     0   NaN   NaN     0     1     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN   NaN     0     0     0   NaN   NaN     0     0     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN     0     0     0     0     0     0     0     0     0     0   NaN   NaN   NaN;
   NaN   NaN   NaN     0     1     0     0     0     0     0     1     0     0   NaN   NaN   NaN;
   NaN   NaN     0     0     0     0     0     0     0     0     0     0     0     0   NaN   NaN;
   NaN     0     0     1     0     0     0   NaN     0     0     0     1     0     0     0   NaN;
   0     0     0     1     0     0     0   NaN     0     0     0     1     0     0     0     0;
   0     0     0     1     0     0     0     0     0     0     0     1     0     0     0     0;
   0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0;
   0     0     1     0     0     0   NaN   NaN   NaN   NaN     0     0     1     0     0     0;
   0     0     1     0     0     0   NaN   NaN   NaN   NaN     0     0     1     0     0     0;
   0     0     0     0     0     0   NaN   NaN   NaN   NaN     0     0     0     0     0     0;
   0     0     0     0     0     0   NaN   NaN   NaN   NaN     0     0     0     0     0     0;
   0     0     0     0     0     0   NaN   NaN   NaN   NaN     0     0     0     0     0     0];
Bfig(:,:,2) =[ ...
   NaN   NaN   NaN   NaN     0     0     0   NaN   NaN     0     0     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN   NaN     0     0     0   NaN   NaN     0     0     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN   NaN     0     1     0   NaN   NaN     0     1     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN   NaN     0     0     0   NaN   NaN     0     0     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN     0     0     0     0     0     0     0     0     0     0   NaN   NaN   NaN;
   NaN   NaN   NaN     0     1     0     0     0     0     0     1     0     0   NaN   NaN   NaN;
   NaN   NaN     0     0     0     0     0     0     0     0     0     0     0     0   NaN   NaN;
   NaN     0     0     1     0     0     0   NaN     0     0     0     1     0     0     0   NaN;
   0     0     0     1     0     0     0   NaN     0     0     0     1     0     0     0     0;
   0     0     0     1     0     0     0     0     0     0     0     1     0     0     0     0;
   0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0;
   0     0     1     0     0     0   NaN   NaN   NaN   NaN     0     0     1     0     0     0;
   0     0     1     0     0     0   NaN   NaN   NaN   NaN     0     0     1     0     0     0;
   0     0     0     0     0     0   NaN   NaN   NaN   NaN     0     0     0     0     0     0;
   0     0     0     0     0     0   NaN   NaN   NaN   NaN     0     0     0     0     0     0;
   0     0     0     0     0     0   NaN   NaN   NaN   NaN     0     0     0     0     0     0];
Bfig(:,:,3) = [...
   NaN   NaN   NaN   NaN     0     0     0   NaN   NaN     0     0     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN   NaN     0     0     0   NaN   NaN     0     0     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN   NaN     0     1     0   NaN   NaN     0     1     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN   NaN     0     0     0   NaN   NaN     0     0     0   NaN   NaN   NaN   NaN;
   NaN   NaN   NaN     0     0     0     0     0     0     0     0     0     0   NaN   NaN   NaN;
   NaN   NaN   NaN     0     1     0     0     0     0     0     1     0     0   NaN   NaN   NaN;
   NaN   NaN     0     0     0     0     0     0     0     0     0     0     0     0   NaN   NaN;
   NaN     0     0     1     0     0     0   NaN     0     0     0     1     0     0     0   NaN;
   0     0     0     1     0     0     0   NaN     0     0     0     1     0     0     0     0;
   0     0     0     1     0     0     0     0     0     0     0     1     0     0     0     0;
   0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0;
   0     0     1     0     0     0   NaN   NaN   NaN   NaN     0     0     1     0     0     0;
   0     0     1     0     0     0   NaN   NaN   NaN   NaN     0     0     1     0     0     0;
   0     0     0     0     0     0   NaN   NaN   NaN   NaN     0     0     0     0     0     0;
   0     0     0     0     0     0   NaN   NaN   NaN   NaN     0     0     0     0     0     0;
   0     0     0     0     0     0   NaN   NaN   NaN   NaN     0     0     0     0     0     0];

EraserIm = Efig;
SaveIm = Pfig;
BinocIm = Bfig;
end