/// Outputs typography styles for the Serif font.
/// Including: family, size, line-height, weight, style, and font sizes
/// for progressive font loading.
///
/// @example Output a font-family property only for the serif font.
/// 	@include oTypographySerif();
///
/// @example Output font-family, font-size, and line-height for the serif font.
/// 	@include oTypographySerif($scale: 1);
///
/// @example Output font-family, font-size, and line-height for the serif font. Set a custom line-height of 1.6.
/// 	@include oTypographySerif($scale: 1, $line-height: 1.6);
///
/// @example Output font-family, font-size, line-height, and font-weight for the serif font.
/// 	@include oTypographySerif($scale: 1, $weight: 'bold');
///
/// @example Output font-family, font-size, line-height, and font-style for the serif font.
/// 	@include oTypographySerif($scale: 1, $style: 'italic');
///
/// @example Output serif font properties without font-family.
/// 	@include oTypographySerif($scale: 1, $style: 'italic', $include-font-family: false);
///
/// @example Output serif font properties without sizes for the fallback font (without progressive font loading).
/// 	@include oTypographySerif($scale: 1, $style: 'italic', $include-progressive: false);
///
/// @param {Null | Number} $scale [null] - a scale number to output a font-size and line-height property
/// @param {Null | Number} $line-height [null] - custom line-height value to use instead of the scale's default
/// @param {Null | String} $weight [null] - output a font-weight property, e.g. 'bold', 'semibold'
/// @param {Null | String} $style [null] - output a font-style property, e.g. 'italic'
/// @param {Boolean} $include-font-family [true] - Set to false to exclude the font-family property from the output. This is useful when resizing typography, where the serif font-family is inherited from a parent element.
/// @param {Boolean} $include-progressive [true] - Set to false to exclude styles used for progressive font loading (font-family and size fallbacks for loading fonts).
@mixin oTypographySerif(
	$scale: null,
	$line-height: null,
	$weight: null,
	$style: null,
	$include-font-family: true,
	$include-progressive: $o-typography-progressive-font-loading
) {
	@include _oTypographyFor(
		$_o-typography-serif,
		$scale,
		$line-height,
		$weight,
		$style,
		$include-font-family,
		$include-progressive
	);
}

/// Outputs typography styles for the Display font.
/// Including: family, size, line-height, weight, style, and font sizes
/// for progressive font loading.
///
/// @example Output a font-family property only for the display font.
/// 	@include oTypographyDisplay();
///
/// @example Output font-family, font-size, and line-height for the display font.
/// 	@include oTypographyDisplay($scale: 1);
///
/// @example Output font-family, font-size, and line-height for the display font. Set a custom line-height of 1.6.
/// 	@include oTypographyDisplay($scale: 1, $line-height: 1.6);
///
/// @example Output font-family, font-size, line-height, and font-weight for the display font.
/// 	@include oTypographyDisplay($scale: 1, $weight: 'bold');
///
/// @example Output font-family, font-size, line-height, and font-style for the display font.
/// 	@include oTypographyDisplay($scale: 1, $style: 'italic');
///
/// @example Output display font properties without font-family.
/// 	@include oTypographyDisplay($scale: 1, $style: 'italic', $include-font-family: false);
///
/// @example Output display font properties without sizes for the fallback font (without progressive font loading).
/// 	@include oTypographyDisplay($scale: 1, $style: 'italic', $include-progressive: false);
///
/// @param {Null | Number} $scale [null] - a scale number to output a font-size and line-height property
/// @param {Null | Number} $line-height [null] - custom line-height value to use instead of the scale's default
/// @param {Null | String} $weight [null] - output a font-weight property, e.g. 'bold', 'semibold'
/// @param {Null | String} $style [null] - output a font-style property, e.g. 'italic'
/// @param {Boolean} $include-font-family [true] - Set to false to exclude the font-family property from the output. This is useful when resizing typography, where the display font-family is inherited from a parent element.
/// @param {Boolean} $include-progressive [true] - Set to false to exclude styles used for progressive font loading (font-family and size fallbacks for loading fonts).
@mixin oTypographyDisplay(
	$scale: null,
	$line-height: null,
	$weight: null,
	$style: null,
	$include-font-family: true,
	$include-progressive: $o-typography-progressive-font-loading
) {
	@include _oTypographyFor(
		$_o-typography-display,
		$scale,
		$line-height,
		$weight,
		$style,
		$include-font-family,
		$include-progressive
	);
}

