@function __list-each($list, $iteratee) {
  $index: 1;
  $length: length($list);
  $break: false;
  $result-list: ();

  @while ($index <= $length) and not $break {
    $item: __exec($iteratee, nth($list, $index), $index, $list);

    @if (__exec($iteratee, nth($list, $index), $index, $list) == false) {
      $break: true;
    } @else {
      $result-list: append($result-list, $item);
    }

    $index: $index + 1;
  }

  @return $result-list;
}

@function __list-each-right($list, $iteratee) {
  $list: __list-reverse($list);
  $result-list: __list-each($list, $iteratee);
  $result-list: __list-reverse($result-list);

  @return $result-list;
}

@function __base-each($collection, $iteratee) {
  $length: if($collection, length($collection), 0);

  @if not(__is-list($collection)) {
    @return __base-for-own($collection, $iteratee);
  }

  $index: 1;
  $iterable: __to-map($collection);
  $break: false;

  @while ($index <= $length and not $break) {
    $iteration: __exec($iteratee, __get($iterable, $index), $index, $iterable);

    @if ($iteration == false) {
      $break: true;
    } @else {
      $iterable: __set($iterable, $index, $iteration);
    }

    $index: $index + 1;
  }

  $collection: if(__is-list($collection), map-values($iterable), $iterable);

  @return $collection;
}

@function __base-each-right($collection, $iteratee) {
  $length: if($collection, length($collection), 0);

  @if not(__is-list($collection)) {
    @return __base-for-own-right($collection, $iteratee);
  }

  $iterable: __to-map($collection);
  $break: false;

  @while ($length > 0 and not $break) {
    $iteration: __exec(
      $iteratee,
      __get($iterable, $length),
      $length,
      $iterable
    );

    @if ($iteration == false) {
      $break: true;
    } @else {
      $iterable: __set($iterable, $length, $iteration);
    }

    $length: $length - 1;
  }

  $collection: if(__is-list($collection), map-values($iterable), $iterable);

  @return $collection;
}

@function __base-for($map, $iteratee, $keys-function) {
  $index: 1;
  $iterable: __to-map($map);
  $props: __exec($keys-function, $map);
  $length: length($props);
  $result-map: ();
  $break: false;

  @while $index <= $length and not $break {
    $key: nth($props, $index);
    $iteration: __exec($iteratee, __get($iterable, $key), $key, $iterable);

    @if ($iteration == false) {
      $break: true;
    } @else {
      $result-map: __set($result-map, $key, $iteration);
    }

    $index: $index + 1;
  }

  @return $result-map;
}

@function __base-for-right($map, $iteratee, $keys-function) {
  $iterable: __to-map($map);
  $props: __exec($keys-function, $map);
  $length: length($props);
  $result-map: ();
  $break: false;

  @while $length > 0 and not $break {
    $key: nth($props, $length);
    $iteration: __exec($iteratee, __get($iterable, $key), $key, $iterable);

    @if ($iteration == false) {
      $break: true;
    } @else {
      $result-map: __set($result-map, $key, $iteration);
    }

    $length: $length - 1;
  }

  @return $result-map;
}

@function __base-for-in($map, $iteratee) {
  @return __base-for($map, $iteratee, "__keys-in");
}

@function __base-for-own($map, $iteratee) {
  @return __base-for($map, $iteratee, "__keys");
}

@function __base-for-own-right($map, $iteratee) {
  @return __base-for-right($map, $iteratee, "__keys");
}

@function __for-each(
  $collection,
  $iteratee: "__identity",
  $this-arg: $__undefined__
) {
  @return if(
    __function-exists($iteratee) and __is-undefined($this-arg) and
      __is-list($collection),
    __list-each($collection, $iteratee),
    __base-each($collection, __bind-callback($iteratee, $this-arg, 3))
  );
}

@function __for-each-right(
  $collection,
  $iteratee: "__identity",
  $this-arg: $__undefined__
) {
  @return if(
    __function-exists($iteratee) and __is-undefined($this-arg) and
      __is-list($collection),
    __list-each-right($collection, $iteratee),
    __base-each-right($collection, __bind-callback($iteratee, $this-arg, 3))
  );
}

