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

@function mergeColorMaps($bulma-colors, $custom-colors) {
  // We return at least Bulma's hard-coded colors
  $merged-colors: $bulma-colors;

  // We want a map as input
  @if meta.type-of($custom-colors) == "map" {
    @each $name, $components in $custom-colors {
      // The color name should be a string
      // and the components either a single color
      // or a colors list with at least one element
      @if meta.type-of($name) ==
        "string" and
        (
          meta.type-of($components) ==
            "list" or
            meta.type-of($components) ==
            "color"
        ) and
        list.length($components) >=
        1
      {
        $color-base: null;
        $color-invert: null;
        $color-light: null;
        $color-dark: null;
        $value: null;

        // The param can either be a single color
        // or a list of 2 colors
        @if meta.type-of($components) == "color" {
          $color-base: $components;
          $color-invert: bulmaFindColorInvert($color-base);
          $color-light: bulmaFindLightColor($color-base);
          $color-dark: bulmaFindDarkColor($color-base);
        } @else if meta.type-of($components) == "list" {
          $color-base: list.nth($components, 1);

          // If Invert, Light and Dark are provided
          @if list.length($components) > 3 {
            $color-invert: list.nth($components, 2);
            $color-light: list.nth($components, 3);
            $color-dark: list.nth($components, 4);

            // If only Invert and Light are provided
          } @else if list.length($components) > 2 {
            $color-invert: list.nth($components, 2);
            $color-light: list.nth($components, 3);
            $color-dark: bulmaFindDarkColor($color-base);

            // If only Invert is provided
          } @else {
            $color-invert: list.nth($components, 2);
            $color-light: bulmaFindLightColor($color-base);
            $color-dark: bulmaFindDarkColor($color-base);
          }
        }

        $value: $color-base, $color-invert, $color-light, $color-dark;

        // We only want to merge the map if the color base is an actual color
        @if meta.type-of($color-base) == "color" {
          // We merge this colors elements as map with Bulma's colors map
          // (we can override them this way, no multiple definition for the same name)
          // $merged-colors: map_merge($merged-colors, ($name: ($color-base, $color-invert, $color-light, $color-dark)))
          $merged-colors: map.merge(
            $merged-colors,
            (
              $name: $value,
            )
          );
        }
      }
    }
  }

  @return $merged-colors;
}

@function powerNumber($number, $exp) {
  $value: 1;

  @if $exp > 0 {
    @for $i from 1 through $exp {
      $value: $value * $number;
    }
  } @else if $exp < 0 {
    @for $i from 1 through -$exp {
      $value: math.div($value, $number);
    }
  }

  @return $value;
}

@function bulmaColorLuminance($color) {
  @if meta.type-of($color) != "color" {
    @return 0.55;
  }

  $color-rgb: (
    "red": color.channel($color, "red", $space: rgb),
    "green": color.channel($color, "green", $space: rgb),
    "blue": color.channel($color, "blue", $space: rgb),
  );

  @each $name, $value in $color-rgb {
    $adjusted: 0;
    $value: math.div($value, 255);

    @if $value < 0.03928 {
      $value: math.div($value, 12.92);
    } @else {
      $value: math.div($value + 0.055, 1.055);
      $value: powerNumber($value, 2);
    }

    $color-rgb: map.merge(
      $color-rgb,
      (
        $name: $value,
      )
    );
  }

  @return map.get($color-rgb, "red") * 0.2126 + map.get($color-rgb, "green") *
    0.7152 + map.get($color-rgb, "blue") * 0.0722;
}

