//------------------------------------*\
//    $COLOR FUNCTIONS
//------------------------------------*/

@import 'variables';
@import 'blendmodes';

/// Get a color value from the global `$base-colors` map. Darker and lighter tones are available by passing a second string.
/// @example
///   .foo {
///      background-color: colors(links, light));
///   }
/// @param {string} $color-name - The color you
/// @param {string} $tone ['base']
/// @param {number} $opacity [1]
/// @returns {color}
///
/// @group core
///
/// @link http://blog.12spokes.com/web-design-development/simple-css-color-management-with-sass/
/// @see $base-colors
///
@function colors($color-name, $tone: 'base', $opacity: 1) {
    $color-map: $base-colors;

    @if not map-has-key($color-map, $color-name) {
        @warn "Color '#{$color-name}' not found in $base-colors map. Returning #f00 instead";
        @return red;
    }
    @else if not map-has-key(map-get($color-map, $color-name), $tone) {
        @warn "Tone '#{$tone}' not found in #{$color-name}. Returning #f00 instead.";
        @return red;
    }

    @if $opacity < 1 {
        @return rgba(
            map-get(map-get($color-map, $color-name), $tone),
            $opacity
        );
    }

    @return map-get(map-get($color-map, $color-name), $tone);
}
/// @alias colors
/// @group core
///
@function color($color, $tone: 'base', $opacity: 1) {
    @return colors($color, $tone, $opacity);
}
/// @alias colors
///
@function clr($color, $tone: 'base', $opacity: 1) {
    @return colors($color, $tone, $opacity);
}

/// Change text color based on background-color's darkness.
/// @param {color} $color
/// @returns {color}
///
/// @example
///    div {
///        background-color: #f8cd12;
///        color: black-or-white(#f8cd12);
///    }
///    // Your text color would be black because #f8cd12 and #00000 have more contrast.
///
/// @group utils
///
@function black-or-white($color) {
    @if (color-luminance($color) > 0.45) {
        @return #000;
    } @else {
        @return #fff;
    }
}

/// @alias black-or-white
///
@function set-text-color($color) {
    @return black-or-white($color);
}

///
/// Compares a set of colors against `$base` and returns the color
/// with the most contrast. `$colors` should be a list.
/// [Taken from here](https://gist.github.com/voxpelli/6304812)
///
/// @group utils
/// @param {Color} $base
/// @param {List} $colors
/// @param {Number} $tolerance [0]
///
/// @example
///   div {
///       color: pick_best_color(#f8cd12, (#a0a0c0, #fff, #2c2c2c));
///   }
///   // The function would test the colors and find that #2c2c2c is the best.
///
/// @returns {color}
///
@function pick-best-color($base, $colors, $tolerance: 0) {
    $contrast: color-contrast($base, nth($colors, 1));
    $best: nth($colors, 1);

    @for $i from 2 through length($colors) {
        $current_contrast: color-contrast($base, nth($colors, $i));

        @if ($current_contrast - $contrast > $tolerance) {
            $contrast: color-contrast($base, nth($colors, $i));
            $best: nth($colors, $i);
        }
    }
    @return $best;
}

/// Find the relative luminance of a color. Used by `color-contrast()`. [Adapted from color.js](https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/color.js)
/// @param {Color} $color
/// @returns {number} 0 - 1
///
@function color-luminance($color) {
    $rgba: red($color), green($color), blue($color);
    $rgba2: ();

    @for $i from 1 through 3 {
        $rgb: nth($rgba, $i);
        $rgb: $rgb / 255;

        $rgb: if(
            $rgb < 0.03928,
            $rgb / 12.92,
            pow(($rgb + 0.055) / 1.055, 2.4)
        );

        $rgba2: append($rgba2, $rgb);
    }
    @return (0.2126 * nth($rgba2, 1)) + (0.7152 * nth($rgba2, 2)) +
        (0.0722 * nth($rgba2, 3));
}

/// @alias color-luminance
///
@function luma($color) {
    @return color-luminance($color);
}

/// Find contrast between two colors.
/// [Adapted from color.js](https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/color.js)
/// @requires color-luminance
/// @returns {number} 0 - 1
///
@function color-contrast($color1, $color2) {
    $luminance1: color-luminance($color1) + 0.05;
    $luminance2: color-luminance($color2) + 0.05;
    $ratio: $luminance1 / $luminance2;

    $ratio: round($ratio * 10) / 10;

    @if $luminance2 > $luminance1 {
        $ratio: 1 / $ratio;
    }

    @return $ratio;
}

// /* SG
// # Colors/Debugging [[dev]]
//
// @file tools/_t-color-functions.scss
//
// ##### `@color-palette-output()`
// ###### mixin(`$selector`, `$color-map:` `$$base-colors`)
// Debugging tool that creates a pretty color palette (in columns) using psuedo selectors (nth-child required). Used for "color blocks" output, in style guide.
//
// @requires `map-add-depth()`, `black-or-white()`
//
// */

