// RIVER Design-System: System - Mixins / CSS Variables //

// Mixins for outputting CSS Variables.
// This follows from the functions defined in 'system/functions-system'.

// Apply CSS Variables from the design system
// This should most liekly be called within a :root selector, or a given scope
@mixin design-apply-variables($groupOrRangeKey, $outputRange: true, $outputReferences: true) {

    // Manage a range
    @if(str-contains($groupOrRangeKey, '.')) {
        // Look for a range
        $map: design-range($groupOrRangeKey);

        // Output variables directly
        $variableRangeName: str-replace($groupOrRangeKey, '.', '-');
        @include _design-apply-variables-output($map, $variableRangeName, $outputRange, $outputReferences);
    }

    // Manage a group
    @if(not(str-contains($groupOrRangeKey, '.'))) {
        // Look for a group
        $map: design-group($groupOrRangeKey);

        // Loop the group map
        @each $rangeKey, $values in $map {

            // Check if $value is a map (default), or a single value reference
            @if(type-of($values) == 'map') {
                $variableRangeName: $groupOrRangeKey + design-setting('lang.variables.range-seperator') + $rangeKey;
                @include _design-apply-variables-output($values, $variableRangeName, $outputRange, $outputReferences);
            } @else if(length($values) > 0) {
                $values: ($rangeKey: $values); // eg, 'colors' => ('white': white)
                @include _design-apply-variables-output($values, $groupOrRangeKey, $outputRange, $outputReferences);
            }

        }
    }
}

// Output the given values as CSS Variables
@mixin _design-apply-variables-output($values, $variableRangeName, $outputRange: true, $outputReferences: true) {
    // Note, this should not be called if design-setting('variables') == false
    @each $key, $value in $values {

        // Recursively include maps
        @if(type-of($value) == 'map') {
            // Value is a sub-range
            @include design-apply-variables-output($value, $variableRangeName + '-' + $key);
        }

        // Output the value if it's not empty; skip keys that begin with '-' as these refer to map settings
        @if(type-of($value) != 'map'
            and not($value == null)
            and not(type-of($key) == 'string' and str-char-first($key) == '_'))
        {
            // Note variable name
            $variableName: $variableRangeName + design-setting('lang.variables.value-seperator') + $key;
            $variableName: design-transform-variable-name($variableName);

            // Note whether variables are enabled
            $variablesReferencesEnabled: design-setting('variables') and design-setting('variables.references');

            // Output the variable normaly
            @if($variablesReferencesEnabled) {
                // Resolve all values; accounts for any @ design system references
                $value: design-variable-resolve($value);

                // Direct Value
                // Include matching key/value pairs (eg, a size map may include (0: 0) as a valid key/value pair)
                @if((not map-has-key($values, $value) and $value) or ($value == $key)) {
                    @if ($outputRange or (not(type-of($key) == 'number') and $outputReferences)) {
                        --#{$variableName}: #{$value};
                    }
                }
                // TODO, refactor into design-key-is-range($values, $key) and $outputRange

                // Refernece or Alias (value is the parent key)
                // Ignore matching key/value pairs (eg, a size map may include (0: 0) as a valid key/value pair)
                @if(map-has-key($values, $value) and $value and not ($value == $key)) {
                    @if($outputReferences) {
                        $referenceName: $variableRangeName + design-setting('lang.variables.value-seperator') + $value;
                        $referenceName: design-transform-variable-name($referenceName);
                        --#{$variableName}: var(--#{$referenceName});
                    }
                }
                // TODO, refactor into design-key-is-reference($values, $key) and $outputReferences
            }

            // Output the variable as literal values (no variable references)
            @if(not($variablesReferencesEnabled)) {
                
                // Resolve any aliases/references within the range map
                // Ignore cases where key and value match (eg, sizes 0: 0)
                @while(map-has-key($values, $value) and not($value == map-get($values, $value))) {
                    $value: map-get($values, $value);
                }

                // Resolve the final value
                $value: design-value-resolve($value);

                // Output the literal value as a variable
                --#{$variableName}: #{$value};
            }
        }

    }
}

// Apply CSS Variables from the design system
// Include range values only (exclude aliases/references)
@mixin design-apply-variables-range($groupOrRangeKey) {
    @include design-apply-variables($groupOrRangeKey, true, false);
}

// Apply CSS Variables from the design system
// Include aliases/references only (exclude range values)
@mixin design-apply-variables-references($groupOrRangeKey) {
    @include design-apply-variables($groupOrRangeKey, false, true);
}

