/// Check if a font variant exists for a family.
///
/// @access public
/// @param {String} $family - one of $_o-fonts-families
/// @param {String} $weight [regular] - The font weight.
/// @param {String} $style [normal] - The font style.
/// @return {Bool}
/// @see oFontsDefineCustomFont To register a custom font's avalible variants.
@function oFontsVariantExists($family, $weight, $style) {
	$weight: if($weight, $weight, 'regular'); // Handles a falsy `$weight` value.
	$style: if($style, $style, 'normal'); // Handles a falsy `$style` value.
	$font-properties: map-get($_o-fonts-families, $family);
	// Font is not registered.
	@if type-of($font-properties) != 'map' {
		@return false;
	}
	$font-variants: map-get($font-properties, variants);

	@each $variant in $font-variants {
		// Check if this weight and style variant exists for this family
		// Known issue with LibSass 3.0.2 - https://github.com/sass/libsass/issues/741
		@if (map-get($variant, weight) == $weight and map-get($variant, style) == $style) {
			@return true;
		}
	}
	@return false;
}

/// Check if a font variant has been included
/// in your project with the `oFonts` mixin.
///
/// @access public
/// @param {String} $family - one of $_o-fonts-families
/// @param {String} $weight [regular] - The font weight.
/// @param {String} $style [normal] - The font style.
/// @return {Bool}
/// @see oFonts To include fonts
@function oFontsVariantIncluded($family, $weight, $style) {
	$weight: if($weight, $weight, 'regular'); // Handles a falsy `$weight` value.
	$style: if($style, $style, 'normal'); // Handles a falsy `$style` value.
	$variant-key: _oFontsVariantKey($family, $weight, $style);

	@return map-has-key($_o-fonts-families-included, $variant-key);
}

/// Get a font-family stack with the appropriate fallbacks
///
/// @example scss
/// 	font-family: oFontsGetFontFamilyWithFallbacks(FinancierDisplayWeb);
///
/// @param {String} family
/// @return {String} - font-stack
@function oFontsGetFontFamilyWithFallbacks($family) {
	@if not map-has-key($_o-fonts-families, $family) {
		@error 'Font "#{inspect($family)}" not found. "$family" must be one of #{map-keys($_o-fonts-families)}.';
	}

	$properties: map-get($_o-fonts-families, $family);
	@return unquote(map-get($properties, font-family));
}

/// Removes a fonts fallbacks.
///
/// @example
///     $family: oFontsGetFontFamilyWithoutFallbacks('FinancierDisplayWeb, sans'); //FinancierDisplayWeb
///
/// @param {String|List} $family - Font family potentially with fallbacks.
/// @returns {String} Font family without fallbacks.
@function oFontsGetFontFamilyWithoutFallbacks($family) {
	@if type-of($family) == 'list' {
		@return nth($family, 1);
	}
	$index: str-index($family, ',');
	@if $index {
		$family: str-slice($family, 0, $index - 1);
	}
	@return if($family, $family, '');
}

/// Machine-readable CSS font-weight.
///
/// @example scss
/// 	font-weight: oFontsWeight(lighter);
///
/// @param {String} $keyword - Human-readable keyword e.g. 'semibold', 'regular', 'bold'.
/// @return {Number} - CSS font-weight
@function oFontsWeight($keyword) {
	@if not map-has-key($_o-fonts-weights, $keyword) {
		@error 'Font weight "#{$keyword}" not found. Must be one of #{inspect(map-keys($_o-fonts-weights))}.';
	}

	@return map-get($_o-fonts-weights, $keyword);
}

/// Capitalise first letter of $string
///
/// @access private
/// @param {String} $string - string to update
/// @return {String} - the string with a capitalised first letter
@function _oFontsStringCapitalise($string) {
	$string: $string + unquote(''); // Make sure $string has a type of String
	$first-letter: to-upper-case(str-slice($string, 0, 1));
	$rest-of-string: str-slice($string, 2);

	@return $first-letter + $rest-of-string;
}


/// For a font variant (family, weight, style) return
/// a key to identify the variant.
///
/// @access private
/// @param {String} $family - one of $_o-fonts-families
/// @param {String} $weight [regular] - The font weight.
/// @param {String} $style [normal] - The font style.
/// @return {String} - the key
@function _oFontsVariantKey($family, $weight, $style) {
	@return "#{$family}-#{$weight}-#{$style}";
}

/// Given an o-fonts options map, return a list of variant maps e.g.
/// (('family': 'MetricWeb', 'weight': 'regular', 'style': 'normal'),
/// ('family': 'FinancierDisplayWeb', 'weight': 'bold', 'style': 'normal'))
/// @param {Maps} $opts - An o-fonts options map (see `oFonts` and `oFontsVariantsIncluded`)
/// @return {List} - A list of font variant maps, which include 'family', 'weight', and 'style'.
@function _oFontsOptionsToVariants($opts) {
	$all-validated-variants: ();
	// Map of options to font name.
	$fonts: (
		'metric': 'MetricWeb',
		'financier-display': 'FinancierDisplayWeb',
		'financier-text': 'FinancierTextWeb',
	);
	// Include each font variant given.
	@each $option, $family in $fonts {
		// Get font variants to include from the opt map by font family.
		$variants: map-get($opts, $option);
		$variants: if($variants, $variants, ());
		// Merge recommended font variants if recommended fonts were requested.
		@if map-get($opts, 'recommended') == true {
			$recommended-variants: map-get($_o-fonts-recommended, $option);
			@if length($variants) > 0 {
				$variants: append($recommended-variants, $variants);
			} @else {
				$variants: $recommended-variants;
			}
		}
		@each $variant in $variants {
			// Validate that the variant style and weight is given.
			$weight: map-get($variant, 'weight');
			$style: map-get($variant, 'style');
			@if(type-of($weight) != 'string') {
				@error 'Could not include the "#{$family}" font with weight "#{inspect($weight)}". Excepted a string.';
			}
			@if(type-of($style) != 'string') {
				@error 'Could not include the "#{$family}" font with style "#{inspect($style)}". Excepted a string.';
			}
			// Include the font face for this variant.
			$all-validated-variants: append(
				$all-validated-variants,
				('family': $family, 'weight': $weight, 'style': $style)
			);
		}
	}
	@return $all-validated-variants;
}
