function varyingBlockMask(Type,CB,varargin)
% Mask callback for parameter-varying blocks. Redraws block diagram under
% the mask.

%   Author(s): P. Gahinet
%   Copyright 2017 The MathWorks, Inc.

switch Type
   case 'butterworth'
      % N even:
      %                            w^N
      %    H(s) = ----------------------------------------------------
      %           prod{k = 1:N/2} (s^2 + 2*sin((2k-1)/(2N)*pi)*s + w^2)
      % N odd:
      %                                   w^N
      %    H(s) = ---------------------------------------------------------------
      %           (s+w) prod{k = 1:(N-1)/2} (s^2 + 2*sin((2k-1)/(2N)*pi)*s + w^2)
      N = varargin{1};  % order
      Ts = varargin{2};
      if Ts==0
         FOS = find_system(CB,'FollowLinks','on','LookUnderMasks','all','SearchDepth',1,...
            'ReferenceBlock','cstextras/FirstOrderSection');
         SOS = find_system(CB,'FollowLinks','on','LookUnderMasks','all','SearchDepth',1,...
            'ReferenceBlock','cstextras/SecondOrderSection');
      else
         FOS = find_system(CB,'FollowLinks','on','LookUnderMasks','all','SearchDepth',1,...
            'ReferenceBlock','cstextras/DiscreteFirstOrderSection');
         SOS = find_system(CB,'FollowLinks','on','LookUnderMasks','all','SearchDepth',1,...
            'ReferenceBlock','cstextras/DiscreteSecondOrderSection');
      end
      if numel(FOS)+2*numel(SOS)~=N
         % Delete blocks and lines
         for ct=1:numel(FOS)
            delete_block(FOS{ct})
         end
         for ct=1:numel(SOS)
            delete_block(SOS{ct})
         end
         lines = get_param(CB,'lines');
         for ct=1:length(lines)
            delete_line(lines(ct).Handle);
         end
         % Add SOS and FOS blocks
         x0 = 180;  y0 = 42;
         L = 70;  H = 50;
         FreqPort = 'frequency/1';
         LastPort = 'u/1';
         for ct=1:fix(N/2)
            SOS = sprintf('SOS%d',ct);
            if Ts==0
               add_block('cstextras/SecondOrderSection',sprintf('%s/%s',CB,SOS),...
                  'Position',[x0 y0 x0+L y0+H],'zeta',sprintf('sin(%d*pi/%d)',2*ct-1,2*N))
            else
               add_block('cstextras/DiscreteSecondOrderSection',sprintf('%s/%s',CB,SOS),...
                  'Position',[x0 y0 x0+L y0+H],'Ts','Ts','w0','w0',...
                  'zeta',sprintf('sin(%d*pi/%d)',2*ct-1,2*N))
            end
            add_line(CB,LastPort,[SOS '/1']);
            add_line(CB,FreqPort,[SOS '/2'],'autorouting','on');
            LastPort = [SOS '/1'];
            x0 = x0 + L + 40;
         end
         if rem(N,2)>0
            FOS = 'FOS';
            if Ts==0
               add_block('cstextras/FirstOrderSection',sprintf('%s/%s',CB,FOS),...
                  'Position',[x0 y0 x0+L y0+H])
            else
               add_block('cstextras/DiscreteFirstOrderSection',sprintf('%s/%s',CB,FOS),...
                  'Position',[x0 y0 x0+L y0+H],'Ts','Ts','w0','w0')
            end
            add_line(CB,LastPort,[FOS '/1']);
            add_line(CB,FreqPort,[FOS '/2'],'autorouting','on');
            LastPort = [FOS '/1'];
            x0 = x0 + L + 40;
         end
         add_line(CB,LastPort,'y/1');
         set_param([CB '/y'],'Position',[x0 63 x0+30 77])
      end
      
   case 'tf'
      % Varying transfer function
      N = varargin{1};
      Ts = varargin{2};
      if Ts==0
         IB = find_system(CB,'FollowLinks','on','LookUnderMasks','all',...
            'SearchDepth',1,'BlockType','Integrator');
      else
         IB = find_system(CB,'FollowLinks','on','LookUnderMasks','all',...
            'SearchDepth',1,'BlockType','UnitDelay');
      end
      if numel(IB)~=N
         % Delete blocks and lines
         MaskBlocks = get_param(CB,'Blocks');
         for ct=1:length(MaskBlocks)
            name = MaskBlocks{ct};
            if ~any(strcmp(name,{'u','y'}))
               delete_block([CB '/' name]);
            end
         end
         lines = get_param(CB,'lines');
         for ct=1:length(lines)
            delete_line(lines(ct).Handle);
         end
         % Positions
         LeftProd = [180  47  210  78];
         LeftPort = [155  93  185  107];
         RightProd = [385  47  415  78];
         RightPort = [405  93  435  107];
         CenterInteg = [275 -25 315 15];
         CenterSum = [280 50 310 80];
         dY = 135;
         % Port ordering from top to bottom
         if Ts==0
            aPorts = N-1:-1:0;   bPorts = N:-1:0;
         else
            aPorts = 1:N;   bPorts = 0:N;
         end
         % Draw first stage
         add_block('built-in/Product',sprintf('%s/Product1',CB),...
            'Position',LeftProd,'ShowName','off')
         LIP = sprintf('b%d',bPorts(1));
         add_block('built-in/Inport',sprintf('%s/%s',CB,LIP),...
            'Position',LeftPort,'Orientation','left')
         add_line(CB,'u/1','Product1/1');
         add_line(CB,[LIP '/1'],'Product1/2','autorouting','on');
         add_block('built-in/Sum',sprintf('%s/Sum1',CB),'IconShape','round',...
            'Inputs','|++','Position',CenterSum,'ShowName','off')
         add_line(CB,'Product1/1','Sum1/1');
         add_line(CB,'Sum1/1','y/1');
         % Draw additional stages
         for ct=1:N
            LeftProd = LeftProd + [0 dY 0 dY];
            LeftPort = LeftPort + [0 dY 0 dY];
            CenterSum = CenterSum + [0 dY 0 dY];
            CenterInteg = CenterInteg + [0 dY 0 dY];
            RightProd = RightProd + [0 dY 0 dY];
            RightPort = RightPort + [0 dY 0 dY];
            % Integrator or UnitDelay
            if Ts==0
               IB = sprintf('Integrator%d',ct);
               add_block('built-in/Integrator',sprintf('%s/%s',CB,IB),...
                  'Position',CenterInteg,'Orientation','up')
            else
               IB = sprintf('Delay%d',ct);
               add_block('built-in/UnitDelay',sprintf('%s/%s',CB,IB),...
                  'Position',CenterInteg,'Orientation','up',...
                  'SampleTime','Ts')
            end
            % Summing junction
            if ct<N
               sgns = '+|+|-'; rport = '/3';
            else
               sgns = '+|-'; rport = '/2';
            end
            SB = sprintf('Sum%d',ct+1);
            add_block('built-in/Sum',sprintf('%s/%s',CB,SB),...
               'IconShape','round','Inputs',sgns,...
               'Position',CenterSum,'ShowName','off','Orientation','up')
            add_line(CB,[IB '/1'],sprintf('Sum%d/2',ct));
            add_line(CB,[SB '/1'],[IB '/1']);
            % Product blocks
            LPB = sprintf('Product%d',2*ct+1);
            RPB = sprintf('Product%d',2*ct);
            add_block('built-in/Product',sprintf('%s/%s',CB,LPB),...
               'Position',LeftProd,'ShowName','off')
            add_block('built-in/Product',sprintf('%s/%s',CB,RPB),...
               'Position',RightProd,'ShowName','off','Orientation','left')
            add_line(CB,[LPB '/1'],[SB '/1']);
            add_line(CB,'u/1',[LPB '/1'],'autorouting','on');
            add_line(CB,[RPB '/1'],[SB rport]);
            add_line(CB,[315 65;465 65;465 55+ct*dY;417 55+ct*dY]);
            % Input ports
            LIP = sprintf('b%d',bPorts(ct+1));
            RIP = sprintf('a%d',aPorts(ct));
            add_block('built-in/Inport',sprintf('%s/%s',CB,LIP),...
               'Position',LeftPort,'Orientation','left')
            add_line(CB,[LIP '/1'],[LPB '/2'],'autorouting','on');
            add_block('built-in/Inport',sprintf('%s/%s',CB,RIP),...
               'Position',RightPort)
            add_line(CB,[RIP '/1'],[RPB '/2'],'autorouting','on');
         end
         % Reorder ports as
         %   CT: b0,a0,b1,a1,...,bN
         %   DT: b0,a1,b1,...,aN,bN
         Offset = double(Ts==0);
         for ct=1:N
            set_param(sprintf('%s/b%d',CB,ct-1),'Port',int2str(2*ct))
            set_param(sprintf('%s/a%d',CB,ct-Offset),'Port',int2str(2*ct+1))
         end
      end
      % Update mask display
      if Ts==0
         switch N
            case 1
               num = 'b_0 + b_1 s';
               den = 'a_0 + s';
            case 2
               num = 'b_0 + b_1 s + b_2 s^2';
               den = 'a_0 + a_1 s + s^2';
            case 3
               num = 'b_0 + b_1 s + b_2 s^2 + b_3 s^3';
               den = 'a_0 + a_1 s + a_2 s^2 + s^3';
            otherwise
               num = sprintf('b_0 + ... + b_{%d} s^{%d} + b_{%d} s^{%d}',N-1,N-1,N,N);
               den = sprintf('a_0 + ... + a_{%d} s^{%d} + s^{%d}',N-1,N-1,N);
         end
      else
         switch N
            case 1
               num = 'b_0 + b_1 z^{-1}';
               den = '1 + a_1 z^{-1}';
            case 2
               num = 'b_0 + b_1 z^{-1} + b_2 z^{-2}';
               den = '1 + a_1 z^{-1} + a_2 z^{-2}';
            case 3
               num = 'b_0 + b_1 z^{-1} + b_2 z^{-2} + b_3 z^{-3}';
               den = '1 + a_1 z^{-1} + a_2 z^{-2} + a_3 z^{-3}';
            otherwise
               num = sprintf('b_0 + b_1 z^{-1} + ... + b_{%d} z^{-%d}',N,N);
               den = sprintf('1 + a_1 z^{-1} + ... + a_{%d} z^{-%d}',N,N);
         end
      end
      dx = min(0.8,0.35+(N-1)*0.2)/2;
      MD = ["port_label('input'," + (2:2:2*(N+1)) + ",'b" + (0:N) + "');" ...
         "port_label('input'," + (3:2:2*(N+1)) + ",'a" + ((1:N)-(Ts==0)) + "');" ...
         "plot(" + mat2str([0.5-dx,0.5+dx]) + ",[0.5 0.5])" ...
         "text(0.5, 0.7, '" + num + "' ,..." ...
         "        'horizontalAlignment', 'center','texmode','on')" ...
         "text(0.5, 0.3, '" + den + "' ,..." ...
         "        'horizontalAlignment', 'center','texmode','on')"];
      set_param(CB,'MaskDisplay',sprintf('%s\n',MD))
      
   case 'ss'
      OutputX = varargin{1};
      OutputDX = varargin{2};
      if OutputX
         PBT = find_system(CB,'FollowLinks','on','LookUnderMasks','all',...
            'SearchDepth',1,'Name','xTerm');
      else
         PBT = find_system(CB,'FollowLinks','on','LookUnderMasks','all',...
            'SearchDepth',1,'Name','x');
      end
      if ~isempty(PBT)
         Pos = get_param(PBT{1},'Position');
         delete_block(PBT{1})
         if OutputX
            add_block('built-in/Outport',sprintf('%s/%s',CB,'x'),...
               'Position',Pos,'Orientation','right')
         else
            add_block('built-in/Terminator',sprintf('%s/%s',CB,'xTerm'),...
               'Position',Pos,'Orientation','right')
         end
      end
      if OutputDX
         PBT = find_system(CB,'FollowLinks','on','LookUnderMasks','all',...
            'SearchDepth',1,'Name','dxTerm');
      else
         PBT = find_system(CB,'FollowLinks','on','LookUnderMasks','all',...
            'SearchDepth',1,'Name','dx');
      end
      if ~isempty(PBT)
         Pos = get_param(PBT{1},'Position');
         delete_block(PBT{1})
         if OutputDX
            add_block('built-in/Outport',sprintf('%s/%s',CB,'dx'),...
               'Position',Pos,'Orientation','right')
         else
            add_block('built-in/Terminator',sprintf('%s/%s',CB,'dxTerm'),...
               'Position',Pos,'Orientation','right')
         end
      end      
end