@function __for-in($map, $iteratee: "__identity", $this-arg: $__undefined__) {
  @if not __function-exists($iteratee) or not __is-undefined($this-arg) {
    $iteratee: __bind-callback($iteratee, $this-arg, 3);
  }

  @return __base-for($map, $iteratee, "__keys-in");
}

@function __for-in-right($map, $iteratee: "__identity", $this-arg: null) {
  $iteratee: __bind-callback($iteratee, $this-arg, 3);

  @return __base-for-right($map, $iteratee, "__keys-in");
}

@function __for-own($map, $iteratee: "__identity", $this-arg: $__undefined__) {
  @if not __function-exists($iteratee) or not __is-undefined($this-arg) {
    $iteratee: __bind-callback($iteratee, $this-arg, 3);
  }

  @return __base-for-own($map, $iteratee);
}

@function __for-own-right($map, $iteratee: "__identity", $this-arg: null) {
  $iteratee: __bind-callback($iteratee, $this-arg, 3);

  @return __base-for-right($map, $iteratee, "__keys");
}

/// Iterates over elements of `$collection` invoking `$iteratee` for each element.
/// The `$iteratee` is bound to `$this-arg` and invoked with three arguments;
/// (value, index|key, collection). Iterator functions may exit iteration early
/// by explicitly returning `false`.
///
///
/// @access public
/// @group Collection
/// @param {List|Map|string} $collection The collection to iterate over.
/// @param {Function} $iteratee [_identity] - The function invoked per iteration.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {List|Map|string} Returns `$collection`.
/// @example scss
/// // todo

@function _for-each($args...) {
  @return call(get-function("__for-each"), $args...);
}

/// @alias _for-each

@function _each($args...) {
  @return call(get-function("__for-each"), $args...);
}

/// This method is like `_for-each` except that it iterates over elements of
/// `$collection` from right to left.
///
///
/// @access public
/// @group Collection
/// @param {List|Map|string} $collection The collection to iterate over.
/// @param {Function} $iteratee [_identity] - The function invoked per iteration.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {List|Map|string} Returns `$collection`.
/// @example scss
/// // todo

@function _for-each-right($args...) {
  @return call(get-function("__for-each-right"), $args...);
}

/// @alias _for-each-right

@function _each-right($args...) {
  @return call(get-function("__for-each-right"), $args...);
}

/// Iterates over own and inherited enumerable properties of a map invoking
/// `$iteratee` for each property. The `$iteratee` is bound to `$this-arg` and invoked
/// with three arguments; (value, key, map). Iterator functions may exit
/// iteration early by explicitly returning `false`.
///
///
/// @access public
/// @group Map
/// @param {Map} $map The map to iterate over.
/// @param {Function} $iteratee [_identity] - The function invoked per iteration.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {Map} Returns new map.
/// @example scss
/// // todo

@function _for-in($args...) {
  @return call(get-function("__for-in"), $args...);
}

/// This method is like `_for-in` except that it iterates over properties of
/// `$map` in the opposite order.
///
///
/// @access public
/// @group Map
/// @param {Map} $map The map to iterate over.
/// @param {Function} $iteratee [_identity] - The function invoked per iteration.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {Map} Returns new map.
/// @example scss
/// // todo

@function _for-in-right($args...) {
  @return call(get-function("__for-in-right"), $args...);
}

/// Iterates over own enumerable properties of a map invoking `$iteratee`
/// for each property. The `$iteratee` is bound to `$this-arg` and invoked with
/// three arguments; (value, key, map). Iterator functions may exit iteration
/// early by explicitly returning `false`.
///
///
/// @access public
/// @group Map
/// @param {Map} $map The map to iterate over.
/// @param {Function} $iteratee [_identity] - The function invoked per iteration.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {Map} Returns new map.
/// @example scss
/// // todo

@function _for-own($args...) {
  @return call(get-function("__for-own"), $args...);
}

/// This method is like `_for-own` except that it iterates over properties of
/// `$map` in the opposite order.
///
///
/// @access public
/// @group Map
/// @param {Map} $map The map to iterate over.
/// @param {Function} $iteratee [_identity] - The function invoked per iteration.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {Map} Returns `map`.
/// @example scss
/// // todo

@function _for-own-right($args...) {
  @return call(get-function("__for-own-right"), $args...);
}