@function bulmaFindColorInvert($color) {
  @if bulmaColorLuminance($color) > 0.55 {
    @return rgba(#000, 0.7);
  } @else {
    @return #fff;
  }
}

@function bulmaFindLightColor($color, $l: 96%) {
  @if meta.type-of($color) == "color" {
    $l: 96%;

    @if color.channel($color, "lightness", $space: hsl) > 96% {
      $l: color.channel($color, "lightness", $space: hsl);
    }

    @return color.change($color, $lightness: $l);
  }

  @return $background;
}

@function bulmaFindDarkColor($color, $base-l: 29%) {
  @if meta.type-of($color) == "color" {
    $luminance: bulmaColorLuminance($color);
    $luminance-delta: 0.53 - $luminance;
    $target-l: math.round($base-l + $luminance-delta * 53);

    @return color.change($color, $lightness: max($base-l, $target-l));
  }

  @return $text-strong;
}

@function bulmaRgba($color, $alpha) {
  @if meta.type-of($color) != "color" {
    @return $color;
  }

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

@function bulmaDarken($color, $amount) {
  @if meta.type-of($color) != "color" {
    @return $color;
  }

  @return color.adjust($color, $lightness: -$amount, $space: hsl);
}

@function bulmaLighten($color, $amount) {
  @if meta.type-of($color) != "color" {
    @return $color;
  }

  @return color.adjust($color, $lightness: $amount, $space: hsl);
}

@function bulmaColorBrightness($n) {
  $color-brightness: math.round(
    math.div(
      (color.channel($n, "red", $space: rgb) * 299) +
        (color.channel($n, "green", $space: rgb) * 587) +
        (color.channel($n, "blue", $space: rgb) * 114),
      1000
    )
  );
  $light-color: math.round(
    math.div(
      (color.channel(#ffffff, "red", $space: rgb) * 299) +
        (color.channel(#ffffff, "green", $space: rgb) * 587) +
        (color.channel(#ffffff, "blue", $space: rgb) * 114),
      1000
    )
  );

  @if abs($color-brightness) < math.div($light-color, 2) {
    @return "dark";
  }

  @return "bright";
}

@function bulmaEnoughContrast($foreground, $background) {
  $r: (
      max(
        color.channel($foreground, "red", $space: rgb),
        color.channel($background, "red", $space: rgb)
      )
    ) -
    (
      min(
        color.channel($foreground, "red", $space: rgb),
        color.channel($background, "red", $space: rgb)
      )
    );
  $g: (
      max(
        color.channel($foreground, "green", $space: rgb),
        color.channel($background, "green", $space: rgb)
      )
    ) -
    (
      min(
        color.channel($foreground, "green", $space: rgb),
        color.channel($background, "green", $space: rgb)
      )
    );
  $b: (
      max(
        color.channel($foreground, "blue", $space: rgb),
        color.channel($background, "blue", $space: rgb)
      )
    ) -
    (
      min(
        color.channel($foreground, "blue", $space: rgb),
        color.channel($background, "blue", $space: rgb)
      )
    );
  $sum-rgb: $r + $g + $b;

  @if $sum-rgb < 500 {
    @return false;
  }

  @return true;
}

// By Cory Simmons https://corysimmons.com/
@function bulmaStringToNumber($value) {
  @if meta.type-of($value) == "number" {
    @return $value;
  } @else if meta.type-of($value) != "string" {
    $_: log("Value for `to-number` should be a number or a string.");
  }

  $result: 0;
  $digits: 0;
  $minus: string.slice($value, 1, 1) == "-";
  $numbers: (
    "0": 0,
    "1": 1,
    "2": 2,
    "3": 3,
    "4": 4,
    "5": 5,
    "6": 6,
    "7": 7,
    "8": 8,
    "9": 9,
  );

  @for $i from if($minus, 2, 1) through string.length($value) {
    $character: string.slice($value, $i, $i);

    @if not(list.index(map.keys($numbers), $character) or $character == ".") {
      @return to-length(
        if($minus, -$result, $result),
        string.slice($value, $i)
      );
    }

    @if $character == "." {
      $digits: 1;
    } @else if $digits == 0 {
      $result: $result * 10 + map.get($numbers, $character);
    } @else {
      $digits: $digits * 10;
      $result: $result + map.get($numbers, $character) / $digits;
    }
  }

  @return if($minus, -$result, $result);
}
