part of dart._engine;
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.



const String ahemFontFamily = 'Ahem';
const String ahemFontUrl = '/assets/fonts/ahem.ttf';
const String robotoFontFamily = 'Roboto';
const String robotoTestFontUrl = '/assets/fonts/Roboto-Regular.ttf';

/// The list of test fonts, in the form of font family name - font file url pairs.
/// This list does not include embedded test fonts, which need to be loaded and
/// registered separately in [FontCollection.debugDownloadTestFonts].
const Map<String, String> testFontUrls = <String, String>{
  ahemFontFamily: ahemFontUrl,
  robotoFontFamily: robotoTestFontUrl,
  'RobotoVariable': '/assets/fonts/RobotoSlab-VariableFont_wght.ttf',
};

/// This class downloads assets over the network.
///
/// Assets are resolved relative to [assetsDir] inside the absolute base
/// specified by [assetBase] (optional).
///
/// By default, URLs are relative to the `<base>` of the current website.
class AssetManager {
  /// Initializes [AssetManager] with paths.
  AssetManager({
    this.assetsDir = _defaultAssetsDir,
    String? assetBase,
  })  : assert(
          assetBase == null || assetBase.endsWith('/'),
          '`assetBase` must end with a `/` character.',
        ),
        _assetBase = assetBase;

  static const String _defaultAssetsDir = 'assets';

  /// The directory containing the assets.
  final String assetsDir;

  /// The absolute base URL for assets.
  String? _assetBase;

  // Cache a value for `_assetBase` so we don't hit the DOM multiple times.
  String get _baseUrl => _assetBase ??= _deprecatedAssetBase ?? '';

  // Retrieves the `assetBase` value from the DOM.
  //
  // This warns the user and points them to the new initializeEngine style.
  String? get _deprecatedAssetBase {
    final DomHTMLMetaElement? meta = domWindow.document
        .querySelector('meta[name=assetBase]') as DomHTMLMetaElement?;

    final String? fallbackBaseUrl = meta?.content;

    if (fallbackBaseUrl != null) {
      // Warn users that they're using a deprecated configuration style...
      domWindow.console.warn('The `assetBase` meta tag is now deprecated.\n'
          'Use engineInitializer.initializeEngine(config) instead.\n'
          'See: https://docs.flutter.dev/development/platform-integration/web/initialization');
    }
    return fallbackBaseUrl;
  }

  /// Returns the URL to load the asset from, given the asset key.
  ///
  /// We URL-encode the asset URL in order to correctly issue the right
  /// HTTP request to the server.
  ///
  /// For example, if you have an asset in the file "assets/hello world.png",
  /// two things will happen. When the app is built, the asset will be copied
  /// to an asset directory with the file name URL-encoded. So our asset will
  /// be copied to something like "assets/hello%20world.png". To account for
  /// the assets being copied over with a URL-encoded name, the Flutter
  /// framework URL-encodes the asset key  so when it sends a request to the
  /// engine to load "assets/hello world.png", it actually sends a request to
  /// load "assets/hello%20world.png". However, on the web, if we try to load
  /// "assets/hello%20world.png", the request will be URL-decoded, we will
  /// request "assets/hello world.png", and the request will 404. Therefore, we
  /// must URL-encode the asset key *again* so when it is decoded, it is
  /// requesting the once-URL-encoded asset key.
  String getAssetUrl(String asset) {
    if (Uri.parse(asset).hasScheme) {
      return Uri.encodeFull(asset);
    }
    return Uri.encodeFull('$_baseUrl$assetsDir/$asset');
  }

  /// Loads an asset and returns the server response.
  Future<HttpFetchResponse> loadAsset(String asset) {
    return httpFetch(getAssetUrl(asset));
  }

  /// Loads an asset using an [DomXMLHttpRequest] and returns data as [ByteData].
  Future<ByteData> load(String asset) async {
    final String url = getAssetUrl(asset);
    final HttpFetchResponse response = await httpFetch(url);

    if (response.status == 404 && asset == 'AssetManifest.json') {
      printWarning('Asset manifest does not exist at `$url` - ignoring.');
      return Uint8List.fromList(utf8.encode('{}')).buffer.asByteData();
    }

    return (await response.payload.asByteBuffer()).asByteData();
  }
}

/// An asset manager that gives fake empty responses for assets.
class WebOnlyMockAssetManager extends AssetManager {
  /// Mock asset directory relative to base url.
  String defaultAssetsDir = '';

  /// Mock empty asset manifest.
  String defaultAssetManifest = '{}';

  /// Mock font manifest overridable for unit testing.
  String defaultFontManifest = '''
  [
   {
      "family":"$robotoFontFamily",
      "fonts":[{"asset":"$robotoTestFontUrl"}]
   },
   {
      "family":"$ahemFontFamily",
      "fonts":[{"asset":"$ahemFontUrl"}]
   }
  ]''';

  @override
  String get assetsDir => defaultAssetsDir;

  @override
  String getAssetUrl(String asset) => asset;

  @override
  Future<HttpFetchResponse> loadAsset(String asset) async {
    if (asset == getAssetUrl('AssetManifest.json')) {
      return MockHttpFetchResponse(
        url: asset,
        status: 200,
        payload: MockHttpFetchPayload(
          byteBuffer: _toByteData(utf8.encode(defaultAssetManifest)).buffer,
        ),
      );
    }
    if (asset == getAssetUrl('FontManifest.json')) {
      return MockHttpFetchResponse(
        url: asset,
        status: 200,
        payload: MockHttpFetchPayload(
          byteBuffer: _toByteData(utf8.encode(defaultFontManifest)).buffer,
        ),
      );
    }

    return MockHttpFetchResponse(
      url: asset,
      status: 404,
    );
  }

  @override
  Future<ByteData> load(String asset) {
    if (asset == getAssetUrl('AssetManifest.json')) {
      return Future<ByteData>.value(
          _toByteData(utf8.encode(defaultAssetManifest)));
    }
    if (asset == getAssetUrl('FontManifest.json')) {
      return Future<ByteData>.value(
          _toByteData(utf8.encode(defaultFontManifest)));
    }
    throw HttpFetchNoPayloadError(asset, status: 404);
  }

  ByteData _toByteData(List<int> bytes) {
    final ByteData byteData = ByteData(bytes.length);
    for (int i = 0; i < bytes.length; i++) {
      byteData.setUint8(i, bytes[i]);
    }
    return byteData;
  }
}
