/// Grid Math Engine
/// ================
/// The `su` functions give you direct access to the math layer,
/// without any syntax-sugar like shorthand parsing, and normalization.
/// If you prefer named arguments, and stripped-down syntax,
/// you can use these functions directly in your code –
/// replacing `span`, `gutter`, and `slice`.
///
/// These functions are also useful
/// for building mixins or other extensions to Susy.
/// Apply the Susy syntax to new mixins and functions,
/// using our "Plugin Helpers",
/// or write your own syntax and pass the normalized results along
/// to `su` for compilation.
///
/// @group su-math
///
/// @see su-span
/// @see su-gutter
/// @see su-slice
/// @ignore _su-sum
/// @ignore _su-calc-span
/// @ignore _su-calc-sum
/// @ignore _su-needs-calc-output



// Su Span
// -------
/// Calculates and returns a CSS-ready span width,
/// based on normalized span and context data –
/// a low-level version of `susy-span`,
/// with all of the logic and none of the syntax sugar.
///
/// - Grids defined with unitless numbers will return `%` values.
/// - Grids defined with comparable units
///   will return a value in the units provided.
/// - Grids defined with a mix of units,
///   or a combination of untiless numbers and unit-lengths,
///   will return a `calc()` string.
///
/// @group su-math
/// @see susy-span
///
/// @param {number | list} $span -
///   Number or list of grid columns to span
/// @param {list} $columns -
///   List of columns available
/// @param {number} $gutters -
///   Width of a gutter in column-comparable units
/// @param {0 | 1 | -1} $spread -
///   Number of gutters spanned,
///   relative to `span` count
/// @param {0 | 1 | -1} $container-spread [$spread] -
///   Number of gutters spanned,
///   relative to `columns` count
/// @param {integer} $location [1] -
///   Optional position of sub-span among full set of columns
///
/// @return {length} -
///   Relative or static length of a span on the grid
@function su-span(
  $span,
  $columns,
  $gutters,
  $spread,
  $container-spread: $spread,
  $location: 1
) {
  $span: su-valid-span($span);
  $columns: su-valid-columns($columns);
  $gutters: su-valid-gutters($gutters);
  $spread: su-valid-spread($spread);

  @if (type-of($span) == 'number') {
    @if (not unitless($span)) {
      @return $span;
    }

    $location: su-valid-location($span, $location, $columns);
    $span: su-slice($span, $columns, $location, $validate: false);
  }

  @if _su-needs-calc-output($span, $columns, $gutters, $spread, not 'validate') {
    @return _su-calc-span($span, $columns, $gutters, $spread, $container-spread, not 'validate');
  }

  $span-width: _su-sum($span, $gutters, $spread, $validate: false);

  @if unitless($span-width) {
    $container-spread: su-valid-spread($container-spread);
    $container: _su-sum($columns, $gutters, $container-spread, $validate: false);
    @return percentage($span-width / $container);
  }

  @return $span-width;
}



// Su Gutter
// ---------
/// Calculates and returns a CSS-ready gutter width,
/// based on normalized grid data –
/// a low-level version of `susy-gutter`,
/// with all of the logic and none of the syntax sugar.
///
/// - Grids defined with unitless numbers will return `%` values.
/// - Grids defined with comparable units
///   will return a value in the units provided.
/// - Grids defined with a mix of units,
///   or a combination of untiless numbers and unit-lengths,
///   will return a `calc()` string.
///
/// @group su-math
/// @see susy-gutter
///
/// @param {list} $columns -
///   List of columns in the grid
/// @param {number} $gutters -
///   Width of a gutter in column-comparable units
/// @param {0 | 1 | -1} $container-spread -
///   Number of gutters spanned,
///   relative to `columns` count
///
/// @return {length} -
///   Relative or static length of one gutter in a grid
@function su-gutter(
  $columns,
  $gutters,
  $container-spread
) {
  @if (type-of($gutters) == 'number') {
    @if ($gutters == 0) or (not unitless($gutters)) {
      @return $gutters;
    }
  }

  @if _su-needs-calc-output($gutters, $columns, $gutters, -1, not 'validate') {
    @return _su-calc-span($gutters, $columns, $gutters, -1, $container-spread, not 'validate');
  }

  $container: _su-sum($columns, $gutters, $container-spread);
  @return percentage($gutters / $container);
}



