@use 'sass:list';
@use 'sass:math';
@use 'sass:string';
@use 'is-falsey' as *;
@use 'is-list' as *;
@use 'is-map' as *;
@use 'is-string' as *;
@use 'to-list' as *;
@use '../variables/misc' as smth;

/// Sorts values of `$list` by $order using quick-sort algorithm. By default,
/// the order is alphabetical and is an imported variable called $sort-order.
///
/// @param {List} $list - The List to sort.
/// @param {List} $order [smth.$sort-order] - The order to respect for the newly
/// sorted List.
///
/// @return {List} - The newly sorted List.
///
/// @example
/// list-sort('b' 'a' 'c')
/// // 'a' 'b' 'c'
///
/// @example
/// list-sort(3 5 1)
/// // 1 3 5
///
/// @access public
/// @group Utilities
/// @require {function} is-falsey
/// @require {function} is-map
/// @require {function} to-list
/// @require {function} _str-compare
/// @require {variable} $sort-order
/// @since 0.16.0
@function list-sort($list, $order: smth.$sort-order) {
  $less: ();
  $equal: ();
  $large: ();

  @if is-falsey($order) {
    $order: smth.$sort-order;
  }

  @if is-map($list) {
    @error 'Invalid $list data type for the [ list-sort() ] function. ' +
        'You may not pass a value of data type \'map\'';
  }

  @if not is-list($order) {
     @error 'Invalid $order data type for the [ list-sort() ] function. ' +
        'If you choose to pass your own sort order, this value must ' +
        'be of type \'list\'.';
  }

  @if list.length($list) > 1 {
    $seed: list.nth($list, math.ceil(list.length($list) * 0.5));

    @each $item in $list {
      @if string.to-lower-case('#{$item}') == string.to-lower-case('#{$seed}') {
        $equal: list.append($equal, $item, list.separator($list));
      } @else if _str-compare(($item), ($seed), $order) {
        $less: list.append(($less), ($item), list.separator($list));
      } @else if not _str-compare(($item), ($seed), $order) {
        $large: list.append($large, $item, list.separator($list));
      }
    }

    @return list.join(list.join(list-sort($less, $order), $equal), list-sort($large, $order));
  }

  @return to-list($list);
}

/// Sorts values of `$list` by $order using quick-sort algorithm. By default,
/// the order is alphabetical and is an imported variable called $sort-order.
///
/// @param {List} $list - The List to sort.
/// @param {List} $order [smth.$sort-order] - The order to respect for the newly
/// sorted List.
///
/// @return {List} - The newly sorted List.
///
/// @example
/// list-sort('b' 'a' 'c')
/// // 'a' 'b' 'c'
///
/// @example
/// list-sort(3 5 1)
/// // 1 3 5
///
/// @access public
/// @group Utilities
/// @require {function} list-sort
/// @require {variable} $sort-order
/// @since 0.16.0
///
/// @alias list-sort
@function list-order($list, $order: smth.$sort-order) {
  @return list-sort($list, $order);
}

/// Compares `$a` and `$b` based on `$order`.
///
/// @param {*} $a - The first value.
/// @param {*} $b - The second value
/// @param {List} $order - alphabetical order
///
/// @return {Bool} - The helper Boolean that tells the `list-sort()` function
/// which value should come first.
///
/// @access private
/// @group Utilities
/// @since 0.16.0
@function _str-compare($a, $b, $order) {
  @if type-of($a) == 'number' and type-of($b) == 'number' {
    @return $a < $b;
  }

  $a: string.to-lower-case($a + string.unquote(''));
  $b: string.to-lower-case($b + string.unquote(''));

  @for $i from 1 through math.min(string.length($a), string.length($b)) {
    $char-a: string.slice($a, $i, $i);
    $char-b: string.slice($b, $i, $i);

    @if $char-a and $char-b and list.index($order, $char-a) != list.index($order, $char-b) {
      @return list.index($order, $char-a) < list.index($order, $char-b);
    }
  }

  @return string.length($a) < string.length($b);
}
