function [mempc_u0, mempc_status, mempc_region, mempc_state, mempc_ts] = mempcblock_MaskInitFcnResize(blk)
% MPC_BLOCK_RESIZE_MULTIPLE Modify the I/O structure depending on whether
% MDs, and time-varying LIMITS are present.

%   Author: A. Bemporad
%   Copyright 1986-2016 The MathWorks, Inc.


% mpc controllers
names = strtrim(get_param(blk,'empcobjs'));
if isempty(names)
    return
end
try
    % Load from workspace
    currempcobjs = slResolve(names,blk);
catch ME
    throw(ME);
end
currempcobjs = controllib.internal.util.hString2Char(currempcobjs);
if iscell(currempcobjs)
    if all(cellfun('isclass',currempcobjs,'char'))
        % names is a cell array of strings, each represents an empc variable
        IsObjectMPC = false;
    elseif all(cellfun('isclass',currempcobjs,'explicitMPC'))
        % names is a variable that represents a cell array of empc objects
        IsObjectMPC = true;
    else
        ctrlMsgUtils.error('MPC:designtool:BadCellArrayController');
    end    
else
    ctrlMsgUtils.error('MPC:designtool:BadCellArrayController');
end    
% initial states
states = strtrim(get_param(blk,'x0s'));
try
    % Load from workspace
    currmpcx0s = slResolve(states,blk);
catch ME
    throw(ME);
end
currmpcx0s = controllib.internal.util.hString2Char(currmpcx0s);
if iscell(currmpcx0s)
    if all(cellfun('isclass',currmpcx0s,'char')) || all(cellfun('isempty',currmpcx0s))
        % names is a cell array of strings, each represents a state variable or []
        IsObjectState = false;
    elseif all(cellfun('isclass',currmpcx0s,'mpcstate'))
        % names is a variable that represents a cell array of mpcstate objects
        IsObjectState = true;
    else
        ctrlMsgUtils.error('MPC:designtool:BadCellArrayState');
    end    
else
    ctrlMsgUtils.error('MPC:designtool:BadCellArrayState');
end    
% others
currmpcnmv = eval(get_param(blk,'nmv'));
currmpcnmd = eval(get_param(blk,'nmd'));
currmpcnts = eval(get_param(blk,'nts'));
currmpcstateest = get_param(blk,'state_inport_multiple');
currmpcdatatype = get_param(blk,'BlockDataType_multiple');
currmpcsampletimeinherited = get_param(blk,'SampleTimeInherited_multiple');

%% Obtain current availability of the optional in/out ports in the block
AvailableBlocks = get_param(blk,'Blocks');
type_in_block = ismember({'md','ext.mv'},AvailableBlocks);
type_out_block = ismember({'status','region','est.state'},AvailableBlocks);

%% Obtain availability of the optional in/out ports from the block dialog
type_in_mask = [...
    strcmp(get_param(blk,'md_inport_multiple'),'on'), ...
    strcmp(get_param(blk,'mv_inport_multiple'),'on'),...
    ];
type_out_mask = [...
    strcmp(get_param(blk,'return_status_multiple'),'on'), ...
    strcmp(get_param(blk,'return_region_multiple'),'on'), ...
    strcmp(get_param(blk,'return_state_multiple'),'on'), ...
    ];

%% Update MO or X
maskdisplay=get_param(blk,'MaskDisplay');
CustomEstimation = strcmp(get_param(blk,'state_inport_multiple'),'on');
if CustomEstimation
    label = '''mo''';
    pos = strfind(maskdisplay, label);
    if ~isempty(pos)
        % Change mo inport to x inport
        maskdisplay = [maskdisplay(1:pos-1),'''x[k|k]''',maskdisplay(pos+length(label):end)];
    end
else
    label = '''x[k|k]''';
    pos = strfind(maskdisplay, label);
    if ~isempty(pos)
        % Change x inport to mo inport
        maskdisplay = [maskdisplay(1:pos-1),'''mo''',maskdisplay(pos+length(label):end)];
    end
