// Source: https://www.sitepoint.com/extra-map-functions-sass/

// --------------------------------------------------
// Map Deep Get

/// Fetch nested keys
/// @param {Map} $map - Map
/// @param {Arglist} $keys - Keys to fetch
/// @return {*} -

@function map-deep-get($map, $keys...) {
  @each $key in $keys {
    $map: map-get($map, $key);
  }

  @return $map;
}

// Example:
// $grid-configuration: (
//   'columns': 12,
//   'layouts': (
//     'small': 800px,
//     'medium': 1000px,
//     'large': 1200px,
//   ),
// );

// // Without `map-deep-get`
// $medium: map-get(map-get($grid-configuration, 'layouts'), 'medium');

// // With `map-deep-get`
// $medium: map-deep-get($grid-configuration, 'layouts', 'medium');

// --------------------------------------------------
//Map Deep Get

/// Update a key deeply nested
/// @author Hugo Giraudel
/// @param {Map} $map - Map to update
/// @param {Arglist} $keys - Keys to access to value to update
/// @param {*} $value - New value (last member of `$keys`)
/// @return {Map} - Updated map
@function map-deep-set($map, $keys... /*, $value */) {
  $map-list: ($map);
  $result: null;

  @if length($keys) == 2 {
    @return map-merge(
      $map,
      (
        nth($keys, 1): nth($keys, -1)
      )
    );
  }

  @for $i from 1 through length($keys) - 2 {
    $map-list: append($map-list, map-get(nth($map-list, -1), nth($keys, $i)));
  }

  @for $i from length($map-list) through 1 {
    $result: map-merge(
      nth($map-list, $i),
      (
        nth($keys, $i): if($i == length($map-list), nth($keys, -1), $result)
      )
    );
  }

  @return $result;
}

// Example:
// $grid-configuration: (
//   'columns': 12,
//   'layouts': (
//     'small': 800px,
//     'medium': 1000px,
//     'large': 1200px,
//   ),
// );

// // Without `map-deep-set`
// $grid-configuration: map-merge($grid-configuration, map-merge(map-get($grid-configuration, 'layouts'), ('large': 1300px)));

// // With `map-deep-set`
// $medium: map-deep-set($grid-configuration, 'layouts', 'medium', 1300px);

// --------------------------------------------------
// Map Has Keys

/// Test if map got all `$keys` at first level
/// @author Hugo Giraudel
/// @param {Map} $map - Map
/// @param {Arglist} $keys - Keys to test
/// @return {Bool} -
@function map-has-keys($map, $keys...) {
  @each $key in $keys {
    @if not map-has-key($map, $key) {
      @return false;
    }
  }

  @return true;
}

// Example
// $grid-configuration: (
//   'columns': 12,
//   'layouts': (
//     'small': 800px,
//     'medium': 1000px,
//     'large': 1200px,
//   ),
// );

// $depth: map-has-keys($grid-configuration, 'columns', 'layouts');
// // -> true

// $depth: map-has-keys($grid-configuration, 'columns', 'options');
// // -> false

// --------------------------------------------------
// Map Zip

/// An equivalent of `zip` function but for maps.
/// Takes two lists, the first for keys, second for values.
/// @param {List} $keys - Keys for map
/// @param {List} $values - Values for map
/// @return {Map} Freshly created map
/// @see http://sass-lang.com/documentation/Sass/Script/Functions.html#zip-instance_method
@function map-zip($keys, $values) {
  $l-keys: length($keys);
  $l-values: length($values);
  $min: min($l-keys, $l-values);
  $map: ();

  @if $l-keys != $l-values {
    @warn "There are #{$l-keys} key(s) for #{$l-values} value(s) in the map for `map-zip`. "
      + "Resulting map will only have #{$min} pairs.";
  }

  @if $min == 0 {
    @return $map;
  }

  @for $i from 1 through $min {
    $map: map-merge(
      $map,
      (
        nth($keys, $i): nth($values, $i)
      )
    );
  }

  @return $map;
}

// Example
// $layout-names: 'small', 'medium', 'large', 'huge';
// $layout-values: 600px, 900px, 1200px, 1500px;

// $breakpoints: map-zip($layout-names, $layout-values);
// // -> ('small': 600px, 'medium': 900px, 'large': 1200px, 'huge': 1500px)

// --------------------------------------------------
// Map Extend

