function unit = validateLengthUnit(unit, varargin)
%validateLengthUnit Validate and standardize length unit name
%
%   standardName = validateLengthUnit(UNIT, FUNCNAME, VARNAME, ARGINDEX)
%   checks that UNIT is a valid length unit and converts it to a standard
%   unit name.  The following table lists each standard name that is
%   supported, and the values that correspond to that name. The function is
%   case-insensitive with respect to its input.  Spaces, periods, and
%   apostrophes are ignored.  Plural forms are accepted in most cases, but
%   the result (standardName) is always singular. The optional inputs
%   FUNCNAME, VARNAME, and ARGINDEX may be included for use in error
%   message formatting, with behavior identical to that provided by the
%   VALIDATEATTRIBUTES inputs of the same names.
%
%     Standard Name            Supported Names
%     -------------            ---------------
%     meter                'm', 'meter(s)', 'metre(s)'
%
%     centimeter           'cm', 'centimeter(s)', 'centimetre(s)'
%
%     millimeter           'mm', 'millimeter(s)', 'millimetre(s)'
%
%     micron               'micron(s)'
%
%     kilometer            'km', 'kilometer(s)', 'kilometre(s)'
%
%     nautical mile        'nm', 'naut mi', 'nautical mile(s)'
%
%     foot                 'ft',   'international ft',
%                          'foot', 'international foot',
%                          'feet', 'international feet'
%
%     inch                 'in', 'inch', 'inches'
%
%     yard                 'yd', 'yds', 'yard(s)'
%
%     mile                 'mi', 'mile(s)', 'international mile(s)'
%
%     U.S. survey foot     'sf',
%                          'survey ft',   'US survey ft', 'U.S. survey ft',
%                          'survey foot', 'US survey foot', 'U.S. survey foot',
%                          'survey feet', 'US survey feet', 'U.S. survey feet',
%
%     U.S. survey mile     'sm', 'survey mile(s)', 'statute mile(s)',
%     (statute mile)       'US survey mile(s)', 'U.S. survey mile(s)'
%
%     Clarke's foot        'Clarke''s foot', 'Clarkes foot'
%
%     German legal metre   'German legal metre', 'German legal meter'
%
%     Indian foot          'Indian foot'
%
%   Examples
%   --------
%   % The following return 'foot'
%   validateLengthUnit('foot')
%   validateLengthUnit('feet')
%   validateLengthUnit('international feet')
%
%   % The following return 'kilometer'
%   validateLengthUnit('kilometer')
%   validateLengthUnit('km')
%   validateLengthUnit('kilometre')
%   validateLengthUnit('kilometers')
%   validateLengthUnit('kilometres')
%
%   % The following return 'U.S. survey foot'
%   validateLengthUnit('U.S. survey foot')
%   validateLengthUnit('US survey ft')
%   validateLengthUnit('sf')
%   validateLengthUnit('U. S. survey feet')
%   validateLengthUnit('u s survey foot')
%
%   % In the following, a non-char input to validateLengthUnit results in
%   % an error message referencing a function name, 'FOO', a variable name,
%   % 'UNIT', and an argument number, 5.
%   validateLengthUnit(17,'FOO','UNIT',5)
%
%   See also UNITSRATIO.

% Copyright 2011-2017 The MathWorks, Inc.

% Ensure that UNIT is scalar text.
if nargin > 0
    unit = convertStringsToChars(unit);
end
validateattributes(unit,{'char','string'},{'nonempty','scalartext'},varargin{:})

% Save a copy of the original UNIT, to be used in case of error.
originalString = unit;

