@use "sass:map";
@use "sass:string";
@use "sass:math";
@use "sass:list";
@use "mediaQueries.scss" as *;
@use "utilities.scss" as *;
@use "../variables/typographyScreenSmall.scss" as *;
@use "../variables/typographyScreenLarge.scss" as *;
@use "../variables/typographyScreenExtraLarge.scss" as *;
@use "fontFamily.scss" as *;
@forward "../variables/typographyList.scss";
@forward "../variables/typographyFontWeight.scss";
@forward "../variables/typographyLetterSpacing.scss";
@forward "../variables/typographyLineHeight.scss";

$dsTypographyKeys: (
  fontFamily: "font-family",
  fontSize: "font-size",
  fontWeight: "font-weight",
  lineHeight: "line-height",
  letterSpacing: "letter-spacing",
  textDecorationLine: "text-decoration-line",
  color: "color",
  fontStyle: "font-style",
  textTransform: "text-transform",
);

$dsSansWeights: (
  light: 300,
  regular: 400,
  medium: 500,
  semibold: 600,
  bold: 700,
  extrabold: 800,
  black: 900,
);

$dsSerifWeights: (
  light: 300,
  regular: 400,
  medium: 500,
  semibold: 600,
  bold: 700,
  extrabold: 800,
  black: 900,
);

// Serif variation changes, text 18px, headline 26px, display 34px. As fallback we will always use headline

@mixin ds-typography(
  $variable,
  $forcePx: false,
  $fontWeight: null,
  $letterSpacing: null,
  $lineHeight: null,
  $fontStyle: null
) {
  // making sure override values have matching keys in typography object
  $addons: (
    "letterSpacing": $letterSpacing,
    "fontWeight": null,
    "lineHeight": null,
    "fontStyle": null,
  );
  $tmpMap: map.merge($addons, map.get($typographyScreenSmall, $variable));
  $tmpMapScreenLarge: map.merge($addons, map.get($typographyScreenLarge, $variable));
  $tmpMapScreenExtraLarge: map.merge($addons, map.get($typographyScreenExtraLarge, $variable));

  @if $tmpMap {
    @include ds-mq-largest-breakpoint(mobile) {
      @include _ds-normalize-letter-spacing($tmpMap);
      @each $key in map.keys($tmpMap) {
        $value: _ds-get-override-value($key, $fontWeight, $letterSpacing, $lineHeight, $fontStyle);
        @include _ds-typography-get-property($tmpMap, $key, $forcePx, "small", $value);
      }
    }
  }

  @if $tmpMapScreenLarge {
    @include ds-mq-only-breakpoint(tablet) {
      @include _ds-normalize-letter-spacing($tmpMapScreenLarge);
      @each $key in map.keys($tmpMapScreenLarge) {
        $value: _ds-get-override-value($key, $fontWeight, $letterSpacing, $lineHeight, $fontStyle);
        @include _ds-typography-get-property($tmpMapScreenLarge, $key, $forcePx, "large", $value);
      }
    }
  }

  @if $tmpMapScreenExtraLarge {
    @include ds-mq-smallest-breakpoint(desktop) {
      @include _ds-normalize-letter-spacing($tmpMapScreenExtraLarge);
      @each $key in map.keys($tmpMapScreenExtraLarge) {
        $value: _ds-get-override-value($key, $fontWeight, $letterSpacing, $lineHeight, $fontStyle);
        @include _ds-typography-get-property($tmpMapScreenExtraLarge, $key, $forcePx, "extraLarge", $value);
      }
    }
  }

  & {
    @content;
  }
}

@mixin ds-typography-without-mq(
  $variable,
  $screen: mobile,
  $forcePx: false,
  $fontWeight: null,
  $letterSpacing: null,
  $lineHeight: null,
  $fontStyle: null
) {
  $tmpMap: false;
  $scaling: "small";
  // making sure override values have matching keys in typography object
  $addons: (
    "letterSpacing": $letterSpacing,
    "fontWeight": null,
    "lineHeight": null,
    "fontStyle": null,
  );

  @if $screen == mobile {
    $tmpMap: map.merge($addons, map.get($typographyScreenSmall, $variable));
  } @else if $screen == tablet {
    $tmpMap: map.merge($addons, map.get($typographyScreenLarge, $variable));
    $scaling: "large";
  } @else if $screen == desktop {
    $tmpMap: map.merge($addons, map.get($typographyScreenExtraLarge, $variable));
    $scaling: "extraLarge";
  }

  @if $tmpMap {
    @include _ds-normalize-letter-spacing($tmpMap);
    @each $key in map.keys($tmpMap) {
      $value: _ds-get-override-value($key, $fontWeight, $letterSpacing, $lineHeight, $fontStyle);
      @include _ds-typography-get-property($tmpMap, $key, $forcePx, $scaling, $value);
    }
  }
}

