classdef  Material < handle & matlab.mixin.CustomDisplay & matlab.mixin.Copyable & matlab.mixin.SetGet
% SHAPE.MATERIAL defines the material properties of the shape
%
%   d = shape.Material creates a material. The default 
%   material has air dielectric and PEC.
%
%   d = shape.Material('Property1', value1,'Property2', value2,...) returns a
%   material, based on the specified properties. Properties you
%   do not specify retain their default values.
%
%   dielectric properties:
%
%   Conductivity - Conductivity of the metal material in shape. Default value is inf.
%   Thickness    - Thickness of metal in shape. The default value is 0.
%   EpsilonR     - Relative permittivity of the dielectric material in
%                  shape. The default value is 1.
%   LossTangent  - Loss in the dielectric material of shape. The default
%                  value is 0.
%
%   % Examples:
%
%   % Example 1:
%   % Create a material with copper and air dielectric properties and assign it
%   % to box shape
%   sh = shape.Box;
%   mat = shape.Material;
%   mat.Conductivity = 59600000;
%   mat.Thickness = 3.55e-5;
%   
%
%   See also shape.Box

%   Copyright 2022-2023 The MathWorks, Inc.


    properties(Dependent)
        % Conductivity of the shape material
        Conductivity
        % Thickness of the shape material
        Thickness
        % Relative permitivity
        EpsilonR
        % Loss Tangent
        LossTangent
    end

    properties(Hidden,Dependent)
        Parent
    end


    properties(Access=private)
        % Magnetic Permeability
        Permeability
        LossTangentMu
    end
    properties(Access=protected)
        MaterialStruct = struct('Conductivity',inf,...
            'Thickness',0,...
            'LossTangent',0,...
            'EpsilonR',1,...
            'Permeability',[],...
            'LossTangentMu',[], ...
            'Parent',[])
    end

    methods
        function obj = Material(varargin)
            parserObj = inputParser;
            addParameter(parserObj,'Conductivity',inf);
            addParameter(parserObj,'Thickness',0);
            addParameter(parserObj,'EpsilonR',1);
            addParameter(parserObj,'LossTangent',0);
            addParameter(parserObj,'Permeability',1);
            addParameter(parserObj,'LossTangentMu',0);
            parse(parserObj,varargin{:});
            obj.Conductivity   = parserObj.Results.Conductivity;
            obj.Thickness      = parserObj.Results.Thickness;
            obj.EpsilonR      = parserObj.Results.EpsilonR;
            obj.LossTangent      = parserObj.Results.LossTangent;
            obj.Permeability      = parserObj.Results.Permeability;
            obj.LossTangentMu      = parserObj.Results.LossTangentMu;

        end

        function set.Conductivity(obj,propVal)
            if isequal(obj.Conductivity,propVal)
                return;
            end
            validateattributes(propVal,{'numeric'},                     ...
                {'nonempty','scalar','real','nonnan','positive'},       ...
                class(obj), 'Conductivity');
            if propVal < 1e5
                error(message('antenna:antennaerrors:LowConductivity'));
            end
            obj.MaterialStruct.Conductivity=propVal;
            if ~isempty(obj.Parent)
                setHasStructureChanged(obj.Parent);
            end
        end

        function set.Thickness(obj,propVal)
            if isequal(obj.Thickness,propVal)
                return;
            end
            validateattributes(propVal,{'numeric'},                     ...
                {'nonempty', 'scalar','real','nonnan', 'finite','nonnegative'},...
                class(obj), 'Thickness');
             obj.MaterialStruct.Thickness=propVal;
            if ~isempty(obj.Parent)
                setHasStructureChanged(obj.Parent);
            end
        end

        function set.EpsilonR(obj,propVal)
            if isequal(obj.EpsilonR,propVal)
                return;
            end
            validateattributes(propVal,{'numeric'},                     ...
                {'nonempty', 'scalar','real','nonnan', 'finite','positive'},...
                class(obj), 'EpsilonR');
            obj.MaterialStruct.EpsilonR=propVal;
            if ~isempty(obj.Parent)
                setHasStructureChanged(obj.Parent);
            end
        end

        function set.LossTangent(obj,propVal)
            if isequal(obj.LossTangent,propVal)
                return;
            end
            validateattributes(propVal,{'numeric'},                     ...
                {'nonempty', 'scalar','real','nonnan', 'finite','nonnegative'},...
                class(obj), 'LossTangent');
            obj.MaterialStruct.LossTangent=propVal;
            if ~isempty(obj.Parent)
                setHasStructureChanged(obj.Parent);
            end
        end

        function propVal = get.Conductivity(obj)
            propVal = obj.MaterialStruct.Conductivity;
        end
        
        function propVal = get.Thickness(obj)
            propVal = obj.MaterialStruct.Thickness;
        end
        
        function propVal = get.EpsilonR(obj)
            propVal = obj.MaterialStruct.EpsilonR;
        end
        
        function propVal = get.LossTangent(obj)
            propVal = obj.MaterialStruct.LossTangent;
        end
       
        function set.Parent(obj,propVal)
            obj.MaterialStruct.Parent = propVal;
        end

        function propVal = get.Parent(obj)
            propVal = obj.MaterialStruct.Parent;
        end
       
    end

    methods(Access=private)

        function tf= isPropertyChanged(obj,val1,val2)
            if isempty(val1)        % During contructor call
                tf = true;
            else
                % Handles scalar char and double case
                if ischar(val1) || isstring(val1)
                    tf = ~(strcmpi(val1,val2) && isa(val1,class(val2)));
                else
                    tf = ~isequal(val1,val2);
                end
            end
            if ~isempty(obj.Parent)&&tf
                setHasStructureChanged(obj.Parent)
            end
        end
    end

    methods (Access=protected)
        function group = getPropertyGroups(obj)
            if isempty(obj.Parent)
                propList = {'Conductivity','Thickness','EpsilonR','LossTangent'};
            elseif strcmpi(obj.Parent.ShapeDimension,'3D')
                % For 3d shapes
                createGeometry(obj.Parent);
                if isempty(freeBoundary(triangulation(obj.Parent.Triangles,obj.Parent.Points)))
                    propList = {'Conductivity','Thickness','EpsilonR','LossTangent',};
                else
                    propList = {'Conductivity','Thickness'};
                end
            elseif strcmpi(obj.Parent.ShapeDimension,'2D')
                % for 2d shapes
                propList = {'Conductivity','Thickness'};
            end
            group = matlab.mixin.util.PropertyGroup(propList,'');
        end
    end
    
     methods(Access = protected)
       function copyObj = copyElement(obj)
            
            % Make a shallow copy of all properties
            copyObj     = copyElement@matlab.mixin.Copyable(obj);
            
            % Find the property which has a handle in it and deep copy
            hmeta       = metaclass(obj);
            metaProps   = hmeta.PropertyList;
            x           = {};
            for i = 1:numel(metaProps)
                % --- The check for public property access is so that isa
                % on the obj.prop, 2 lines down does not fail.
                propGetAccess   = metaProps(i).GetAccess;
                propSetAccess   = metaProps(i).SetAccess;
                if any(strcmpi('public',propGetAccess))&&any(strcmpi('public',propSetAccess))
                    propName    = metaProps(i).Name;
                    if isfield(obj.MaterialStruct,propName)
                        tf          = isa(obj.(propName),'handle');
                        if tf
                            x       = [x {propName}]; %#ok<AGROW>
                        end
                    end
                end
            end  
            % propGetAccess = {metaProps(:).GetAccess};
            % propSetAccess = {metaProps(:).SetAccess};
            % p_field_get = cellfun(@(x) strcmpi(x,'public'),propGetAccess);
            % p_field_set = cellfun(@(x) strcmpi(x,'public'),propSetAccess);
            % p_field = p_field_set&p_field_get;
            % propName = {metaProps(p_field').Name};
            % n_prop1 = cellfun(@(x) isfield(obj.MaterialStruct,x),propName);
            % propName = propName(n_prop1);
            % x =[];
            % n_prop2 = cellfun(@(x) isa(obj.x,'handle'),propName);
        end 
    end
     
end