// Su Slice
// --------
/// Returns a list of columns
/// based on a given span/location slice of the grid –
/// a low-level version of `susy-slice`,
/// with all of the logic and none of the syntax sugar.
///
/// @group su-math
/// @see susy-slice
///
/// @param {number} $span -
///   Number of grid columns to span
/// @param {list} $columns -
///   List of columns in the grid
/// @param {number} $location [1] -
///   Starting index of a span in the list of columns
/// @param {bool} $validate [true] -
///   Check that arguments are valid before proceeding
///
/// @return {list} -
///   Subset list of grid columns, based on span and location
@function su-slice(
  $span,
  $columns,
  $location: 1,
  $validate: true
) {
  @if $validate {
    $columns: su-valid-columns($columns);
    $location: su-valid-location($span, $location, $columns);
  }

  $floor: floor($span);
  $sub-columns: ();

  @for $i from $location to ($location + $floor) {
    $sub-columns: append($sub-columns, nth($columns, $i));
  }

  @if $floor != $span {
    $remainder: $span - $floor;
    $column: $location + $floor;
    $sub-columns: append($sub-columns, nth($columns, $column) * $remainder);
  }

  @return $sub-columns;
}



// Su Sum
// ------
/// Get the total sum of column-units in a layout.
///
/// @group su-math
/// @access private
///
/// @param {list} $columns -
///   List of columns in the grid
/// @param {number} $gutters -
///   Width of a gutter in column-comparable units
/// @param {0 | 1 | -1} $spread -
///   Number of gutters spanned,
///   relative to `columns` count
/// @param {bool} $validate [true] -
///   Check that arguments are valid before proceeding
///
/// @return {number} -
///   Total sum of column-units in a grid
@function _su-sum(
  $columns,
  $gutters,
  $spread,
  $validate: true
) {
  @if $validate {
    $columns: su-valid-span($columns);
    $gutters: su-valid-gutters($gutters);
    $spread: su-valid-spread($spread);
  }

  // Calculate column-sum
  $column-sum: 0;
  @each $column in $columns {
    $column-sum: $column-sum + $column;
  }

  $gutter-sum: (ceil(length($columns)) + $spread) * $gutters;
  $total: if(($gutter-sum > 0), $column-sum + $gutter-sum, $column-sum);

  @return $total;
}