end

%% Reset mask display to two inports (MO, REF) and one outport (MV)
aux='port_label(''input'', 3, ''ref'')';
pos=strfind(maskdisplay,aux)+length(aux);
maskdisplay=maskdisplay(1:pos-1);
nextInport = 4;
nextOutport = 2;

%% Update MD
[maskdisplay, nextInport] = update_inport(blk,AvailableBlocks,type_in_mask,type_in_block,1,'md',nextInport,3,maskdisplay);

%% Update EXT.MV
if type_in_mask(2)
    if ~type_in_block(2)
        % add inport
        delete_line(blk,'u[k-1]/1','Goto 4/1');
        add_block([blk '/ref'],[blk '/ext.mv']);
        add_line(blk,'ext.mv/1','Goto 4/1');
        set_param([blk '/ext.mv'],'position',[870   595   890   615],'orientation','left','port',int2str(nextInport));
    end
    set_param([blk '/ext.mv'],'icondisplay','port number');
    maskdisplay=[maskdisplay char(10) 'port_label(''input'', ' num2str(nextInport) ', ''ext.mv'')'];
    nextInport = nextInport + 1; %#ok<NASGU>
else
    if type_in_block(2)
        % remove inport
        if ismember('ext.mv',AvailableBlocks)
            delete_line(blk,'ext.mv/1','Goto 4/1');
            delete_block([blk '/ext.mv']);
            add_line(blk,'u[k-1]/1','Goto 4/1','autorouting','on');
        end
    end
end

%% Update STATUS
[maskdisplay, nextOutport] = update_outport(blk,AvailableBlocks,type_out_mask,type_out_block,1,'status',nextOutport,maskdisplay);

%% Update REGION
[maskdisplay, nextOutport] = update_outport(blk,AvailableBlocks,type_out_mask,type_out_block,2,'region',nextOutport,maskdisplay);

%% Update Estimated State
[maskdisplay, nextOutport] = update_outport(blk,AvailableBlocks,type_out_mask,type_out_block,3,'est.state',nextOutport,maskdisplay); %#ok<ASGLU>

%% Update Mask Display
set_param(blk,'MaskDisplay',maskdisplay);

%% Obtain MPC1 position
position = get_param([blk '/MPC1'],'Position');

%% Now set correct dimensions for zero signals
% if any MPC controller is defined in the MMPC block, try to get it from
% base workspace. If failed, use defaults.  If no controller is defined,
% use defaults.
[Successful, nmv, nmd, mempc_ts, nx] = mempc_getdimensions_multiple(blk);
if ~Successful
    nmv = currmpcnmv;
    nmd = currmpcnmd;
    mempc_ts = currmpcnts;
end
DataType = get_param(blk,'BlockDataType_multiple');
mempc_u0 = zeros(nmv,1,DataType);
mempc_status = zeros(1,1,DataType);
mempc_region = zeros(1,1,DataType);
mempc_state = zeros(nx,1,DataType);
if strcmp(get_param(blk,'SampleTimeInherited_multiple'),'on')
    mempc_ts = -1;
end
AvailableBlocks = get_param(blk,'Blocks');
if ismember('md_zero',AvailableBlocks)
    set_param([blk '/md_zero'],'Value',sprintf('zeros(%d,1)',max(nmd,1)));
end

%% Add or remove MPC blocks if the number of MPC controllers is changed
% Required number of MPC blocks from mask parameter
N = length(currempcobjs);
% Current number of MPC blocks in the block
oldN = str2double(get_param([blk '/mv_switch'],'Inputs'))-1; % the last input the
% detect whether a MPC block should be added or deleted
dN = N-oldN;
% Because of the Add/Delete buttons, dN = N-oldN is either 0, 1 or -1. But
% the code below works for any dN.
if dN>0
    %% add MPC controllers
    addMPCBlock(blk, N, oldN, position)
