@use 'sass:map';
@use 'sass:math';
@use 'sass:meta';
@use 'sass:string';
@use '../../functions/is-alias' as *;
@use '../../functions/is-falsey' as *;
@use '../../functions/is-map' as *;
@use '../../functions/is-number' as *;
@use '../../functions/is-string' as *;
@use '../../variables/misc' as *;

/// Apply a background image with a version that is scaled to 2x for Retina and
/// other HiDPI displays. This mixin will apply the background-image and
/// background-size properties. You can set other background properties
/// manually. For the 2x version of the image in the media query this mixin will
/// generate, you can pass additional background properties to the mixin inside
/// a map via the $hidpi-bg-props parameter.
///
/// @param {String} $img-name - File path for the image. Must either be a
/// quoted string or inside a function to be read correctly.
/// @param {String | Number | List} $bg-size - Value for the background-size
/// property. Should most often be the width followed by the height of the 1x
/// image.
/// @param {Number} $min-ratio [1.25] - A minimum pixel ratio resolution value
/// used to calculate the media query targeting Retina and other HiDPI screens.
/// Common targeting ratios are 1.25, 1.3, 1.5, and 2.
/// @param {String} $img-2x-suffix [$image-suffix-2x|'@2x']- The suffix that is
/// appended to the end of image name to designate that it is the 2x version for
/// HiDPI displays.
/// @param {Map} $hidpi-bg-props [null] - You can pass this argument
/// a map of property: value pairs that will be added to the HiDPI/Retina
/// media query.
///
/// @group Utilities
/// @require {function} is-alias
/// @require {function} is-falsey
/// @require {function} is-map
/// @require {function} is-number
/// @require {function} is-string
/// @require {variable} $image-suffix-2x
///
/// @throw Invalid $img-name file name, no file extension used error.
/// @throw SVG file used for $img-name error.
/// @throw Invalid $min-ratio data type error.
/// @throw Invalid $img-2x-suffix data type error.
/// @throw Invalid $hidpi-bg-props data type error.
/// @throw Unknown file type for $img-name error.
@mixin bg-image(
  $img-name,
  $bg-size,
  $min-ratio: 1.5,
  $img-2x-suffix:
    if(
      meta.variable-exists('image-suffix-2x') or
      meta.global-variable-exists('image-suffix-2x'),
      $image-suffix-2x,
      '@2x'
    ),
  $hidpi-bg-props: null
) {
  $hidpi-img: $img-name;

  @if is-string($img-2x-suffix) or is-number($img-2x) {
    $img-2x-suffix: string.quote('#{$img-2x-suffix}');
  } @else {
    @error 'Invalid data type for $img-2x-suffix of ' +
        '`#{meta.type-of($img-2x-suffix)}` passed to the [ bg-image() ] mixin. ' +
        'This value must be a string or a number.';
  }

  @if not is-number($min-ratio) or not math.is-unitless($min-ratio) {
    @error 'Invalid data type for $min-ratio passed to [ bg-image() ] mixin. ' +
        'This value must be a number.';
  }

  @if is-falsey($hidpi-bg-props) or
      is-alias('none', $hidpi-bg-props) or
      $hidpi-bg-props == ' '
  {
    $hidpi-bg-props: null;
  } @else if not is-map($hidpi-bg-props) {
    @error 'Invalid data type for $hidpi-bg-props passed to the [ bg-image() ]' +
        ' mixin. If this value is not null, you must pass a map containig the ' +
        'property: value pairs to be added to the HiDPI/Retina media query.';
  }

  @if string.index($img-name, '.avif') or
    string.index($img-name, '.apng') or
    string.index($img-name, '.djvu') or
    string.index($img-name, '.flif') or
    string.index($img-name, '.heic') or
    string.index($img-name, '.heif') or
    string.index($img-name, '.jpeg') or
    string.index($img-name, '.nitf') or
    string.index($img-name, '.orff') or
    string.index($img-name, '.pict') or
    string.index($img-name, '.tiff') or
    string.index($img-name, '.wbmp') or
    string.index($img-name, '.webp')
  {
    $hidpi-img: string.insert(
      $img-name,
      $img-2x-suffix,
      string.length($img-name) - 4
    );
  } @else if
    string.index($img-name, '.art') or
    string.index($img-name, '.bmp') or
    string.index($img-name, '.bpg') or
    string.index($img-name, '.cdr') or
    string.index($img-name, '.dc3') or
    string.index($img-name, '.dds') or
    string.index($img-name, '.dib') or
    string.index($img-name, '.dic') or
    string.index($img-name, '.dmc') or
    string.index($img-name, '.dwg') or
    string.index($img-name, '.gif') or
    string.index($img-name, '.hdr') or
    string.index($img-name, '.icb') or
    string.index($img-name, '.ico') or
    string.index($img-name, '.iff') or
    string.index($img-name, '.jpe') or
    string.index($img-name, '.jpf') or
    string.index($img-name, '.jpg') or
    string.index($img-name, '.jps') or
    string.index($img-name, '.jpx') or
    string.index($img-name, '.jp2') or
    string.index($img-name, '.jxl') or
    string.index($img-name, '.j2c') or
    string.index($img-name, '.j2k') or
    string.index($img-name, '.mng') or
    string.index($img-name, '.mpo') or
    string.index($img-name, '.ota') or
    string.index($img-name, '.pbm') or
    string.index($img-name, '.pct') or
    string.index($img-name, '.pcx') or
    string.index($img-name, '.pgf') or
    string.index($img-name, '.pgm') or
    string.index($img-name, '.png') or
    string.index($img-name, '.pnm') or
    string.index($img-name, '.ppm') or
    string.index($img-name, '.ptx') or
    string.index($img-name, '.pxr') or
    string.index($img-name, '.sgi') or
    string.index($img-name, '.swf') or
    string.index($img-name, '.tdi') or
    string.index($img-name, '.tga') or
    string.index($img-name, '.tif') or
    string.index($img-name, '.vda') or
    string.index($img-name, '.vst') or
    string.index($img-name, '.web') or
    string.index($img-name, '.wmf') or
    string.index($img-name, '.xbm') or
    string.index($img-name, '.xpm') or
    string.index($img-name, '.xwd')
  {
    $hidpi-img: string.insert(
      $img-name,
      $img-2x-suffix,
      string.length($img-name) - 3
    );
  } @else if string.index($img-name, '.svg') {
    @error 'You are using an SVG image file with the [ bg-image() ] mixin. ' +
        'This is not necessary as SVG images naturally scale and this mixin ' +
        'is designed to provide a 2x version of the image to devices with ' +
        'a high device pixel ratio. If you want to have an SVG background ' +
        'image with a fallback in a different file type, try the ' +
        '[ bg-image-svg() ] mixin instead.';
  } @else if string.index($img-name, '.') {
    $hidpi-img: string.insert(
      $img-name,
      $img-2x-suffix,
      string.index($img-name, '.')
    );

    @warn 'You are using an unknown file type on the image you referenced in ' +
        'the [ bg-image() ] mixin. Please ensure you are using a valid image ' +
        'file type. If you use an unknown file type, or a file with multiple ' +
        '`.` characters the [ bg-image() ] mixin may not work correctly.';
  } @else {
    @error 'The image you referenced in the [ bg-image() ] mixin does not ' +
        'have a file extension. Please use the full image name including the ' +
        'image\'s file extension.';
  }

  // Normal sized image
  background-image: url(#{$img-name});
  background-size: #{$bg-size};

  // Scaled up version of the image for HiDPI displays
  @media screen and (-webkit-min-device-pixel-ratio: $min-ratio),
    screen and (min-resolution: $min-ratio * 96dpi),
    screen and (min-resolution: $min-ratio * 1dppx) {
    background-image: url(#{$hidpi-img});
    background-size: #{$bg-size};

    @if $hidpi-bg-props {
      $key-list: map.keys($hidpi-bg-props);

      @each $key in $key-list {
        #{$key}: map.get($hidpi-bg-props, $key);
      }
    }
  }
}
