////
/// @group utils.bem
///
/// Provides a series of functions and mixins that make it easier to generate
/// [bem](http://getbem.com/) style class names. Modified from the great thinking done
/// [here](https://codepen.io/gionkunz/pen/rkswl?editors=010).
///
/// Unlike many bem mixins, there is no specific mixin for creating modifier classes. Instead,
/// modifiers and pseudo classes can be included directly in the call to the block or element mixin.
///
////

@use 'units' as *;

$-block-prefix: '' !default;
$-element-prefix: '__' !default;
$-modifier-prefix: '--' !default;

@function -bem-name($name: false, $modifier: '', $pseudo: '', $prefix: '') {
    @if (not $name) {
        @error "Missing block or element name.";
    }

    @if ($modifier != '') {
        $modifier: #{$-modifier-prefix + $modifier};
    }

    @if ($pseudo != '') {
        $pseudo: #{':' + $pseudo};
    }

    @return #{'.' + $prefix + $name + $modifier + $pseudo};
}

@mixin -bem-base($name: false, $modifier: '', $pseudo: '', $prefix: '') {
    $selector: false;

    @if is-list($name) {
        $selector: ();

        @each $n in $name {
            $selector: append($selector, -bem-name($n, $modifier, $pseudo, $prefix), comma);
        }
    } @else {
        $selector: -bem-name($name, $modifier, $pseudo, $prefix);
    }
    #{$selector} {
        @content;
    }
}

/// Extracts the block name form a selector. So if $selector is something like
/// `.block__element--mod` the function would return `block`.
///
/// @param {*} $selector

@function get-block-name($selector) {
    $selector: #{$selector};
    $inf: 9999999;
    $dot-index: if-null(str-index($selector, '.'), 1);
    $space-index: if-null(str-index($selector, ' '), $inf);
    $modifier-index: if-null(str-index($selector, $-modifier-prefix), $inf);
    $element-index: if-null(str-index($selector, $-element-prefix), $inf);
    $pseudo-index: if-null(str-index($selector, ':'), $inf);
    $min-index: min($space-index, $modifier-index, $element-index, $pseudo-index);

    @return str-slice($selector, $dot-index + 1, $min-index - 1);
}

/// Creates a block class selector using the bem approach to class naming. This is often unecessary
/// since elements can be nested under the block class using a normal declation.
///
/// @param {string} $name - The block name string
/// @param {string} $modifier [''] - An optional modifier string. Use this to indicate that the
/// block is in a modified state (e.g. selected).
///
/// @param {string} $pseudo [''] - An optional pseudo class that should be appended to the selector
/// (e.g. hover or after)
///
/// @example
/// // An intentionally complex example:
///  @include block(block, modifier) {
///     @include element(element, modifier) {
///         @media only screen and (max-width: 800px) {
///             @include element(element) { ... }
///         }
///     }
/// }

@mixin block($name, $modifier: '', $pseudo: '') {
    @include -bem-base($name, $modifier, $pseudo, $-block-prefix) {
        @content;
    }
}

/// Creates a element--modifier class name using the bem approach to class naming. Can be used
/// inside of the block mixin or just within a class declartion. Should also work with media media
/// queries and other complex scnearios.
///
/// @param {string | list} $name - The element name string (if a list is provided the selector will
/// include a selector for each item in the list)
///
/// @param {string} $modifier [''] - An optional modifier string. Use this to indicate that the
/// block is in a modified state (e.g. selected).
///
/// @param {string} $pseudo [''] - An optional pseudo class that should be appended to the selector
/// (e.g. hover or after)
///
/// @example
///     .block { @include bem(element) { ... } }
///     // => .block__element { ... }
///
/// @example
///     .block { @include bem,(element, modifier) { ... } }
///     // => block__element--modifier { ... }
///
/// @example
///     .block { @include bem(element, $pseudo:after) { ... } }
///     // => block__element:after { ... }
///
/// @example
///     .block { @include bem,(element, modifier) { @include bem(child) { ... } } }
///     // => .block__element--modifier { .block__child { ... } }
///
/// @example
///     .block { @include bem((element1 element2)) { ... } }
///     // => .block__element1, block__element2 { ... }

@mixin bem($name, $modifier: '', $pseudo: '') {
    @include -bem-base(
        $name,
        $modifier,
        $pseudo,
        #{$-block-prefix + get-block-name(&) + $-element-prefix}
    ) {
        @content;
    }
}
