function varargout=mpc_constraints(Specs,p,n,deftype,off,moves,isDisplay)
% MPC_CONSTRAINTS Given the array of structures Specs (already checked by MPC_SPECS), 
%    convert data to matrix format
%
%    ManipulatedVariables:  varargout=[umin,umax,Vumin,Vumax,dumin,dumax,Vdumin,Vdumax,utarget,dset,dvalues]
%    OutputVariables: varargout=[ymin,ymax,Vymin,Vymax]

%    A. Bemporad
%    Copyright 2001-2020 The MathWorks, Inc.

% NOTE:  constraint values returned as dimensionless
if nargin == 6
    isDisplay = false;
end
amin=zeros(p,n);
amax=zeros(p,n);
Vmin=zeros(p,n);
Vmax=zeros(p,n);
if deftype=='u' 
   dumin=zeros(p,n);
   dumax=zeros(p,n);
   Vdumin=zeros(p,n);
   Vdumax=zeros(p,n);
   utarget=zeros(p,n);
   dset=zeros(n,1); 
   dvalues=[];
end

for i=1:n
   ScaleFactors = isfield(Specs(i), 'ScaleFactor');
   if ScaleFactors
       amin(:,i)=augment(Specs(i).Min/Specs(i).ScaleFactor,p);
       amax(:,i)=augment(Specs(i).Max/Specs(i).ScaleFactor,p);
   else
       amin(:,i)=augment(Specs(i).Min,p);
       amax(:,i)=augment(Specs(i).Max,p);
   end
   Vmin(:,i)=augment(Specs(i).MinECR,p);
   Vmax(:,i)=augment(Specs(i).MaxECR,p);
   
   if deftype=='u'
      if ScaleFactors
          dumin(:,i)=augment(Specs(i).RateMin/Specs(i).ScaleFactor,p);
          dumax(:,i)=augment(Specs(i).RateMax/Specs(i).ScaleFactor,p);
      else
          dumin(:,i)=augment(Specs(i).RateMin,p);
          dumax(:,i)=augment(Specs(i).RateMax,p);
      end
      Vdumin(:,i)=augment(Specs(i).RateMinECR,p);
      Vdumax(:,i)=augment(Specs(i).RateMaxECR,p);
      if ischar(Specs(i).Target) % Target='nominal'
          % Must inherit target from off=Model.Nominal.U(mvindex)
          % NOTE:  offset has already been scaled by caller
          utarget(:,i)=augment(off(i),p);
      else
          if ScaleFactors
              utarget(:,i)=augment(Specs(i).Target/Specs(i).ScaleFactor,p);
          else
              utarget(:,i)=augment(Specs(i).Target,p);
          end
      end
      
      % Handle discrete inputs. Discrete constraints are uniform over the
      % horizon. They can be tightened by having time-varying umin,umax though. 
      % dset(i) = how many values u(i) can take.
      % dvalues collects the cascade of the values u(1),u(2),...,u(nmv) can
      % take. Values are treated as uniform over the horizon.
      if ischar(Specs(i).Type)
          switch Specs(i).Type
              case 'continuous'
                  dset(i)=0;
              case 'binary'
                  dset(i)=2; % only two values allowed
                  %dvalues=[dvalues;0-off(i);(1-off(i))/Specs(i).ScaleFactor];
                  dvalues=[dvalues;0;1/Specs(i).ScaleFactor]; %#ok<*AGROW> 
                  % Note: scaled input offset have not been subtracted from dvalues
              case 'integer'
                  mm=[0 cumsum(moves(1:end-1))]+1;
                  minp=min(amin(mm,i));
                  maxp=max(amax(mm,i));
                  if isinf(minp) || isinf(maxp)
                      % constraints on free integer variables must be bounded
                      if isDisplay
                          ctrlMsgUtils.warning('MPC:utility:IntegerRangeInf',i);
                          dset(i) = 0;
                      else
                          ctrlMsgUtils.error('MPC:utility:IntegerRangeInf',i);
                      end
                  else
                      values=(ceil(minp*Specs(i).ScaleFactor):floor(maxp*Specs(i).ScaleFactor))/Specs(i).ScaleFactor;
                      % possible redundant values will be ruled out by the upper 
                      % and lower bounds imposed in the optimization problem.
                      dset(i)=numel(values);
                      dvalues=[dvalues;values(:)];
                  end
          end
      else
          % finite set
          vset=sort(unique(Specs(i).Type(:))); % sort and remove redundant elements
          dset(i)=numel(vset);
          dvalues=[dvalues;vset/Specs(i).ScaleFactor]; 
          % Note: scaled input offset have not been subtracted from dvalues
      end
   end
end

% utarget must be p-by-nu and then put in one column      
if deftype=='u'
   utarget=utarget'-kron(ones(p,1),off(:)')';    
   utarget=utarget(:); 
end   

% InputSpecs:  varargout=[umin,umax,Vumin,Vumax,dumin,dumax,Vdumin,Vdumax,utarget,dset,dvalues]
off=kron(ones(p,1),off(:)');

switch deftype
case 'u'
   varargout={amin-off,amax-off,Vmin,Vmax,dumin,dumax,Vdumin,Vdumax,utarget,dset,dvalues}; 
case 'y'   
   % OutputSpecs: varargout=[ymin,ymax,Vymin,Vymax]
   varargout={amin-off,amax-off,Vmin,Vmax}; 
end


function b=augment(a,p)

% Possibly augment to size=p by copying the last entry
a=a(:);
nrow=length(a);

%newamin=[amin;kron(ones(p-nrow,1),amin(nrow,:))];    
b=[a;ones(p-nrow,1)*a(nrow)];    
