function output = instrfind(obj, varargin)
%INSTRFIND Find serial port objects with specified property values.
%
%    OUT = INSTRFIND returns all serial port objects that exist in memory.
%    The serial port objects are returned as an array to OUT.
%
%    OUT = INSTRFIND('P1', V1, 'P2', V2,...) returns an array, OUT, of
%    serial port objects whose property names and property values match 
%    those passed as param-value pairs, P1, V1, P2, V2,... The param-value
%    pairs can be specified as a cell array. 
%
%    OUT = INSTRFIND(S) returns an array, OUT, of serial port objects whose
%    property values match those defined in structure S whose field names 
%    are serial port object property names and the field values are the 
%    requested property values.
%   
%    OUT = INSTRFIND(OBJ, 'P1', V1, 'P2', V2,...) restricts the search for 
%    matching param-value pairs to the serial port objects listed in OBJ. 
%    OBJ can be an array of serial port objects.
%
%    Note that it is permissible to use param-value string pairs, structures,
%    and param-value cell array pairs in the same call to INSTRFIND.
%
%    When a property value is specified, it must use the same format as
%    GET returns. For example, if GET returns the Name as 'MyObject',
%    INSTRFIND will not find an object with a Name property value of
%    'myobject'. However, properties which have an enumerated list data type
%    will not be case sensitive when searching for property values. For
%    example, INSTRFIND will find an object with a Parity property value
%    of 'Even' or 'even'. 
%
%    Example:
%      s1 = serial('COM1', 'Tag', 'Oscilloscope');
%      s2 = serial('COM2', 'Tag', 'FunctionGenerator');
%      out1 = instrfind('Type', 'serial')
%      out2 = instrfind('Tag', 'Oscilloscope')
%      out3 = instrfind({'Port', 'Tag'}, {'COM2', 'FunctionGenerator'})
%
%    See also SERIAL/GET.
%

%    MP 7-13-99
%    Copyright 1999-2000 The MathWorks, Inc. 
%    $Revision: 1.5 $  $Date: 2000/08/29 23:48:08 $


% Error checking.
if ~isa(obj, 'instrument')
    error('OBJ must be an instrument object.');
end

switch nargin
case 1
    output = obj;
    return;
otherwise
    % Get the java objects.
    jobject = igetfield(obj, 'jobject');
   
	% Determine if a structure property was specifeid.
    [allPropOut, allValOut, errflag] = createArrayOfAllProps(varargin{:});
	if (errflag)
		error(lasterr);
	end
    [propToSearch, valToSearch, structPropsIndex] = findNonStructProps(allPropOut, allValOut);
    
    if (length(propToSearch) == length(allPropOut))
        % No struct properties were specified.
		try
	        out = find(jobject, varargin{:});
		catch
			error(lasterr);
		end
        if isempty(out)
            output = [];
            return;
        else
            output = javaToMATLAB(out);
        end
    else
        % Search on the non-struct properties.
        if ~isempty(propToSearch)
	        out = find(jobject, propToSearch, valToSearch);
        else
            out = find(jobject);
        end
        
        % Search objects for struct props.
        if (isempty(out))
            output = [];
            return;
        else
            % Search MATLAB objects for struct props. 
            out = checkStructProps(out, structPropsIndex, allPropOut, allValOut);
  
            % Convert to MATLAB objects.
            output = javaToMATLAB(out);
        end
    end
end

% ************************************************************************
% Create a cell of properties and property values that are being searched.
function [allProps, allValues, errflag] = createArrayOfAllProps(varargin)

% Create a cell array of properties that are being searched.
index = 1;
allProps = {};
allValues = {};
errflag = 0;

