function optionStruct = mapOptionStruct(argin, varargin)
% maps options to a structure including standard value and error handling
%
% Syntax: optionStruct = mapOptionStruct(argin, varargin)
%
% optionStruct = mapOption(argin, 'option-name-1', default_value_1, ...
%   'option-name-2', default_value_2, ...) creates a structure with the
%   given option names. Option name/value pairs in argin are processed 
%   subsequently, overwriting the default values defined in varargin.
%
% A special option name 'option' for argin is reserved. It is expected to 
%   contain a structure with fields corresponding to the defined name/value
%   pairs. It overwrites the current optionStruct at the position where it 
%   is provided in argin.
%
% If argin consists of only a structure, this structure is returned to
%   as optionStruct, while missing fields are filled with the default 
%   values.
%
% You can view a template implementation, <a href="matlab: help mapOptionStruct>helpTemplate">here</a>.
%
% See also: mergeStruct, mapOption

% MDT: MATLAB developement tools
%   Copyright (C) 2013 Alexander Reinhold
%
% This program is free software: you can redistribute it and/or modify it 
%   under the terms of the GNU Lesser General Public License as published 
%   by the Free Software Foundation, either version 3 of the License, or 
%   (at your option) any later version.
%
% This program is distributed in the hope that it will be useful, but 
%   WITHOUT ANY WARRANTY; without even the implied warranty of 
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
%   Lesser General Public License for more details.
%
% You should have received a copy of the GNU Lesser General Public License 
%   along with this program.  If not, see <http://www.gnu.org/licenses/>.

%% preliminary error handling (varargin >> internal errors)
% Error, if varargin does not contain option-strings
if ~iscellstr(varargin(1:2:end))
    error('Varargin must be {''option-name-1'', default_value_1, ''option-name-2'', default_value_2, ..}');
end
% Error, if vargin is odd
if mod(length(varargin),2) ~= 0
    error(['The option ''' varargin{end} ''' has no default value']);
end
% Error, if number of outputs is wrong
if nargout ~= 1
    error(['mapOptionStruct returns a strructure, you might want to ' ...
           'use mapOption instead.']);
end
%% preliminary error handling (argin >> final user errors)
% Error, if cell-array does not contain option-strings
if ~iscellstr(argin(1:2:end)) && ~isstruct(argin{1})
    error('Argin must be {''option-name-1'', option, ''option-name-2'', option, ..}');
end
% Error, if argin is odd
if mod(length(argin),2) ~= 0 && ~isstruct(argin{1})
    error(['The option ''' argin{end} ''' is not mapped: corresponding value is missing.']);
end

%% set default values
optionFieldName = '';
for iDefault = 1:2:length(varargin)
    optionStruct.(varargin{iDefault}) = varargin{iDefault+1};
end

%% return if input arguments is empty
if isempty(argin)
    return
end

%% map options
if ~isstruct(argin{1})
    % handle optoinal input
    for iInput = 1:2:length(argin)
        
        % handle option input struct (overwrite, NOW)
        if strcmpi(argin{iInput}, 'option')
            optionStruct = mergeStruct(argin{iInput+1}, optionStruct);
            continue
        end
        
        % search for an option
        optionFound = 0;
        for iDefault = 1:2:length(varargin)
            if strcmpi(argin{iInput}, varargin{iDefault})
                optionStruct.(varargin{iDefault}) = argin{iInput+1};
                optionFound = 1;
                break
            end
        end
        if ~optionFound
            error(['Specified option ''' argin{iInput} ''' is unknown.']);
        end
    end
else
    % handle single input struct
    optionStruct = mergeStruct(argin{1}, optionStruct);
end
end

function helpTemplate
% %%% Template implementation
% function improveWorld(nameWho, timeWhen, varargin)
% % create a miraculous wonders
% %
% % improveWorld(.., 'planet', planetName) defines the planet on which the 
% %   wonder will be created. Default: 'earth'
% % improveWorld(.., 'method', methodName) defines the action that will be
% %   done. Default: 'preserve'
% % improveWorld(.., 'algorithm', algorithmName) defines how this wonder
% %   will be achieved. Default: 'humility'
% % Instead of the name/value pairs above, you can use 
% %   impoveWorld(.., atOnce) with a structure atOnce that contains some
% %   or all options as fieldnames and corresponding values.
% % If you already have a subset of options in a structure optionStruct,
% %   use impoveWorld(.., 'option', optionStruct, ..) to apply them as 
% %   custom default values or as impoveWorld(.., 'option', optionStruct)
% %   to overwrite any prior input name/value pair.
%
% % set optional parameters
% optionStruct = mapOptionStruct(varargin, ...
%                               'planet', 'earth', ...
%                               'method', 'preserve', ...
%                               'algorithm', 'humility');
% [..execution code missing..]
% end
% %/
end
