/// Syntax Normalization
/// ====================
/// Susy is divided into two layers:
/// "Su" provides the core math functions with a stripped-down syntax,
/// while "Susy" adds global settings, shorthand syntax,
/// and other helpers.
/// Each setting (e.g. span, location, columns, spread, etc.)
/// has a single canonical syntax in Su.
///
/// This normalization module helps translate between those layers,
/// transforming parsed Susy input into
/// values that Su will understand.
///
/// @group _normal
///
/// @see susy-normalize
/// @see susy-normalize-span
/// @see susy-normalize-columns
/// @see susy-normalize-spread
/// @see susy-normalize-location



// Susy Normalize
// --------------
/// Normalize the values in a configuration map.
/// In addition to the global `$susy` properties,
/// this map can include local span-related information,
/// like `span` and `location`.
///
/// Normalization does not check that values are valid,
/// which will happen in the Su math layer.
/// These functions merely look for known Susy syntax –
/// returning a map with those shorthand values
/// converted into low-level data for Su.
/// For example `span: all` and `location: first`
/// will be converted into specific numbers.
///
/// @group _normal
/// @see $susy
/// @see susy-parse
///
/// @param {map} $config -
///   Map of Susy configuration settings to normalize.
///   See `$susy` and `susy-parse()` documentation for details.
/// @param {map | null} $context [null] -
///   Map of Susy configuration settings to use as global reference,
///   or `null` to use global settings.
///
/// @return {map} -
///   Map of Susy configuration settings,
///   with all values normalized for Su math functions.
@function susy-normalize(
  $config,
  $context: null
) {
  // Spread
  @each $setting in ('spread', 'container-spread') {
    $value: map-get($config, $setting);

    @if $value {
      $value: susy-normalize-spread($value);
      $config: map-merge($config, ($setting: $value));
    }
  }

  // Columns
  $columns: map-get($config, 'columns');

  @if $columns {
    $columns: susy-normalize-columns($columns, $context);
    $config: map-merge($config, ('columns': $columns));
  }

  @if not $columns {
    $map: type-of($context) == 'map';
    $columns: if($map, map-get($context, 'columns'), null);
    $columns: $columns or susy-get('columns');
  }

  // Span
  $span: map-get($config, 'span');

  @if $span {
    $span: susy-normalize-span($span, $columns);
    $config: map-merge($config, ('span': $span));
  }

  // Location
  $location: map-get($config, 'location');

  @if $location {
    $location: susy-normalize-location($span, $location, $columns);
    $config: map-merge($config, ('location': $location));
  }

  @return $config;
}



// Normalize Span
// --------------
/// Normalize `span` shorthand for Su.
/// Su span syntax allows an explicit length (e.g. `3em`),
/// unitless column-span number (e.g. `3` columns),
/// or an explicit list of columns (e.g. `(3 5 8)`).
///
/// Susy span syntax also allows the `all` keyword,
/// which will be converted to a slice of the context
/// in normalization.
///
/// @group _normal
///
/// @param {number | list | 'all'} $span -
///   Span value to normalize.
/// @param {list} $columns -
///   Normalized list of columns in the grid
///
/// @return {number | list} -
///   Number or list value for `$span`
@function susy-normalize-span(
  $span,
  $columns: susy-get('columns')
) {
  @if ($span == 'all') {
    @return length($columns);
  }

  @return $span;
}



// Normalize Columns
// -----------------
/// Normalize `column` shorthand for Su.
/// Su column syntax only allows column lists (e.g. `120px 1 1 1 120px`).
///
/// Susy span syntax also allows a unitless `slice` number (e.g `of 5`),
/// which will be converted to a slice of the context
/// in normalization.
///
/// @group _normal
///
/// @param {list | integer} $columns -
///   List of available columns,
///   or unitless integer representing a slice of
///   the available context.
/// @param {map | null} $context [null] -
///   Map of Susy configuration settings to use as global reference,
///   or `null` to access global settings.
///
/// @return {list} -
///   Columns list value, normalized for Su input.
///
/// @throws
///   when attempting to access a slice of asymmetrical context
@function susy-normalize-columns(
  $columns,
  $context: null
) {
  $context: $context or susy-settings();

  @if type-of($columns) == 'list' {
    @return _susy-flatten($columns);
  }

  @if (type-of($columns) == 'number') and (unitless($columns)) {
    $span: $columns;
    $context: map-get($context, 'columns');
    $symmetrical: susy-repeat(length($context), nth($context, 1));

    @if ($context == $symmetrical) {
      @return susy-repeat($span, nth($context, 1));
    } @else {
      $actual: 'of `#{$span}`';
      $columns: 'grid-columns `#{$context}`';
      @return _susy-error(
        'context-slice #{$actual} can not be determined based on #{$columns}.',
        'susy-normalize-columns');
    }
  }

  @return $columns;
}



// Normalize Spread
// ----------------
/// Normalize `spread` shorthand for Su.
/// Su spread syntax only allows the numbers `-1`, `0`, or `1` –
/// representing the number of gutters covered
/// in relation to columns spanned.
///
/// Susy spread syntax also allows keywords for each value –
/// `narrow` for `-1`, `wide` for `0`, or `wider` for `1` –
/// which will be converted to their respective integers
/// in normalization.
///
/// @group _normal
///
/// @param {0 | 1 | -1 | 'narrow' | 'wide' | 'wider'} $spread -
///   Spread across adjacent gutters, relative to a column-count —
///   either `narrow` (-1), `wide` (0), or `wider` (1)
///
/// @return {number} -
///   Numeric value for `$spread`
@function susy-normalize-spread(
  $spread
) {
  $normal-spread: (
    'narrow': -1,
    'wide': 0,
    'wider': 1,
  );

  @return map-get($normal-spread, $spread) or $spread;
}



// Normalize Location
// ------------------
/// Normalize `location` shorthand for Su.
/// Su location syntax requires the (1-indexed) number for a column.
///
/// Susy also allows the `first` and `last` keywords,
/// where `first` is always `1`,
/// and `last` is calculated based on span and column values.
/// Both keywords are normalized into an integer index
/// in normalization.
///
/// @group _normal
///
/// @param {number} $span -
///   Number of grid-columns to be spanned
/// @param {integer | 'first' | 'last'} $location -
///   Starting (1-indexed) column position of a span,
///   or a named location keyword.
/// @param {list} $columns -
///   Already-normalized list of columns in the grid.
///
/// @return {integer} -
///   Numeric value for `$location`
@function susy-normalize-location(
  $span,
  $location,
  $columns
) {
  $count: length($columns);
  $normal-locations: (
    'first': 1,
    'alpha': 1,
    'last': $count - $span + 1,
    'omega': $count - $span + 1,
  );

  @return map-get($normal-locations, $location) or $location;
}
