function SampleTime = maskInitialization(blk,Ts)
% Mask initialization function for the PID Controller 1dof and 2dof blocks.
%
% Inputs:
%       blk: the masked subsystem of the PID 1dof or 2dof block

%   Author: A. Zimmerman
%   Copyright 2017-2021 The MathWorks, Inc.

blkObj = get_param(blk,'Object');
sys = getfullname(blk);

% Get Dialog Settings
dlgSett = pidpack.PIDConfig.utilGetDialogSettings(blk);

% Set Sample Time
if ~dlgSett.isDialogDiscrete || dlgSett.isDialogExternalTs
    SampleTime = -1;
else
    SampleTime = Ts;
end

% Do not run initialization code if model is running
invalidStatus = {'external','running','compiled','paused','terminating'};
if any(strcmpi(get_param(bdroot(blk),'SimulationStatus'),invalidStatus))
    return
end

% Error if model is in synchronous semantics
if strcmp(blkObj.IsInSynchronousDomain,'on') && dlgSett.TimeDomain == 1
        errId = message('Simulink:StateConfigurator:TimeDomainNotSupportedByHardwareSemantics', blkObj.getFullName);
        error(errId)
end

% Error Checking for Sample Time
if (dlgSett.TimeDomain == 2) && ~isempty(Ts)
    if ~(Ts == -1 || Ts > 0)
        if Ts==0
            errId = message('Simulink:SampleTime:InvTsParamSetting_No_Continuous',sys,'SampleTime');
        else
            errId = message('Simulink:SampleTime:InvTsParamSetting_Vector',sys,'SampleTime');
        end
        error(errId)
    end
end

%% Add/Remove Ports
if dlgSett.is2DOF
    port = 3;
    inport_names = {'P','I','D','N','b','c','Reset','I0','D0','UpperLimit','LowerLimit','TR','extTs'};
    % What is specified in the dialog (new)
    type_in_mask = [...
                (dlgSett.isDialogProportional && dlgSett.isDialogExternal), ...
                (dlgSett.isDialogIntegral && dlgSett.isDialogExternal), ...
                (dlgSett.isDialogDerivative && dlgSett.isDialogExternal), ...
                (dlgSett.isDialogFilter && dlgSett.isDialogExternal), ...
                (dlgSett.isDialogProportional && dlgSett.isDialogExternal), ...
                (dlgSett.isDialogDerivative && dlgSett.isDialogExternal),...
                dlgSett.isDialogExtReset, ...
                (dlgSett.isDialogExtICs && dlgSett.isDialogIntegral),...
                (dlgSett.isDialogExtICs && dlgSett.isDialogDerivative),...
                dlgSett.isDialogUseExternalSatLimits,...
                dlgSett.isDialogUseExternalSatLimits,...
                dlgSett.isDialogTracking,...
                dlgSett.isAddExternalTsPort,...
                ];
else
    port = 2;
    inport_names = {'P','I','D','N','Reset','I0','D0','UpperLimit','LowerLimit','TR','extTs'};
    % What is specified in the dialog (new)
    type_in_mask = [...
                (dlgSett.isDialogProportional && dlgSett.isDialogExternal), ...
                (dlgSett.isDialogIntegral && dlgSett.isDialogExternal), ...
                (dlgSett.isDialogDerivative && dlgSett.isDialogExternal), ...
                (dlgSett.isDialogFilter && dlgSett.isDialogExternal), ...
                dlgSett.isDialogExtReset, ...
                (dlgSett.isDialogExtICs && dlgSett.isDialogIntegral),...
                (dlgSett.isDialogExtICs && dlgSett.isDialogDerivative),...
                dlgSett.isDialogUseExternalSatLimits,...
                dlgSett.isDialogUseExternalSatLimits,...
                dlgSett.isDialogTracking,...
                dlgSett.isAddExternalTsPort,...
                ];
end
AvailableBlocks = get_param(blk,'Blocks');
type_in_block = ismember(inport_names,AvailableBlocks);

% Update Inports
for k=1:length(type_in_mask)
    port = pidpack.PIDConfig.utilUpdateInport(blk,AvailableBlocks,type_in_mask,type_in_block,k,inport_names{k},port);
end

