//------------------------------------
//      $SPACING UTILITIES
//------------------------------------

/// Utility spacing class configuration.
///
/// @type map
/// @prop {map} $config [()]
/// @prop {boolean} $config.silent [true] - Output silent classes?
/// @prop {boolean} $config.important [false] - Make declarations important?
/// @prop {string} $config.format [] - Class formatting. Accepts `property`, `direction`, `amount`, and `breakpoint`. See `format-class-name` for more information.
/// @prop {map} $config.directions [('-t': 'top')] - Names and directions to append to class names.
/// @prop {map} $config.amounts [('-2': 2)] - Names and values to multiply by each `unit`.
/// @prop {map} $config.properties [('m': margin)] - Names for each property.
/// @prop {boolean} $config.unit ['auto'] - What unit to multiply each 'amounts'. Passed directly to `type-space`.
///
/// @require format-class-name
/// @group config
///
$spacing-classes: (
    silent: true,
    important: false,
    format: (
        'u-{%property%}' '{%direction%}{%amount%}' '{\\@}{%breakpoint%}',
    ),
    properties: (
        'margin': margin,
    ),
    directions: (
        '': '',
    ),
    amounts: (
        '': 1,
    ),
    unit: 'auto',
) !default;

/// Generates responsive spacing and padding classes.
///
/// @group utils
/// @param {map} $config - same values as $spacing-classes
/// @see $spacing-classes
///
@mixin _output-spacing-classes($config: ()) {
    $class-name-list: ();
    $class-cache: ();

    $classtype: if(map-get($config, silent), unquote('%'), unquote('.'));

    //Loop through base names, then amounts, then directions.
    @each $given-name, $base-property in map-get($config, 'properties') {
        @each $amount-name, $amount in map-get($config, 'amounts') {
            @each $direction-name,
                $property-directions in map-get($config, 'directions')
            {
                $class_name: $classtype +
                    format-class-name(
                        map-get($config, 'format'),
                        (
                            'class':
                                '#{$given-name}#{$direction-name}#{$amount-name}',
                            'direction': $direction-name,
                            'amount': $amount-name,
                            'property': $given-name,
                        ),
                        true
                    );

                // Skip if the padding is a negative value.
                @if not(($base-property == 'padding') and ($amount < 0)) {
                    //
                    // Start class declaration
                    //
                    #{$class_name} {
                        @each $direction in $property-directions {
                            $property: $base-property;

                            @if ($direction) and ($direction != '') {
                                $property: '#{$property}-#{$direction}';
                            }

                            $property-amount: '#{$property}#{$amount}';

                            // If this property and amount have been used before,
                            // extend previous value instead of creating.
                            @if map-get($class-cache, $property-amount) {
                                @extend #{map-get(
                                    $class-cache, $property-amount
                                )};
                            } @else {
                                $class-cache: map-merge(
                                    $class-cache,
                                    (
                                        $property-amount: $class_name,
                                    )
                                );
                                //Using type-space mixin,
                                // output the REM-based value
                                @include type-space(
                                    $property,
                                    $amount,
                                    map-get($config, 'important'),
                                    map-get($config, 'unit')
                                );
                            }
                        }
                    }

                    // Add to the registry if needed
                    @if map-get($config, silent) and $debug-silent-classes {
                        $class-name-list: map-merge(
                            $class-name-list,
                            (
                                $class_name: $amount,
                            )
                        );
                    }
                }
            }
        }
    }

    // Register silent classes for debugging.
    @if map-get($config, silent) and $debug-silent-classes {
        $_silent-class-registry: silents-register(
            $class-name-list,
            'spacing'
        ) !global;
    }
}

/// The responsive component to `_output-spacing-classes`. Invokes `spacing-utilites`
/// that loops through a set of media-queries defined in `$breakpoints`.
/// This is most useful for creating responsive spacing classes.
///
/// @group core
///
/// @param {map} $breakpoints [$_all-breakpoints] - breakpoints to loop over.
/// @param {map} $config [$spacing-classes] - Passed to `_output-spacing-classes` mixin.
///
@mixin spacing-classes(
    $breakpoints: map-keys($breakpoints),
    $config: $spacing-classes
) {
    $config: _merge-spacing-configs($config);
    $format: format-class-name(
        map-get($config, 'format'),
        (
            'breakpoint': '',
        )
    );
    @include _output-spacing-classes(
        map-merge(
            $config,
            (
                'format': $format,
            )
        )
    );

    @each $breakpoint in $breakpoints {
        $format: format-class-name(
            map-get($config, 'format'),
            (
                'breakpoint': $breakpoint,
            )
        );
        @include media-query($breakpoint) {
            @include _output-spacing-classes(
                map-merge(
                    $config,
                    (
                        'format': $format,
                    )
                )
            );
        }
    }
}

/// @alias spacing-classes
///
@mixin spacing-utilities-init($args...) {
    @include spacing-classes($args...);
}

@function _is-set($value) {
    @return ($value != null or length($value) != 0);
}

@function _merge-spacing-configs($config) {
    $_default-spacing-classes-config: (
        silent: true,
        important: false,
        properties: (
            'margin': margin,
        ),
        directions: (
            '': '',
        ),
        amounts: (
            '': 1,
        ),
        unit: 'auto',
        format: 'u-{%property%}' '{%direction%}' '{-}{%amount%}'
            '{\\@}{%breakpoint%}',
    );

    $config: map-extend($_default-spacing-classes-config, $config);
    $properties: map-get($config, properties);
    $directions: map-get($config, directions);
    $amounts: map-get($config, amounts);
    // Allow for legacy 'units' config property
    $amounts: if(_is-set($amounts), $amounts, map-get($config, units));
    $important: map-get($config, important);
    $unit: map-get($config, unit);

    // Check for null properties, set reasonable defaults
    $properties: if(
        _is-set($properties),
        $properties,
        map-get($_default-spacing-classes-config, 'properties')
    );
    $directions: if(
        _is-set($directions),
        $directions,
        map-get($_default-spacing-classes-config, 'directions')
    );
    $amounts: if(
        _is-set($amounts),
        $amounts,
        map-get($_default-spacing-classes-config, 'amounts')
    );

    @return (
        silent: map-get($config, 'silent'),
        important: map-get($config, 'important'),
        properties: $properties,
        directions: $directions,
        amounts: $amounts,
        unit: map-get($config, 'unit'),
        format: map-get($config, 'format')
    );
}