@mixin ds-typography-manual(
  $family,
  $size,
  $lineHeight: 1.2,
  $weight: 400,
  $fontStyle: "",
  $letterSpacing: "",
  $scaling: "small"
) {
  $dsTypographyKeys: (
    fontFamily: $family,
    fontSize: $size,
    lineHeight: $lineHeight,
    // numbers and strings are both handled as strings in fontWeight
    fontWeight: "#{$weight}",
    fontStyle: $fontStyle,
    letterSpacing: $letterSpacing,
  );
  $forcePx: $scaling == false;
  @each $key in map.keys($dsTypographyKeys) {
    @include _ds-typography-get-property($dsTypographyKeys, $key, $forcePx, $scaling);
  }
}

@mixin _ds-typography-get-property($map, $key, $forcePx, $screen: "small", $value: null) {
  @if not $value {
    $value: map.get($map, $key);
  }
  $unit: "";
  $fontFamilyType: _ds-get-font-family-type($map);
  $fontWeights: if($fontFamilyType == "serif", $dsSerifWeights, $dsSansWeights);

  @if $value and $value != "" {
    @if $key == "fontFamily" {
      @if $fontFamilyType == "serif" {
        font-family: $ds-font--serif;
      } @else if $fontFamilyType == "sans" {
        font-family: $ds-font--sans;
      }
    } @else if $key == "fontWeight" {
      $value: string.to-lower-case($value);
      @if map.has-key($fontWeights, $value) {
        $value: map.get($fontWeights, $value);
      }
      font-weight: #{$value};
    } @else if $key == "fontStyle" {
      font-style: string.to-lower-case($value);
    } @else if $key == "fontSize" {
      @if $forcePx {
        font-size: $value * 1px;
      } @else {
        font-size: ds-px-to-rem($value);
        font-size: clamp($value * 1px, ds-px-to-rem($value), _ds-get-max-scaled-size($value, $screen));
      }
      font-variation-settings: "opsz" $value;
    } @else if $key == "lineHeight" {
      line-height: $value;
    } @else if $key == "letterSpacing" {
      @if $value == 0 {
        letter-spacing: normal;
      } @else {
        @if $forcePx {
          letter-spacing: $value * 1px;
        } @else {
          letter-spacing: ds-px-to-rem($value);
        }
      }
    } @else if list.index(map.keys($dsTypographyKeys), $key) {
      #{map.get($dsTypographyKeys, $key)}: $value;
    }
  }
}

@function _ds-get-max-scaled-size($size, $screen: "small") {
  $midPoint: 26;
  $scalingFactor: 0.33;
  @if ($screen == "large") {
    $midPoint: 30;
    $scalingFactor: 0.2;
  } @else if ($screen == "extraLarge") {
    $midPoint: 32;
    $scalingFactor: 0.166;
  }

  $calculatedScalingFactor: 2 - math.div(1, (1 + math.pow(math.$e, $scalingFactor * ($midPoint - $size))));
  @return math.round($calculatedScalingFactor * $size * 1px);
}

@function _ds-get-font-family-type($map) {
  $fontFamily: map.get($map, "fontFamily");

  @if string.index($fontFamily, "Serif") {
    @return "serif";
  }

  @return "sans";
}

@mixin _ds-normalize-letter-spacing($map) {
  $letterSpacing: map.get($map, "letterSpacing");

  @if not $letterSpacing or $letterSpacing == "" {
    letter-spacing: normal;
  }
}

@mixin ds-typography-with-force-px($variable) {
  @include ds-typography($variable);
  // If called from element without a wrapper class, ex. a it will generate incorrect selector .ds-force-pxa
  @at-root .ds-force-px#{&} {
    @include ds-typography($variable, true);
  }
  & {
    @content;
  }
}

@function _ds-get-override-value($key, $fontWeight, $letterSpacing, $lineHeight, $fontStyle) {
  @if $key == "fontWeight" {
    @return $fontWeight;
  } @else if $key == "letterSpacing" {
    @return $letterSpacing;
  } @else if $key == "lineHeight" {
    @return $lineHeight;
  } @else if $key == "fontStyle" {
    @return $fontStyle;
  }
  @return null;
}
