function [runtimedata, userdata, z0] = znlmpc_generateRuntimeData(...
    coredata, x, lastMV, ref0, MVTarget0, md0, ...
    OutputWeights0, MVWeights0, MVRateWeights0, ECRWeight0, ...
    OutputMin0, OutputMax0, StateMin0, StateMax0, ...
    MVMin0, MVMax0, MVRateMin0, MVRateMax0, ...
    Parameters0, X0, MV0, Slack0)
% generate runtime data, user data and handles structures as well as z0

%   Author: N. L. Ricker, R. Chen
%   Copyright 1986-2018 The MathWorks, Inc.

%#codegen
%% process "ref" to get p-by-ny OV references (unscaled, default to 0)
if isempty(ref0)
%     if coder.target('MATLAB')
%         fprintf('Assumed zero OV references as the "ref" signal is not specified.\n')
%     end
    ref = znlmpc_setRows(zeros(1,coredata.ny), coredata.p);
else
    validateattributes(ref0,{'double'},{'2d','nonnan','real','finite','ncols',coredata.ny},'','"ref"');
    ref = znlmpc_setRows(ref0, coredata.p);
end
%% process "MVTarget" to get p-by-nmv MV references (unscaled, default to 0)
if isempty(MVTarget0)
%     if coder.target('MATLAB')
%         fprintf('Assumed zero MV references as the "MVTarget" signal is not specified.\n')
%     end
    MVTarget = znlmpc_setRows(zeros(1,coredata.nmv), coredata.p);
else
    validateattributes(MVTarget0,{'double'},{'2d','nonnan','real','finite','ncols',coredata.nmv},'','"MVTarget"');
    MVTarget = znlmpc_setRows(MVTarget0, coredata.p);
end
MVTargetScaled = znlmpc_scaleRows(MVTarget, coredata.MVscale);
%% process "md" to get (p+1)-by-nmd MD signal (unscaled, default to 0)
% if no measured disturbance, "md" is [].
if coredata.nmd>0
    if isempty(md0)
%         if coder.target('MATLAB')
%             fprintf('Assumed zero measured disturbances as the "md" signal is not specified.\n')
%         end
        md =  znlmpc_setRows(zeros(1,coredata.nmd), coredata.p+1);
    else
        validateattributes(md0,{'double'},{'2d','nonnan','real','finite','ncols',coredata.nmd},'','"md"');
        md =  znlmpc_setRows(md0, coredata.p+1);
    end
else
    md = [];
end
%% process weights to get p-by-(ny/nmv) weights (default to coredata)
OutputWeights = getWeights(coredata, OutputWeights0, 'OutputWeights', coredata.ny);
MVWeights = getWeights(coredata, MVWeights0, 'MVWeights', coredata.nmv);
MVRateWeights = getWeights(coredata, MVRateWeights0, 'MVRateWeights', coredata.nmv);
ECRWeight = getECRWeight(coredata, ECRWeight0);
%% process constraints to get p-by-(ny/nx/nmv) constraints (scaled, default to coredata)
% we don't check conflict between Min and Max at runtime
OutputMin = getConstraints(coredata, OutputMin0, 'OutputMin', coredata.ny, coredata.Yscale);
OutputMax = getConstraints(coredata, OutputMax0, 'OutputMax', coredata.ny, coredata.Yscale);
StateMin = getConstraints(coredata, StateMin0, 'StateMin', coredata.nx, coredata.Xscale);
StateMax = getConstraints(coredata, StateMax0, 'StateMax', coredata.nx, coredata.Xscale);
MVMin = getConstraints(coredata, MVMin0, 'MVMin', coredata.nmv, coredata.MVscale);
MVMax = getConstraints(coredata, MVMax0, 'MVMax', coredata.nmv, coredata.MVscale);
MVRateMin = getConstraints(coredata, MVRateMin0, 'MVRateMin', coredata.nmv, coredata.MVscale);
MVRateMax = getConstraints(coredata, MVRateMax0, 'MVRateMax', coredata.nmv, coredata.MVscale);
%% process parameters to get a cell vector with npara elements
if coredata.npara == 0
    Parameters = {};
