function objectInstance = create(targetObjectClassString, varargin)
%TARGET.CREATE    Create a target feature object.
%   targetFeatureObject = TARGET.CREATE(targetFeatureClass) creates and
%   returns an object of type targetFeatureClass.
%
%   targetFeatureObject = TARGET.CREATE(targetFeatureClass, Name, Value)
%   configures the object using one or more name-value pair arguments.
%   Supported argument names include:
%
%   Copy                Create a target feature object by copying values
%                       from the specified target feature object.
%   'propertyName'      Create a target feature object with the specified
%                       property set to the specified value. If used in
%                       conjunction with 'Copy', the specified value
%                       overrides the value in the copied object.
%
%   See also TARGET.ADD, TARGET.GET, TARGET.REMOVE

%   Copyright 2018-2020 The MathWorks, Inc.
    
    targetObjectClassString = string(targetObjectClassString);
    fullyQualifiedClassString = "target." + targetObjectClassString;
    
    % Create target repository instance
    tr = targetrepository.create();
    
    % Create object instance. This line will validate the first input.
    objectInstance = tr.create(targetObjectClassString);
    
    % Validation of any further name-value pairs is performed by a
    % dynamically created input parser as we have different acceptable
    % parameters depending which object is added.
    
    % Create name-value manager
    parser = target.internal.NameValueManager('FunctionName', 'target.create');
    
    % Add the parameter value pairs that exist for all target object
    % classes
    parser.addParameter('Copy', '');
    
    % Now add the dynamic property value pairs. These correspond to the
    % public settable properties on the target object class
    parser.addSettableProperties(objectInstance, ...
        'ValidationFunction', @(x) validateattributes(x, {'numeric', 'string', 'char', 'cell', 'target.Data'}, {}));
    
    parser.parse(varargin{:});
    
    arguments = parser.Results;
    
    % Handle Copy parameter.
    processCopy(tr, targetObjectClassString, fullyQualifiedClassString, arguments.Copy, objectInstance);
    
    % Get the properties we need to set
    nameValueParis = parser.getProcessedResultsWithout('Copy');
    
    propertiesToSet = fieldnames(nameValueParis);
    for i = 1:numel(propertiesToSet)
        propertyName = propertiesToSet{i};
        objectInstance.(propertyName) = nameValueParis.(propertyName);
    end
end

function processCopy(tr, classString, fullyQualifiedClassString, idOrInstance, toObject)
    
    import targetrepository.query.where
    import targetrepository.query.is
    
    % If the id is not specified, we don't copy anything
    if isempty(idOrInstance)
        return;
    end
    
    % Handle the specified copy value.
    if (isstring(idOrInstance) || ischar(idOrInstance))
        
        % We have a string, so query the target repository for the given
        % identifier
        fromObject = tr.get(classString, idOrInstance);
        
        % Error if the identifier does not correspond to an object instance
        if isempty(fromObject)
            error(message('targetrepository:Command:NoTargetObjectWithID',  fullyQualifiedClassString, idOrInstance));
        end
    elseif (isa(idOrInstance, fullyQualifiedClassString))
        % We already have the object instance to copy
        fromObject = idOrInstance;
    else
        % Unsupported type
        error(message('targetrepository:Command:IncorrectCopySpecifierType',  fullyQualifiedClassString));
    end
    
    % Copy the properties from the specified object to the new instance
    toObject.copyFrom(fromObject);
end
