function [index_pairs, match_metric] = matchFeatures(varargin)
% This function provides support for code generation of matchFeatures.

% Copyright 2010 The MathWorks, Inc.
% $Revision: 1.1.10.4 $  $Date: 2011/04/02 01:31:15 $
%#codegen

coder.extrinsic('cvstGetCoordsChoice');
isXY = eml_const(cvstGetCoordsChoice('fcnMatchFeatures'));

% Parse and check inputs
[features1, features2, metric, match_thresh] = parseInputs(isXY, varargin{:});

% Code below is only used by Simulink, when the function is
% placed in the MATLAB Function block. Simulink first propagates
% the sizes followed by the types. The block of code below sets
% the sizes during the Simulink size propagation stage.
if eml_ambiguous_types
    % Set default values and sizes for outputs
    if isXY
        N1 = size(features1, 1);
        N2 = size(features2, 1);
        index_pairs  = zeros(N1*N2, 2);
        match_metric = zeros(N1*N2, 1);
    else
        N1 = size(features1, 2);
        N2 = size(features2, 2);
        index_pairs  = zeros(2, N1*N2);
        match_metric = zeros(1, N1*N2);
    end
    return;
end

% Declare that the inputs are most likely constant in order to generate
% more optimized code.
eml_prefer_const(varargin);

% Match features
if ~isXY
    [index_pairs, match_metric] = cvalgMatchFeatures(...
        features1, features2, metric, match_thresh);
else
    f1 = features1';
    f2 = features2';
    [index_pairs_internal, match_metric_internal] = cvalgMatchFeatures(...
        f1, f2, metric, match_thresh);
    index_pairs = index_pairs_internal';
    match_metric = match_metric_internal';
end

%==========================================================================
% Parse and check inputs
%==========================================================================
function [features1, features2, metric, match_thresh] = ...
    parseInputs(isXY, varargin)

eml_lib_assert(nargin == 3 || nargin > 4, ...
    'vision:matchFeatures:NotEnoughArgs', ...
    'Not enough input arguments.');

features1 = varargin{1};
features2 = varargin{2};

checkFeatures(features1);
checkFeatures(features2);

eml_lib_assert(isequal(class(features1), class(features2)), ...
    'vision:matchFeatures:featuresNotSameClass', ...
    'Expected FEATURES1 and FEATURES2 to be the same class.');
  
if isXY
    eml_lib_assert(size(features1, 2) == size(features2, 2), ...
        'vision:matchFeatures:featuresNotSameColumns', ...
        ['Expected FEATURES1 and FEATURES2 to have the same number ', ...
        'of columns.']);
else
    eml_lib_assert(size(features1, 1) == size(features2, 1), ...
        'vision:matchFeatures:featuresNotSameRows', ...
        ['Expected FEATURES1 and FEATURES2 to have the same number ', ...
        'of rows.']);
end

% Set default values
metric_val = 'SSD';
match_thresh_val = 1.0;

% Set parser inputs 
parms = struct( ...
    'Metric',                uint32(0), ...
    'MatchThreshold',        uint32(0));

popt = struct( ...
    'CaseSensitivity', true, ...
    'StructExpand',    true, ...
    'PartialMatching', false);
        
% Parse input parameters
if (nargin > 3)
    % Parse parameter/value pairs 
    optarg = eml_parse_parameter_inputs(parms, popt, varargin{3:end});
        
    metric = eml_tolower(eml_get_parameter_value(optarg.Metric, ...
        metric_val, varargin{3:end}));
    match_thresh  = eml_get_parameter_value(optarg.MatchThreshold, ...
        match_thresh_val, varargin{3:end});
        
    % Check parameters    
    checkMetric(metric);
    checkMatchThreshold(match_thresh);
else
    % Set parameters to default values
    metric = eml_tolower(metric_val);
    match_thresh = match_thresh_val;
end
    
%==========================================================================
function checkFeatures(features)

eml_lib_assert(ndims(features) == 2, ...
    'vision:matchFeatures:invalidFeatures', ...
    'Expected FEATURES to be two-dimensional.');

eml_lib_assert((isreal(features) && ~issparse(features)), ...
    'vision:matchFeatures:invalidFeatures', ...
    'Expected FEATURES to be real and nonsparse.');

classF = class(features);
eml_lib_assert(strcmp(classF,'logical') || ...
    strcmp(classF,'int8') || ...
    strcmp(classF,'uint8') || ...
    strcmp(classF,'int16') || ...
    strcmp(classF,'uint16') || ...
    strcmp(classF,'int32') || ...
    strcmp(classF,'uint32') || ...
    strcmp(classF,'single') || ...
    strcmp(classF,'double'), 'vision:matchFeatures:invalidFeatures', ...
    ['Data type of FEATURES must be double, single, logical, int8, ', ...
    'uint8, int16, uint16, int32, or uint32']);

%==========================================================================
function checkMetric(metric)
eml_lib_assert(ischar(metric) && (strcmp(metric, 'ssd') || ...
    strcmp(metric, 'sad') || ...
    strcmp(metric, 'normxcorr')), ...
    'vision:matchFeatures:invalidMetric', ['Expected ''Metric'' ', ...
    'value to match one of these strings: SSD, SAD, or normxcorr.']);

%==========================================================================
function checkMatchThreshold(T)
eml_lib_assert(~isempty(T), ...
    'vision:matchFeatures:invalidT', ...
    'Expected ''MatchThreshold'' value to be nonempty.');

eml_lib_assert(~isnan(T), ...
    'vision:matchFeatures:invalidT', ...
    'Expected ''MatchThreshold'' value to be non-NaN.');

eml_lib_assert(isfinite(T), ...
    'vision:matchFeatures:invalidT', ...
    'Expected ''MatchThreshold'' value to be finite.');

eml_lib_assert(~issparse(T), ...
    'vision:matchFeatures:invalidT', ...
    'Expected ''MatchThreshold'' value to be nonsparse.');

eml_lib_assert(isreal(T), ...
    'vision:matchFeatures:invalidT', ...
    'Expected ''MatchThreshold'' value to be real.');

eml_lib_assert(isscalar(T), ...
    'vision:matchFeatures:invalidT', ...
    'Expected ''MatchThreshold'' value to be a scalar.');

classT = class(T);
eml_lib_assert(...
    strcmp(classT,'int8') || ...
    strcmp(classT,'uint8') || ...
    strcmp(classT,'int16') || ...
    strcmp(classT,'uint16') || ...
    strcmp(classT,'int32') || ...
    strcmp(classT,'uint32') || ...
    strcmp(classT,'int64') || ...
    strcmp(classT,'uint64') || ...
    strcmp(classT,'single') || ...
    strcmp(classT,'double'), 'vision:matchFeatures:invalidT', ...
    ['Data type of ''MatchThreshold'' value must be double, ', ...
    'single, int8, uint8, int16, uint16, int32, uint32, int64, ', ...
    'or uint64']);

eml_lib_assert(isnumeric(T) && (T > 0 && T <= 100), ...
    'vision:matchFeatures:invalidT', ...
    ['Expected ''MatchThreshold'' value to be greater than zero ', ...
    'and less than or equal to 100.']);
