@use 'sass:list';
@use 'sass:meta';
@use 'sass:math';
@use 'sass:map';

@use 'config' as *;
@use 'breakpoint' as *;

/// Contents of the mixins
// * Grid
// * Columns
// * Rows
// * No gutter
// * Hang
// * Functions

// -----------------------------------------------------------------------------
// Grid
// -----------------------------------------------------------------------------

/// Create the container for a grid. Will cause full-bleed for the grid unless
/// max-width properties are added with `make-container-max-width`
/// @param {Map} $breakpoints [$grid-breakpoints] - A map of breakpoints where the key is the name
/// @access private
/// @group @cds/grid
@mixin -make-container($breakpoints: $grid-breakpoints) {
  margin-inline: auto;

  @include -set-largest-breakpoint($breakpoints);

  @each $name, $value in $breakpoints {
    $prev-breakpoint: map.get($breakpoints, breakpoint-prev($name));
    $margin: map.get($value, margin);

    @if $prev-breakpoint {
      $prev-margin: map.get($prev-breakpoint, margin);

      @if $prev-margin != $margin {
        @include cds-breakpoint($name) {
          padding-inline: #{($grid-gutter * 0.5) + $margin};
        }
      }
    } @else {
      @include cds-breakpoint($name) {
        padding-inline: #{($grid-gutter * 0.5) + $margin};
      }
    }
  }
}

/// Get the last breakpoint width and set max-width to its value
/// @param {Map} $breakpoints [$grid-breakpoints] - A map of breakpoints where the key is the name
/// @access private
/// @group @cds/grid
@mixin -set-largest-breakpoint($breakpoints: $grid-breakpoints) {
  $largest-breakpoint: -last-map-item($breakpoints);
  max-inline-size: map.get($largest-breakpoint, 'width');
}

/// Add in the max-widths for each breakpoint to the container
/// @param {Map} $breakpoints [$grid-breakpoints] - A map of breakpoints where the key is the name
/// @access private
/// @group @cds/grid
@mixin -make-container-max-widths($breakpoints: $grid-breakpoints) {
  @each $name, $value in $breakpoints {
    @include cds-breakpoint($name) {
      max-inline-size: map.get($value, width);
    }
  }
}


// -----------------------------------------------------------------------------
// Columns
// -----------------------------------------------------------------------------

/// Used to initialize the default properties for a column class, most notably
/// for setting width and default gutters when a column's breakpoint has not been
/// hit yet.
/// @param {Number} $gutter [$grid-gutter] - The gutter for the grid system
/// @param {Number} $collapsed-gutter [$grid-gutter-condensed] - The condensed mode gutter
/// @access private
/// @group @cds/grid
@mixin -make-col-ready($gutter: $grid-gutter, $condensed-gutter: $grid-gutter-condensed) {
  // Prevent columns from becoming too narrow when at smaller grid tiers by
  // always setting `width: 100%;`. This works because we use `flex` values
  // later on to override this initial width.
  inline-size: 100%;
  padding-inline: $gutter * 0.5;

  // For our condensed use-case, our gutters collapse to 2px solid, 1px on each side.
  .#{$prefix}-row-condensed &,
  .#{$prefix}-grid-condensed & {
    padding-inline: $condensed-gutter * 0.5;
  }

  // For our narrow use-case, our container hangs 16px into the gutter
  .#{$prefix}-row-narrow &,
  .#{$prefix}-grid-narrow & {
    padding-inline: 0 $gutter * 0.5;
  }
}

/// Define the width of the column for a given span and column count.
/// A width of 0 will hide the column entirely.
/// @param {Number} $span - The number of columns covered
/// @param {Number} $columns - The total number of columns available
/// @access private
/// @group @cds/grid
@mixin -make-col($span, $columns) {
  @if $span == 0 {
    display: none;
  } @else {
    // Explicitly include `display: block` to override
    display: block;
    // Add a `max-width` to ensure content within each column does not blow out
    // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari
    // do not appear to require this.
    flex: 0 0 math.percentage(math.div($span, $columns));
    max-inline-size: math.percentage(math.div($span, $columns));
  }
}

/// Create a column offset for a given span and column count.
/// @param {Number} $span - The number of columns the offset should cover
/// @param {Number} $columns - The total number of columns available
/// @access private
/// @group @cds/grid
@mixin -make-col-offset($span, $columns) {
  $offset: 0;
  $offset: math.div($span, $columns);

  @if $offset == 0 {
    margin-inline-start: 0;
  } @else {
    margin-inline-start: math.percentage($offset);
  }
}

/// Create a column order for a given span and column count.
/// @param {Number} $span - The number of columns the order should cover
/// @access private
/// @group @cds/grid
@mixin -make-col-order($span) {
  order: $span;
}

