@use 'sass:meta';
@use 'sass:list';
@use 'sass:math';
@use 'sass:string';
@use '../../functions/equals-false' as *;
@use '../../functions/is-alias' as *;
@use '../../functions/is-falsey' as *;
@use '../../functions/is-list' as *;
@use '../../functions/is-number' as *;
@use '../../functions/is-string' as *;
@use '../../functions/list-to-string' as *;

/// Adds a shadow dropping animation to a given element's text
///
/// @param {String} $direction ['center'] - The direction the shadow will fall.
/// @param {Number} $duration [0.7s] The duration of the animation (`s` or `ms`).
/// @param {Number} $delay [null] The duration of the optional animation delay.
/// @param {Color} $shadow-color [#000] The color for the shadow. Must be in
/// 3 or 6 digit hexadecimal notation.
/// @param {Number | String} $anim-num [null] - If the mixin is used more than
/// once with different values in a stylesheet, you can pass a number or string
/// to the mixin here that gets appeneded to end of the animation name so that
/// the animations do not overwrite each other.
///
/// @group Animations
/// @require {function} equals-false
/// @require {function} is-alias
/// @require {function} is-falsey
/// @require {function} is-list
/// @require {function} is-number
/// @require {function} is-string
/// @require {function} list-to-string
///
/// @throw Invalid $direction value error.
/// @throw Invalid $duration value error.
/// @throw Invalid $delay value error.
@mixin text-drop-shadow(
  $direction: 'center',
  $duration: 0.7s,
  $delay: null,
  $shadow-color: #000,
  $anim-num: null
) {
  // Values for the default of center
  $dir-vals: 0 0 18px;
  $anim-suffix: '-center';

  @if $direction {
    @if is-list($direction) {
      $direction: list-to-string($direction);
    }

    @if is-string($direction) {
      $direction: string.to-lower-case($direction);
    }
  }

  @if $delay and is-string($delay) {
    $delay: string.to-lower-case($delay);
  }

  @if not $shadow-color or not is-color($shadow-color) {
    $shadow-color: #000;

    @warn 'Invalid $shadow-color value passed to the [ text-drop-shadow() ] ' +
        'mixin. You must pass the color in 3 or 6 digit hexadecimal notation.' +
        ' The $shadow-color has been reset to the default of black (#000).';
  }

  @if is-alias('bottom', $direction) {
    $dir-vals: 0 6px 18px;
    $anim-suffix: '-bottom';
  } @else if is-alias('bottom right', $direction) {
    $dir-vals: 6px 6px 18px;
    $anim-suffix: '-bottom-right';
  } @else if is-alias('right', $direction) {
    $dir-vals: 6px 0 18px;
    $anim-suffix: '-right';
  } @else if is-alias('top left', $direction) {
    $dir-vals: -6px -6px 18px;
    $anim-suffix: '-top-left';
  } @else if is-alias('top right', $direction) {
    $dir-vals: 6px -6px 18px;
    $anim-suffix: '-top-right';
  } @else if is-alias('left', $direction) {
    $dir-vals: -6px 0 18px;
    $anim-suffix: '-left';
  } @else if is-alias('top', $direction) {
    $dir-vals: 0 -6px 18px;
    $anim-suffix: '-top';
  } @else if is-alias('bottom left', $direction) {
    $dir-vals: -6px 6px 18px;
    $anim-suffix: '-bottom-left';
  } @else if not is-alias('center', $direction) {
    @error 'Invalid $direction value of `#{meta.inspect($direction)}` ' +
        'for the [ text-drop-shadow() ] mixin. The value of $direction ' +
        'must be one of the following: `center`, `top`, `top-right`, ' +
        '`right`, `bottom-right`, `bottom`, `bottom-left`, `left`, or ' +
        '`top-left`.';
  }

  @if not is-number($duration) {
    @error 'Invalid $duration value of `#{meta.inspect($duration)}` for the ' +
        '[ text-drop-shadow() ] mixin. The duration property must be a number.';
  } @else if math.is-unitless($duration) {
    // If no there is no unit for $duration, unless $duration > 60, assume seconds
    $duration: if($duration > 60, $duration * 1ms, $duration * 1s);
  } @else if not list.index('s' 'ms', math.unit($duration)) {
    @error 'Invalid $duration value of `#{meta.inspect($duration)}` for the ' +
        '[ text-drop-shadow() ] mixin. The duration property must be in either ' +
        's (seconds) or ms (milliseconds) units.';
  } @else if $duration % 10 == 0ms {
    // If time in ms would be shorter as seconds, convert it
    $duration: math.div($duration, 1000ms) * 1s;

    // Get rid of possible leading zero in front of decimal place
    @if string.slice($duration + '', 1, 2) == '0.' {
      $duration: #{string.slice($duration + '', 2)};
    }
  }

  @if is-falsey($delay) or
      is-alias('none', $delay) or
      $delay == 0s or $delay == 0ms
  {
    $delay: null;
  }

  @if $delay {
    @if not is-number($delay) {
      @error 'Invalid delay value of `#{meta.inspect($delay)}` for the ' +
          '[ text-drop-shadow() ] mixin. The delay property must be a number.';
    } @else if math.is-unitless($delay) {
      // If no there is no unit for $delay, unless $delay > 60, assume seconds
      $delay: if($delay > 60, $delay * 1ms, $delay * 1s);
    } @else if not list.index('s' 'ms', math.unit($delay)) {
      @error 'Invalid delay value of `#{meta.inspect($delay)}` for the ' +
          '[ text-drop-shadow() ] mixin. The delay property must be in ' +
          'either s or ms units.';
    } @else if $delay % 10 == 0ms {
      // If time in ms would be shorter as seconds, convert it
      $delay: math.div($delay, 1000ms) * 1s;

      @if string.slice($delay + '', 1, 2) == '0.' {
        $delay: #{string.slice($delay + '', 2)};
      }
    }
  }

  @if equals-false($anim-num) or $anim-num == '' {
    $anim-num: null;
  } @else {
    $anim-suffix: string.insert(
      $anim-suffix,
      '--#{$anim-num}',
      string.length($anim-suffix) + 1
    );
  }

  animation: txt-shadow-drop#{$anim-suffix} #{$duration} #{$delay} both;

  @at-root {
    @keyframes txt-shadow-drop#{$anim-suffix} {
      0% {
        text-shadow: 0 0 0 rgba($shadow-color, 1);
      }

      100% {
        text-shadow: #{$dir-vals} rgba($shadow-color, 0.3);
      }
    }
  }
}
