content: // Copyright 2019 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  import 'dart:collection';
  
  import 'package:collection/collection.dart';
  
  import '../callable.dart';
  import '../exception.dart';
  import '../module/built_in.dart';
  import '../value.dart';
  
  /// The global definitions of Sass list functions.
  final global = UnmodifiableListView([
    _length.withDeprecationWarning('list'),
    _nth.withDeprecationWarning('list'),
    _setNth.withDeprecationWarning('list'),
    _join.withDeprecationWarning('list'),
    _append.withDeprecationWarning('list'),
    _zip.withDeprecationWarning('list'),
    _index.withDeprecationWarning('list'),
    _isBracketed.withDeprecationWarning('list'),
    _separator.withDeprecationWarning('list').withName("list-separator"),
  ]);
  
  /// The Sass list module.
  final module = BuiltInModule(
    "list",
    functions: <Callable>[
      _length, _nth, _setNth, _join, _append, _zip, _index, _isBracketed, //
      _separator, _slash,
    ],
  );
  
  final _length = _function(
    "length",
    r"$list",
    (arguments) => SassNumber(arguments[0].asList.length),
  );
  
  final _nth = _function("nth", r"$list, $n", (arguments) {
    var list = arguments[0];
    var index = arguments[1];
    return list.asList[list.sassIndexToListIndex(index, "n")];
  });
  
  final _setNth = _function("set-nth", r"$list, $n, $value", (arguments) {
    var list = arguments[0];
    var index = arguments[1];
    var value = arguments[2];
    var newList = list.asList.toList();
    newList[list.sassIndexToListIndex(index, "n")] = value;
    return list.withListContents(newList);
  });
  
  final _join = _function(
    "join",
    r"$list1, $list2, $separator: auto, $bracketed: auto",
    (arguments) {
      var list1 = arguments[0];
      var list2 = arguments[1];
      var separatorParam = arguments[2].assertString("separator");
      var bracketedParam = arguments[3];
  
      var separator = switch (separatorParam.text) {
        "auto" => switch ((list1.separator, list2.separator)) {
            (ListSeparator.undecided, ListSeparator.undecided) =>
              ListSeparator.space,
            (ListSeparator.undecided, var separator) ||
            (var separator, _) =>
              separator,
          },
        "space" => ListSeparator.space,
        "comma" => ListSeparator.comma,
        "slash" => ListSeparator.slash,
        _ => throw SassScriptException(
            '\$separator: Must be "space", "comma", "slash", or "auto".',
          ),
      };
  
      var bracketed =
          bracketedParam is SassString && bracketedParam.text == 'auto'
              ? list1.hasBrackets
              : bracketedParam.isTruthy;
  
      var newList = [...list1.asList, ...list2.asList];
      return SassList(newList, separator, brackets: bracketed);
    },
  );
  
  final _append = _function("append", r"$list, $val, $separator: auto", (
    arguments,
  ) {
    var list = arguments[0];
    var value = arguments[1];
    var separatorParam = arguments[2].assertString("separator");
  
    var separator = switch (separatorParam.text) {
      "auto" => list.separator == ListSeparator.undecided
          ? ListSeparator.space
          : list.separator,
      "space" => ListSeparator.space,
      "comma" => ListSeparator.comma,
      "slash" => ListSeparator.slash,
      _ => throw SassScriptException(
          '\$separator: Must be "space", "comma", "slash", or "auto".',
        ),
    };
  
    var newList = [...list.asList, value];
    return list.withListContents(newList, separator: separator);
  });
  
  final _zip = _function("zip", r"$lists...", (arguments) {
    var lists = arguments[0].asList.map((list) => list.asList).toList();
    if (lists.isEmpty) {
      return const SassList.empty(separator: ListSeparator.comma);
    }
  
    var i = 0;
    var results = <SassList>[];
    while (lists.every((list) => i != list.length)) {
      results.add(SassList(lists.map((list) => list[i]), ListSeparator.space));
      i++;
    }
    return SassList(results, ListSeparator.comma);
  });
  
  final _index = _function("index", r"$list, $value", (arguments) {
    var list = arguments[0].asList;
    var value = arguments[1];
  
    var index = list.indexOf(value);
    return index == -1 ? sassNull : SassNumber(index + 1);
  });
  
  final _separator = _function(
    "separator",
    r"$list",
    (arguments) => switch (arguments[0].separator) {
      ListSeparator.comma => SassString("comma", quotes: false),
      ListSeparator.slash => SassString("slash", quotes: false),
      _ => SassString("space", quotes: false),
    },
  );
  
  final _isBracketed = _function(
    "is-bracketed",
    r"$list",
    (arguments) => SassBoolean(arguments[0].hasBrackets),
  );
  
  final _slash = _function("slash", r"$elements...", (arguments) {
    var list = arguments[0].asList;
    if (list.length < 2) {
      throw SassScriptException("At least two elements are required.");
    }
  
    return SassList(list, ListSeparator.slash);
  });
  
  /// Like [BuiltInCallable.function], but always sets the URL to `sass:list`.
  BuiltInCallable _function(
    String name,
    String arguments,
    Value callback(List<Value> arguments),
  ) =>
      BuiltInCallable.function(name, arguments, callback, url: "sass:list");
  