elseif dN<0
    %% remove MPC controllers
    theEMPC = str2double(get_param(blk,'theEMPC'));
    if theEMPC>0
        % delete from delete button
        % Delete MPC controllers indexed by 'theEMPC'
        deleteMPCBlock(blk, theEMPC, N, oldN, position);
    else
        % delete blocks from canceling dialog
        for ct = N:-1:N+dN+1
            deleteMPCBlock(blk, ct, N, oldN, position);
        end
    end
else
    update_eststate(blk, oldN);
end

%% Update mask parameters for each MPC block based on mempc values
% we only loop through minimum of N, length of mpcobjs and length of x0s to
% avoid set_param failure (which does not generate hard error).  In normal
% cases these values are the same.  When 'add' button is clicked, N is
% updated first, then mocobjs, then x0s so at different moments the values
% are different.  In the end they will be the same after all the set_param
% references are executed in the 'add' button callback.
for ct = 1:min(min(N,length(currempcobjs)),length(currmpcx0s))
    empcblk = [blk '/MPC' num2str(ct)];
    if IsObjectMPC
        mpcname = [names '{' num2str(ct) '}'];
    else
        mpcname = currempcobjs{ct};
    end
    if IsObjectState
        statename = [states '{' num2str(ct) '}'];
    else
        if isempty(currmpcx0s{ct})
            statename = '[]';
        else
            statename = currmpcx0s{ct};
        end
    end
    set_param(empcblk,'empcobj',mpcname,...
        'x0',statename,...
        'state_inport',currmpcstateest,...
        'BlockDataType',currmpcdatatype,...
        'SampleTimeInherited',currmpcsampletimeinherited);
end
%% End of main function

function delete_inport(blk,name,outport)
inportblk = [blk '/' name];
constantblk = [blk '/' name '_zero'];
delete_line(blk,[name '/1'],outport);
position = get_param(inportblk,'position');
delete_block(inportblk);
add_block('built-in/Constant',constantblk);
add_line(blk,[name '_zero/1'],outport);
set_param(constantblk,'position',position,'orientation','left','OutDataTypeStr','BlockDataType_multiple');

function add_inport(blk,name,port,outport)
inportblk = [blk '/' name];
constantblk = [blk '/' name '_zero'];
delete_line(blk,[name '_zero/1'],outport);
position = get_param(constantblk,'position');
delete_block(constantblk);
add_block([blk '/ref'],inportblk);
add_line(blk,[name '/1'],outport);
set_param(inportblk,'position',position,'orientation','left','port',num2str(port));