// Apply CSS Variables from the design system
// This should most liekly be called within a :root selector, or a given scope
@mixin design-apply-variables($groupOrRangeKey, $outputRange: true, $outputReferences: true) {

    // Manage a range
    @if(str-contains($groupOrRangeKey, '.')) {
        // Look for a range
        $map: design-range($groupOrRangeKey);

        // Output variables directly
        $variableRangeName: str-replace($groupOrRangeKey, '.', '-');
        @include _design-apply-variables-output($map, $variableRangeName, $outputRange, $outputReferences);
    }

    // Manage a group
    @if(not(str-contains($groupOrRangeKey, '.'))) {
        // Look for a group
        $map: design-group($groupOrRangeKey);

        // Loop the group map
        @each $rangeKey, $values in $map {

            // Check if $value is a map (default), or a single value reference
            @if(type-of($values) == 'map') {
                $variableRangeName: $groupOrRangeKey + design-setting('lang.variables.range-seperator') + $rangeKey;
                @include _design-apply-variables-output($values, $variableRangeName, $outputRange, $outputReferences);
            } @else if(length($values) > 0) {
                $values: ($rangeKey: $values); // eg, 'colors' => ('white': white)
                @include _design-apply-variables-output($values, $groupOrRangeKey, $outputRange, $outputReferences);
            }

        }
    }
}

// Output the given values as CSS Variables
@mixin _design-apply-variables-output($values, $variableRangeName, $outputRange: true, $outputReferences: true) {
    // Note, this should not be called if design-setting('variables') == false
    @each $key, $value in $values {

        // Recursively include maps
        @if(type-of($value) == 'map') {
            // Value is a sub-range
            @include design-apply-variables-output($value, $variableRangeName + '-' + $key);
        }

        // Output the value if it's not empty; skip keys that begin with '-' as these refer to map settings
        @if(type-of($value) != 'map'
            and not($value == null)
            and not(type-of($key) == 'string' and str-char-first($key) == '_'))
        {
            // Note variable name
            $variableName: $variableRangeName + design-setting('lang.variables.value-seperator') + $key;
            $variableName: design-transform-variable-name($variableName);

            // Note whether variables are enabled
            $variablesReferencesEnabled: design-setting('variables') and design-setting('variables.references');

            // Output the variable normaly
            @if($variablesReferencesEnabled) {
                // Resolve all values; accounts for any @ design system references
                $value: design-variable-resolve($value);

                // Direct Value
                // Include matching key/value pairs (eg, a size map may include (0: 0) as a valid key/value pair)
                @if((not map-has-key($values, $value) and $value) or ($value == $key)) {
                    @if ($outputRange or (not(type-of($key) == 'number') and $outputReferences)) {
                        --#{$variableName}: #{$value};
                    }
                }
                // TODO, refactor into design-key-is-range($values, $key) and $outputRange

                // Refernece or Alias (value is the parent key)
                // Ignore matching key/value pairs (eg, a size map may include (0: 0) as a valid key/value pair)
                @if(map-has-key($values, $value) and $value and not ($value == $key)) {
                    @if($outputReferences) {
                        $referenceName: $variableRangeName + design-setting('lang.variables.value-seperator') + $value;
                        $referenceName: design-transform-variable-name($referenceName);
                        --#{$variableName}: var(--#{$referenceName});
                    }
                }
                // TODO, refactor into design-key-is-reference($values, $key) and $outputReferences
            }

            // Output the variable as literal values (no variable references)
            @if(not($variablesReferencesEnabled)) {
                
                // Resolve any aliases/references within the range map
                // Ignore cases where key and value match (eg, sizes 0: 0)
                @while(map-has-key($values, $value) and not($value == map-get($values, $value))) {
                    $value: map-get($values, $value);
                }

                // Resolve the final value
                $value: design-value-resolve($value);

                // Output the literal value as a variable
                --#{$variableName}: #{$value};
            }
        }

    }
}

// Apply CSS Variables from the design system
// Include range values only (exclude aliases/references)
@mixin design-apply-variables-range($groupOrRangeKey) {
    @include design-apply-variables($groupOrRangeKey, true, false);
}

// Apply CSS Variables from the design system
// Include aliases/references only (exclude range values)
@mixin design-apply-variables-references($groupOrRangeKey) {
    @include design-apply-variables($groupOrRangeKey, false, true);
}
