@function wf-str-replace($string, $find, $replace, $all: true) {
  $index: str-index($string, $find);
  @if $index {
    $before: str-slice($string, 1, $index - 1);
    $after: str-slice($string, $index + str-length($find), str-length($string));
    $string: $before + $replace + $after;

    @if $all and not str-index($find, $replace) {
      $string: wf-str-replace($string, $find, $replace);
    }
  }
  @return $string;
}

@function wf-url-encode($string) {
  $replacements: (
    "!": "%21",
    "#": "%23",
    "$": "%24",
    "&": "%26",
    "'": "%27",
    "(": "%28",
    ")": "%29",
    "*": "%2A",
    "+": "%2B",
    ",": "%2C",
    "/": "%2F",
    ":": "%3A",
    ";": "%3B",
    "=": "%3D",
    "?": "%3F",
    "@": "%40",
    "[": "%5B",
    "]": "%5D",
    " ": "%20"
  );

  @each $from, $to in $replacements {
    $string: wf-str-replace($string, $from, $to);
  }

  @return $string;
}

@function wf-implode($list, $separator: ',') {
  $string: '';
  @for $i from 1 through length($list) {
    $el: nth($list, $i);
    $string: $string + $el;
    @if ($i < length($list)) {
      $string: $string + $separator;
    }
  }
  @return $string;
}

@function wf-serialize($fonts) {
  @if type-of($fonts) == 'list' or type-of($fonts) == 'arglist' {
    $serialized: ();
    @each $font in $fonts {
      $serialized: append($serialized, wf-serialize($font));
    }
    @return wf-implode($serialized, '|');
  }

  @if type-of($fonts) == 'map' {
    $serialized: ();
    @each $family, $variants in $fonts {
      $variants: wf-implode($variants, ',');
      $variants: wf-str-replace($variants, ' ', '');
      $serialized: append($serialized, "#{$family}:#{$variants}");
    }
    @return wf-serialize($serialized);
  }

  @if type-of($fonts) == 'string' {
    @return wf-url-encode($fonts);
  }

  @warn "Unsupported font type: #{type-of($fonts)}";
}

@function wf-protocol() {
  $web-fonts-protocol: '' !global !default;
  $protocol: $web-fonts-protocol;
  @if str-length($protocol) > 0 {
    $protocol: $protocol + ':';
  }
  @return $protocol;
}

@function wf-query-string-encode($params) {
  $query-string: "";
  @each $key, $value in $params {
    $query-string: $query-string + wf-url-encode($key) + "=";
    $query-string: $query-string + wf-url-encode($value) + "&";
  }
  // remove trailing ampersand
  $query-string: str-slice($query-string, 1, -2);
  @return $query-string;
}

@function wf-params-string($fonts) {
  $web-fonts-params: () !global !default;
  $params: map-merge((family: wf-serialize($fonts)), $web-fonts-params);
  @return wf-query-string-encode($params);
}

@function web-fonts-url($fonts...) {
  $protocol: wf-protocol();
  $query-string: wf-params-string($fonts);
  $url: "#{$protocol}//fonts.googleapis.com/css?#{$query-string}";
  @return $url;
}
