function dlgStruct = drawBlockHDLProps(this)
%

%   Copyright 2009-2023 The MathWorks, Inc.

% get the current arch selection and properties of that architecture
archSelection = this.getCurrentArchName;

if isempty(archSelection)
    items = {getNoInfoGroup(this)};
    dlgStruct = drawPanel(this, items, 1);
else
    currArchImplInfo = this.getCurrentArchImplParams;
    implInfo = this.getImplInfo(currArchImplInfo);
    
    % architecture name group
    archGroup = getArchitectureGroup(this);
    
    panelStruct = constructHierarchy(implInfo);
    tabKeys = sort(keys(panelStruct));
    numTabs = length(tabKeys);
    tabs = {};
    
    if(numTabs == 0)
        tab.Tag = strcat('Tab', '1');
        tab.Name = message('hdlcoder:hdlblockdialog:GeneralTab').getString;
        tab.ColStretch = 1;
        totalRows = 1;
        tab.Items = {archGroup};
        tab.LayoutGrid = [totalRows 1];
        tab.RowStretch = ones(1,totalRows);
        tabs{1} = tab;
    else
        for ii = 1:numTabs
            tabValue = panelStruct(tabKeys{ii});
            tab = getEachTab(this, tabKeys{ii}, tabValue, archGroup);
            tabs{ii} = tab;
        end
    end
    
    tabContainer.Type = 'tab';
    tabContainer.Tag = 'TabContainer';
    tabContainer.Tabs = tabs;
    
    dlgName = message('hdlcoder:hdlblockdialog:HDLProperties').getString;
    blkName = getBlockName(this);
    if ~isempty(blkName)
        dlgName = sprintf('%s: %s', dlgName, blkName);
    end
    
    dlgStruct.DialogTitle = dlgName;
    dlgStruct.Items = {tabContainer};
    
    dlgStruct.PostApplyMethod = 'dlgApplyCallback';
    dlgStruct.PostApplyArgs   = {'%dialog'};
    dlgStruct.PostApplyArgsDT = {'handle'};
    
    blkPath = hdlgetblocklibpath(this.SLBlockHandle);
    docPath = getMapFilePath(this, blkPath);
    topicID = hdlgethelptagname(blkPath, class(this.defImplObj), this.SLBlockHandle);
    
    dlgStruct.HelpMethod      = 'helpview';
    dlgStruct.HelpArgs        = {docPath, topicID};
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function tab = getEachTab(this, tabKey, tabValue, archGroup)
tab.Tag = strcat('Tab', tabKey);
tab.Name = tabValue.tabName;
tab.ColStretch = 1;

groupMap = tabValue.groupMap;
groupKeys = sort(keys(groupMap));
numGroups = length(groupKeys);
groups = {};
totalRows = 1;

for ii = 1:numGroups
    groupValue = groupMap(groupKeys{ii});
    [group, groupRows] = getEachGroup(this, groupValue);
    groups{ii} = group;
    totalRows = totalRows + groupRows;
end

if strcmp(tabKey, '1')
    % Main dialog (first tab has two groups)
    %  1. architecture combobox
    %  2. implementation params (edit/combo)
    groups = [{archGroup} groups];
end

tab.Items = groups;
tab.LayoutGrid = [totalRows 1];
tab.RowStretch = ones(1,totalRows);
end

function [implGroup, numImplParams] = getEachGroup(this, groupValue)
implGroup = [];
implProps = {};

iParamInfo = groupValue.paramMap;

