function [features, valid_points] = extractFeatures(varargin)
% This function provides support for code generation of extractFeatures.

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

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

% Parse inputs
[I, points, blockSize] = 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
    if isXY
        nPoints = size(points, 1);
        features = false(nPoints, blockSize*blockSize);
    else
        nPoints = size(points, 2);
        features = false(blockSize*blockSize, nPoints);
    end
    valid_points = points;
    return;
end

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

% Extract features from image I
if isXY
    [features, valid_points] = cvalgExtractFeatures(I, points, blockSize);
else
    input_points = fliplr(points' + 1);
    [f, output_points] = cvalgExtractFeatures(I, input_points, blockSize);
    features = f';
    valid_points = flipud(output_points' - 1);
end  
%==========================================================================
% Parse and check inputs
%==========================================================================
function [I, points, blockSize] = parseInputs(isXY, varargin)

eml_lib_assert(nargin >= 2, 'vision:extractFeatures:NotEnoughArgs', ...
    'Not enough input arguments.');

I = varargin{1};
points = varargin{2};

eml_lib_assert(~isempty(I), 'vision:extractFeatures:invalidI', ...
    'Expected I to be nonempty.');

eml_lib_assert(ndims(I) == 2, 'vision:extractFeatures:invalidI', ...
    'Expected I to be two-dimensional.');

eml_lib_assert((isreal(I) && ~issparse(I)), ...
    'vision:extractFeatures:invalidI', ...
    'Expected I to be real and nonsparse.');

classI = class(I);
eml_lib_assert(strcmp(classI,'logical') || ...
    strcmp(classI,'uint8') || ...
    strcmp(classI,'int16') || ...
    strcmp(classI,'uint16') || ...
    strcmp(classI,'single') || ...
    strcmp(classI,'double'), 'vision:extractFeatures:invalidI', ...
    ['Data type of I must be logical, uint8, int16, uint16, ' ...
     'single, or double']);

eml_lib_assert(isreal(points) && ~issparse(points), ...
    'vision:extractFeatures:invalidPOINTS', ...
    'Expected POINTS to be real and nonsparse.');

classPOINTS= class(points);
eml_lib_assert(strcmp(classPOINTS,'int16') || ...
    strcmp(classPOINTS,'uint16') || ...
    strcmp(classPOINTS,'int32') || ...
    strcmp(classPOINTS,'uint32') || ...
    strcmp(classPOINTS,'single') || ...
    strcmp(classPOINTS,'double'), ...
    'vision:extractFeatures:invalidPOINTS', ...
    ['Data type of POINTS must be int16, uint16, int32, uint32, ' ...
    'single, or double']);

if ~isempty(points)
    if isXY
        eml_lib_assert(length(size(points)) == 2 && size(points,2) == 2, ...
            'vision:extractFeatures:invalidPOINTS', ...
            'POINTS must be M-by-2 matrix.');
    else
        eml_lib_assert(length(size(points)) == 2 && size(points,1) == 2, ...
            'vision:extractFeatures:invalidPOINTS', ...
            'POINTS must be 2-by-N matrix.');
    end
end

%==========================================================================
% Parse optional inputs
%==========================================================================
parms = struct( ...
    'BlockSize', uint32(0), ...
    'Method',    uint32(0));

popt = struct( ...
    'CaseSensitivity', true, ...
    'StructExpand',    true, ...
    'PartialMatching', false);

methodOpt1 = 'Auto';
methodOpt2 = 'Block';

optarg = eml_parse_parameter_inputs(parms, popt, varargin{3:end});

blockSize = eml_get_parameter_value(optarg.BlockSize, ...
                                    11, varargin{3:end});

method = eml_tolower(eml_get_parameter_value(optarg.Method, ...
                     methodOpt1, varargin{3:end}));

eml_lib_assert(~isempty(blockSize), ...
    'vision:extractFeatures:invalidBLOCK_SIZE', ...
    'Expected BLOCK_SIZE to be nonempty.');

eml_lib_assert(~isinf(blockSize), ...
    'vision:extractFeatures:invalidBLOCK_SIZE', ...
    'Expected BLOCK_SIZE to be finite.');

eml_lib_assert(~issparse(blockSize), ...
    'vision:extractFeatures:invalidBLOCK_SIZE', ...
    'Expected BLOCK_SIZE to be nonsparse.');

eml_lib_assert(isreal(blockSize), ...
    'vision:extractFeatures:invalidBLOCK_SIZE', ...
    'Expected BLOCK_SIZE to be real.');

eml_lib_assert(isscalar(blockSize), ...
    'vision:extractFeatures:invalidBLOCK_SIZE', ...
    'Expected BLOCK_SIZE to be a scalar.');

eml_lib_assert(mod(blockSize, 2) && ...
    eml_is_positive_integer_scalar(blockSize), ...
    'vision:extractFeatures:invalidBLOCK_SIZE', ...
    'Expected BLOCK_SIZE to be an odd positive integer.');

eml_lib_assert(eml_is_const(blockSize), ...
    'vision:extractFeatures:invalidBLOCK_SIZE', ...
    'Expected BLOCK_SIZE to be constant.');

eml_lib_assert(strcmp(method, eml_tolower(methodOpt1)) ...
    || strcmp(method, eml_tolower(methodOpt2)), ...
    'vision:extractFeatures:invalidMethod', ...
    ['Expected Method to be ''', methodOpt1, ...
     ''' or ''', methodOpt2, '''.']);

