function [OPT,varargout] = passvalopt1(SPEC,varargin)
% passvalopt  Pass in and validate optional arguments. Initialise and
% permanently store default options for IRIS functions. If called with two
% output arguments, it passes out unused option names-values and does not
% throw a warning.
%
% Backend IRIS function.
% No help provided.

% -IRIS Toolbox.
% -Copyright (c) 2007-2012 Jaromir Benes.

persistent DEF;

if (nargin == 0 && nargout == 0) || isempty(DEF)
    % Initialise default options, and store them as a persistent struct.
    munlock();
    clear('functions');
    DEF = struct();
    DEF.data = irisopt.data();
    DEF.dates = irisopt.dates();
    DEF.estimateobj = irisopt.estimateobj();
    DEF.FAVAR = irisopt.FAVAR();
    DEF.freqdom = irisopt.freqdom();
    DEF.grfun = irisopt.grfun();
    DEF.latex = irisopt.latex();
    DEF.model = irisopt.model();
    DEF.qreport = irisopt.qreport();
    DEF.report = irisopt.report();
    DEF.tseries = irisopt.tseries();
    DEF.VAR = irisopt.VAR();
    DEF.SVAR = irisopt.SVAR();
    DEF.sstate = irisopt.sstate();
    DEF.iris = irisopt.IRIS();
    DEF.preparser = irisopt.preparser();
    DEF.strfun = irisopt.strfun();
    DEF.poster = irisopt.poster();
    DEF.bkwmodel = irisopt.bkwmodel();
    DEF.rhsmodel = irisopt.rhsmodel();
    DEF.syeq = irisopt.syeq();
    classlist = fieldnames(DEF);
    for i = 1 : length(classlist)
        funclist = fieldnames(DEF.(classlist{i}));
        for j = 1 : length(funclist)
            DEF.(classlist{i}).(funclist{j}) = ...
                xxconvert(DEF.(classlist{i}).(funclist{j}));
        end
    end
    mlock();
end

if nargout == 0
    return
elseif nargin == 0
    OPT = DEF;
    return
end

%**************************************************************************

if ischar(SPEC)
    index = strfind(SPEC,'.');
    SPEC = DEF.(SPEC(1:index-1)).(SPEC(index+1:end));
else
    SPEC = xxconvert(SPEC);
end

defaultname = SPEC.name;
defaultprimaryname = SPEC.primaryname;
changed = SPEC.changed;
validate = SPEC.validate;
OPT = SPEC.options;

% Return list of unused options.
varargout{1} = {};

if ~isempty(varargin)
    
    if iscellstr(varargin(1:2:end))
        % Called passvalopt(spec,'name',value,...).
        % This is the preferred way.
        username = varargin(1:2:end);
        uservalue = varargin(2:2:end);
        
    elseif nargin == 2 && isstruct(varargin{1})
        % Called passvalopt(spec,struct).
        username = fieldnames(varargin{1});
        uservalue = struct2cell(varargin{1})';
        
    elseif nargin == 2 && iscell(varargin{1});
        % Called passvalopt(spec,{'name',value}).
        username = varargin{1}(1:2:end);
        uservalue = varargin{1}(2:2:end);
    else
        utils.error('options', ...
            'Incorrect list of user options.');
    end
    
    if length(username) > length(uservalue)
        utils.error('options',...
            'No value assigned to the last option: ''%s''.', ...
            varargin{end});
    end
    
    % changed = false(1,length(defaultName));
    
    % Remove non-alphanumeric characters from user names.
    username = regexp(username,'[a-zA-Z]\w*','once','match');

    for i = 1 : length(username)
        if isempty(username{i})
            continue
        end
        index = strcmpi(username{i},defaultname);
        if any(index)
            pos = find(index,1);
            name = defaultprimaryname{pos};
            OPT.(name) = uservalue{i};
            changed.(name) = username{i};
        else
            varargout{1}{end+1} = username{i};
            varargout{1}{end+1} = uservalue{i};
        end
    end
    
    if nargout == 1 && ~isempty(varargout{1})
        utils.error('options',...
            'Invalid or obsolete option: ''%s''.',...
            varargout{1}{1:2:end});
    end
    
    % Validate the user-supplied options; default options are NOT validated.
    invalid = {};
    name = fieldnames(OPT);
    for i = 1 : length(name)
        if isempty(changed.(name{i}))
            continue
        end
        if ~isempty(validate.(name{i}))
            flag = validate.(name{i})(OPT.(name{i}));
            if ~flag
                invalid{end+1} = changed.(name{i}); %#ok<AGROW>
                invalid{end+1} = func2str(validate.(name{i})); %#ok<AGROW>
            end
        end
    end
    
    if ~isempty(invalid)
        utils.error('options',...
            'Value assigned to option ''%s='' does not pass validation ''%s''.',...
            invalid{:});
    end
    
end

end

% Subfunctions.

%**************************************************************************
function Y = xxconvert(X)

name = X(1:3:end);
nname = length(name);
name = regexp(name,'[a-zA-Z]\w*','match');
primaryname = {};
options = struct();
changed = struct();
validate = struct();
for i = 1 : nname
    n = length(name{i});
    % List of primary names.
    primaryname = [primaryname,name{i}(ones(1,n))]; %#ok<AGROW>
    options.(name{i}{1}) = X{(i-1)*3+2};
    % If this option is changed, save the exact name the user used so that an
    % error can refer to it should the user value fail to validate.
    changed.(name{i}{1}) = '';
    % Anonymous functions to validate user supplied values.
    validate.(name{i}{1}) = X{(i-1)*3+3};
end
% List of all possible names; name{i} maps into primaryname{i}.
name = [name{:}];

Y = struct();
Y.name = name; % List of all possible option names.
Y.primaryname = primaryname; % List of corresponding primary names.
Y.options = options; % Struct with primary names and default values.
Y.changed = changed; % Struct with empty chars, to be filled with the names used actually by the user.
Y.validate = validate; % Struct with validating functions.

end
% xxconvert().