numImplParams = length(iParamInfo);
if numImplParams > 0
    % get to the implementation class
    [implClass, implObjfound] = getImplementationObject(this);
    if implObjfound
        % get the order of ImplParams from the ImplObj
        implObj = eval(implClass);
        implParamNames = implObj.getImplParamOrder(iParamInfo);
    else
        implParamNames = sort(keys(iParamInfo));
    end
    
    for ii = 1:numImplParams
        ip = iParamInfo(implParamNames{ii});
        implProp.Name = ip.ImplParamName;
        implProp.Type = 'text';
        implProp.RowSpan = [ii ii];
        implProp.ColSpan = [1 1];
        implProps{end+1} = implProp;
    end
    
    for ii = 1:numImplParams
        ip = iParamInfo(implParamNames{ii});
        implProp.Name = '';
        implProp.Tag = ip.ImplParamName;
        if strcmpi(ip.ImplParamType, 'ENUM')
            implProp.Type = 'combobox';
            implProp.RowSpan = [ii ii];
            implProp.ColSpan = [2 2];
            enumVal = getParamVal(ip.Value);
            allVal = ip.AllValues;
            implProp.Value = validateEnumVal(enumVal, allVal);
            implProp.Entries = allVal;
        else
            implProp.Type = 'edit';
            implProp.RowSpan = [ii ii];
            implProp.ColSpan = [2 2];
            implProp.Value = getParamVal(ip.Value);
        end
        
        implProp.ObjectMethod = 'updateImplParamCallBack';
        implProp.MethodArgs   = {implProp.Tag, '%dialog'};
        implProp.ArgDataTypes = {'string', 'handle'};
        
        implProp.DialogRefresh = 0;
        
        implProps{end+1} = implProp; %#ok<*AGROW>
    end
end

if numImplParams > 0
    implGroup.Type = 'group';
    implGroup.Name = groupValue.groupName;
    implGroup.Items = implProps;
    
    implGroup.LayoutGrid = [numImplParams 2];
    implGroup.RowStretch = ones(1,numImplParams);
    implGroup.ColStretch = [1 2];
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function panelStruct = constructHierarchy(implInfo)
numImplParams = length(implInfo);
tabMap = containers.Map;
if numImplParams > 0
    % get to the implementation class
    implParamNames = keys(implInfo);
    for ii = 1:numImplParams
        ip = implInfo(implParamNames{ii});
        tabMap = addToTabMap(tabMap, ip);
    end
end
panelStruct = tabMap;
end

function tabMap = addToTabMap(tabMap, ip)
tabKey = num2str(ip.tabPosition);  % use tab order as tabMap key
tabName = ip.tabName;
if(~isKey(tabMap,tabKey))
    % no this tab before
    v = struct;
    v.tabName = tabName;
    groupMap = containers.Map;
    v.groupMap = addToGroupMap(groupMap, ip);
    tabMap(tabKey) = v;
else
    % has this tab before
    tabValue = tabMap(tabKey);
    if (~strcmp(tabValue.tabName, tabName))
        error('One tab position are assigned to different names: %s and %s', tabValue.tabName, tabName);
    end
    tabValue.groupMap = addToGroupMap(tabValue.groupMap, ip);
end
end

function groupMap = addToGroupMap(groupMap, ip)
groupKey = num2str(ip.groupPosition);
groupName = ip.groupName;   % use group order as groupMap key
if(~isKey(groupMap,groupKey))
    % no this group before
    v = struct;
    v.groupName = groupName;
    paramMap = containers.Map;
    v.paramMap = addToParamMap(paramMap, ip);
    groupMap(groupKey) = v;
else
    % has this group before
    groupValue = groupMap(groupKey);
    if (~strcmp(groupValue.groupName, groupName))
        error('One group position are assigned to different names: %s and %s', groupValue.groupName, groupName);
    end
    groupValue.paramMap = addToParamMap(groupValue.paramMap, ip);
end
end

function paramMap = addToParamMap(paramMap, ip)
paramKey = lower(ip.ImplParamName);
paramMap(paramKey) = ip;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function dlgStruct = drawPanel(this, items, totalRows)
dlgName = message('hdlcoder:hdlblockdialog:HDLProperties').getString;
blkName = getBlockName(this);
if ~isempty(blkName)
    dlgName = sprintf('%s: %s', dlgName, blkName);
end

dlgStruct.DialogTitle = dlgName;
dlgStruct.LayoutGrid = [totalRows 1];
dlgStruct.RowStretch = ones(1,totalRows);
dlgStruct.ColStretch = 1;
% dlgstruct.OpenCallback = @openCallBack;

dlgStruct.Items = items;

