classdef vias < handle & matlab.mixin.SetGet & matlab.mixin.Copyable & matlab.mixin.CustomDisplay
% vias creates the vias object w.r.t the InputType in the board.
%
% v = vias(pobj) creates the vias object which takes pcbFileRead as input 
% and returns the vias of the respective InputType in the board. 
% 
% v = vias(pobj,'Cadnet','Cadnetname') takes pcbFileRead as input and 
% creates the vias object w.r.t. the Cadnet name.
%
% v = vias(pobj,'Layer',1) takes pcbFileRead as an input and creates the
% vias object for the 1st metal layer.
%
% vias properties:
%
%   pcBoard         - pcbFileRead object.
%   InputType       - Type of the input to get the vias. The choices
%                     are 'Layer' or 'Cadnet'.
%                     The default InputType is 'layer'. 
%   NumVias         - Number of vias w.r.t the InputType.
%   Center          - Center of the circular via in inch.
%   DrillHoleDiameter - Drill hole diameter of the via in inch.
%   ViaPadDiameter    - pad diameter of the via in inch.
%
%   If InputType = 'Layer'
%   LayerNumber  - Index/Position of the layer number in the stack. It 
%                  accepts the scalar value. The default layer number is
%                  first metal layer in the stack.
%   or
%
%   LayerName    - Name of the layer in the stack. It accepts the string.   
%   ViaInfo      - Info table of the pins in a respective layer. Returns 
%                  the table with below fields.
%                  ViaIndex          : The index of each via in a layer.
%                                      Max(ViaIndex) is the count of vias 
%                                      in a layer.
%                  PadStackName      : Name of the padstack.
%                  CadnetName        : Name of the cadnet.
%
%   If InputType = 'Cadnet'
%   CadnetName   - Name of the cadnet. 
%   ViaInfo      - Info table of the vias of a respective cadnet. Returns
%                  the table with below fields.
%                  ViaIndex          : The index of each via in a cadnet.
%                                      Max(ViaIndex) is the count of vias 
%                                      in a cadnet.
%                  PadStackName      : Name of the padstack.
%                  StartLayerName    : Start layer name of the via.
%                  StartLayerNumber  : Start layer number of the via.
%                  StopLayerName     : Stop layer name of the via.
%                  StopLayerNumber   : Stop layer number of the via.
%
% vias methods:
%   
%   shapes       - Returns the shape of the vias w.r.t the InputType
%
%   Copyright 2023 The MathWorks, Inc.

    properties(SetObservable)
        %pcbFileRead object.
        pcBoard
        %Type of the input to return the via.
        InputType
        %Layer Number.
        LayerNumber
        %Layer Name.
        LayerName
        %Cadnet Name.
        CadnetName
    end

    properties(SetAccess=protected, GetAccess = public)
        %Number of vias w.r.t the InputType
        NumVias
        %Center of the via pad
        Center
        %Drill hole diameter
        DrillHoleDiameter
        %Via pad diameter
        ViaPadDiameter
    end

    % properties (Access=private)
    %     hUiTable
    %     hUiTableFig
    %     hTab
    %     hTable
    %     SearchCompName
    %     SearchPartName
    %     SearchTable
    % end

    properties(SetAccess = private, GetAccess = public)
        % Info of the vias w.r.t the input type.
        ViaInfo
    end

     properties(Access=private)
        LayerNumFlag
     end

    methods
        function obj = vias(pcBoard,InputType,value)
            parserObj = inputParser;

            % add parameters
            addRequired(parserObj,'pcBoard');
            addRequired(parserObj,'InputType');
           
            if strcmpi(InputType,'Layer')
                if isstring(value) || ischar(value)
                   addOptional(parserObj,'LayerName',value);
                else
                   addOptional(parserObj,'LayerNumber',value);
                end
            elseif strcmpi(InputType,'Cadnet')
                addOptional(parserObj,'CadnetName',value);
            end

            % parse the input
            parse(parserObj,pcBoard,InputType);
            obj.pcBoard        = pcBoard;
          
            if strcmpi(InputType,'Layer')
                if isstring(value) || ischar(value)
                   obj.LayerName     = parserObj.Results.LayerName;
                   getLayerNumber(obj,value); 
                else
                   obj.LayerNumber     = parserObj.Results.LayerNumber; 
                   getLayerName(obj,value);
                   obj.LayerNumFlag = 1;
                end
            elseif strcmpi(InputType,'Cadnet')
                obj.CadnetName      = parserObj.Results.CadnetName;
            end
            obj.InputType = InputType;
        end

        function set.pcBoard(obj,propval)
            if ~isa(propval,'pcbFileRead')
                error(message('rfpcb:rfpcberrors:Unsupported','This input',...
                    'pcBoard. Supported input is pcbFileRead object'));
            end
            if isPropertyChanged(obj,obj.pcBoard,propval)
                obj.pcBoard =  propval;
            end
        end

        function set.InputType(obj,propval)
            if ~isempty(propval)
                validatestring(propval,{'Layer','Cadnet'}, ...
                    class(obj),'InputType');
                if isPropertyChanged(obj,obj.InputType,propval)
                    if isstring(propval)
                        propval = char(propval);
                    end
                    obj.InputType =  propval;
                    getViaInfo(obj);
                end
            end
        end
        
        function set.LayerName(obj,propval)
            mustBeNonempty(propval);
            if ~isempty(propval)
                validateLayerName(obj,propval);
                if isPropertyChanged(obj,obj.LayerName,propval)
                    if isstring(propval)
                        propval = char(propval);
                    end
                    obj.LayerName =  propval;
                    getLayerNumber(obj,obj.LayerName);
                    UpdateViaProp(obj);
                end
            end
        end

        function set.LayerNumber(obj,propval)
             mustBeNonempty(propval);
            if ~isempty(propval)
                % validate the input Layer number
                validateLayerNumber(obj,propval);
                if isPropertyChanged(obj,obj.LayerNumber,propval)
                    obj.LayerNumber =  propval;
                    getLayerName(obj,obj.LayerNumber);
                    UpdateViaProp(obj);
                end
            end
        end

        function set.CadnetName(obj,propval)
            mustBeNonempty(propval);
            if ~isempty(propval)
                if ~((ischar(propval) || isStringScalar(propval)) && strlength(propval) > 0)
                    error(message('rfpcb:rfpcberrors:PCBNameNotString','CadnetName'));
                end
                validateCadnetName(obj,propval)
                if isPropertyChanged(obj,obj.CadnetName,propval)
                    if isstring(propval)
                        propval = char(propval);
                    end
                    obj.CadnetName =  propval;
                    UpdateViaProp(obj);
                end
            end
        end
      
        function propVal = get.NumVias(obj)
            if strcmpi(obj.InputType,'Layer')
                viaCnt = getLayerViasCount(int32(obj.LayerNumber));
            elseif strcmpi(obj.InputType,'Cadnet')
                viaCnt = getCadnetViasCount(obj.CadnetName);
            end
            propVal = viaCnt;
        end
        function propVal = get.DrillHoleDiameter(obj)
            if strcmpi(obj.InputType,'Layer')
                viaIdx = 1:obj.NumVias;
                LayerN = obj.LayerNumber;
                Hdia = cell2mat(arrayfun(@(x)  getLayerViaData(int32(LayerN),x).drillHoleDiameter,int32(viaIdx),'UniformOutput',false))';
            elseif strcmpi(obj.InputType,'Cadnet')
                viaIdx = 1:obj.NumVias;
                Cname = obj.CadnetName;
                Hdia = cell2mat(arrayfun(@(x)  getCadnetViaData(Cname,x).drillHoleDiameter,int32(viaIdx),'UniformOutput',false))';
            end
            propVal = Hdia;
        end

        function propVal = get.ViaPadDiameter(obj)
            if strcmpi(obj.InputType,'Layer')
                viaIdx = 1:obj.NumVias;
                LayerN = obj.LayerNumber;
                VPdia = cell2mat(arrayfun(@(x)  getLayerViaData(int32(LayerN),x).diameter,int32(viaIdx),'UniformOutput',false))';
            elseif strcmpi(obj.InputType,'Cadnet')
                viaIdx = 1:obj.NumVias;
                Cname = obj.CadnetName;
                VPdia = cell2mat(arrayfun(@(x)  getCadnetViaData(Cname,x).diameter,int32(viaIdx),'UniformOutput',false))';
            end
            propVal = VPdia;
        end

        function propVal = get.Center(obj)
            if strcmpi(obj.InputType,'Layer')
                viaIdx = 1:obj.NumVias;
                LayerN = obj.LayerNumber;
                Vcenx = cell2mat(arrayfun(@(x)  getLayerViaData(int32(LayerN),x).xPosition,int32(viaIdx),'UniformOutput',false))';
                Vceny = cell2mat(arrayfun(@(x)  getLayerViaData(int32(LayerN),x).yPosition,int32(viaIdx),'UniformOutput',false))';
                Vcen = [Vcenx,Vceny];   
            elseif strcmpi(obj.InputType,'Cadnet')
                viaIdx = 1:obj.NumVias;
                Cname = obj.CadnetName;
                Vcenx = cell2mat(arrayfun(@(x)  getCadnetViaData(Cname,x).xPosition,int32(viaIdx),'UniformOutput',false))';
                Vceny = cell2mat(arrayfun(@(x)  getCadnetViaData(Cname,x).yPosition,int32(viaIdx),'UniformOutput',false))';
                Vcen = [Vcenx,Vceny];                
            end
            propVal = Vcen;
        end
    end
    methods (Hidden)
         function UpdateViaProp(obj)
            if ~isempty(obj.InputType)
                prop = get(obj);
                getViaInfo(obj);
            end
        end
        function validateLayerName(obj,propval)
            mLnum = obj.pcBoard.MetalLayer;
            mLname = stackUp(obj.pcBoard,mLnum).LayerName;
            mustBeMember(propval,mLname);
        end

        function validateCadnetName(obj,propval)
            mCnetName = cadnetList(obj.pcBoard).CadnetList;
            mustBeMember(propval,mCnetName);
        end

        function validateLayerNumber(obj,propval)
            mLnum = obj.pcBoard.MetalLayer;
            mustBeMember(propval,mLnum);
        end

        function LNum = getLayerNumber(obj,LName)
            stack = stackUp(obj.pcBoard,obj.pcBoard.MetalLayer);
            Lind = strcmpi(stack.LayerName,LName);
            LNum = stack.LayerNumber(Lind);
            obj.LayerNumber = LNum;
        end

        function LName = getLayerName(obj,LNum)
            stack = stackUp(obj.pcBoard,obj.pcBoard.MetalLayer);
            Lind = find(stack.LayerNumber(:,1)==LNum);
            LName = stack.LayerName(Lind);
            LName = LName{1};
            obj.LayerName = LName;
        end
        function getViaInfo(obj)
            if strcmpi(obj.InputType,'Layer')
                LayerN = obj.LayerNumber;
                viaCount = getLayerViasCount(int32(LayerN));
                % Pin Index
                viaIdx = 1:viaCount;
                % PadStackName
                PadStackN = arrayfun(@(x)  getLayerViaData(int32(LayerN),x).padstackName,int32(viaIdx),'UniformOutput',false)';
                % Cadnet name
                CadnetN = arrayfun(@(x)  getLayerViaData(int32(LayerN),x).cadnetName,int32(viaIdx),'UniformOutput',false)';
                % Variable names.
                varNames = {'ViaIndex','PadStackName','CadnetName'} ;
                obj.ViaInfo = table(viaIdx',PadStackN,CadnetN);

            elseif strcmpi(obj.InputType,'Cadnet')
                Cname = obj.CadnetName;
                viaCount = getCadnetViasCount(Cname);
                % Pin Index
                viaIdx = 1:viaCount;
                % PadStackName
                PadStackN = arrayfun(@(x)  getCadnetViaData(Cname,x).padstackName,int32(viaIdx),'UniformOutput',false)';
                % Start Layer Name
                StartLayerName = arrayfun(@(x)  getCadnetViaData(Cname,x).startLayerName,int32(viaIdx),'UniformOutput',false)';
                % Stop Layer NAme
                StopLayerName = arrayfun(@(x)  getCadnetViaData(Cname,x).endLayerName,int32(viaIdx),'UniformOutput',false)';
                % Start Layer Number
                StartLayerNum = cell2mat(arrayfun(@(x)  getCadnetViaData(Cname,x).startLayerNumber,int32(viaIdx),'UniformOutput',false))';
                % Stop Layer Number
                StopLayerNum = cell2mat(arrayfun(@(x)  getCadnetViaData(Cname,x).endLayerNumber,int32(viaIdx),'UniformOutput',false))';
                % Variable names.
                varNames = {'ViaIndex','PadStackName','StartLayerName','StartLayerNumber','StopLayerName','StopLayerNumber'} ;
                obj.ViaInfo = table(viaIdx',PadStackN,StartLayerName,StartLayerNum,StopLayerName,StopLayerNum);
            end
            obj.ViaInfo.Properties.VariableNames = varNames;
        end

        function PcbVias = createGeometry(obj,varargin)
           
            viacount = obj.NumVias;

            if(viacount > 0)
                viaIdx = 1:viacount;
                PcbVias = arrayfun(@(x) drawVia(obj,x),viaIdx);
            end
        end
        function S = shapes(obj)
            %shapes returns all the shapes of the vias w.r.t InputType.
            %
            % S = shapes(obj) returns the shapes of the vias w.r.t InputType.
            %

            % Copyright 2023 The MathWorks, Inc.

            poly = createGeometry(obj);
            npoly = numel(poly);
            S = cell(npoly,1);
            for i = 1:npoly
                S{i} = poly(i);
            end
        end 
    end

    methods(Hidden)
       
        function ViaPoly = drawVia(obj,viaIdx)

            orig_state = warning;
            warning('off','MATLAB:polyshape:boundary3Points');
            % Create the via polygons
            ViapolygonVertices = [nan,nan];
            Viaseparator = [nan,nan];

            if strcmpi(obj.InputType,'Layer')
                LayerN = obj.LayerNumber;
                [xValues, yValues, ~, ~] = getLayerViaPolygonData(int32(LayerN), int32(viaIdx));
                ViaD = getLayerViaData(int32(LayerN), int32(viaIdx));
                CName = ViaD.cadnetName;
            elseif strcmpi(obj.InputType,'Cadnet')
                StartLayerN = cell2mat(arrayfun(@(x) obj.ViaInfo.StartLayerNumber(x),viaIdx,'UniformOutput',false));
                StopLayerN = cell2mat(arrayfun(@(x) obj.ViaInfo.StopLayerNumber(x),viaIdx,'UniformOutput',false));
                StartLName = arrayfun(@(x) obj.ViaInfo.StartLayerName{x},viaIdx,'UniformOutput',false);
                StopLName = arrayfun(@(x) obj.ViaInfo.StopLayerName{x},viaIdx,'UniformOutput',false);
                CName = obj.CadnetName;
                [xValues, yValues, ~, ~] = getCadnetViaPolygonData(CName, int32(viaIdx));
            end  

            pinVertices = [xValues(:), yValues(:)];
            ViapolygonVertices = [ViapolygonVertices; Viaseparator; pinVertices];
            ViaPoly = antenna.Polygon("Vertices",ViapolygonVertices);

            % ToolTip
            Cname = strrep(CName, '_', '\_');
            if strcmpi(obj.InputType,'Layer')
                ToolTip = ['Via:',newline, 'Cadnet: ' Cname ,newline, 'LayerNumber: ', num2str(obj.LayerNumber), newline, 'LayerName: ', num2str(obj.LayerName)];
            elseif strcmpi(obj.InputType,'Cadnet')
                ToolTip = ['Via:',newline, 'Cadnet: ' Cname ,newline, 'StartLayer: ', num2str(StartLayerN(1)), newline, 'StopLayer: ', num2str(StopLayerN(1)),newline, 'StartLayerName: ',StartLName{1},newline, 'StopLayerName: ',StopLName{1} ];
            end
            ViaPoly.Name = ToolTip;
            warning(orig_state);
        end

        function tf = isPropertyChanged(~,val1,val2)
            % Check if property values are different
            if isempty(val1)        % During constructor call
                tf = true;
            else
                tf = ~isequal(val1,val2);
            end
        end

    end

    methods(Hidden)
        % function out = search(obj,options)
        % 
        %     arguments
        %         obj
        %         options.Component
        %         options.Part
        %         %options.PartName (1,:) {mustBeA(Name,{'char','string'}), mustBeValidSearchName(obj,Name)
        %         options.Layer
        %         options.ComponentType (1,:) string
        %         options.Value (1,1) double
        %     end
        %     obj.SearchTable = obj.PinInfo;
        %     Pindata = obj.SearchTable;
        %     % if obj.SearchCompName
        %     %     idxComp = matches(Pindata.ComponentName,SearchName);
        %     %     obj.SearchTable = Pindata(idxComp,:);
        %     % elseif obj.SearchPartName
        %     %     idxComp = matches(Pindata.PartName,SearchName);
        %     %     obj.SearchTable = Pindata(idxComp,:);
        %     % end
        % 
        %     if isfield(options,'Layer')
        %         pcbL = upper(options.Layer);
        %         if isa(pcbL,"string") ||  isa(pcbL,"char")
        %             mustBeMember(cellstr(upper(pcbL)),Pindata.LayerName)
        %             idxLayer = matches(Pindata.LayerName,pcbL);
        %         elseif isa(pcbL,"double")
        %             mustBeMember(options.Layer,Pindata.LayerName)
        %             idxLayer = matches(Pindata.LayerNumber,pcbL);
        %         end
        %         searchL = Pindata;
        %         obj.SearchTable = searchL(idxLayer,:);
        %     end
        % 
        %     if isfield(options,'Part')
        %         Pname = options.Part;
        %         mustBeMember(Pname,Pindata.PartName)
        %         searchTp = obj.SearchTable;
        %         idxPart = matches(searchTp.PartName,Pname);
        %         obj.SearchTable = searchTp(idxPart,:);
        %         % Search for the respective component
        %     end
        % 
        %     if isfield(options,'Component')
        %         Cname = options.Component;
        %         mustBeMember(Cname,Pindata.ComponentName)
        %         searchTc = obj.SearchTable;
        %         idxComp = matches(searchTc.ComponentName,Cname);
        %         obj.SearchTable = searchTc(idxComp,:);
        %         % Search for the respective component
        %     end
        % 
        %     % if isfield(options,'LayerNumber')
        %     %     mustBeMember(options.Vendor,obj.Vendor)
        %     %     vendors = options.Vendor;
        %     %     idxVendor = matches(obj.Vendor,options.Vendor,IgnoreCase=true);
        %     %     vendorXMLs = obj.xmlVendor(idxVendor);
        %     % else
        %     %     vendors = obj.Vendor;
        %     %     vendorXMLs = obj.xmlVendor;
        %     % end
        % 
        %     % if isfield(options,'Value')
        %     %     mustBeMember(options.Type,obj.Type)
        %     %     idx = find(obj.Availability(matches(obj.Vendor,vendors),...
        %     %         matches(obj.Type,options.Type)));
        %     %     vendors = obj.Vendor(idx);
        %     %     vendorXMLs = obj.xmlVendor(idx);
        %     % else
        %     %     options.Type = obj.Type;
        %     % end
        %     if nargout ==0
        %         pinD = obj.SearchTable;
        %         createUITable(obj,pinD)
        %     else
        %         out = obj.SearchTable;
        %     end
        % end
        % 
        % function createUITable(obj,pinD)
        %     pos = get(0,'defaultFigurePosition');
        %     fig = figure( ...
        %         'Name', 'Pins', ...
        %         'MenuBar', 'none', ...
        %         'Position', pos, ...
        %         'Tag', 'pinSearchTable', ...
        %         'NumberTitle', 'off');
        %     obj.hUiTableFig = fig;
        %     createFigureTab(obj,pinD);
        % end
        % 
        % function createFigureTab(obj,pinD)
        %     % Create Tabbed panel
        %     % pos = obj.hUiTableFig.Position;
        %     % First table
        %     tg = uitabgroup(obj.hUiTableFig);
        %     obj.hTab = uitab(tg,'Title','Search Results');
        %     obj.hTable = pinD;
        %     createTabTable(obj);
        % 
        %     % Second table
        %     obj.hTab = uitab(tg,'Title','PinInfo');
        %     obj.hTable = obj.PinInfo;
        %     createTabTable(obj);
        % end
        % 
        % function createTabTable(obj)
        %     pos = get(0,'defaultFigurePosition');
        %     pos(1:2) = [20 20];
        %     pos(3) = pos(3)-50;
        %     pos(4) = pos(4)-50;
        %     data = table2cell(obj.hTable);
        %     name = obj.hTable.Properties.VariableNames;
        % 
        %     obj.hUiTable = uitable( ...
        %         'Parent', obj.hTab, ...
        %         'Data', data, ...
        %         'ColumnName', name, ...
        %         'ColumnEditable', false, ...
        %         'Position', pos);
        % end

    end

    methods(Access=protected)
        function group = getPropertyGroups(obj)
            title = '';
            if strcmpi(obj.InputType,'Layer')
                if obj.LayerNumFlag==1
                    p = {'pcBoard','InputType','LayerNumber','NumVias','Center','DrillHoleDiameter','ViaPadDiameter','ViaInfo'};
                else
                    p = {'pcBoard','InputType','LayerName','NumVias','Center','DrillHoleDiameter','ViaPadDiameter','ViaInfo'};
                end
                group = objectProps(obj,p,title);
            elseif strcmpi(obj.InputType,'Cadnet')
                p = {'pcBoard','InputType','CadnetName','NumVias','Center','DrillHoleDiameter','ViaPadDiameter','ViaInfo'};
                group = objectProps(obj,p,title);
            end
        end

        function group = objectProps(obj, propertyDisplayList,title)
            propList    = properties(obj);
            numProps    = max(size(propertyDisplayList));
            for i = 1:numProps
                if(strcmpi(propertyDisplayList{i},propList(strcmpi(propertyDisplayList{i},propList))))
                    antennaProps.(char(propertyDisplayList{i})) = get(obj,propertyDisplayList{i});
                end
            end
            group = matlab.mixin.util.PropertyGroup(antennaProps,title);
        end

    end

end
