@use '../utils/array';
@use 'conversions' as *;
@use 'sass:list';
@use 'sass:math';

// *********************
// Based on: https://drafts.csswg.org/css-color-4/utilities.js
// Credit: Chris Lilley, https://svgees.us/
// *********************

// rgb [0, 1]
@function sRGB_to_luminance($RGB) {
  // convert an array of gamma-corrected sRGB values
  // in the 0.0 to 1.0 range
  // to linear-light sRGB, then to CIE XYZ
  // and return luminance (the Y value)

  $XYZ: lin_sRGB_to_XYZ(lin_sRGB($RGB));
  @return list.nth($XYZ, 2);
}

// lc [0+] h [0, 360)
@function LCH_to_luminance($LCH) {
  $XYZ: Lab_to_XYZ(LCH_to_Lab($LCH));
  @return list.nth($XYZ, 2);
}

@function contrast($RGB1, $RGB2) {
  // return WCAG 2.1 contrast ratio
  // https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
  // for two sRGB values
  // given as arrays of 0.0 to 1.0

  $L1: sRGB_to_luminance($RGB1) + 0.05;
  $L2: sRGB_to_luminance($RGB2) + 0.05;

  @return math.div(math.max($L1, $L2), math.min($L2, $L1));
}

// rgb [0, 1] -> lc [0+] h [0, 360)
@function sRGB_to_LCH($RGB) {
  // convert an array of gamma-corrected sRGB values
  // in the 0.0 to 1.0 range
  // to linear-light sRGB, then to CIE XYZ,
  // then adapt from D65 to D50,
  // then convert XYZ to CIE Lab
  // and finally, convert to CIE LCH

  @return Lab_to_LCH(XYZ_to_Lab(D65_to_D50(lin_sRGB_to_XYZ(lin_sRGB($RGB)))));
}

// rgb [0, 1] -> lc [0+] h [0, 360)
@function P3_to_LCH($RGB) {
  // convert an array of gamma-corrected display-p3 values
  // in the 0.0 to 1.0 range
  // to linear-light display-p3, then to CIE XYZ,
  // then adapt from D65 to D50,
  // then convert XYZ to CIE Lab
  // and finally, convert to CIE LCH

  @return Lab_to_LCH(XYZ_to_Lab(D65_to_D50(lin_P3_to_XYZ(lin_P3($RGB)))));
}

// rgb [0, 1] -> lc [0+] h [0, 360)
@function r2020_to_LCH($RGB) {
  // convert an array of gamma-corrected rec.2020 values
  // in the 0.0 to 1.0 range
  // to linear-light sRGB, then to CIE XYZ,
  // then adapt from D65 to D50,
  // then convert XYZ to CIE Lab
  // and finally, convert to CIE LCH

  @return Lab_to_LCH(XYZ_to_Lab(D65_to_D50(lin_2020_to_XYZ(lin_2020($RGB)))));
}

// l [0+] ab [±160] -> rgb [0, 1]
@function Lab_to_sRGB($Lab) {
  // convert an array of CIE Lab values to XYZ,
  // adapt from D50 to D65,
  // then convert XYZ to linear-light sRGB
  // and finally to gamma corrected sRGB
  // for in-gamut colors, components are in the 0.0 to 1.0 range
  // out of gamut colors may have negative components
  // or components greater than 1.0
  // so check for that :)

  @return gam_sRGB(XYZ_to_lin_sRGB(D50_to_D65(Lab_to_XYZ($Lab))));
}

// lc [0+] h [0, 360) -> rgb [0, 1]
@function LCH_to_sRGB($LCH) {
  // convert an array of CIE LCH values
  // to CIE Lab, and then see Lab_to_sRGB…
  @return Lab_to_sRGB(LCH_to_Lab($LCH));
}

// lc [0+] h [0, 360) -> rgb [0, 1]
@function LCH_to_P3($LCH) {
  // convert an array of CIE LCH values
  // to CIE Lab, and then to XYZ,
  // adapt from D50 to D65,
  // then convert XYZ to linear-light display-p3
  // and finally to gamma corrected display-p3
  // for in-gamut colors, components are in the 0.0 to 1.0 range
  // out of gamut colors may have negative components
  // or components greater than 1.0
  // so check for that :)

  @return gam_P3(XYZ_to_lin_P3(D50_to_D65(Lab_to_XYZ(LCH_to_Lab($LCH)))));
}