function [maskdisplay, port] = update_inport(blk,AvailableBlocks,type_in_mask,type_in_block,index,name,port,outport,maskdisplay)
if type_in_mask(index)
    if ~type_in_block(index)
        add_inport(blk,name,port,['Goto ' num2str(outport) '/1']);
    end
    set_param([blk '/' name],'icondisplay','port number');
    maskdisplay=[maskdisplay char(10) 'port_label(''input'', ' num2str(port) ', ''' name ''')'];
    port = port + 1;
else
    if type_in_block(index)
        if ismember(name,AvailableBlocks)
            delete_inport(blk,name,['Goto ' num2str(outport) '/1']);
        end
    end
end

function delete_outport(blk,name,inport)
outportblk = [blk '/' name];
terminatorblk = [blk '/' name '_terminator'];
delete_line(blk,inport,[name '/1']);
position = get_param(outportblk,'position');
delete_block(outportblk);
add_block('built-in/Terminator',terminatorblk);
add_line(blk,inport,[name '_terminator/1']);
set_param(terminatorblk,'position',position,'Orientation','left');

function add_outport(blk,name,port,inport)
outportblk = [blk '/' name];
terminatorblk = [blk '/' name '_terminator'];
delete_line(blk,inport,[name '_terminator/1']);
position = get_param(terminatorblk,'position');
delete_block(terminatorblk);
add_block([blk '/mv'],outportblk);
add_line(blk,inport,[name '/1']);
set_param(outportblk,'position',position,'orientation','left','port',int2str(port));

function [maskdisplay, port] = update_outport(blk,AvailableBlocks,type_out_mask,type_out_block,index,name,port,maskdisplay)
if type_out_mask(index)
    if ~type_out_block(index)
        add_outport(blk,name,port,[name '_switch/1']);
    end
    set_param([blk '/' name],'icondisplay','port number');
    maskdisplay=[maskdisplay char(10) 'port_label(''output'', ' num2str(port) ', ''' name ''')'];
    port = port + 1;
else
    if type_out_block(index)
        if ismember(name,AvailableBlocks)
            delete_outport(blk,name,[name '_switch/1']);
        end
    end
end

function update_eststate(blk, oldN)
if strcmp(get_param(blk,'return_state_multiple'),'on')
    % if estimated states outport enabled, replace constant with mpc signal
    for ct=1:oldN
        try
            delete_line(blk,'est.state_zero/1',['est.state_switch/',num2str(ct+1)]);
        end
        try
            add_line(blk,['MPC' num2str(ct) '/4'],['est.state_switch/',num2str(ct+1)],'autorouting','on');
        end
    end
else
    % if estimated states outport disabled, replace mpc signal with constant
    for ct=1:oldN
        try
            delete_line(blk,['MPC' num2str(ct) '/4'],['est.state_switch/',num2str(ct+1)]);
        end
        try
            add_line(blk,'est.state_zero/1',['est.state_switch/',num2str(ct+1)],'autorouting','on');
        end
    end
end


%---------------
function addMPCBlock(blk, N, oldN, position)
% delete the default signal from the old last data port
delete_line(blk,'u[k-1]/1',['mv_switch/' num2str(oldN+2)]);
delete_line(blk,'status_zero/1',['status_switch/' num2str(oldN+2)]);
delete_line(blk,'region_zero/1',['region_switch/' num2str(oldN+2)]);
delete_line(blk,'est.state_zero/1',['est.state_switch/' num2str(oldN+2)]);
delete_line(blk,'u0_zero/1',['u0_switch/' num2str(oldN+2)]);
% change number of inputs
set_param([blk '/mv_switch'],'Inputs',num2str(N+1)); % N>1
set_param([blk '/status_switch'],'Inputs',num2str(N+1)); % N>1
set_param([blk '/region_switch'],'Inputs',num2str(N+1)); % N>1
set_param([blk '/est.state_switch'],'Inputs',num2str(N+1)); % N>1
set_param([blk '/u0_switch'],'Inputs',num2str(N+1)); % N>1
% add the default signal to the new last data port
add_line(blk,'u[k-1]/1',['mv_switch/' num2str(N+2)],'autorouting','on');
add_line(blk,'status_zero/1',['status_switch/' num2str(N+2)],'autorouting','on');
add_line(blk,'region_zero/1',['region_switch/' num2str(N+2)],'autorouting','on');
add_line(blk,'est.state_zero/1',['est.state_switch/' num2str(N+2)],'autorouting','on');
add_line(blk,'u0_zero/1',['u0_switch/' num2str(N+2)],'autorouting','on');
% Create dN new MPC controllers
for i=oldN+1:N
    % Copy MPC block MPC1 into a new one
    MPCname = sprintf('MPC%d',i);
    add_block([blk '/MPC1'],[blk '/' MPCname]);
    set_param([blk '/' MPCname],'Position',position+10*(i-1),'enable_value',num2str(i),'empcobj','');
    % Connect from MPC block
    % 1
    add_line(blk,[MPCname '/1'],['mv_switch/',num2str(i+1)],'autorouting','on');
    % 2
    add_line(blk,[MPCname '/2'],['status_switch/',num2str(i+1)],'autorouting','on');
    % 3
    add_line(blk,[MPCname '/3'],['region_switch/',num2str(i+1)],'autorouting','on');    
    % 4: special treatment for est.state
    % add terminator block
    add_block([blk '/est.state_MPC1'],[blk '/est.state_' MPCname]);
    add_line(blk,[MPCname '/4'],['est.state_' MPCname '/1'],'autorouting','on');
    % connect mpc or constant to switch
    if strcmp(get_param(blk,'return_state_multiple'),'on')
        add_line(blk,[MPCname '/4'],['est.state_switch/',num2str(i+1)],'autorouting','on');
    else
        add_line(blk,'est.state_zero/1',['est.state_switch/',num2str(i+1)],'autorouting','on');
    end
    % 5
    add_line(blk,[MPCname '/5'],['u0_switch/',num2str(i+1)],'autorouting','on');
    % Connect to MPC block
    for j=1:5
        add_line(blk,sprintf('From %d/1',j),[MPCname sprintf('/%d',j)],'autorouting','on');
    end
end

function deleteMPCBlock(blk, theEMPC, N, oldN, position)
% which block to delete
MPCname = sprintf('MPC%d',theEMPC);
% delete connections to MPC block
for j=1:14
    delete_line(blk,sprintf('From %d/1',j),[MPCname sprintf('/%d',j)]);
end
% Delete connections from MPC block
% 1
delete_line(blk,[MPCname '/1'],['mv_switch/' num2str(theEMPC+1)]);
% 2
delete_line(blk,[MPCname '/2'],['status_switch/' num2str(theEMPC+1)]);
% 3
delete_line(blk,[MPCname '/3'],['region_switch/' num2str(theEMPC+1)]);
% 4: special treatment for est.state
% delete terminator block
delete_line(blk,[MPCname '/4'],['est.state_' MPCname '/1']);
delete_block([blk '/est.state_' MPCname]);
% delete line from either mpc or constant
if strcmp(get_param(blk,'return_state_multiple'),'on')
    delete_line(blk,[MPCname '/4'],['est.state_switch/',num2str(theEMPC+1)]);
else
    delete_line(blk,'est.state_zero/1',['est.state_switch/',num2str(theEMPC+1)]);
end
% 5
delete_line(blk,[MPCname '/5'],['u0_switch/' num2str(theEMPC+1)]);
% Delete MPC block
delete_block([blk '/' MPCname]);
% Rename other MPC blocks from theEMPC+1 to the last one and remove
% connection to switches
for i = theEMPC+1:oldN
    % block to be renamed
    MPCname = sprintf('MPC%d',i);
    % 1
    delete_line(blk,[MPCname '/1'],['mv_switch/' num2str(i+1)]);
    % 2
    delete_line(blk,[MPCname '/2'],['status_switch/' num2str(i+1)]);
    % 3
    delete_line(blk,[MPCname '/3'],['region_switch/' num2str(i+1)]);
    % 4: delete line from either mpc or constant to est.state switch
    if strcmp(get_param(blk,'return_state_multiple'),'on')
        delete_line(blk,[MPCname '/4'],['est.state_switch/',num2str(i+1)]);
    else
        delete_line(blk,'est.state_zero/1',['est.state_switch/',num2str(i+1)]);
    end
    % 5
    delete_line(blk,[MPCname '/5'],['u0_switch/' num2str(i+1)]);
    % name
    newMPCname = sprintf('MPC%d',i-1); % new name
    set_param([blk '/' MPCname],'Name',newMPCname);
    % rename est.state terminator block
    set_param([blk '/est.state_' MPCname],'Name',['est.state_' newMPCname]);
end
% delete the default signal from the old last data port
delete_line(blk,'u[k-1]/1',['mv_switch/' num2str(oldN+2)]);
delete_line(blk,'status_zero/1',['status_switch/' num2str(oldN+2)]);
delete_line(blk,'region_zero/1',['region_switch/' num2str(oldN+2)]);
delete_line(blk,'est.state_zero/1',['est.state_switch/' num2str(oldN+2)]);
delete_line(blk,'u0_zero/1',['u0_switch/' num2str(oldN+2)]);
% change number of inputs
set_param([blk '/mv_switch'],'Inputs',num2str(N+1)); % N>1
set_param([blk '/status_switch'],'Inputs',num2str(N+1)); % N>1
set_param([blk '/region_switch'],'Inputs',num2str(N+1)); % N>1
set_param([blk '/est.state_switch'],'Inputs',num2str(N+1)); % N>1
set_param([blk '/u0_switch'],'Inputs',num2str(N+1)); % N>1
% add the default signal to the new last data port
add_line(blk,'u[k-1]/1',['mv_switch/' num2str(N+2)],'autorouting','on');
add_line(blk,'status_zero/1',['status_switch/' num2str(N+2)],'autorouting','on');
add_line(blk,'region_zero/1',['region_switch/' num2str(N+2)],'autorouting','on');
add_line(blk,'est.state_zero/1',['est.state_switch/' num2str(N+2)],'autorouting','on');
add_line(blk,'u0_zero/1',['u0_switch/' num2str(N+2)],'autorouting','on');
% Reconnect remaining MPC blocks from theEMPC+1 to the last one.
% Parameter 'enable_value' is updated by the mask function
for i = theEMPC:N
    % block to be reconnected
    MPCname = sprintf('MPC%d',i);
    % 1
    add_line(blk,[MPCname '/1'],['mv_switch/' num2str(i+1)],'autorouting','on');
    % 2
    add_line(blk,[MPCname '/2'],['status_switch/',num2str(i+1)],'autorouting','on');
    % 3
    add_line(blk,[MPCname '/3'],['region_switch/',num2str(i+1)],'autorouting','on');
    % 4: add line from either mpc or constant to switch
    if strcmp(get_param(blk,'return_state_multiple'),'on')
        add_line(blk,[MPCname '/4'],['est.state_switch/',num2str(i+1)],'autorouting','on');
    else
        add_line(blk,'est.state_zero/1',['est.state_switch/',num2str(i+1)],'autorouting','on');
    end
    % 5
    add_line(blk,[MPCname '/5'],['u0_switch/',num2str(i+1)],'autorouting','on');
end
% Adjust enable_value and position of all MPC blocks in diagram
for i=1:N
    MPCname=sprintf('MPC%d',i);
    set_param([blk '/' MPCname],'enable_value',num2str(i),'Position',position+10*(i-1));
end



function [Successful, nmv, nmd, ts, nx] = mempc_getdimensions_multiple(mempcblock)
% Obtain nmv, nmo, nmd and Ts from existing MPC object in a MMPC block.

%   Author: A. Bemporad
%   Copyright 1986-2016 The MathWorks, Inc.

empcobjs=get_param(mempcblock,'empcobjs'); % String containing MPC object names
ifind=strfind(empcobjs,'''');
Successful = false;
for ct=1:length(ifind)/2
    empcname = empcobjs(ifind(2*(ct-1)+1)+1:ifind(2*ct)-1);
    if ~isempty(empcname)
        empc = [];
        try %#ok<*TRYNC>
            empc = slResolve(empcname, mempcblock);
        end
        if isa(empc,'explicitMPC') && ~isempty(empc)
            Successful = true;
            break;
        end
    end
end
if Successful
    EMPCData=getExplicitMPCData(empc);
    nmd = EMPCData.nmd;
    nmv = EMPCData.nmv;
    ts = EMPCData.Ts;
    nx = EMPCData.nx;
else
    nmd = 1;
    nmv = 1;
    ts = 1;
    nx = 1;
end