% Preprocess the UNIT:
%   1. Remove periods and apostrophes.
%   2. Remove spaces.
%   3. Convert to lower case.
unit(unit == '.' | unit == '''') = [];
unit(isspace(unit)) = [];
unit = lower(unit);

% Synonyms: Values of UNIT found in the first column corresponded to
% standard names in the second column.  However, some names in the
% second column (e.g., 'ussurveyfoot') are not fully in standard form,
% because they have been contracted and converted to lower case (to match
% the processing applied to UNIT).  Such names are fully standardized in
% a subsequent filtering step.
synonyms = {...
                      'm',  'meter'; ...
                 'meters',  'meter'; ...
                  'metre',  'meter'; ...
                 'metres',  'meter'; ...
                'microns',  'micron'; ...
                     'cm',  'centimeter'; ...
            'centimeters',  'centimeter'; ...
             'centimetre',  'centimeter'; ...
            'centimetres',  'centimeter'; ...
                     'mm',  'millimeter'; ...
            'millimeters',  'millimeter'; ...
             'millimetre',  'millimeter'; ...
            'millimetres',  'millimeter'; ...
                     'km',  'kilometer'; ...
             'kilometers',  'kilometer'; ...
              'kilometre',  'kilometer'; ...
             'kilometres',  'kilometer'; ...
                     'nm',  'nauticalmile'; ...
                 'nautmi',  'nauticalmile'; ...
          'nauticalmiles',  'nauticalmile'; ...
                     'in',  'inch'; ...
                 'inches',  'inch'; ...
                     'yd',  'yard'; ...
                    'yds',  'yard'; ...
                  'yards',  'yard'; ...
                     'ft',  'foot'; ...
        'internationalft',  'foot'; ...
      'internationalfoot',  'foot'; ...
                   'feet',  'foot'; ...
      'internationalfeet',  'foot'; ...
                     'mi',  'mile'; ...
                  'miles',  'mile'; ...
      'internationalmile',  'mile'; ...
     'internationalmiles',  'mile'; ...
                     'sf',  'ussurveyfoot'; ...
             'surveyfoot',  'ussurveyfoot'; ...
               'surveyft',  'ussurveyfoot'; ...
             'ussurveyft',  'ussurveyfoot'; ...
           'ussurveyfoot',  'ussurveyfoot'; ...
             'surveyfeet',  'ussurveyfoot'; ...
           'ussurveyfeet',  'ussurveyfoot'; ...
                     'sm',  'ussurveymile'; ...
             'surveymile',  'ussurveymile'; ...
            'surveymiles',  'ussurveymile'; ...
            'statutemile',  'ussurveymile'; ...
           'statutemiles',  'ussurveymile'; ...
          'ussurveymiles',  'ussurveymile'; ...
            'clarkesfoot',  'clarkesfoot'; ...
       'germanlegalmeter',  'germanlegalmetre'; ...
             'indianfoot',  'indianfoot' ...
         };

if ~any(strcmp(unit,synonyms(:,2)))
    % UNIT doesn't match any of the standard length unit names in the
    % second column of the synonyms array; see if it matches any of the
    % synonyms in the first column. If a match is found, replace the
    % synonym with the standard name.
    index = strcmp(unit, synonyms(:,1));
    if any(index)
        % The first column is known to be free of duplicates, so we
        % can assume that exactly one element in index is true.
        % Select the corresponding unit from the second column.
        unit = synonyms{index,2};
    else
        error(message('maputils:unitsratio:unsupportedUnit', originalString))
    end
end

% Filter contracted names: UNIT now corresponds to a standard name, except
% that missing spaces, missing periods, and capitalization may need to be
% restored.
contractedNames = { ...
    'nauticalmile',     'nautical mile'; ...
    'ussurveyfoot',     'U.S. survey foot'; ...
    'ussurveymile',     'U.S. survey mile'; ...
    'clarkesfoot',      'Clarke''s foot'; ...
    'germanlegalmetre', 'German legal metre'; ...
    'indianfoot',       'Indian foot' ...
     };
index = strcmp(unit,contractedNames(:,1));
if any(index)
    % There's a (unique) match with an element of the first column of
    % contractedNames; replace unit with the corresponding element from the
    % second column (the uncontracted form).
    unit = contractedNames{index,2};
end