@mixin color-palette-output($selector, $color-map: ()) {
    $color-names: ();
    $color-subnames: ();
    $color-fullnames: ();
    $color-values: ();

    //Loop through the color map,
    // adding all names, variants, and values to individual lists
    @each $color, $color-variants in $color-map {
        //Put color names into a list
        $color-names: append($color-names, nth($color, 1));

        //Put 'base' at beginning of list
        @if index(map-keys($color-variants), base) ==
            length(map-keys($color-variants))
        {
            $color-variants: list-reverse($color-variants);
        }

        //Loop through color variants (light, dark, etc.)
        @each $color-variant in $color-variants {
            //Add a non-breaking space before the color name
            $subname-temp: unquote(' ' + nth($color-variant, 1));

            //Create name by adding the color and color-subname (e.g. blue dark)
            $fullname-temp: unquote($color + $subname-temp);

            //Add names to lists for later use
            $color-fullnames: append($color-fullnames, $fullname-temp);
            $color-subnames: append(
                $color-subnames,
                unquote(nth($color-variant, 1))
            );
            $color-values: append($color-values, nth($color-variant, 2));

            //Remove 'base' from titles for nicer output
            @if $subname-temp == ' base' {
                $subname-temp: unquote('');
            }
        }
    }

    //Store color lengths for selector loop
    $top-colors-len: length($color-names);
    $sub-colors-len: length($color-subnames) / $top-colors-len;
    $color-index: 1;
    $color-loop: 1;


    //Loop through all colors
    @for $i from 1 through length($color-fullnames) {
        //Grab the current color value and name
        $current-color-value: nth($color-values, $color-index);
        $current-color-name: nth($color-fullnames, $color-index);

        //Determine the index of the color to use
        // This essentially makes the color chosen 1 loop ahead of $i
        // E.G. if there are 9 top-level colors, $color-index will be 9 ahead of the current $i value
        $color-index: ($color-index - $color-loop) +
            ($sub-colors-len + $color-loop);

        //Apply it to the selection based on nth-child (using $i)
        #{$selector}:nth-child(#{$i}) {
            background-color: #{$current-color-value};
            //Determine if black or white should be used for text color
            color: #{black-or-white($current-color-value)};

            //Describe the color and show its hex value
            &:before {
                content: '#{$current-color-name}';
            }
            &:after {
                content: '#{$current-color-value}';
            }

            //Make first row of colors a little larger
            @if $i <= $top-colors-len {
                padding: 2% 1% 2.5%;
            }
        }

        // Increase the loop when we've reached the highest value
        // And reset the color index.
        @if $color-index > length($color-fullnames) {
            $color-loop: $color-loop + 1;
            $color-index: $color-loop;
        }
    }

    //Base styles
    #{$selector} {
        float: left;
        margin-right: 0.95%;
        padding: 1%;
        //Width is a percentage of the total top-level colors (minus 1% for margin)
        width: percentage(1 / $top-colors-len) - 1%;
        overflow: hidden;

        &:before {
            display: block;
            white-space: nowrap;
            @include rems(font-size, modular-scale(-1));
        }

        &:after {
            display: block;
            @include rems(font-size, modular-scale(-2));
        }
    }
}

/// Takes all colors in a map and blends them with a secondary color to even them out.
/// * Should only be used as a tool to create color palletes.
/// * Shouldn't be used on every compilation since it can make development fairly abstract and difficult to understand.
///
/// @group utils
///
/// @param {map} $color-list -
/// @param {string} $blending-mode ['overlay'] - blending mode to use over each color
/// @param {color} $blending-color [blue] - color to blend with.
/// @param {percent} $amount [0%] - amount to blend.
///
@function harmonize-colors(
    $color-list,
    $blending-mode: 'overlay',
    $blending-color: blue,
    $amount: 0%
) {
    $temp-name-map: ();
    $temp-top-map: ();
    $temp-list: ();
    $return: null;

    //Make sure non-prefixed blending modes
    // are appended with the correct function namespace
    $blending-mode:
        if(
            str-contains($blending-mode, 'blend'),
            $blending-mode,
            'blend-' + $blending-mode
        );

    @if type-of($color-list) == 'map' {
        $color-list: map-add-depth($color-list);

        @each $color, $name in $color-list {
            @each $key, $value in $name {
                $value: call(safe-get-function($blending-mode), $blending-color, $value, $amount);
                $temp-key: (
                    $key: $value
                );
                $temp-color: (
                    $color: ($temp-key)
                );
                $temp-name-map: map-merge($temp-name-map, $temp-color);
                @debug $temp-name-map;
            }
            $temp-top-map: map-merge($temp-top-map, $temp-name-map);
        }

        $return: map-merge($color-list, $temp-top-map);
    } @else {
        @each $value in $color-list {
            $value: call(
                safe-get-function($blending-mode),
                $blending-color,
                $value,
                $amount
            );
            $temp-list: append($temp-list, $value);
        }
        $return: $temp-list;
    }

    @return $return;
}

