% SETPROPERTYVALUE sets the value of a property of one or more objects.
% 
% CALL:
%        varargout = setPropertyValue(inputs, ...
%                                     input_names, ...
%                                     callerIsMethod, ...
%                                     propName, ...
%                                     setterFcn, ...
%                                     copy, ...
%                                     getInfo)
% 
% 
% 
% 
% 
% The setterFcn should have the following signature:
% 
%    setterFcn(object, plist, value)
% 
% The plist is passed to allow the setter function to modify the plist if
% necessary.
% 
% 
function varargout = setPropertyValue(varargin)

  % get inputs
  inputs         = varargin(1:end-6);
  input_names    = varargin{end-5};
  callerIsMethod = varargin{end-4};
  propName       = varargin{end-3};
  setterFcn      = varargin{end-2};
  doCopy         = varargin{end-1};
  getInfo        = varargin{end};
  
  % Check if this is a call for parameters
  if utils.helper.isinfocall(varargin{1:end-6})
    varargout{1} = getInfo(varargin{3});
    return
  end
  
  % Collect inputs
  if callerIsMethod
    objects = inputs{1};
    pls     = [];
    rest    = inputs(2:end);
    obj_invars = {};
  else
    [objects, obj_invars, rest] = utils.helper.collect_objects(inputs(:), '', input_names);
    [pls, pl_invars, rest]      = utils.helper.collect_objects(rest(:), 'plist');
    % Copy the user input plists because they may be modified in the setter
    % functions.
    if ~isempty(pls)
      pls = copy(pls, 1);
    end    
  end
  
  % Process the values we want to set
  [objects, values] = processSetterValues(objects, pls, rest, propName);
  
  % Decide on a deep copy or a modify
  bs = copy(objects, doCopy);
  
  usedValues = cell(size(bs));
  if isempty(setterFcn)
    
    if isempty(values)
      error('Please provide a value for the property ''%s''', propName);
    end
    
    for jj=1:numel(bs)
      usedValues{jj} = defaultSetter(bs(jj), values{jj}, propName);
    end    
    
  else
    % Loop over objects and set the values by calling the setter function
    for jj = 1:numel(bs)
      if isempty(values)
        if isempty(pls)
          % here we create a plist so that the setterFcn has a plist in
          % case it wants to modify it before it goes in the history.
          pls = plist();
        end
        usedValues{jj} = setterFcn(bs(jj), pls);
      else
        usedValues{jj} = setterFcn(bs(jj), pls, values{jj});
      end
    end
  end
  

  % Set output
  varargout{1} = bs;  
  if nargout > 1
    varargout{2} = usedValues;
  end
  if nargout > 2
    varargout{3} = pls;
  end
  if nargout > 3
    varargout{4} = obj_invars;
  end
  
end
% END

% Default setter function to set the description
% 
%  defaultSetter(obj, value, propertyName)
% 
function value = defaultSetter(varargin)
    
  obj      = varargin{1};
  value    = varargin{2};
  propName = varargin{3};

  % TODO: we could check the type of value here against the default/defined value
  
  obj.(propName) = value;
  
end