/// jQuery-style extend function
/// About `map-merge()`:
/// * only takes 2 arguments
/// * is not recursive
/// @param {Map} $map - first map
/// @param {ArgList} $maps - other maps
/// @param {Bool} $deep - recursive mode
/// @return {Map} -
@function map-extend($map, $maps... /*, $deep */) {
  $last: nth($maps, -1);
  $deep: $last == true;
  $max: if($deep, length($maps) - 1, length($maps));

  // Loop through all maps in $maps...
  @for $i from 1 through $max {
    // Store current map
    $current: nth($maps, $i);

    // If not in deep mode, simply merge current map with map
    @if not $deep {
      $map: map-merge($map, $current);
    }

    @else {
      // If in deep mode, loop through all tuples in current map
      @each $key, $value in $current {
        // If value is a nested map and same key from map is a nested map as well
        @if type-of($value) == "map" and type-of(map-get($map, $key)) == "map" {
          // Recursive extend
          $value: map-extend(map-get($map, $key), $value, true);
        }

        // Merge current tuple with map
        $map: map-merge(
          $map,
          (
            $key: $value
          )
        );
      }
    }
  }

  @return $map;
}

//   Example
//   $grid-configuration-default: (
//     'columns': 12,
//     'layouts': (
//       'small': 800px,
//       'medium': 1000px,
//       'large': 1200px,
//     ),
//   );

//   $grid-configuration-custom: (
//     'layouts': (
//       'large': 1300px,
//       'huge': 1500px
//     ),
//   );

//   $grid-configuration-user: (
//     'direction': 'ltr',
//     'columns': 16,
//     'layouts': (
//       'large': 1300px,
//       'huge': 1500px
//     ),
//   );

//   // Not deep
//   $grid-configuration: map-extend($grid-configuration-default, $grid-configuration-custom, $grid-configuration-user);
//   // -> ("columns": 16, "layouts": (("large": 1300px, "huge": 1500px)), "direction": "ltr")

//   // Deep
//   $grid-configuration: map-extend($grid-configuration-default, $grid-configuration-custom, $grid-configuration-user, true);
//   // -> ("columns": 16, "layouts": (("small": 800px, "medium": 1000px, "large": 1300px, "huge": 1500px)), "direction": "ltr")

// --------------------------------------------------
// Map Depth

/// Compute the maximum depth of a map
/// @param {Map} $map
/// @return {Number} max depth of `$map`
@function map-depth($map) {
  $level: 1;

  @each $key, $value in $map {
    @if type-of($value) == "map" {
      $level: max(map-depth($value) + 1, $level);
    }
  }

  @return $level;
}

//   Example
//   $grid-configuration: (
//     'columns': 12,
//     'layouts': (
//       'small': 800px,
//       'medium': 1000px,
//       'large': 1200px,
//     ),
//   );

//   // Maximum depth
//   $depth: map-depth($grid-configuration);
//   // -> 2

// --------------------------------------------------
// Map Has Keys

/// Test if map got all `$keys` at first level
/// @author Hugo Giraudel
/// @param {Map} $map - Map
/// @param {Arglist} $keys - Keys to test
/// @return {Bool} -
@function map-has-keys($map, $keys...) {
  @each $key in $keys {
    @if not map-has-key($map, $key) {
      @return false;
    }
  }

  @return true;
}

// Example
// $grid-configuration: (
//   'columns': 12,
//   'layouts': (
//     'small': 800px,
//     'medium': 1000px,
//     'large': 1200px,
//   ),
// );

// $depth: map-has-keys($grid-configuration, 'columns', 'layouts');
// // -> true

// $depth: map-has-keys($grid-configuration, 'columns', 'options');
// // -> false

// --------------------------------------------------
// Map Has Nested Keys

/// Test if map got all `$keys` nested with each others
/// @author Hugo Giraudel
/// @param {Map} $map - Map
/// @param {Arglist} $keys - Keys to test
/// @return {Bool} -
@function map-has-nested-keys($map, $keys...) {
  @each $key in $keys {
    @if not map-has-key($map, $key) {
      @return false;
    }
    $map: map-get($map, $key);
  }

  @return true;
}

// Example
// $grid-configuration: (
//     'columns': 12,
//     'layouts': (
//     'small': 800px,
//     'medium': 1000px,
//     'large': 1200px,
//     ),
// );

// $depth: map-has-keys($grid-configuration, 'layouts', 'medium');
// // -> true

// $depth: map-has-keys($grid-configuration, 'layouts', 'huge');
// // -> false