// Su Calc
// -------
/// Return a usable span width as a `calc()` function,
/// in order to create mixed-unit grids.
///
/// @group su-math
/// @access private
///
/// @param {number | list} $span -
///   Pre-sliced list of grid columns to span
/// @param {list} $columns -
///   List of columns available
/// @param {number} $gutters -
///   Width of a gutter in column-comparable units
/// @param {0 | 1 | -1} $spread -
///   Number of gutters spanned,
///   relative to `span` count
/// @param {0 | 1 | -1} $container-spread [$spread] -
///   Number of gutters spanned,
///   relative to `columns` count
/// @param {bool} $validate [true] -
///   Check that arguments are valid before proceeding
///
/// @return {length} -
///   Relative or static length of a span on the grid
@function _su-calc-span(
  $span,
  $columns,
  $gutters,
  $spread,
  $container-spread: $spread,
  $validate: true
) {
  @if $validate {
    $span: su-valid-span($span);
    $columns: su-valid-columns($columns);
    $gutters: su-valid-gutters($gutters);
    $spread: su-valid-spread($spread);
    $container-spread: su-valid-spread($container-spread);
  }

  // Span and context
  $span: _su-calc-sum($span, $gutters, $spread, not 'validate');
  $context: _su-calc-sum($columns, $gutters, $container-spread, not 'validate');

  // Fixed and fluid
  $fixed-span: map-get($span, 'fixed');
  $fluid-span: map-get($span, 'fluid');
  $fixed-context: map-get($context, 'fixed');
  $fluid-context: map-get($context, 'fluid');

  $calc: '#{$fixed-span}';
  $fluid-calc: '(100% - #{$fixed-context})';

  // Fluid-values
  @if (not $fluid-span) {
    $fluid-calc: null;
  } @else if ($fluid-span != $fluid-context) {
    $fluid-span: '* #{$fluid-span}';
    $fluid-context: if($fluid-context, '/ #{$fluid-context}', '');
    $fluid-calc: '(#{$fluid-calc $fluid-context $fluid-span})';
  }

  @if $fluid-calc {
    $calc: if(($calc != ''), '#{$calc} + ', '');
    $calc: '#{$calc + $fluid-calc}';
  }

  @return calc(#{unquote($calc)});
}



// Su Calc-Sum
// -----------
/// Get the total sum of fixed and fluid column-units
/// for creating a mixed-unit layout with `calc()` values.
///
/// @group su-math
/// @access private
///
/// @param {list} $columns -
///   List of columns available
/// @param {number} $gutters -
///   Width of a gutter in column-comparable units
/// @param {0 | 1 | -1} $spread -
///   Number of gutters spanned,
///   relative to `span` count
/// @param {bool} $validate [true] -
///   Check that arguments are valid before proceeding
///
/// @return {map} -
///   Map with `fixed` and `fluid` keys
///   containing the proper math as strings
@function _su-calc-sum(
  $columns,
  $gutters,
  $spread,
  $validate: true
) {
  @if $validate {
    $columns: su-valid-span($columns);
    $gutters: su-valid-gutters($gutters);
    $spread: su-valid-spread($spread);
  }

  $fluid: 0;
  $fixed: ();
  $calc: null;

  // Gutters
  $gutters:  $gutters * (length($columns) + $spread);

  // Columns
  @each $col in append($columns, $gutters) {
    @if unitless($col) {
      $fluid: $fluid + $col;
    } @else {
      $fixed: _su-map-add-units($fixed, $col);
    }
  }

  // Compile Fixed Units
  @each $unit, $total in $fixed {
    @if ($total != (0 * $total)) {
      $calc: if($calc, '#{$calc} + #{$total}', '#{$total}');
    }
  }

  // Calc null or string
  @if $calc {
    $calc: if(str-index($calc, '+'), '(#{$calc})', '#{$calc}');
  }

  // Fluid 0 => null
  $fluid: if(($fluid == 0), null, $fluid);


  // Return map
  $return: (
    'fixed': $calc,
    'fluid': $fluid,
  );

  @return $return;
}



// Needs Calc
// ----------
/// Check if `calc()` will be needed in defining a span,
/// if the necessary units in a grid are not comparable.
///
/// @group su-math
/// @access private
///
/// @param {list} $span -
///   Slice of columns to span
/// @param {list} $columns -
///   List of available columns in the grid
/// @param {number} $gutters -
///   Width of a gutter
/// @param {0 | 1 | -1} $spread -
///   Number of gutters spanned,
///   relative to `span` count
/// @param {bool} $validate [true] -
///   Check that arguments are valid before proceeding
///
/// @return {bool} -
///   `True` when units do not match, and `calc()` will be required
@function _su-needs-calc-output(
  $span,
  $columns,
  $gutters,
  $spread,
  $validate: true
) {
  @if $validate {
    $span: su-valid-span($span);
    $columns: su-valid-columns($columns);
    $gutters: su-valid-gutters($gutters);
  }

  $has-gutter: if((length($span) > 1) or ($spread >= 0), true, false);
  $check: if($has-gutter, append($span, $gutters), $span);
  $safe-span: _su-is-comparable($check...);

  @if ($safe-span == 'static') {
    @return false;
  } @else if (not $safe-span) {
    @return true;
  }

  $safe-fluid: _su-is-comparable($gutters, $columns...);

  @return not $safe-fluid;
}