%% Configure Variants
if ~(startsWith(sys,'simulink') || startsWith(sys,'slpidlib'))
    %If in library, don't select variants

    %% Select Appropriate Variants
    pidpack.PIDConfig.utilSelectVariants(sys,dlgSett);

    %% Select Variants for Feedback Features and Saturation
    pidpack.PIDConfig.utilSatWindupTracking(blk, dlgSett);
end

%% Use Set_Param for Integrator Method
if dlgSett.isDialogDiscrete && (dlgSett.isDialogIntegral || dlgSett.isDialogFilter)
    intBlocks = [strcat({sys},'/Integrator/Discrete/Integrator'); strcat({sys},'/Integrator/Continuous/Integrator')];
    if dlgSett.isDialogExternalTs
        set_param(intBlocks{1},'IntegratorMethod',['Accumulation: ' blkObj.IntegratorMethod]);
    else
        set_param(intBlocks{1},'IntegratorMethod',['Integration: ' blkObj.IntegratorMethod]);
    end
end

%% Init code from Discrete Derivative
if dlgSett.isDialogDiscrete
    if dlgSett.isDialogDifferentiator
        DiffOutDataTypeStr = blkObj.DifferentiatorOutDataTypeStr;
        if strcmp(DiffOutDataTypeStr, 'Inherit: Inherit via internal rule')
            diffDT   = 'Inherit: Inherit via back propagation';
            tsampDT  = 'Inherit: Inherit via internal rule';
            tsampImp = 'Offline Scaling Adjustment';
        else
            diffDT   = DiffOutDataTypeStr;
            tsampDT  = 'Inherit: Inherit via back propagation';
            tsampImp = 'Online Calculations';
        end
        set_param([sys,'/Filter/Differentiator/Diff'],'OutDataTypeStr',diffDT,'OutMin','DifferentiatorOutMin','OutMax','DifferentiatorOutMax');
        set_param([sys,'/Filter/Differentiator/Tsamp/Internal Ts/Tsamp'],'OutDataTypeStr',tsampDT,'TsampMathImp',tsampImp);
        set_param([sys,'/Filter/Differentiator/Y'],'OutMin','DifferentiatorOutMin','OutMax','DifferentiatorOutMax');
    else % Handle Integration vs Accumulation
        filterBlock = [sys, '/Filter/Disc. Forward Euler Filter/Filter'];
        if dlgSett.isDialogExternalTs
            set_param(filterBlock,'IntegratorMethod','Accumulation: Forward Euler');
        else
            set_param(filterBlock,'IntegratorMethod','Integration: Forward Euler');
        end
    end
end

%% Configure constant and data type conversion block settings
if dlgSett.isDialogClamping
    if dlgSett.ControllerParametersSource==1 && dlgSett.Form==1
        val = strrep(blkObj.PParamDataTypeStr,'Inherit: Inherit via internal rule','Inherit: Inherit via back propagation');
        val = strrep(val, 'Inherit: Same as input','Inherit: Inherit via back propagation');
        val = strrep(val, 'Inherit: Inherit from ''Gain''', 'Inherit: Inherit from ''Constant Value''');
        set_param([sys '/P Copy/Internal Parameters Ideal/P Copy'],'OutDataTypeStr',val)
    end
end

%% Configure External Saturation Blocks
if dlgSett.isDialogUseExternalSatLimits
    val = strrep(blkObj.SaturationOutDataTypeStr,'Inherit: Inherit via back propagation','Inherit: Same as second input');
    val = strrep(val, 'Inherit: Same as input','Inherit: Same as second input');
    set_param(sys,'ExternalSaturationOutDataTypeStr',val)
end


%% Configure all integrator blocks
if dlgSett.isDialogIntegral
    if dlgSett.isDialogDiscrete
        % Fix for Mask Editor Setting Empty Character to '[]'
        StateStorageTypeQual = blkObj.IntegratorRTWStateStorageTypeQualifier;
        if strcmp(StateStorageTypeQual,'[]')
           StateStorageTypeStr = '';
        else
           StateStorageTypeStr = StateStorageTypeQual;
        end
        
        % Storage Type and Class are deprecated, maintaining set_param here
        % to set values of DTI's to their default which is forced by
        % utilStateAttributes
        set_param([sys '/Integrator/Discrete/Integrator'],...
            'StateMustResolveToSignalObject',blkObj.IntegratorStateMustResolveToSignalObject,...
            'RTWStateStorageClass',blkObj.IntegratorRTWStateStorageClass,...
            'RTWStateStorageTypeQualifier',StateStorageTypeStr);
    end