/// Takes base color values and generates a full color palette. Used by the `$$base-colors` map to create a project's palette, accessible via `colors()`.
///
/// **Arguments:**
/// * `$map`: Color map you want to create variations of. Defaults to `$$base-colors`.
/// * `$functions`: color functions used to generate variations (e.g. lighten or darken). Can use any `blend` function, provided `$blending-colors` are provided.
/// * `$increments`: percentage amount to apply `$function` to each `$variations`.
/// * `$variations`: actual names for each color tone when `colors()` used.
/// * `$blending-colors`: used when a function is a `blend`. Can be a list or a single color.
///
/// @param {Map} $map [$colors]
/// @param {List | Strings} $functions [(tint, tint, tint, shade, shade, shade)]
/// @param {List | Numbers} $increments [(20%, 40%, 80%, 20%, 40%, 80%)]
/// @param {List | Strings} $variations [(light, lighter, lightest, dark, darker, darkest)]
/// @param {List | Colors} $blending-colors [null]
///
@function generate-color-variations(
    $map: $base-colors,
    $functions: (
        tint,
        tint,
        tint,
        shade,
        shade,
        shade
    ),
    $increments: (
        20%,
        40%,
        80%,
        20%,
        40%,
        80%
    ),
    $variations: (
        light,
        lighter,
        lightest,
        dark,
        darker,
        darkest
    ),
    $blending-colors: null
) {
    // 1. Loop through the pallets and colors of the map input.
    // 2a. Store $color map in a list
    //     (this is required to prevent bugs on colors with names of real colors).
    // 2b. Define color function increment.
    // 3.  Get index and find the amount to use with the current color function.
    // 4a. Divide the loop increment by the number of color functions.
    // 4b. If the loop is larger than the variation frequency,
    //	   move to the next color function.
    // 5a. Get the current color function and current color.
    // 5b. Add blending mode color if necessary (useful for mixing colors).
    // 5c. If blending-color is defined, use it to blend colors (use list-value if exists)
    // 6a. Call the color function, the color(s), and the amount and store that value.
    // 6b. Create the variation and color definition in map format ("name": color).
    // 6c. Merge the current base color map with the new variant map.
    // 7a. Format the previous loop results into a nested map.
    // 7b. Merge the input map with itself and the new nested map.

    // Determine how often to switch functions.
    $variation-frequency: (length($variations) / length($functions));
    // Make sure map is properly formatted.
    $map: map-add-depth($map, 'base');
    // Define new map (for easier to understand return value).
    $new-map: $map;
    $blend-value: '';

    // Loop through the variation names.
    @each $palette, $color in $map {
        $values-list: $color;
        // 3:
        $color_fn-i: 1;

        @each $variant in $variations {        	            //[4a]
            @if not map-has-key($color, $variant) {
                $i: index($variations, $variant);	        //[4b]
                $amount: nth($increments, $i);			    //[4b]

                $color_fn_place: $i/$variation-frequency;	//[5a]

                @if $color_fn_place > $color_fn-i{ 			//[5b]
                    $color_fn-i: $color_fn-i + 1;			//[5b]
                }

                $curr_fn: nth($functions, $color_fn-i); 		//[6a]
                $curr_color: map-get($color, base);				//[6b]

                @if $blending-colors != null
                    or type-of($blending-colors) == 'list'{		//[6c]

                    @if type-of($blending-colors) == 'list' {
                        $blend-value: nth($blending-colors, $color_fn-i);
                    }@else {
                        $blend-value: $blending-colors;
                    }

                    @if $blend-value == 'self' {
                        $blend-value: $curr_color;
                    }

                    @if $blend-value != null {
                        $amount: 1 - ($amount / 100%); //Invert the amount
                        $temp-color: $curr_color;
                        //Use amount to transparency of blending color
                        $curr_color: transparentize($blend-value, $amount);
                        //$amount becomes 'background' color we're blending
                        $amount: $temp-color;
                    }
                }
                //Try to add "blend" to the current function if it's not valid
                @if not function-exists($curr_fn){
                    $curr_fn: unquote('blend-') + $curr_fn;
                }

                $new-color: call(safe-get-function($curr_fn), $curr_color, $amount); 	//[7a]
                $value: ($variant: $new-color); 					//[7b]

                $values-list: map-merge($value, $values-list);		//[7c]
            }
        }

        // Sass maps are formatted
        // with double parenthesis as they get more nested.
        // (first-level: ( (second-level: value) ) )
        $inner-map: ($palette: $values-list);				    //[8a]
        $new-map: _combine-color-maps($new-map, $inner-map); 	//[8b]

    }

    @return $new-map; //[9]
}

/// Combines two color maps (or really any double-nested map)
@function _combine-color-maps($map1, $map2) {
    @each $pal, $color in $map2 {
        $value: (
            $pal: $color
        );
        $map1: map-merge($map1, $value);
    }

    @return $map1;
}
