@use 'sass:list';
////
/// @group site.color
////

@use '../utils' as *;
@use 'config' as *;

// ---------------------------------------------------------------------------
// CONFIG
// ---------------------------------------------------------------------------

/// Produce a shade (a lighter or darker version) of a color based on the value of $shade and an
/// optional darkest to lightest shade range.
///
/// @param {color} $color [null] The color to be shaded. If not provided, it will be calculated as
/// the middle value within $shade-range.
///
/// @param {number} $shade [0] The shading to be applied, as a percentage from -100% to 100% or as a
/// shade value between -10, 10 (can be overriden with config(shade-steps)). 0 returns the color
/// itself. Negative values make the color darker, postive values make it lighter.
///
/// @param {(color, color)} $shade-range [null] The range of colors will be produced as a list
/// (darkest, lightest).
///
/// @example $color: shade(red, 5, null) // => #ff8080

@function shade($color: null, $shade: 0, $shade-range: null) {
    $shade: if(sass(is-percentage($shade)): strip-unit($shade * 0.1) ; else: $shade);

    @if ($shade-range == null) {
        $shade-range: (get('colors.shade-dark'), get('colors.shade-light'));
    }

    @return color-shade($color, $shade, $shade-range);
}

/// Lookup a color by name and optionally get a shade of it or apply alpha.
///
/// @param {*} $shade [0] The shading to be applied, as a value between -10, 10
/// (can be overriden with config(shade-steps)). 0 returns the color itself.
/// Negative values make the color darker, postive values make it lighter.
///
/// @param {number} $alpha [1] The alpha value of the color to be returned. If a value other than 1
/// is provided, the ooutput will be in rgba format.
///
/// @example
/// // Add the color to the global configuration
/// config('colors.purple', #C578DD);
///
/// // Retrieve the color
/// clr(purple) => #C578DD
/// clr(purple, -1) => (#b16cc7)
/// clr(purple, $alpha: 0.5) => (rgba(197, 120, 221, 0.5))

@function clr($name, $shade: 0, $alpha: 1, $tag: 1) {
    $color: get('colors.' + $name);

    @if ($color == null) {
        @error 'The color #{$name} could not be found.';
    }

    // if the color is a list, we find the correct tag by index or name, and select the correct color
    @if (is-list($color)) {
        $tag-index: $tag;

        @if (is-string($tag)) {
            $tag-index: if-null(list.index(get('color-tags'), $tag), 1);
        }

        $tag-index: clamp-number($tag-index, 1, list.length($color));

        $color: list.nth($color, $tag-index);
    }

    $base: $color;
    $range: null;

    // $color is a list of 2 values representing the dark and light shade range,
    // so calculate base as the middle value between them
    @if (is-list($color) and list.length($color) == 2) {
        $range: (list.nth($color, 1), list.nth($color, 2));
        $base: null;
    }

    // $color is a list of 3 values like: (base, range-dark, range-light)
    @if (is-list($color) and list.length($color) == 3) {
        $base: list.nth($color, 1);
        $range: (list.nth($color, 2), list.nth($color, 3));
    }

    // get the shaded version of the color
    $shade: shade($base, $shade: $shade, $shade-range: $range);

    @return rgba($shade, $alpha);
}

/// Get a standardized shade of site's base gray.
///
/// @param {number} $shade [0] The shading to be applied, as a value between -10, 10
/// (can be overriden with config(shade-steps)). 0 returns the color itself.
/// Negative values make the color darker, postive values make it lighter.
///
/// @param {number} $alpha [1] The alpha value of the final color that will be returned.
///
/// @example gray(2, 0.5) // => rgba(173, 179, 183, 0.5)

@function gray($shade: 0, $alpha: 1) {
    @return clr(gray, $shade, $alpha);
}

/// Get a standardized shade of the site's primary accent color
///
/// @param {*} $shade [0] The shading to be applied, as a value between -10, 10 (can be overriden
/// with config(shade-steps)). 0 returns the color itself. Negative values make the color darker,
/// postive values make it lighter.
///
/// @param {number} $alpha [1] The alpha value of the color to be returned. If a value other than 1
/// is provided, the output will be in rgba format.
///
/// @example accent(2, 0.5) // => rgba(51, 173, 253, 0.5)

@function accent($shade: 0, $alpha: 1) {
    @return clr(accent, $shade, $alpha);
}

/// Choose a contrasting foreground based on the contrast between a background and two foreground
/// options (a light and dark).
///
/// @example contrast-color(rgb(233, 229, 31), rgb(199, 192, 192), rgb(133, 133, 231)) // => #8585e7

@function contrast-color($bg, $light-fg: null, $dark-fg: null, $contrast-threshold: 128) {
    @if (not(is-color($bg))) {
        $bg: clr($bg);
    }

    $light-fg: if-null($light-fg, get('colors.contrast-light'));
    $dark-fg: if-null($dark-fg, get('colors.contrast-dark'));

    @return if(sass(yiq($bg) < $contrast-threshold): $light-fg; else: $dark-fg);
}

/// Helper mixin that generates a set of classes for previewing each of the site's registered
//colors. It will also output the html required for preview unless supressed.
///
/// @example color-preview-classes()

@mixin color-preview-classes($colors: get('colors'), $output-html: true) {
    $html: '';
    @each $name, $color in $colors {
        $html: $html + '\a<div class="color-preview-' + $name + '">' + $name + '</div>';
        .color-preview-#{$name} {
            background-color: $color;
            color: contrast-color($color);
        }
    }

    @if ($output-html) {
        @debug $html;
    }
}