dlgStruct.PostApplyMethod  = 'dlgApplyCallback';
dlgStruct.PostApplyArgs   = {'%dialog'};
dlgStruct.PostApplyArgsDT = {'handle'};

docPath = fullfile(docroot,'hdlcoder','helptargets.map');

blkPath = hdlgetblocklibpath(this.SLBlockHandle);
topicID = hdlgethelptagname(blkPath, class(this.defImplObj));

dlgStruct.HelpMethod = 'helpview';
dlgStruct.HelpArgs = {docPath, topicID};
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function noInfoGroup = getNoInfoGroup(~)
noInfoGroup.Type = 'group';
noInfoGroup.LayoutGrid = [1 2];
noInfoGroup.RowStretch = 1;
noInfoGroup.ColStretch = [1 1];

% nothing to be displayed for this block
noInfoProp.Name = message('hdlcoder:hdlblockdialog:HDLInfoNotAvailable').getString;
noInfoProp.Type = 'text';
noInfoProp.RowSpan = [1 1];
noInfoProp.ColSpan = [1 2];

% don't set any group name
noInfoGroup.Name = '';

noInfoGroup.Items = {noInfoProp};
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function archGroup = getArchitectureGroup(this)
% gather all arch names to be shown in a combobox
archNames = this.getArchitectureNames;

archGroup.Type = 'group';
%archGroup.LayoutGrid = [1 2];
%archGroup.RowStretch = 1;
%archGroup.ColStretch = [1 1];

archNameProp.Type = 'text';
archNameProp.Name = 'Architecture';
archNameProp.RowSpan = [1 1];
archNameProp.ColSpan = [1 1];

archProp.Tag = 'Architecture';
archProp.Name = '';
archProp.Type = 'combobox';
archProp.RowSpan = [1 1];
archProp.ColSpan = [2 2];
archProp.Entries = archNames;
archProp.ObjectProperty = 'archSelection';
archProp.Mode = 1;
archProp.DialogRefresh = 1;

% group Name
archGroup.Name = message('hdlcoder:hdlblockdialog:Implementation').getString;

archGroup.Items = {archNameProp, archProp};

archGroup.LayoutGrid = [1 2];
archGroup.RowStretch = 1;
archGroup.ColStretch = [1 2];
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function outVal = validateEnumVal(enumVal, allVal)
idx = strcmpi(allVal,  enumVal);
if any(idx)
    % found matching value
    outVal = allVal{find(idx, 1)};
else
    % see if we find a partial match and map it full name
    m = find(strncmpi(enumVal, allVal, length(enumVal)));
    if isempty(m)
        warning(message('HDLShared:hdldialog:unknownname', enumVal));
        outVal = allVal{1};
    elseif length(m) > 1
        warning(message('HDLShared:hdldialog:unknownvalue', enumVal));
        outVal = allVal{1};
    else
        outVal = allVal{m};
    end
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function outVal = getParamVal(pVal)
outVal = pVal;
if (isnumeric(pVal) || isstring(pVal)) && ~isscalar(pVal)
    % for vectors and matrices, print values as strings
    outVal = mat2str(pVal);
elseif iscell(pVal)
    % for cell arrays, print values as strings.
    % validation for correct format occurs during hdlset_param
    outVal = hdlCellArray2Str(pVal);
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function docPath = getMapFilePath(this, blkPath)

if ~isempty(this.helpInfo)
    prodshortname = this.helpInfo.hTag;
    % if hTargets field does not exist or hTargets is true...
    if ~isfield(this.helpInfo, 'hTargets') || (this.helpInfo.hTargets)
        docPath = fullfile(docroot, prodshortname, 'helptargets.map');
    else
        docPath = fullfile(docroot,prodshortname,strcat(prodshortname,'.map'));
    end
    return;
end

helpInfo = this.defImplObj.getHelpInfo(blkPath);

if (helpInfo.hTargets)
    docPath = fullfile(docroot,helpInfo.hTag,'helptargets.map');
else
    docPath = fullfile(docroot,helpInfo.hTag,strcat(helpInfo.hTag,'.map'));
end

end
% LocalWords:  ug impls hdlblockdialog helptargets hdlset
