
% Initialization code section
function hdlssclib_SMVP(thisBlk,varargin)
%

%   Copyright 2016-2023 The MathWorks, Inc.

matSource = get_param(thisBlk,'MatrixSource');

% find the output DTC block
DTCOutBlk = find_system(thisBlk, ...
    'LookUnderMasks', 'all', ...
    'FollowLinks', 'on', ...
    'SearchDepth', 1, ...
    'Name', 'Data Type Conversion1');

% get the outDataType
Type = get_param(thisBlk,'PropDataType');
% get the default word length for the matrix fixed point conversion
defWL = str2double(get_param(thisBlk,'defWL'));

if strcmp(matSource,'Dialog')
    constMatrix = varargin{1};
    % validate functoin input (matrix) for both sparsity pattern and matrix value
    validateattributes(constMatrix,{'single','double','embedded.fi'},{'3d','nonempty','real'},'',"'Matrix'");

    if issparse(constMatrix)
        constMatrix = full(constMatrix);
    end
end

% get the matrix input source
% Mask initialization will create a third input port or create a constant
% block under the mask

% "Dialog" -> Constant Block (2 block inputs)
% "Input Port" -> Input port (3 block inputs)

% if matrix source is 'Dialog'
if strcmp(matSource,'Dialog')
    % find if there is matrix input port present
    matrixInputPort = find_system(thisBlk,'LookUnderMasks', 'all','FollowLinks', 'on','SearchDepth', 1,'Name', 'Matrix Data');
    % if input port is already present, delete the input port block and the lines
    % connected with input port
    if(~isempty(matrixInputPort))
        matrixInportBlkLines = get_param(strcat(thisBlk,'/Matrix Data'), 'LineHandles');
        delete_line(matrixInportBlkLines.Outport(1));
        delete_block(matrixInputPort);
    end

    % find if MATLAB Function is commented
    isCommented = get_param(strcat(thisBlk,'/Create Matrix'), 'Commented');

    % if the Create Matrix block is not commented out, comment the block
    if strcmp(isCommented,'off')
        set_param(strcat(thisBlk,'/Create Matrix'), 'Commented','on');
    end

    MLFncBlkLines = get_param(strcat(thisBlk,'/Create Matrix'), 'LineHandles');
    if MLFncBlkLines.Outport(1) ~= -1
        delete_line(MLFncBlkLines.Outport(1));
    end

    % find if the constant block is already present under the mask
    constMatrixBlk = find_system(thisBlk,'LookUnderMasks', 'all','FollowLinks', 'on','SearchDepth', 1,'Name', 'Constant');
    % if constant matrix block is not present, add a constant block with
    % matrix data
    if(isempty(constMatrixBlk))
        constMatrixBlk = add_block('hdlsllib/Sources/Constant', strcat(thisBlk, '/Constant'), ...
            'MakeNameUnique', 'on', ...
            'Value', 'constMatrix', ...
            'SampleTime', '-1');
    else
        % if constant block is already present, delete it's output line
        constBlkLines = get_param(strcat(thisBlk,'/Constant'), 'LineHandles');
        delete_line(constBlkLines.Outport(1));
    end

    % don't interpret vector parameter as 1-D
    % 1*1*n will be treated as n dimentional vector
    vectorParam1D = get_param(constMatrixBlk,'VectorParams1D');

    if(strcmp(vectorParam1D,'on'))
        set_param(constMatrixBlk,'VectorParams1D','off');
    end

    % set port dimension of vector input  port
    vectorInputPortSize = get_param(strcat(thisBlk,'/Vector'), 'PortDimensions');
    if(strcat(vectorInputPortSize,'-1'))
        set_param(strcat(thisBlk,'/Vector'), 'PortDimensions',num2str(size(constMatrix,2)));
    end

    % find the selector block
    selectorBlk = find_system(thisBlk,'LookUnderMasks', 'all','FollowLinks', 'on','SearchDepth', 1,'Name', 'Selector');
    constMatrix = constMatrix;
    if issparse(constMatrix)
        constMatrix = full(constMatrix);
    end

    if size(constMatrix,3) == 1
        % 2-d constMatrix
        if strncmpi(Type,'fixdt',5)
            % fixed point
            typeProposalSettings = coder.internal.Float2FixedConverter.getTypeSettingsForApproximation();
            typeProposalSettings.defaultWL = defWL;
            % get the best fixed point type for constMatrix
            newDT = coder.internal.getBestNumericTypeForVal(min(constMatrix(:)),max(constMatrix(:)),false,typeProposalSettings);
            newDTfi = sprintf('fixdt(%d,%d,%d)',newDT.SignednessBool,newDT.WordLength,newDT.FractionLength);
            % set the fixed point type to constant block's output data type
            set_param(strcat(thisBlk, '/Constant'),"OutDataTypeStr",newDTfi);
            set_param(thisBlk,'matrixType',newDTfi);
        else
            % floating point
            newDTfi = class(constMatrix);
            set_param(strcat(thisBlk, '/Constant'),"OutDataTypeStr",newDTfi);
            set_param(thisBlk,'matrixType',newDTfi);
        end

        if ~isempty(selectorBlk)
            % delete the selector block
            selectorBlkLines = get_param(selectorBlk, 'LineHandles');
            delete_line(selectorBlkLines{1}.Inport(2));
            delete_line(selectorBlkLines{1}.Outport(1));
            delete_block(selectorBlk);

            % add a terminator block
            hTerminatorBlk = add_block('hdlsllib/Sinks/Terminator', ...
                strcat(thisBlk, '/Terminator'), ...
                'Position', [225 170 255 200]);
            % connect the data type conversion block to the terminator block
            add_line(thisBlk, 'Data Type Conversion/1', 'Terminator/1', ...
                'autorouting', 'on');
        end

        % connect the constant block to the product block
        add_line(thisBlk, 'Constant/1', 'Product/1', ...
            'autorouting', 'on');
    else

        % 3-d constMatrix
        if strncmpi(Type,'fixdt',5)
            % fixed point
            typeProposalSettings = coder.internal.Float2FixedConverter.getTypeSettingsForApproximation();
            typeProposalSettings.defaultWL = defWL;
            % get the best fixed point type for constMatrix
            newDT = coder.internal.getBestNumericTypeForVal(min(constMatrix(:)),max(constMatrix(:)),false,typeProposalSettings);
            newDTfi = sprintf('fixdt(%d,%d,%d)',newDT.SignednessBool,newDT.WordLength,newDT.FractionLength);
            set_param(strcat(thisBlk, '/Constant'),"OutDataTypeStr",newDTfi);
            set_param(thisBlk,'matrixType',newDTfi);
        else
            % floating point
            newDTfi = class(constMatrix);
            set_param(strcat(thisBlk, '/Constant'),"OutDataTypeStr",newDTfi);
            set_param(thisBlk,'matrixType',newDTfi);
        end

        if isempty(selectorBlk)
            % delete the terminator block
            terminatorBlkLines = get_param(strcat(thisBlk, '/Terminator'), 'LineHandles');
            delete_line(terminatorBlkLines.Inport(1));
            delete_block(strcat(thisBlk, '/Terminator'));

            % add the selector block
            hSelectorBlk = add_block('hdlsllib/Signal Routing/Selector', ...
                strcat(thisBlk, '/Selector'), ...
                'Position', [240 116 280 154], ...
                'NumberOfDimensions', '3', ...
                'IndexMode', 'Zero-based', ...
                'IndexOptionArray', [{'Select all'}; {'Select all'}; {'Starting index (port)'}], ...
                'OutputSizeArray', [{'1'} {'1'} {'1'}]);

            % connect the data type conversion block to the selector block
            add_line(thisBlk, 'Data Type Conversion/1', 'Selector/2', ...
                'autorouting', 'on');
            add_line(thisBlk, 'Selector/1', 'Product/1', ...
                'autorouting', 'on');
        end

        % connect the constant block to the selector block
        add_line(thisBlk, 'Constant/1', 'Selector/1', ...
            'autorouting', 'on');
    end
