classdef HDLPropertySchema <handle
    %HDLPROPERTYSCHEMA This class provides us with the schema with which
    %the HDLPropertyInspector is to be updated

%   Copyright 2022-2023 The MathWorks, Inc.

    properties(Access=private)
        BlockHandle;
        BlockName; %Gives the name of the block along with the path
        HDLDispTabs; %Stores all HDL Display tabs
        HDLStruct;%Stores the HDL property info as a structure
        HDLDialogProps;
        HDLArchitecture;
        ParamLookup; % Dictionary with implparams to speedup lookups
    end



    methods
        function out = getPropertySchema(obj)
            %This is the function call used to update the PropertyInspector
            %Source
            out = obj;
        end

        function updateSchema(hdlPropertySchema,h)
            hdlPropertySchema.BlockHandle = get(h,'Handle');
            hdlPropertySchema.BlockName = [get_param(hdlPropertySchema.BlockHandle,'Parent'),'/',get_param(hdlPropertySchema.BlockHandle,'Name')];
            hdlPropertySchema.HDLStruct=hdlPropertySchema.getHDLPropertiesStructure();
            hdlPropertySchema.HDLDispTabs = hdlPropertySchema.getHDLTabs();
        end

        function hdlPropertySchema = HDLPropertySchema(h)
            hdlPropertySchema.updateSchema(h);
		end

        % Called from Simulink PI side code for in-place display of
        % evaluated value of non-numeric value. TODO: revisit this function
        % after g3125436 is resolved
        function out = resolveExpression(obj,t)
            try
                out = slResolve(t,obj.BlockHandle);
            catch
                out=[];
            end
        end

        function dlg = getDialogSchema(~,~)
            text.Type = 'text';
            text.WordWrap = true;
            text.Name = DAStudio.message('HDLShared:hdldialog:hdlpropinspectorNohdl');
            grp.Type = 'group';
            grp.Items = {text};
            dlg.DialogTitle = '';
            dlg.EmbeddedButtonSet = {''};
            dlg.DialogMode = 'Slim';
            dlg.Items = {grp};
        end

        function result = getOwnerGraphHandle(obj)
            %Function to return the handle of the parent of the selected
            %block
            parent = get_param(obj.BlockHandle,'Parent');
            result = get_param(parent,'Handle');
        end

        function title = getDisplayTitle(obj)
            %This is used to provide the display title of the block
            name = get_param(obj.BlockHandle,'Name');
            title = ['HDL Block Properties: ',name];
        end

        function editor = propertyEditor(obj,prop)
            %Function to get the Property Editor for the specified property
            
            editor = obj.getEditor(prop);
            editor.Tag = prop;

        end

        function mode = propertyRenderMode(obj,prop)
            %Provides the render mode for the property specified
            mode = 'RenderAsText';
            if ~isempty(prop) && ~obj.istabName(prop) && ~strcmp(prop,'NOHDL')
                implparamtype = obj.getImplParamType(prop);
                if strcmp(implparamtype,'ENUM')
                    mode = 'RenderAsComboBox';
                end
            end
        end

        function mode = rootNodeViewMode(this,~)
            %Gives the mode with which we display the properties
            %currently we are going with the tree view
            if strcmp(this.HDLDispTabs,'NOHDL')
                mode = 'SlimDialogView';
            else
                mode = 'TreeView';
            end
            
        end

        function value = propertyValue(obj,prop)
            %Gets the property Value to be displayed in the
            %HDLPropertyInspector
            
            if  ~isempty(prop) && ~obj.hasSubProperties(prop) && ~strcmp(prop,'NOHDL')
                implParamType = obj.getImplParamType(prop);
                if ~strcmp(prop,'Architecture')
                    val = obj.ParamLookup({lower(prop)}).Value;
                else
                    val = obj.HDLArchitecture;
                end
                if (~isscalar(val) && isstring(val)) || (strcmp(implParamType,'MxARRAY') && isnumeric(val))
                    % string arrays, also bypass below condition for
                    % numeric MxARRAYs so they are not displayed as raw
                    % values
                    value = mat2str(val);
                elseif iscell(val)
                    % From getParamVal in drawBlockHDLProps
                    value = hdlCellArray2Str(val);
                elseif strcmp(implParamType,'ENUM')||strcmp(implParamType, 'MxARRAY')...
                    ||strcmp(implParamType,'STRING')
                   value = val;
                elseif ~isempty(val)
                   value = num2str(val);
                else
                   value = '';
                end
            else
                value = '';
            end

        end

    

        function value = supportTabView(~)
            %Function is false when we support Tree view
            value = false;
        end

        function value = propertyDisplayLabel(~,prop)
            %Gives the propertyDisplaylablel for the properties
            if ~isempty(prop)
                if strcmp(prop,'NOHDL')
                    value = "NO HDL Properties Available for this Block";
                else
                    value = string(prop);
                end
            else
                value = " ";
            end
        end

        function setPropertyValue(obj,prop,newvalue)
            %Sets the property value, when there is a change in the
            %properties of the UI
            implParamType = obj.getImplParamType(prop);
            if strcmp(implParamType,'ENUM')||strcmp(implParamType,'STRING')
                hdlset_param(obj.BlockName,prop,newvalue);
            elseif strcmp(implParamType,'MxARRAY')
                % for MxARRAY properties we need to convert from character vector to
                % appropriate type to use, below is how it is done in older implementation
                % would prefer alternative to eval as it is unsafe on user input without any restriction
                try
                    if ischar(newvalue)
                        propVal = evalin('base', newvalue);
                    else
                        propVal = newvalue;
                    end
                catch me %#ok<*NASGU>
                    propVal = newvalue;
                end
                hdlset_param(obj.BlockName,prop,propVal);
            elseif strcmp(implParamType,'POSINT_AUTO')
                if isnumeric(newvalue)
                    propVal = num2str(newvalue);
                else
                    propVal = newvalue;
                end
                hdlset_param(obj.BlockName,prop,propVal);
            else
                if ~isnan(str2double(newvalue))
                    newvalue = str2double(newvalue);
                end
                hdlset_param(obj.BlockName,prop,newvalue);
            end

            if strcmp(prop,'Architecture')%Update the hdlproperties and tabs if the architecture is changed
                
                obj.HDLStruct = obj.getHDLPropertiesStructure;
                obj.HDLDispTabs = obj.getHDLTabs;
                
            end

        end

 

        function out = isPropertyEnabled(~,~)
            %True for all the properties, as all the properties are enabled
            out = true;
        end

        function out = isPropertyEditable(obj,prop)
            %True for all the properties as all the properties are editable
            out = false;
            if ~obj.istabName(prop) || strcmp(prop,'Architecture')
                out = true;
            end
        end

        function out = hasSubProperties(obj,prop)
            %Function is true only for the different tab names
            out = false;
            if obj.istabName(prop) && ~strcmp(prop,'Architecture') 
                out = true;
            end
        end

        function value = subProperties(obj,prop)
            %Gives a list of subproperties if there are sub properties for
            %a given property
            value={""};
            if isempty(prop)
                value = obj.HDLDispTabs;
            end
            if obj.hasSubProperties(prop)
                value = obj.getSubProperties(prop);
            end
        end

        function out = getObjectType(~)
            out = "HDL Block Properties ";
        end

        function out = getObjectName(obj)
            out = get_param(obj.BlockHandle,'Name');
        end

        function tooltip = propertyTooltip(~, ~)
            tooltip = '';
        end

        function showPropertyHelp(obj,~)
            helptagname=hdlgethelptagname(obj.HDLDialogProps.defImplObj.Blocks{1},...
                class(obj.HDLDialogProps.defImplObj));
            if (obj.HDLDialogProps.helpInfo.hTargets)
                helpview(fullfile(docroot , obj.HDLDialogProps.helpInfo.hTag, 'helptargets.map'),helptagname);
            else
                helpview(fullfile(docroot, obj.HDLDialogProps.helpInfo.hTag,strcat(obj.HDLDialogProps.helpInfo.hTag,'.map')),helptagname);
            end
        end
        


    end


    methods(Access=private)
        function hdlstruct=getHDLPropertiesStructure(obj)
            %Get the hdldialog props
            objType = get_param(obj.BlockHandle,'Object');
            if isa(objType,'Simulink.Block')
                obj.HDLDialogProps = slprops.hdlblkdlg(obj.BlockHandle, true);
                obj.HDLArchitecture =hdlget_param(obj.BlockName,'Architecture');
                archInfo = obj.HDLDialogProps.archInfo;
            else
                archInfo = '';
                obj.HDLArchitecture = '';
            end
            try
                archInfovalues = values(archInfo,{obj.HDLArchitecture});
                obj.ParamLookup = dictionary(archInfovalues{1}.keys,cell2mat(archInfovalues{1}.values));
            catch ME
                archInfovalues = {''}; %Empty when the block has no hdlblock properties
            end
            archInfovalues = archInfovalues{1};
            if isempty(archInfovalues)
                hdlstruct = {};
            else
                % Order the parameters according to getImplParamOrder
                [implClass, implObjfound] = getImplementationObject(obj.HDLDialogProps);
                if implObjfound
                    % get the order of ImplParams from the ImplObj
                    implObj = eval(implClass);
                    implParamNames = implObj.getImplParamOrder(archInfovalues);
                else
                    implParamNames = sort(keys(archInfovalues));
                end
                hdlstruct = values(archInfovalues,implParamNames);
            end
        end

        function tabnames = getHDLTabs(obj)
            totaltabs = {};
            if ~isempty(obj.HDLStruct) || ~isempty(obj.HDLArchitecture)
                for i=1:length(obj.HDLStruct)
                    if ~any(ismember(totaltabs,obj.HDLStruct{i}.tabName))
                        temp = {obj.HDLStruct{i}.tabName};
                        totaltabs = cat(2,totaltabs,temp);
                    end
                end
                tabnames = cat(2,'Architecture',totaltabs);
            else
                tabnames = {'NOHDL'};
            end
        end



        function editor = getEditor(obj,prop)
            %Updates the editor
            implparamtype=obj.getImplParamType(prop);
            if strcmp(implparamtype,'ENUM')
                editor = DAStudio.UI.Widgets.ComboBox;
                editor.Editable = false;
                editor.CurrentText = hdlget_param(obj.BlockName,prop);
                editor.Entries = obj.getEditorEntries(prop);
            else
                editor = DAStudio.UI.Widgets.Edit;
                editor.Enabled = true;
                editor.Text = obj.propertyValue(prop);
            end
        end

        function editorEntries = getEditorEntries(obj,prop)
            %Gets the ediotr entries for ComboBox
            if strcmp(prop,'Architecture')
                editorEntries=obj.HDLDialogProps.archInfo.keys;
            else
                editorEntries = obj.ParamLookup({lower(prop)}).AllValues;
            end
        end

        function implparamtype = getImplParamType(obj,prop)
            %Gets the Implemenataion paramtype for a property
            if strcmp(prop,'Architecture')
                implparamtype = 'ENUM';
            else
                implparamtype = obj.ParamLookup({lower(prop)}).ImplParamType;
            end
        end

        function out = istabName(obj,prop)
            %Outputs if the given property is a tab name or not
            if strcmp(prop,'NOHDL')
                out = false;
            else
                out = any(strcmp(obj.HDLDispTabs(2:end), prop));
            end
        end

        function subprops = getSubProperties(obj,prop)
            %Lists all the subproperties.
            if strcmp(prop,'NOHDL')
                subprops = {};
            else
                lindices = cellfun(@(mystruct) strcmp(mystruct.tabName,prop),obj.HDLStruct);
                indices = find(lindices==1);
                subprops = cell(1,length(indices));
                for i=1:length(indices)
                    subprops{i} = obj.HDLStruct{indices(i)}.ImplParamName;
                end
            end
        end


    end
end

% LocalWords:  hdlstructure NOHDL Displaylablel hdlproperties subproperties helptargets hdlblock
% LocalWords:  ediotr Implemenataion paramtype POSINT implparams hdlpropinspectorNohdl MxARRAYs
