// =============================================================================
// =ALEKSI - MAP-SET-DEEP
// =============================================================================
////
//// @group aleksi-maps
//// @author [Yoannis Jamar](https://yoannis.com)

@import "aleksi/general/throw";
@import "aleksi/lists/slice";
@import "aleksi/maps/is-map";
@import "aleksi/maps/map-get-deep";

// =map-set-deep( $map, $keys..., $value )
// -----------------------------------------------------------------------------
/// Sets a value in nested maps.
///
/// @param {map} $map - The nested-map in which to set the value
/// @param {string} $keys... - The keys path for which to set the value
/// @param {any} $value - The value to set
///
/// @return {map} - $map, with $value set for $keys path
///
/// @throw {error} $map is not a map
/// @throw {error} one of $keys is not a string
///
/// @access public
/// @since 0.4.0
///
/// @author [at-import](https://github.com/at-import/)
/// @author [Yoannis Jamar](http://yoannis.com)

@function map-set-deep($map, $keys... )
{
  @if not is-map($map) {
    @return throw-error('map-set-deep(): $map must be a map – was #{inspect($map)}');
  }

  $value: null;
  $length: length($keys);

  // missing argument $value
  @if $length == 1 {
    @return throw-error('map-set-deep(): missing argument $value – minimum 3 arguments required, the last one being $value');
  }

  // allow passing a list of keys as second argument
  @else if $length == 2 {
    $value: nth($keys, 2);
    $keys: nth($keys, 1);
  }

  // allow passing keys as separate arguments
  // - value is the last argument
  @else {
    $value: nth($keys, -1);
    $keys: slice($keys, 1, $length - 1);
  }

  // do not log warnings when iterating
  $suppress-deep-map-warnings: $__aleksi-suppress-deep-map-warnings__;
  $__aleksi-suppress-deep-map-warnings__: true !global;

  $length: length($keys);
  $get-keys: ();
  $map-level: ();

  @if $length > 1 {
    $get-keys: slice($keys, 1, $length - 1);
    $map-level: map-get-deep($map, $get-keys);

    @if not is-map($map-level) {
      $map-level: ();
    }
  }

  $merge: (nth($keys, $length): $value);

  @if $map-level {
    $merge: map-merge($map-level, $merge);
  }

  // loop over keys in reverse order
  $i: $length - 1;
  @while $i > 0
  {
    $key: nth($keys, $i);
    
    @if $i > 1
    {
      $get-keys: slice($keys, 1, $i - 1);
      $map-level: map-get-deep($map, $get-keys);

      @if not is-map($map-level) {
        $map-level: ();
      }

      @if $map-level {
        $merge: map-merge($map-level, ($key: $merge));
      }

      @else {
        $merge: ($key: $merge);
      }
    }

    @else {
      $merge: ($key: $merge);
    }

    $i: $i - 1;
  }

  $map: map-merge($map, $merge);

  // restore logging of warnings when iterating
  $__aleksi-suppress-deep-map-warnings__: $suppress-deep-map-warnings !global;

  @return $map;
}