else
    % matrix source is input port

    matrixSize = varargin{1};
    columnIndices = varargin{2};
    rowIndices = varargin{3};

    % validate block mask properties
    validateattributes(matrixSize,{'numeric'},{'vector','nonempty','real','integer','positive','nonnan'});
    validateattributes(columnIndices,{'numeric'},{'vector','nonempty','real','integer','positive','nonnan'});
    validateattributes(rowIndices,{'numeric'},{'vector','nonempty','real','integer','positive','nonnan'});

    % size of column and row vector should be same
    assert((size(rowIndices,2)==size(columnIndices,2)),message('hdlcoder:validate:SMVPSparseMatrixIndicesLengthMismatch'));

    % rows and columns indices must be in range of matrix size
    assert(matrixSize(1) >= max(rowIndices),message('hdlcoder:validate:SMVPIndexOutOfBound',max(rowIndices),'Row Indices Array',matrixSize(1)));
    assert(matrixSize(2) >= max(columnIndices),message('hdlcoder:validate:SMVPIndexOutOfBound', max(columnIndices),'Column Indices Array',matrixSize(2)));

    % find if input port is already present
    matrixInputPort = find_system(thisBlk,'LookUnderMasks', 'all','FollowLinks', 'on','SearchDepth', 1,'Name', 'Matrix Data');

    % if input port is not already present under the mask, add input port
    % block
    if isempty(matrixInputPort)
        matrixInputPort = add_block('hdlsllib/Sources/In1', strcat(thisBlk, '/Matrix Data'),'MakeNameUnique', 'on');
        set_param(matrixInputPort,'Port','1');
    else
        % if input port is already present, delete it's output lines
        matrixInportBlkLines = get_param(strcat(thisBlk,'/Matrix Data'), 'LineHandles');
        delete_line(matrixInportBlkLines.Outport(1));
    end

    % find if constant block is already present
    constMatrixBlk = find_system(thisBlk,'LookUnderMasks', 'all','FollowLinks', 'on','SearchDepth', 1,'Name', 'Constant');
    % if constant block is already present, delete the block and it's
    % output lines connected with the block
    if(~isempty(constMatrixBlk))
        constBlkLines = get_param(strcat(thisBlk,'/Constant'), 'LineHandles');
        delete_line(constBlkLines.Outport(1));
        delete_block(constMatrixBlk);
    end

    % find if MATLAB Function is commented
    isCommented = get_param(strcat(thisBlk,'/Create Matrix'), 'Commented');

    if strcmp(isCommented,'on')
        set_param(strcat(thisBlk,'/Create Matrix'), 'Commented','off');
    end

    % delete output port for MATLAB Function
    MLFncBlkLines = get_param(strcat(thisBlk,'/Create Matrix'), 'LineHandles');
    if MLFncBlkLines.Outport(1) ~= -1
        delete_line(MLFncBlkLines.Outport(1));
    end

    % set port dimension of vector input  port
    vectorInputPortSize = get_param(strcat(thisBlk,'/Vector'), 'PortDimensions');
    if(~strcmp(vectorInputPortSize,'-1'))
        set_param(strcat(thisBlk,'/Vector'), 'PortDimensions','-1');
    end

    selectorBlk = find_system(thisBlk, 'LookUnderMasks', 'all', 'FollowLinks', 'on', 'SearchDepth', 1, 'Name', 'Selector');

    if ~isempty(selectorBlk)
        % delete the selector block
        selectorBlkLines = get_param(selectorBlk, 'LineHandles');
        delete_line(selectorBlkLines{1}.Inport(2));
        delete_line(selectorBlkLines{1}.Outport(1));
        delete_block(selectorBlk);

        % add a terminator block
        hTerminatorBlk = add_block('hdlsllib/Sinks/Terminator', ...
            strcat(thisBlk, '/Terminator'), ...
            'Position', [225 170 255 200]);
        % connect the data type conversion block to the terminator block
        add_line(thisBlk, 'Data Type Conversion/1', 'Terminator/1', ...
            'autorouting', 'on');
    end

    % connect the constant block to the product block
    add_line(thisBlk, 'Matrix Data/1', 'Create Matrix/1', ...
        'autorouting', 'on');
    add_line(thisBlk, 'Create Matrix/1', 'Product/1', ...
        'autorouting', 'on');

end

% add a DTC block at the output to handle outDataType
% The data type from product block inside can be single, but the
% outDataType on the block can be double. Need a DTC
% The data type from product block will be of full fixed point precision,
% but outDataType on the block can be different. Need a DTC
if isempty(DTCOutBlk)
    DTC = add_block('hdlsllib/Signal Attributes/Data Type Conversion', strcat(thisBlk, '/Data Type Conversion1'), ...
        'MakeNameUnique', 'on', ...
        'Position', [225   266   300   304], ...
        'OutDataTypeStr', 'boolean', ...
        'RndMeth', 'Nearest');
    set_param(strcat(thisBlk,'/Data Type Conversion1'),"OutDataTypeStr",Type);
    productBlkLines = get_param(strcat(thisBlk, '/Product'), 'LineHandles');
    delete_line(productBlkLines.Outport(1));
    add_line(thisBlk, 'Product/1', 'Data Type Conversion1/1', ...
        'autorouting', 'on');
    add_line(thisBlk,  'Data Type Conversion1/1', 'Out1/1',...
        'autorouting', 'on');
else
    set_param(strcat(thisBlk,'/Data Type Conversion1'),"OutDataTypeStr",Type);
end
end
% Parameter callback section