/// Outputs typography styles for the Sans font.
/// Including: family, size, line-height, weight, style, and font sizes
/// for progressive font loading.
///
///
/// @example Output a font-family property only for the sans font.
/// 	@include oTypographySans();
///
/// @example Output font-family, font-size, and line-height for the sans font.
/// 	@include oTypographySans($scale: 1);
///
/// @example Output font-family, font-size, and line-height for the sans font. Set a custom line-height of 1.6.
/// 	@include oTypographySans($scale: 1, $line-height: 1.6);
///
/// @example Output font-family, font-size, line-height, and font-weight for the sans font.
/// 	@include oTypographySans($scale: 1, $weight: 'bold');
///
/// @example Output font-family, font-size, line-height, and font-style for the sans font.
/// 	@include oTypographySans($scale: 1, $style: 'italic');
///
/// @example Output sans font properties without font-family.
/// 	@include oTypographySans($scale: 1, $style: 'italic', $include-font-family: false);
///
/// @example Output sans font properties without sizes for the fallback font (without progressive font loading).
/// 	@include oTypographySans($scale: 1, $style: 'italic', $include-progressive: false);
///
/// @param {Null | Number} $scale [null] - a scale number to output a font-size and line-height property
/// @param {Null | Number} $line-height [null] - custom line-height value to use instead of the scale's default
/// @param {Null | String} $weight [null] - output a font-weight property, e.g. 'bold', 'semibold'
/// @param {Null | String} $style [null] - output a font-style property, e.g. 'italic'
/// @param {Boolean} $include-font-family [true] - Set to false to exclude the font-family property from the output. This is useful when resizing typography, where the sans font-family is inherited from a parent element.
/// @param {Boolean} $include-progressive [true] - Set to false to exclude styles used for progressive font loading (font-family and size fallbacks for loading fonts).
@mixin oTypographySans(
	$scale: null,
	$line-height: null,
	$weight: null,
	$style: null,
	$include-font-family: true,
	$include-progressive: $o-typography-progressive-font-loading
) {
	@include _oTypographyFor(
		$_o-typography-sans,
		$scale,
		$line-height,
		$weight,
		$style,
		$include-font-family,
		$include-progressive
	);
}

/// Set a custom font.
///
/// @example This example shows setting a custom font "MySansFont" as the "sans" font.
/// 	@include oTypographySetFont($type: 'sans', $family: 'MySansFont, sans');
///
/// @param {String} $type - One of 'sans', 'serif', or 'display'.
/// @param {String} $family - The font family to set.
@mixin oTypographySetFont($type, $family) {
	$font: oFontsGetFontFamilyWithoutFallbacks($family);
	@if not oFontsVariantExists($font, $weight: null, $style: null) {
		@error 'Could not set the font family "#{$family}" as "#{$font}" does not exist in "o-fonts". If you are setting a custom font, first register your custom font with o-fonts (https://registry.origami.ft.com/components/o-fonts).';
	}
	@if not index($_o-typography-types, $type) {
		@error 'Could not set font-family "#{$family}" for family style "#{$type}", family style must be one of "#{$_o-typography-types}".';
	}
	// Set the custom font.
	@if $type == 'sans' {
		$_o-typography-sans: $family !global;
	}
	@if $type == 'serif' {
		$_o-typography-serif: $family !global;
	}
	@if $type == 'display' {
		$_o-typography-display: $family !global;
	}
}

/// Define the typographic scale for a given font. The font may be an existing or custom font.
/// @param {String} $family - The font name or family to apply a scale to.
/// @param {Map} $scale - A map of scale numbers as keys (-2 to 10), with list values of font size and line height "(-2: (12, 16), -1: (14, 16), 0: (16, 20))" etc...
/// @example This example shows defining a custom font scale for a custom font "MySansFont". At scale "0" the font size will be 16px, and the line-height 20px.
///		@include oTypographyDefineFontScale($family: 'MySansFont, sans', (
///		   -2: (12, 16),
///		   -1: (14, 18),
///		    0: (16, 20),
///		    1: (18, 24),
///		    2: (20, 26),
///		    3: (24, 32),
///		    4: (28, 36),
///		    5: (32, 42),
///		    6: (40, 52),
///		    7: (48, 62),
///		    8: (56, 72),
///		    9: (72, 94),
///		   10: (84, 110)
///		));
@mixin oTypographyDefineFontScale($family, $scale) {
	@if type-of($scale) != 'map' {
		@error 'Could not set the scale for font "#{$family}", expected the scale to be a map but found "#{$scale}", of type "#{type-of($scale)}".';
	}
	@each $scale-number, $scale-item in $scale {
		@if type-of($scale-item) != 'list' or length($scale-item) != 2 {
			@error 'Expected each item in the scale to be a list with two items, the font size followed by the line height.';
		}
	}
	@if map-keys($scale) != (-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) {
		@error 'Expected the scale for font "#{$family}" to contain values for scales -2 to 10, but found scales "#{map-keys($scale)}".';
	}
	$family: oFontsGetFontFamilyWithoutFallbacks($family);
	$_o-typography-font-scale-by-font: map-merge(($family: $scale), $_o-typography-font-scale-by-font) !global;
}

