classdef ParameterValue < modelpack.VariableValue
    %modelpack.ParameterValue class
    %   modelpack.ParameterValue extends modelpack.VariableValue.
    %

    %    modelpack.ParameterValue properties:
    %       Description - Property is of type 'ustring'
    %       Name - Property is of type 'string'  (read only)
    %       Value - Property is of type 'MATLAB array'
    %
    %    modelpack.ParameterValue methods:
    %       copy -  method to make a copy of this object
    %       getDimensions - return dimension of value specified by the parameter value
    %       getID - Returns the handle of the identifier object associated with THIS.
    %       getName - Returns the name of the parameter.
    %       setDimensions - set the dimension of the value specified by the parameter
    %       setID - Sets the identifier object associated with THIS.
    %       setName - Sets the name of the parameter.

    % Copyright 2000-2023 The MathWorks, Inc.

    properties (Access=protected, SetObservable)
        %ID Property is of type 'handle'
        ID = [];
        %DIMENSION Property is of type 'MATLAB array'
        Dimension = [ 0, 0 ];
        %ISDIMENSIONEDITABLE Property is of type 'bool'
        isDimensionEditable = true;
    end

    properties (SetAccess=protected, SetObservable)
        %NAME Property is of type 'string'  (read only)
        Name = '';
    end

    properties (SetObservable)
        %VALUE Property is of type 'MATLAB array'
        Value = [];
    end


    methods  % constructor block
        function this = ParameterValue(parameterID, subsName)
            % PARAMETERVALUE Constructor
            %
            % This constructor takes a parameter identifier object and an optional
            % subscripted name to create a PARAMETERVALUE object.
            %
            % h = modelpack.ParameterValue(parameterID, [subsName]);
            %
            % PARAMETERID a ParameterID object.
            % SUBSNAME    is the optional subscripted parameter name; otherwise set to empty.

            % Create object

            % No argument constructor call.
            ni = nargin;
            if (ni == 0)
                return
            end

            % Set the default value to empty.
            if (ni < 2 || isempty(subsName)), subsName = parameterID.getName; end

            % Set properties.
            this.Version = 1.0;
            this.setID(parameterID);
            this.setName(subsName);

        end  % ParameterValue

    end  % constructor block

    methods
        function set.ID(obj,value)
            % DataType = 'handle'
            validateattributes(value,{'handle'}, {'scalar'},'','ID')
            obj.ID = value;
        end

        function set.Name(obj,value)
            % DataType = 'string'
            validateattributes(value,{'char'}, {'row'},'','Name')
            obj.Name = value;
        end

        function set.isDimensionEditable(obj,value)
            % DataType = 'bool'
            validateattributes(value,{'numeric','logical'}, {'scalar','nonnan'},'','isDimensionEditable')
            value = logical(value); %  convert to logical
            obj.isDimensionEditable = value;
        end

        function set.Value(obj,value)
            obj.Value = LocalSetValue(obj,value,'Value');
        end

    end   % set and get functions

    methods  % public methods
        function newObj = copy(this)
            % COPY  method to make a copy of this object
            %

            for ct=numel(this):-1:1
                newObj(ct) = feval(class(this));  %So that it works if subclass does not overload
                newObj(ct).ID                  = this(ct).ID;
                newObj(ct).Name                = this(ct).Name;
                newObj(ct).Dimension           = this(ct).Dimension;  %This must get copied before values are set
                newObj(ct).isDimensionEditable = this(ct).isDimensionEditable;
                newObj(ct).Value               = this(ct).Value;
            end
        end  % copy

        function dim = getDimensions(this)
            % GETDIMENSIONS return dimension of value specified by the parameter value
            % object
            %

            %dim = get(this,'Dimension');
            dim = this.Dimension;
        end  % getDimensions

        function ID = getID(this)
            % GETID Returns the handle of the identifier object associated with THIS.

            % ID = get(this, {'ID'});
            % ID = cat(1, ID{:});
            ID = [this.ID];

        end  % getID

        function name = getName(this)
            % GETNAME Returns the name of the parameter.

            %name = get(this, 'Name');
            name = this.Name;

        end  % getName

        function setDimensions(this,dim)
            % SETDIMENSIONS set the dimension of the value specified by the parameter
            % value object. Setting the dimension will also reset the value property to
            % zero.
            %
            % this.setDimension(dim)
            %
            % Input
            %   dim - a row vector with the dimension


            if ~(isnumeric(dim) && size(dim,1)==1 && ismatrix(dim) && numel(dim) > 1)
                error(message('SLControllib:modelpack:errDimension'))
            end

            if this.isDimensionEditable
                this.Dimension = dim;
                this.Value     = zeros(dim);
            else
                error(message('SLControllib:modelpack:errEditableDimension'))
            end
        end  % setDimensions

        function this = setID(this, ID)
            % SETID Sets the identifier object associated with THIS.

            % Type checking
            if ~isa(ID, 'modelpack.ParameterID')
                error(message('SLControllib:modelpack:errArgumentType', ...
                    'ID', 'modelpack.ParameterID' ));
            end

            % Handle vectorized call
            for ct = 1:numel(this)
                h  = this(ct);
                id = copy( ID(ct) );

                h.ID = id;
                h.setName( id.getName );
            end

        end  % setID

        function this = setName(this, name)
            % SETNAME Sets the name of the parameter.

            % Type checking
            if ~ischar(name)
                error(message('SLControllib:modelpack:InvalidStringArgument', ...
                    'NAME', 'setName' ));
            end

            % Check name matches parameterID name
            var = modelpack.varnames(name);
            if ~strcmp(this.getID.getName,var)
                error(message('SLControllib:modelpack:IncompatibleSpecName',this.getID.getName));
            end

            this.Name  = modelpack.utCheckVariableSubsref(this.getID, name);
            if strcmp(this.Name,this.getID.getName)
                %Value for a Parameter ID object, dimension must match parameter ID and
                %is not editable
                this.isDimensionEditable = true;
                this.setDimensions(this.getID.getDimensions) %Also initializes property sizes
                this.isDimensionEditable = false;
            else
                %Clear dimensions
                this.isDimensionEditable = true;
                this.setDimensions([0 0]);
            end
        end  % setName

    end  % public methods

end  % classdef

function value = LocalSetValue(this, value, pname)
if numel(value)>1 && ~isequal(this.getDimensions,size(value))
    error(message('SLControllib:modelpack:SizeMismatch'))
end
if isequal(this.getDimensions,[0 0]) && isempty(value)
    %Nothing to do
elseif ~any( isnan(value(:)) )
    value = modelpack.utValidateValue(this, value);
else
    error(message('SLControllib:general:RealDoubleArrayValue', pname ));
end
end  % LocalSetValue

function str = LocalMat2Str(value, N)
try
    str = mat2str(value, N);
catch
    s1  = regexprep( num2str(size(value)), ' *', 'x' );
    str = sprintf('[%s %s]', s1, class(value));
end
end  % LocalMat2Str

% LocalWords:  ISDIMENSIONEDITABLE PARAMETERID SUBSNAME GETDIMENSIONS SETDIMENSIONS
