/// # Accessing Sizes
/// @group sizes


// Size [function]
// ---------------
/// Access a named size in your `$sizes` map,
/// using any comparable units.
///
/// @group sizes
///
/// @param {string | length | list} $size -
///   The name of a size in your configuration (e.g. `line-height`),
///   or a length to be converted into different units (e.g. `24px`),
///   or a base-size and adjustments to be made
///   (e.g. `24px ('minor-third': 2)`),
///   or a `calc(%s + %s) ('root', 'rhythm')` recipie
///   for building `calc` values.
///   For the sake of consistent documentation,
///   I recommend keeping adjustments in the configuration
///   whenever possible.
/// @param {vararg} $unit... -
///   The desired unit for the output (e.g. `px` or `rem`),
///   and any other arguments required for the conversion.
/// @return {length} -
///   The calculated length, in the requested units.
@function size(
  $size,
  $unit...
) {
  $size: _ac-scale-get-size($size);
  $size: if(length($unit) > 0, convert-units($size, $unit...), $size);

  @return $size;
}


// Negative [function]
// -------------------
/// Return the inverse value of any length,
/// in comparable units.
///
/// @group sizes
///
/// @param {string | length | list} $size -
///   The name of a size in your configuration (e.g. `line-height`),
///   or a length to be converted into different units (e.g. `24px`),
///   or a base-size and adjustments to be made
///   (e.g. `24px ('minor-third': 2)`).
///   For the sake of consistent documentation,
///   I recommend keeping adjustments in the configuration
///   whenever possible.
/// @param {vararg} $unit... -
///   The desired unit for the output (e.g. `px` or `rem`),
///   and any other arguments required for the conversion.
/// @return {length} -
///   The calculated negative length, in the requested units.
@function negative(
  $size,
  $unit...
) {
  $size: size($size, $unit...);

  @if (type-of($size) == 'string') {
    $size: _ac-str-replace($size, 'calc(', 'calc(-1 * (');
    $size: str-slice($size, 1, -2) + '))';
    @return $size;
  }

  @return -1 * size($size, $unit...);
}


// Ratio [function]
// ----------------
/// Retrieve a scale ratio by name
/// from either the `$_DEFAULT-RATIOS`
/// or user `$ratios` configurations.
///
/// @group sizes
///
/// @param {string | number} $ratio -
///   The key-name or value of a ratio
/// @return {number} -
///   The numeric value of a ratio
/// @example scss
///   /* Octave: #{ratio('octave')} */
///   /* Fifth: #{ratio('fifth')} */
@function ratio(
  $ratio
) {
  $_ratio-options: map-merge($_DEFAULT-RATIOS, $ratios);
  $_new-ratio: map-get($_ratio-options, $ratio) or $ratio;

  @if ($ratio != $_new-ratio) and map-has-key($_ratio-options, $_new-ratio) {
    $_new-ratio: ratio($_new-ratio);
  }

  @return $_new-ratio;
}


// Square [mixin]
// --------------
/// Create a square by setting equal CSS `height` and `width` properties
/// with the given size & unit value.
///
/// @group sizes
///
/// @param {string | length | list} $size -
///   The name of a size in your configuration (e.g. `line-height`),
///   or a length to be converted into different units (e.g. `24px`),
///   or a base-size and adjustments to be made
///   (e.g. `24px ('minor-third': 2)`).
///   For the sake of consistent documentation,
///   I recommend keeping adjustments in the configuration
///   whenever possible.
/// @param {vararg} $unit... -
///   The desired unit for the output (e.g. `px` or `rem`),
///   and any other arguments required for the conversion.
/// @output -
///   Equal CSS height and width properties,
///   set to the given size and units.
@mixin square(
  $size,
  $unit...
) {
  $size: size($size, $unit...);

  height: $size;
  width: $size;
}



// Get Size
// --------
/// Access a named size in your `$sizes` map.
///
/// @access private
///
/// @param {string | length | list} $size -
///   The name of a size in your configuration (e.g. `line-height`),
///   or a length to be converted into different units (e.g. `24px`),
///   or a base-size and adjustments to be made
///   (e.g. `24px ('minor-third': 2)`),
///   or a `calc(%s + %s) ('root', 'rhythm')` recipie
///   for building `calc` values.
///   For the sake of consistent documentation,
///   I recommend keeping adjustments in the configuration
///   whenever possible.
/// @throw -
///   The calculated value is not a valid CSS length.
/// @return {length} -
///   The calculated length
@function _ac-scale-get-size(
  $size
) {
  // Parse arguments
  $size: map-get($sizes, $size) or $size;
  $base: nth($size, 1);
  $adjust: if(length($size) > 1, nth($size, 2), null);

  // Recursive check
  $size: if(map-has-key($sizes, $base), _ac-scale-get-size($base), $base);

  // Adjustments
  $adjust-type: type-of($adjust);
  $calc: (type-of($size) == 'string') and (str-slice($size, 1, 5) == 'calc(');

  @if (not $calc) and ($adjust-type == 'map') {
    @each $key, $value in $adjust {
      $size: _ac-scale-adjust-size($size, $key, $value...);
    }
  } @else if $calc and ($adjust-type == 'list') {
    $values: ();

    @each $value in $adjust {
      $values: append($values, _ac-scale-get-size($value));
    }

    $size: _ac-interpolate($base, $values...);
  }


  // Validation
  @if (type-of($size) == 'number') or $calc {
    @return $size;
  }

  @error '#{$size} is not a valid length for CSS.';
}



// Adjust Size
// -----------
/// Calculate ratio, linear, or arbitrary adjustments
/// to a base size.
///
/// @access private
///
/// @param {number} $size -
///   The original size to perform adjusments on
/// @param {string | number} $key -
///   The adjustment to perform,
///   given as either a numeric ratio,
///   named ratio keyword,
///   or function name to call.
/// @param {arglist} $value… -
///   Any required arguments,
///   such as the number of times to apply a ratio,
///   or additional information to be passed to a function.
/// @return {number} -
///   Results of the adjustment.
@function _ac-scale-adjust-size(
  $size,
  $key,
  $value...
) {
  $ratio: ratio($key);
  $value: if(length($value) == 1, nth($value, 1), $value);

  @if ($ratio == 'linear') {
    @return $size * $value;
  } @else if (type-of($ratio) == 'number') {
    $multiplier: _acs-pow($ratio, $value);
    @return $size * $multiplier;
  }

  $function: _ac-scale-get-function($key);
  $args: if($value, join($size, $value), $size);
  @return call($function, $args...);
}