/// Output a typography for font
/// @param {String} $font - The font to output typography for.
/// @param {Null | Number} $scale [null] - a scale number to output a font-size and line-height property
/// @param {Null | Number} $line-height [null] - custom line-height value to use instead of the scale's default
/// @param {Null | String} $weight [null] - output a font-weight property, e.g. 'bold', 'semibold'
/// @param {Null | String} $style [null] - output a font-style property, e.g. 'italic'
/// @param {Boolean} $include-font-family [true] - Set to false to exclude the font-family property from the output. This is useful when resizing typography, where the given font-family is inherited from a parent element.
/// @param {Boolean} $include-progressive [true] - Set to false to exclude styles used for progressive font loading (font-family and size fallbacks for loading fonts).
/// @access private
@mixin _oTypographyFor(
	$font,
	$scale,
	$line-height,
	$weight,
	$style,
	$include-font-family,
	$include-progressive
) {
	// Check font variant is supported.
	$font-without-fallbacks: oFontsGetFontFamilyWithoutFallbacks($font);
	$variant-exists: oFontsVariantExists($font-without-fallbacks, $weight, $style);
	@if not $variant-exists {
		@error 'Font family "#{$font-without-fallbacks}" of weight ' +
			'"#{if($weight, $weight, 'regular')}" and style ' +
			'"#{if($style, $style, 'normal')}" is not supported.';
	}

	// Check font variant has been included, and error if an error message
	// has been set by the user.
	$variant-included: oFontsVariantIncluded($font-without-fallbacks, $weight, $style);
	@if not $variant-included and type-of($o-typography-error-for-missing-fonts) == 'string' {
		@error 'The font face for font family "#{$font-without-fallbacks}" of ' +
			'weight "#{if($weight, $weight, 'regular')}" and style ' +
			'"#{if($style, $style, 'normal')}" has not been included in your ' +
			'project. #{inspect($o-typography-error-for-missing-fonts)}';
	}

	@if $include-font-family {
		font-family: if(type-of($font) == 'string', unquote($font), $font);
	}

	@if $scale {
		@include _oTypographySize($scale: $scale, $line-height: $line-height, $font: $font);
	} @else if $line-height {
		line-height: _oTypographyAdjustUnit($line-height);
	}

	@if $weight {
		font-weight: oFontsWeight($weight);
	}

	@if $style {
		font-style: unquote($style);
	}

	@if $include-progressive {
		@include _oTypographyProgressiveFontFallback($font, $scale, $weight, $style, $include-font-family);
	}
}

/// Outputs the font size and line height based on the scale, also
/// accepts an override line-height to output and a font adjustment
/// parameter for when outputting styles for progressively loaded fonts
///
/// @param {Number} $scale number on scale the sizes are based on
/// @param {Boolean | Number} $line-height size to override the line-height property
/// @param {String} $font [''] - The font to get the font size for, as fonts may have different scales. Uses the default scale if not specified.
@mixin _oTypographySize($scale, $line-height: false, $font: '') {
	@if type-of($scale) == map {
		@each $breakpoint, $scale in $scale {
			$current-line-height: if(length($scale) == 2, nth($scale, 2), $line-height);
			$current-scale: if(type-of($scale) == list and length($scale) != 0, nth($scale, 1), $scale);

			@if $breakpoint == 'default' and $current-scale {
				font-size: _oTypographyFontSizeFromScale($current-scale, 1, $font);
				line-height: _oTypographyLineHeightFromScale($current-scale, $current-line-height, $font);
			}

			@if $breakpoint != 'default' and $current-scale {
				@include oGridRespondTo($breakpoint) {
					font-size: _oTypographyFontSizeFromScale($current-scale, 1, $font);
					line-height: _oTypographyLineHeightFromScale($current-scale, $current-line-height, $font);
				}
			}
		}
	} @else {
		font-size: _oTypographyFontSizeFromScale($scale, 1, $font);
		line-height: _oTypographyLineHeightFromScale($scale, $line-height, $font);
	}
}