else
    validateattributes(Parameters0,{'cell'},{'vector','nonempty','numel',coredata.npara},'','"Parameters"');
    Parameters = Parameters0;
end
%% process X0 to get p-by-nx (unscaled, default to x)
initX = getInitialX(coredata, X0, x);
%% process MV0 to get p-by-nmv (unscaled, default to lastMV)
initMV = getInitialMV(coredata, MV0, lastMV);
%% process Slack0 (default to 0)
initE = getInitialSlack(Slack0);
%% generate initial guess of decision variables (scaled)
z0 = znlmpc_setDecisions(coredata, initX, initMV, initE);
%% construct userdata used by custom cost and constraint functions
userdata = struct(...
    'Ts', coredata.Ts, ...
    'CurrentStates', x, ...
    'LastMV', lastMV, ...
    'References', ref, ...
    'MVTarget', MVTarget, ...
    'PredictionHorizon',coredata.p,...
    'NumOfStates',coredata.nx,...
    'NumOfOutputs',coredata.ny,...
    'NumOfInputs',coredata.nu,...
    'MVIndex',coredata.imv,...
    'MDIndex',coredata.imd,...
    'UDIndex',coredata.iud);
%% construct runtimedata
runtimedata = struct(...
    'x', x, ...
    'lastMV', lastMV, ...
    'ref', ref, ...
    'md', md, ...
    'OutputWeights', OutputWeights, ...
    'MVWeights', MVWeights, ...
    'MVRateWeights', MVRateWeights, ...
    'ECRWeight', ECRWeight, ...
    'OutputMin', OutputMin, ...
    'OutputMax', OutputMax, ...
    'StateMin', StateMin, ...
    'StateMax', StateMax, ...
    'MVMin', MVMin, ...
    'MVMax', MVMax, ...
    'MVRateMin', MVRateMin, ...
    'MVRateMax', MVRateMax, ...
    'MVScaledTarget', MVTargetScaled, ...
    'Parameters', {Parameters});

function val = getWeights(coredata, weights, type, n)
if ~isempty(weights)
    validateattributes(weights,{'double'},{'real','finite','nonnegative','nonnan','ncols',n},'',['online ' type]);
    val = znlmpc_setRows(weights, coredata.p);
else
    val = coredata.(type);
end

function val = getECRWeight(coredata, weights)
if ~isempty(weights)
    validateattributes(weights,{'double'},{'real','finite','positive','nonnan','scalar'},'','online ECRWeight');
    val = weights;
else
    val = coredata.ECRWeight;
end

function val = getConstraints(coredata, constraints, type, n, scale)
if ~isempty(constraints)
    if contains(type,'RateMin')
        validateattributes(constraints,{'double'},{'real','nonnan','ncols',n,'<=',0},'',['online ' type]);
    elseif contains(type,'RateMax')
        validateattributes(constraints,{'double'},{'real','nonnan','ncols',n,'>=',0},'',['online ' type]);
    else
        validateattributes(constraints,{'double'},{'real','nonnan','ncols',n},'',['online ' type]);
    end
    val = znlmpc_setRows(constraints, coredata.p);
    val = val./repmat(scale,1,coredata.p)';
else
    val = coredata.(type);
end

function val = getInitialX(coredata, X0, x)
if ~isempty(X0)
    validateattributes(X0,{'double'},{'2d','real','finite','nonnan','ncols',coredata.nx},'','"X0"');
    val = znlmpc_setRows(X0, coredata.p);
else
    % if not specified, use current x as initial guess
    val = znlmpc_setRows(x',coredata.p);
end    

function val = getInitialMV(coredata, MV0, lastMV)
if ~isempty(MV0)
    validateattributes(MV0,{'double'},{'2d','real','finite','nonnan','ncols',coredata.nmv},'','"MV0"');
    val = znlmpc_setRows(MV0, coredata.p);
else
    % if not specified, use lastMV as initial guess
    val = znlmpc_setRows(lastMV',coredata.p);
end    

function val = getInitialSlack(Slack0)
if ~isempty(Slack0)
    validateattributes(Slack0,{'double'},{'real','finite','nonnegative','scalar'},'','"Slack0"');
    val = Slack0;
else
    % if not specified, use 0
    val = 0;
end

