function config = vl_override(config,update,varargin)
% VL_OVERRIDE  Override structure subset
%   CONFIG = VL_OVERRIDE(CONFIG, UPDATE) copies recursively the fileds
%   of the structure UPDATE to the corresponding fields of the
%   struture CONFIG.
%
%   Usually CONFIG is interpreted as a list of paramters with their
%   default values and UPDATE as a list of new paramete values.
%
%   VL_OVERRIDE(..., 'Warn') prints a warning message whenever: (i)
%   UPDATE has a field not found in CONFIG, or (ii) non-leaf values of
%   CONFIG are overwritten.
%
%   VL_OVERRIDE(..., 'Skip') skips fields of UPDATE that are not found
%   in CONFIG instead of copying them.
%
%   VL_OVERRIDE(..., 'CaseI') matches field names in a
%   case-insensitive manner.
%
%   Remark::
%     Fields are copied at the deepest possible level. For instance,
%     if CONFIG has fields A.B.C1=1 and A.B.C2=2, and if UPDATE is the
%     structure A.B.C1=3, then VL_OVERRIDE() returns a strucuture with
%     fields A.B.C1=3, A.B.C2=2. By contrast, if UPDATE is the
%     structure A.B=4, then the field A.B is copied, and VL_OVERRIDE()
%     returns the structure A.B=4 (specifying 'Warn' would warn about
%     the fact that the substructure B.C1, B.C2 is being deleted).
%
%   Remark::
%     Two fields are matched if they correspond exactly. Specifically,
%     two fileds A(IA).(FA) and B(IA).FB of two struct arrays A and B
%     match if, and only if, (i) A and B have the same dimensions,
%     (ii) IA == IB, and (iii) FA == FB.
%
%   See also: VL_ARGPARSE(), VL_HELP().

% Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

warn  = false ;
skip  = false ;
err   = false ;
casei = false ;

if length(varargin) == 1 & ~ischar(varargin{1})
  % legacy
  warn = 1 ;
end

if ~warn & length(varargin) > 0
  for i=1:length(varargin)
    switch lower(varargin{i})
      case 'warn'
        warn = true ;
      case 'skip'
        skip = true ;
      case 'err'
        err = true ;
      case 'argparse'
        argparse = true ;
      case 'casei'
        casei = true ;
      otherwise
        error(sprintf('Unknown option ''%s''.',varargin{i})) ;
    end
  end
end

% if CONFIG is not a struct array just copy UPDATE verbatim
if ~isstruct(config)
  config = update ;
  return ;
end

% if CONFIG is a struct array but UPDATE is not, no match can be
% established and we simply copy UPDATE verbatim
if ~isstruct(update)
  config = update ;
  return ;
end

% if CONFIG and UPDATE are both struct arrays, but have different
% dimensions then nom atch can be established and we simply copy
% UPDATE verbatim
if numel(update) ~= numel(config)
  config = update ;
  return ;
end

% if CONFIG and UPDATE are both struct arrays of the same
% dimension, we override recursively each field

for idx=1:numel(update)
  fields = fieldnames(update) ;

  for i = 1:length(fields)
    updateFieldName = fields{i} ;
    if casei
      configFieldName = findFieldI(config, updateFieldName) ;
    else
      configFieldName = findField(config, updateFieldName) ;
    end

    if ~isempty(configFieldName)
      config(idx).(configFieldName) = ...
          vl_override(config(idx).(configFieldName), ...
                      update(idx).(updateFieldName)) ;
    else
      if warn
        warning(sprintf('copied field ''%s'' which is in UPDATE but not in CONFIG', ...
                        updateFieldName)) ;
      end
      if err
        error(sprintf('The field ''%s'' is in UPDATE but not in CONFIG', ...
                      updateFieldName)) ;
      end
      if skip
        if warn
          warning(sprintf('skipping field ''%s'' which is in UPDATE but not in CONFIG', ...
                          updateFieldName)) ;
        end
        continue ;
      end
      config(idx).(updateFieldName) = update(idx).(updateFieldName) ;
    end
  end
end

% --------------------------------------------------------------------
function field = findFieldI(S, matchField)
% --------------------------------------------------------------------
field =  ''  ;
fieldNames = fieldnames(S) ;
for fi=1:length(fieldNames)
  if strcmpi(fieldNames{fi}, matchField)
    field = fieldNames{fi} ;
  end
end

% --------------------------------------------------------------------
function field = findField(S, matchField)
% --------------------------------------------------------------------

field =  '' ;
fieldNames = fieldnames(S) ;
for fi=1:length(fieldNames)
  if strcmp(fieldNames{fi}, matchField)
    field = fieldNames{fi} ;
  end
end
