////
/// @group color
////

@use "sass:map";
@use "sass:meta";
@use "sass:string";
@use "sass:color";

@use "utils";
@use "selector";

/// The color palette map, can be extended or modified with set() and accessed with get()
/// - Note do not use names that start with "var(" which are reserved for custom properties. Also do not use "inherit" or "transparent" as those are reserved.
/// - The default palette color names are used throughout the system
/// @type map  

$palette: (
  "black":                      #000000,
  "white":                      #ffffff,
  "type":                       #1f2937,
  "type-secondary":             #4b5563,
  "type-tertiary":              #6b7280,
  "type-disabled":              #9ca3af,
  "headline":                   inherit,
  "background":                 #ffffff,
  "focus":                      #2563eb,
  "accent":                     #4f46e5,
  "info":                       #0ea5e9,
  "success":                    #16a34a,
  "warning":                    #ea580c,
  "danger":                     #dc2626,
  "info-background":            #e0f2fe,
  "success-background":         #dcfce7,
  "warning-background":         #ffedd5,
  "danger-background":          #fee2e2,
  // Used for background of elements that are placeholder or that have no value (think charts)
  "placeholder-background":     #e5e7eb,
  "placeholder-background-alt": #d1d5db,
  "selected":                   #0ea5e9,
  "selected-background":        #e0f2fe,
  "box-shadow":                 rgba(0, 0, 0, 0.2),
  "box-shadow-hover":           rgba(0, 0, 0, 0.4),
  "rule":                       #bbbfc6,
  "rule-light":                 #dadde5,
  "link":                       #2563eb,
  "link-hover":                 #1d4ed8,
  "link-active":                #1e40af,
  "link-visited":               #7e22ce,
  "bullet":                     inherit,
  "control":                    #ffffff,
  "control-hover":              #ffffff,
  "control-active":             #ffffff,
  "control-border":             #4f46e5,
  "control-border-hover":       #4338ca,
  "control-border-active":      #312e81,
  "control-background":         #4f46e5,
  "control-background-hover":   #4338ca,
  "control-background-active":  #312e81,
) !default;

/// Pairs of background-color and color definitions
/// @type map
/// @prop {Number|String} $contexts.name.background-color Color value or name of color in $palette
/// @prop {Number|String} $contexts.name.color Color value or name of color in $palette
/// @prop {Boolean} $contexts.name.base-class Print this value in the base module as a class (if base prints)

$contexts: (
  "dark" : (
    "background-color" : "black",
    "color" : "white",
    "base-class" : true
  ),
  "light" : (
    "background-color" : "white",
    "color" : "black",
    "base-class" : true
  ),
);

/// Palette entries that are output as classes when using all-color-class-styles
/// - Use set-color-classes mixin to alter this map
$color-classes: (
  "black" : true,
  "white" : true,
  "type": true
) !default;

/// Used to override or extend the color palette
/// @param {Map} $changes A map to merge into the color palette
/// @example scss Setting the error and type color
///   @include ulu.color-set((
///     "type" : #444,
///     "error" : orange,
///   ));

@mixin set($changes) {
  $palette: map.merge($palette, $changes) !global;
}

/// Get a color from the palette by name
/// @param {String} $name Name of color to get
/// @return {Color} Note if non-string value is passed it is sent back through, along with custom properties ("var(..." and keywords inherit and transparent. This is by design so that you can always pass a user's colors through this (without having to check if it's a color value or a string [color palette])

@function get($name) {
  // Allow non lookup if the value is already a color (helps with code flow)
  @if (meta.type-of($name) == "string") {
    $is-keyword: $name == "inherit" or $name == "transparent" or $name == "currentColor";
    $is-cssvar: string.index($name, "var(") == 1;
    $is-color-mix: string.index($name, "color-mix(") == 1;
    // Allow custom-properties and keyword inherit/transparent to pass through
    @if ($is-keyword or $is-cssvar or $is-color-mix) {
      @return $name;
    // Else look up the color from the palette
    } @else {
      @return utils.require-map-get($palette, $name, 'color');
    }
  }
  // Allow everything other than strings to pass through
  // - color, null, false, etc (so they can not output)
  @return $name;
}

/// Set output classes for all-color-class-styles
/// @param {Map} $changes Map of changes (you can disable defaults this way)

@mixin set-color-classes($changes) {
  $color-classes: map.merge($color-classes, $changes) !global;
}