try
	while index <= nargin
		switch class(varargin{index})
		case 'char'
			% Concatenate properties.
			prop = varargin{index};
			allProps = {allProps{:} prop};
			
			% Concatenate values.
			value = varargin{index+1};
			allValues = {allValues{:} value}; 
			
			% Update index.
			index = index+2;        
		case 'cell'
			% Concatenate properties.
			prop = varargin{index};
			if (iscell([prop{:}]))
				errflag = 1;
				lasterr('Invalid param-value pairs specified.');
				return;
			end
			allProps = {allProps{:} prop{:}};
			
			% Concatenate values.
			value = varargin{index+1};
			allValues = {allValues{:} value{:}};
			
			% Update index.
			index = index+2;        
		case 'struct'
			% Concatenate properties.
			propStruct = varargin{index};
			prop = fieldnames(propStruct);
			allProps = {allProps{:} prop{:}};
			
			% Concatenate values.  
			value = struct2cell(propStruct);
			allValues = {allValues{:} value{:}};
			
			% Update index.
			index = index+1;    
		otherwise
			errflag = 1;
			lasterr('Invalid param-value pairs specified.');
			return;
		end
	end
catch
	errflag = 1;
	lasterr('Invalid param-value pairs specified.');
	return;
end

% ************************************************************************
% Get the PV pairs minus the struct properties.
function [propOut, valOut, structPropsIndex] = findNonStructProps(props, vals)

% Determine the struct properties.
structProps = com.mathworks.toolbox.instrument.Instrument.findStructProps;

% Determine if a struct property was specified.
propOut = props;
valOut = vals;
structPropsIndex = [];
count = 1;

for i = 1:length(structProps)
    allIndex = strmatch(structProps{i}, props);
    index = strmatch(structProps{i}, propOut);
    if ~isempty(index)
        % Remove the struct property and value.
		if (length(index) > 1)
		    allIndex = strmatch(structProps{i}, props, 'exact');
			index = strmatch(structProps{i}, propOut, 'exact');
		end
        propOut(index) = [];
        valOut(index)  = [];
        
        % Store the index of the structure property.
        structPropsIndex(count) = allIndex;
        count = count+1;
    end
end

% ************************************************************************
% Search objects for struct properties.
function out = checkStructProps(obj, structPropsIndex, props, vals)

out = [];
for i = 1:length(structPropsIndex)
    % Get the Structure property and value.
    propName = props{structPropsIndex(i)};
    propValue = vals{structPropsIndex(i)};
    
    % Find the max to search through.
    if isempty(out)
        if i == 1
	       index = length(obj);
       else
           out = [];
           return;
       end
    else
        index = length(out);
    end

    out = [];
    % Loop through objects and determine if a match exists.
    for k = 1:index
        try
            if isequal(get(obj(k), propName), propValue)
                out = [out obj(k)];
            end
        catch
            % An error occurred during the GET - the property
            % is invalid for that object type.
        end
    end
end

% ************************************************************************
% Convert objects to their appropriate MATLAB object type.
function obj = javaToMATLAB(allObjects)

obj = [];

for i = 1:length(allObjects)
    if (strcmp(class(allObjects(i)), 'handle.Database'))
    	% Do nothing. 
    elseif strcmp(class(allObjects(i)), 'javahandle.com.mathworks.toolbox.instrument.SerialComm')
        obj = [obj serial(allObjects(i))];
    elseif strcmp(class(allObjects(i)), 'javahandle.com.mathworks.toolbox.instrument.SerialVisa')
        obj = [obj visa(allObjects(i))];
    elseif strcmp(class(allObjects(i)), 'javahandle.com.mathworks.toolbox.instrument.GpibVisa')
        obj = [obj visa(allObjects(i))];
    elseif strcmp(class(allObjects(i)), 'javahandle.com.mathworks.toolbox.instrument.VxiGpibVisa')
    	obj = [obj visa(allObjects(i))];
    elseif strcmp(class(allObjects(i)), 'javahandle.com.mathworks.toolbox.instrument.VxiVisa')
   		obj = [obj visa(allObjects(i))];
    else
        obj= [obj gpib(allObjects(i))];
    end
end

