// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:_internal' show patch;
import 'dart:_js_helper' show JS;
import 'dart:_string';
import 'dart:_wasm';
import 'dart:convert' show Encoding, utf8;

@patch
class Uri {
  @patch
  static Uri get base {
    final currentUri = JSStringImpl.fromRefUnchecked(
      JS<WasmExternRef?>("""() => {
      // On browsers return `globalThis.location.href`
      if (globalThis.location != null) {
        return globalThis.location.href;
      }
      return null;
    }"""),
    );
    if (currentUri != null) {
      return Uri.parse(currentUri);
    }
    throw UnsupportedError("'Uri.base' is not supported");
  }
}

@patch
class _Uri {
  @patch
  static bool get _isWindows => _isWindowsCached;

  static final bool _isWindowsCached = JS<bool>("""() => {
        return typeof process != "undefined" &&
               Object.prototype.toString.call(process) == "[object process]" &&
               process.platform == "win32"
      }""");

  // Matches a String that _uriEncodes to itself regardless of the kind of
  // component.  This corresponds to `_unreservedMask` table,
  // i.e. characters that are not encoded by any encoding table.
  static final RegExp _needsNoEncoding = RegExp(r'^[\-\.0-9A-Z_a-z~]*$');

  /**
   * This is the internal implementation of JavaScript's encodeURI function.
   * It encodes all characters in the string [text] except for those
   * that appear in [canonicalTable], and returns the escaped string.
   */
  @patch
  static String _uriEncode(
    int canonicalMask,
    String text,
    Encoding encoding,
    bool spaceToPlus,
  ) {
    if (identical(encoding, utf8) && _needsNoEncoding.hasMatch(text)) {
      return text;
    }

    // Encode the string into bytes then generate an ASCII only string
    // by percent encoding selected bytes.
    StringBuffer result = StringBuffer('');
    var bytes = encoding.encode(text);
    for (int i = 0; i < bytes.length; i++) {
      int byte = bytes[i];
      if (byte < 128 && ((_charTables.codeUnitAt(byte) & canonicalMask) != 0)) {
        result.writeCharCode(byte);
      } else if (spaceToPlus && byte == _SPACE) {
        result.write('+');
      } else {
        const String hexDigits = '0123456789ABCDEF';
        result.write('%');
        result.write(hexDigits[(byte >> 4) & 0x0f]);
        result.write(hexDigits[byte & 0x0f]);
      }
    }
    return result.toString();
  }

  @patch
  static String _makeQueryFromParameters(
    Map<String, dynamic /*String?|Iterable<String>*/> queryParameters,
  ) {
    return _makeQueryFromParametersDefault(queryParameters);
  }
}