// lc [0+] h [0, 360) -> rgb [0, 1]
@function LCH_to_r2020($LCH) {
  // convert an array of CIE LCH values
  // to CIE Lab, and then to XYZ,
  // adapt from D50 to D65,
  // then convert XYZ to linear-light rec.2020
  // and finally to gamma corrected rec.2020
  // for in-gamut colors, components are in the 0.0 to 1.0 range
  // out of gamut colors may have negative components
  // or components greater than 1.0
  // so check for that :)

  @return gam_2020(XYZ_to_lin_2020(D50_to_D65(Lab_to_XYZ(LCH_to_Lab($LCH)))));
}

// this is straight from the CSS Color 4 spec

// h [0, 6) sl [0, 1] -> rgb [0, 1]
@function hslToRgb($hsl) {
  // 	For simplicity, this algorithm assumes that the hue has been normalized
  //  to a number in the half-open range [0, 6), and the saturation and lightness
  //  have been normalized to the range [0, 1]. It returns an array of three numbers
  //  representing the red, green, and blue channels of the colors,
  //  normalized to the range [0, 1]
  $temp: array.template($hsl);
  $hue: list.nth($hsl, 1);
  $sat: list.nth($hsl, 2);
  $light: list.nth($hsl, 3);

  $t2: null;

  @if ( $light <= .5 ) {
    $t2: $light * ($sat + 1);
  } @else {
    $t2: $light + $sat - ($light * $sat);
  }

  $t1: $light * 2 - $t2;
  $r: hueToRgb($t1, $t2, $hue + 2);
  $g: hueToRgb($t1, $t2, $hue);
  $b: hueToRgb($t1, $t2, $hue - 2);
  @return list.join($temp, $r $g $b);
}

@function hueToRgb($t1, $t2, $hue) {
  $hue: if(($hue < 0), $hue + 6, $hue);
  $hue: if(($hue >= 6), $hue - 6, $hue);

  @if ($hue < 1) {
    @return ($t2 - $t1) * $hue + $t1;
  } @else if ($hue < 3) {
    @return $t2;
  } @else if ($hue < 4) {
    @return ($t2 - $t1) * (4 - $hue) + $t1;
  }

  @return $t1;
}

// These are the naive algorithms from CS Color 4

// cmyk [0, 1] -> rgb [0, 1]
@function naive_CMYK_to_sRGB($CMYK) {
  // CMYK is an array of four values
  // in the range [0.0, 1.0]
  // the output is an array of [RGB]
  // also in the [0.0, 1.0] range
  // because the naive algorithm does not generate out of gamut colors
  // neither does it generate accurate simulations of practical CMYK colors

  $cyan: list.nth($CMYK, 1);
  $magenta: list.nth($CMYK, 2);
  $yellow: list.nth($CMYK, 3);
  $black: list.nth($CMYK, 4);

  $red: 1 - math.min(1, $cyan * (1 - $black) + $black);
  $green: 1 - math.min(1, $magenta * (1 - $black) + $black);
  $blue: 1 - math.min(1, $yellow * (1 - $black) + $black);

  @return [$red, $green, $blue];
}

// rgb [0, 1] -> cmyk [0, 1]
@function naive_sRGB_to_CMYK($RGB) {
  $temp: array.template($RGB);
  // RGB is an array of three values
  // in the range [0.0, 1.0]
  // the output is an array of [CMYK]
  // also in the [0.0, 1.0] range
  // with maximum GCR and (I think) 200% TAC
  // the naive algorithm does not generate out of gamut colors
  // neither does it generate accurate simulations of practical CMYK colors

  $red: list.nth($RGB, 1);
  $green: list.nth($RGB, 2);
  $blue: list.nth($RGB, 3);

  $black: 1 - math.max($red, $green, $blue);
  $cyan: if(($black == 1.0), 0, math.div(1 - $red - $black, 1 - $black));
  $magenta: if(($black == 1.0), 0, math.div(1 - $green - $black, 1 - $black));
  $yellow: if(($black == 1.0), 0, math.div(1 - $blue - $black, 1 - $black));

  @return list.join($temp, $cyan $magenta $yellow $black);
}