/// Outputs the progressive font fallback styles based on
/// font and scale if the font has fallback settings
///
/// @param {String} $font - Font to output fallback styles for (or .
/// @param {Number} $scale - Number on scale the size is based on.
/// @param {String} $weight [null] - The weight of the font which needs a fallback e.g. 'bold'.
/// @param {String} $style [null] - The style of the font which needs a fallback e.g. 'italic'.
/// @param {Boolean} $include-font-family [true] - Whether to output the font family of the fallback font (vs. just the size)
@mixin _oTypographyProgressiveFontFallback($font, $scale, $weight: null, $style: null, $include-font-family: true) {
	@if not $o-typography-progressive-font-loading {
		@error 'Progressive font fallbacks are disabled.' +
			'If you are sure you want to output progressive font fallbacks in ' +
			'your project set `$o-typography-progressive-font-loading: true;` ' +
			'at the top of your project, before importing Sass files. ' +
			'See the README for details and more options or contact the Origami team.';
	}
	@if $scale {
		// If falsy weight/style default to regular/normal.
		$weight: if($weight, $weight, 'regular');
		$style: if($style, $style, 'normal');
		// Find fallback config for font, weight, and style.
		@each $fallback in $_o-typography-progressive-font-fallbacks {
			// Get fallback properties.
			$fallback-family: map-get($fallback, 'family');
			$fallback-label: map-get($fallback, 'label');
			// Match regular by default.
			$fallback-weight: map-get($fallback, 'weight');
			$fallback-weight: if($fallback-weight, $fallback-weight, ('regular'));
			// Match normal by default.
			$fallback-style: map-get($fallback, 'style');
			$fallback-style: if($fallback-style, $fallback-style, ('normal'));
			$fallback-scale: map-get($fallback, 'fallback-scale');
			// Check if fallback is for the given font, weight, and style.;
			$label-match: $fallback-label == oFontsGetFontFamilyWithoutFallbacks($font); // For backward compatibility
			$family-match: oFontsGetFontFamilyWithoutFallbacks($fallback-family) == oFontsGetFontFamilyWithoutFallbacks($font);
			$weight-match: index($fallback-weight, $weight);
			$style-match: if($fallback-style, index($fallback-style, $style), true);
			// Output the fallback if it matches.
			@if ($label-match or ($family-match and $weight-match and $style-match)) {
				.#{$_o-typography-loading-prefix}-#{$fallback-label} & {
					@include _oTypographyFontSize($scale: $scale, $progressive-font-adjust: $fallback-scale, $font: $fallback-family);
					@if $include-font-family {
						font-family: map-get($fallback, 'fallback');
					}
				}
			}
		}
	}
}

/// Outputs the font size based on the scale, also accepts a font adjustment
/// parameter for when outputting styles for progressively loaded fonts
///
/// @param {Number} $scale - Number on scale the sizes are based on.
/// @param {Number} $progressive-font-adjust - Multiplier for font-size adjustments, when adding styles for progressively loaded font.
/// @param {String} $font [''] - The font to get the font size for, as fonts may have different scales. Uses the default scale if not specified.
@mixin _oTypographyFontSize($scale, $progressive-font-adjust: 1, $font: '') {
	@if type-of($scale) == map {
		@each $breakpoint, $number in $scale {
			@if $breakpoint == 'default' {
				@if type-of($number) == list {
					font-size: _oTypographyFontSizeFromScale(nth($number, 1), $progressive-font-adjust, $font);
				} @else {
					font-size: _oTypographyFontSizeFromScale($number, $progressive-font-adjust, $font);
				}
			} @else if($number) {
				@include oGridRespondTo($breakpoint) {
					@if type-of($number) == list {
						font-size: _oTypographyFontSizeFromScale(nth($number, 1), $progressive-font-adjust, $font);
					} @else {
						font-size: _oTypographyFontSizeFromScale($number, $progressive-font-adjust, $font);
					}
				}
			}

		}
	} @else {
		font-size: _oTypographyFontSizeFromScale($scale, $progressive-font-adjust, $font);
	}
}