end

filterBlocks = strcat({sys},'/Filter/',{'Differentiator/UD';'Cont. Filter/Filter';...
        'Disc. Forward Euler Filter/Filter';'Disc. Backward Euler Filter/Filter Differentiator TF';...
        'Disc. Trapezoidal Filter/Filter Differentiator TF'});
if dlgSett.isDialogFilter
    if dlgSett.isDialogDiscrete
        setBlocks = cell2mat(get_param(filterBlocks(3:end),'Handle'));
        
        % Fix for Mask Editor Setting Empty Character to '[]'
        StateStorageTypeQual = blkObj.FilterRTWStateStorageTypeQualifier;
        if strcmp(StateStorageTypeQual,'[]')
           StateStorageTypeStr = '';
        else
           StateStorageTypeStr = StateStorageTypeQual;
        end
        
        % Storage Type and Class are deprecated, maintaining set_param here
        % to set values of DTI's to their default which is forced by
        % utilStateAttributes
        arrayfun(@(x) set_param(x,...
            'StateMustResolveToSignalObject',blkObj.FilterStateMustResolveToSignalObject,...
            'RTWStateStorageClass',blkObj.FilterRTWStateStorageClass,...
            'RTWStateStorageTypeQualifier',StateStorageTypeStr),...
            setBlocks);
    end
end


%% Set Data Type for Filter Differentiator blocks
if dlgSett.isDialogFilter
    if dlgSett.ControllerParametersSource==1
        val = strrep(blkObj.NParamDataTypeStr,'Inherit: Inherit via internal rule','Inherit: Inherit via back propagation');
        val = strrep(val, 'Inherit: Same as input','Inherit: Inherit via back propagation');
        val = strrep(val, 'Inherit: Inherit from ''Gain''', 'Inherit: Inherit from ''Constant Value''');
        set_param([sys '/N Copy/Internal Parameters/N Copy'],'OutDataTypeStr',val)
    end
end

%% Sync Data Types if they are not equal (occurs with set_param)
if dlgSett.is2DOF
   GainOutDataTypeStrs = {'PGainOutDataTypeStr', 'IGainOutDataTypeStr',...
       'DGainOutDataTypeStr','NGainOutDataTypeStr',...
       'bGainOutDataTypeStr','cGainOutDataTypeStr'};
   ProdOutDataTypeStrs = {'PProdOutDataTypeStr', 'IProdOutDataTypeStr',...
       'DProdOutDataTypeStr','NProdOutDataTypeStr',...
       'bProdOutDataTypeStr','cProdOutDataTypeStr'}; 
else
    GainOutDataTypeStrs = {'PGainOutDataTypeStr', 'IGainOutDataTypeStr',...
       'DGainOutDataTypeStr','NGainOutDataTypeStr'};
    ProdOutDataTypeStrs = {'PProdOutDataTypeStr', 'IProdOutDataTypeStr',...
       'DProdOutDataTypeStr','NProdOutDataTypeStr'}; 
end

for ii=1:length(GainOutDataTypeStrs)
    GainDT = get_param(blk,GainOutDataTypeStrs{ii});
    ProdDT = get_param(blk,ProdOutDataTypeStrs{ii});

    if ~strcmp(GainDT,strrep(ProdDT,'Inherit: Same as first input','Inherit: Same as input'))
        % If Parameter source is external, copy settings from internal to external
        if dlgSett.ControllerParametersSource==1 %Internal
            % Get gain data type strings
            maskParamValues = strrep(GainDT,'Inherit: Same as input','Inherit: Same as first input');
            DataTypeStrSettings = {ProdOutDataTypeStrs{ii}, maskParamValues};
        else % External
            % Get product data type strings
            maskParamValues = strrep(ProdDT,'Inherit: Same as first input','Inherit: Same as input');
            DataTypeStrSettings = {GainOutDataTypeStrs{ii}, maskParamValues}; 
        end
        set_param(blk,DataTypeStrSettings{1},DataTypeStrSettings{2})
    end    
end