/// Output the CSS required for all the columns in a given grid system.
/// @param {Map} $breakpoints [$grid-breakpoints] - The breakpoints in the grid system
/// @param {Number} $gutter [$grid-gutter] - The gutter for the grid system
/// @access private
/// @group @cds/grid
@mixin -make-grid-columns($breakpoints: $grid-breakpoints, $gutter: $grid-gutter) {
  .#{$prefix}-col {
    @include -make-col-ready($gutter);
  }

  @each $breakpoint in map-keys($breakpoints) {
    $infix: breakpoint-infix($breakpoint);
    $columns: map.get(map.get($breakpoints, $breakpoint), columns);

    // Allow columns to stretch full width below their breakpoints
    @for $i from 0 through $columns {
      .#{$prefix}-col#{$infix}-#{$i} {
        @include -make-col-ready($gutter);
      }
    }

    .#{$prefix}-col#{$infix},
    .#{$prefix}-col#{$infix}-auto {
      @include -make-col-ready($gutter);
    }

    @include cds-breakpoint($breakpoint, $breakpoints) {
      // Provide basic `.col-{bp}` classes for equal-width flexbox columns
      .#{$prefix}-col,
      .#{$prefix}-col#{$infix} {
        flex-basis: 0;
        flex-grow: 1;
        max-inline-size: 100%;
      }

      .#{$prefix}-col-auto,
      .#{$prefix}-col#{$infix}-auto {
        @include -make-col-ready($gutter);

        // Reset earlier grid tiers
        width: auto;
        max-inline-size: 100%;
        flex: 0 0 auto;
      }

      @for $i from 0 through $columns {
        .#{$prefix}-col#{$infix}-#{$i} {
          @include -make-col($i, $columns);
        }
      }

      @for $i from 0 through ($columns - 1) {
        @if not($infix == '') {
          .#{$prefix}-offset#{$infix}-#{$i} {
            @include -make-col-offset($i, $columns);
          }
        }
      }

      @for $i from 0 through ($columns - 1) {
        @if not($infix == '') {
          .#{$prefix}-order#{$infix}-#{$i} {
            @include -make-col-order($i);
          }
        }
      }
    }
  }
}


// -----------------------------------------------------------------------------
// Rows
// -----------------------------------------------------------------------------

/// Define the properties for a selector assigned to a row in the grid system.
/// @param {Number} $gutter [$grid-gutter] - The gutter in the grid system
/// @access private
/// @group @cds/grid
@mixin -make-row($gutter: $grid-gutter) {
  display: flex;
  flex-wrap: wrap;
  margin-inline: -1 * $gutter * 0.5;
}


// -----------------------------------------------------------------------------
// No gutter
// -----------------------------------------------------------------------------

/// Add `no-gutter` and `no-gutter-{start,end}` classes to the output CSS. These
/// classes are useful for dropping the gutter in fluid situations.
/// @access private
/// @group @cds/grid
@mixin -no-gutter {
  .#{$prefix}-no-gutter,
  .#{$prefix}-row.#{$prefix}-no-gutter [class*='#{$prefix}-col'] {
    padding-inline: 0;
  }

  .#{$prefix}-no-gutter--start,
  .#{$prefix}-row.#{$prefix}-no-gutter--start [class*='#{$prefix}-col'] {
    padding-inline-start: 0;
  }

  .#{$prefix}-no-gutter--end,
  .#{$prefix}-row.#{$prefix}-no-gutter--end [class*='#{$prefix}-col'] {
    padding-inline-end: 0;
  }
}


// -----------------------------------------------------------------------------
// Hang
// -----------------------------------------------------------------------------
/// Add `hang--start` and `hang--end` classes for a given gutter. These classes are
/// used alongside `no-gutter--start` and `no-gutter--end` to "hang" type.
/// @param {Number} $gutter [$grid-gutter] - The gutter in the grid system
/// @access private
/// @group @carbon/grid
@mixin -hang($gutter: $grid-gutter) {
  .#{$prefix}-hang--start {
    padding-inline-start: $gutter * 0.5;
  }

  .#{$prefix}-hang--end {
    padding-inline-end: $gutter * 0.5;
  }
}

// -----------------------------------------------------------------------------
// Functions
// -----------------------------------------------------------------------------

/// Pass in a map, and get the last one in the list back
/// @access public
/// @param {Map} $map - Map
/// @return {*} Desired value
/// @group @cds/layout
@function -last-map-item($map) {
  $total-length: list.length($map);
  @return map-get($map, -key-by-index($map, $total-length));
}

/// Provide a map and index, and get back the relevant key value
/// @access public
/// @param {Map} $map - Map
/// @param {Integer} $index - Key chain
/// @return {String} Desired value
/// @group @cds/layout
@function -key-by-index($map, $index) {
  $keys: map.keys($map);
  @return nth($keys, $index);
}
