@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...);
}
