// Breakpoint viewport sizes and media queries.
//
// Breakpoints are defined as a map of (name: minimum width), order from small to large:
//
//    (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)
//
// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.
//
@mixin _assert-ascending($map, $map-name) {
  $prev-key: null;
  $prev-num: null;
  @each $key, $num in $map {
    @if $prev-num == null {
      // Do nothing
    } @else if not comparable($prev-num, $num) {
      @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order,
        but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num},
        the value of the previous key '#{$prev-key}' !";
    } @else if $prev-num >= $num {
      @warn "Invalid value for #{$map-name}: This map must be in ascending order,
        but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num},
        the value of the previous key '#{$prev-key}' !";
    }
    $prev-key: $key;
    $prev-num: $num;
  }
}

// Starts at zero
// Used to ensure the min-width of the lowest breakpoint starts at 0.
@mixin _assert-starts-at-zero($map, $map-name: "$grid-breakpoints") {
  $values: map-values($map);
  $first-value: nth($values, 1);
  @if $first-value != 0 {
    @warn "First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.";
  }
}

// prettier-ignore
$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
) !default;
@include _assert-ascending($grid-breakpoints, '$grid-breakpoints');
@include _assert-starts-at-zero($grid-breakpoints);
// prettier-ignore
$container-max-widths: (
  sm: 540px,
  md: 720px,
  lg: 960px,
  xl: 1140px
) !default;
@include _assert-ascending($container-max-widths, '$container-max-widths');

$gutter-width: 30px !default;

// Name of the next breakpoint, or null for the last breakpoint.
//
//    >> breakpoint-next(sm)
//    md
//    >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
//    md
//    >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))
//    md
//    >> breakpoint-next(500px)
//    501px

@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
  @if type-of($name) == 'number' {
    @return $name + 1;
  } @else {
    $n: index($breakpoint-names, $name);
    @if $n !=null {
      @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
    }
    @else {
      @return null;
    }
  }
}

// Name of the previous breakpoint, or null for the first breakpoint.
//
//    >> breakpoint-prev(md)
//    sm
//    >> breakpoint-prev(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
//    xs
//    >> breakpoint-prev(md, $breakpoint-names: (xs sm md lg xl))
//    sm
//    >> breakpoint-prev(500px)
//    499px
//    >> breakpoint-prev(0px)
//    0px

@function breakpoint-prev($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
  @if type-of($name) == 'number' {
    @if $name >= 1 {
      @return $name - 1;
    } @else {
      @return $name;
    }
  } @else {
    $n: index($breakpoint-names, $name);
    @if ($n == 1) {
      @return null;
    }
    @return if($n <= length($breakpoint-names), nth($breakpoint-names, $n - 1), null);
  }
}

// Minimum breakpoint width. Null for the smallest (first) breakpoint.
//
//    >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
//    576px
@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
  $min: map-get($breakpoints, $name);
  @return if($min != 0, $min, null);
}

// Maximum breakpoint width. Null for the largest (last) breakpoint.
// The maximum value is calculated as the minimum of the next one less 1.
//
//    >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
//    767px
@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
  $next: breakpoint-next($name, $breakpoints);
  @return if($next, breakpoint-min($next, $breakpoints) - 1px, null);
}

// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.
// Useful for making responsive utilities.
//
//    >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
//    ""  (Returns a blank string)
//    >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
//    "-sm"
@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
  @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
}

@mixin media-breakpoint-portrait {
  @media (orientation: portrait) {
    @content;
  }
}

@mixin media-breakpoint-landscape {
  @media (orientation: landscape) {
    @content;
  }
}

@mixin media-breakpoint-retina {
  @media (-webkit-min-device-pixel-ratio: 2),
    (min-resolution: 192dpi),
    (min-resolution: 2dppx) {
    @content;
  }
}

@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
  $min: $name;

  @if type-of($name) != 'number' {
    $min: breakpoint-min($name, $breakpoints);
  }

  @if $min {
    @media (min-width: $min) {
      @content;
    }
  } @else {
    @content;
  }
}

@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
  $max: $name;

  @if type-of($name) != 'number' {
    $max: breakpoint-max($name, $breakpoints);
  }

  @if $max {
    @media (max-width: $max) {
      @content;
    }
  } @else {
    @content;
  }
}

@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {
  $min: $lower;
  $max: $upper;

  @if type-of($lower) != 'number' {
    $min: breakpoint-min($lower, $breakpoints);
  }
  @if type-of($upper) != 'number' {
    $max: breakpoint-max($upper, $breakpoints);
  }

  @media (min-width: $min) and (max-width: $max) {
    @content;
  }
}

@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
  $min: breakpoint-min($name, $breakpoints);
  $max: breakpoint-max($name, $breakpoints);

  @if $min != null and $max != null {
    @media (min-width: $min) and (max-width: $max) {
      @content;
    }
  } @else if $max == null {
    @include media-breakpoint-up($name);
  } @else if $min == null {
    @include media-breakpoint-down($name);
  }
}

@mixin create-container($selector: '.container', $gutter: $gutter-width) {
  #{$selector} {
    position: relative;
    margin-left: auto;
    margin-right: auto;
    padding-right: ($gutter / 2);
    padding-left: ($gutter / 2);
  }
}

@mixin create-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints, $selector: '.container') {
  #{$selector} {
    @each $breakpoint, $container-max-width in $max-widths {
      @include media-breakpoint-up($breakpoint, $breakpoints) {
        width: $container-max-width;
        max-width: 100%;
      }
    }
  }
}

@mixin create-container-max-fluid-widths($max-widths: $container-max-widths, $selector: '.container') {
  @each $breakpoint, $max-width in $max-widths {
    #{$selector}-#{$breakpoint}-mw {
      max-width: $max-width;
    }
  }
}