/// Check if a color is set in the palette

@function exists($name) {
  $color: map.get($palette, $name);
  @return utils.when($color != null, true, false);
}

/// Set color contexts
/// @param {Map} $changes A map to merge 
/// @param {Map} $deep Use deep merge
/// @param {Map} $overwrite Overwrite the completely (cannot be used with deep)
/// @example scss Overwriting contexts
///   @include ulu.color-set-contexts((
///     "dark" : (
///       "background-color" : red,
///       "color" : white,
///     )
///   ), false, true);

@mixin set-contexts($changes, $merge-mode: null) {
  $contexts: utils.map-merge($contexts, $changes, $merge-mode) !global;
}

/// Get a context by name
/// @param {String} $name Name of context
/// @return {Map}

@function get-context($name) {
  @return utils.require-map-get($contexts, $name, 'context');
}

/// Get a context's value'
/// @param {String} $name Name of context
/// @param {String} $prop The property to get
/// @return {Color}

@function get-context-value($name, $prop) {
  $context: get-context($name);
  $value: map.get($context, $prop);
  // Get from palette by name
  @if (meta.type-of($value) == "string") {
    $value: get($value);
  }
  @return $value;
}

/// Prints contexts styles
/// @param {String} $name Name of context

@mixin context-styles($name) {
  $context: get-context($name);
  background-color: get-context-value($name, "background-color");
  background: map.get($context, "background"); // Allow gradients/images
  background-image: map.get($context, "background-image"); // Allow gradients/images
  color: get-context-value($name, "color");
}

/// Tint (add white) a color using the default white by a percentage
/// @param {Color, String} $color Color/palette color name to apply to tint
/// @param {Number} $percentage Percentage
/// @return {Color}
/// @author 
/// @link https://css-tricks.com/snippets/sass/tint-shade-functions/ Modified from source (CSS Tricks, Kitty Giraudel)

@function tint($color, $percentage) {
  @return color.mix(get("white"), get($color), $percentage);
}

/// Tint (add white) a color using the default white by a percentage (Using color-mix)
/// - This only works in modern browsers (as of June 2025)
/// - These match ulu.color-tint() and are designed to accept the same arguments with the same results
/// @param {Color, String} $color Color or custom property to apply mix to
/// @param {Number} $percentage Percentage
/// @return {Color}

@function css-tint($color, $percentage) {
  @return color-mix(in srgb, get("white") $percentage, get($color) calc(100% - $percentage));
}

/// Shade (add black) a color with the default black by a percentage
/// @param {Color, String} $color Color/palette color name to shade
/// @param {Number} $percentage Percentage to shade
/// @return {Color}
/// @author Kitty Giraudel
/// @link https://css-tricks.com/snippets/sass/tint-shade-functions/ Modified from source (CSS Tricks, Kitty Giraudel)

@function shade($color, $percentage) {
  @return color.mix(get("black"), get($color), $percentage);
}

/// Shade (add black) a color using the default white by a percentage (Using color-mix)
/// - This only works in modern browsers (as of June 2025)
/// - These match ulu.color-shade() and are designed to accept the same arguments with the same results
/// @param {Color, String} $color Color or custom property to apply mix to
/// @param {Number} $percentage Percentage
/// @return {Color}

@function css-shade($color, $percentage) {
  @return color-mix(in srgb, get("black") $percentage, get($color) calc(100% - $percentage));
}

/// Prints all context styles 
/// @param {String} $with-prop Checks the specific context for a certain prop (has to be truthy)(used for output in helper/base color modules)
/// @example scss
///  @include ulu.all-context-styles();
/// @example html {preview} Example of a color-context
///  <div class="color-context-dark" style="padding: 1rem">
////   Some text in dark context
//// </div>

@mixin all-context-styles($with-prop: null) {
  $prefix: selector.class("color-context");
  @each $name, $context in $contexts {
    @if (not $with-prop or map.get($context, $with-prop)) {
      #{ $prefix }-#{ $name } {
        @include context-styles($name);
      }
    }
  }
}

/// Outputs all color classes
/// @example scss
///  @include ulu.all-color-class-styles();
/// @example html {preview} Example of a color-context
///  <span class="color-name">Some text</span>

@mixin all-color-class-styles() {
  $prefix: selector.class("color");
  @each $name, $output in $color-classes {
    @if ($output) {
      #{ $prefix }-#{ $name } {
        color: get($name);
      }
    }
  }
}