// Copyright 2014 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.

import 'package:meta/meta.dart';

/// The JavaScript bootstrap script to support in-browser hot restart.
///
/// The [requireUrl] loads our cached RequireJS script file. The [mapperUrl]
/// loads the special Dart stack trace mapper. The [entrypoint] is the
/// actual main.dart file.
///
/// This file is served when the browser requests "main.dart.js" in debug mode,
/// and is responsible for bootstrapping the RequireJS modules and attaching
/// the hot reload hooks.
String generateBootstrapScript({
  @required String requireUrl,
  @required String mapperUrl,
  @required String entrypoint,
}) {
  return '''
"use strict";

// Attach source mapping.
var mapperEl = document.createElement("script");
mapperEl.defer = true;
mapperEl.async = false;
mapperEl.src = "$mapperUrl";
document.head.appendChild(mapperEl);

// Attach require JS.
var requireEl = document.createElement("script");
requireEl.defer = true;
requireEl.async = false;
requireEl.src = "$requireUrl";
// This attribute tells require JS what to load as main (defined below).
requireEl.setAttribute("data-main", "main_module");
document.head.appendChild(requireEl);

// Invoked by connected chrome debugger for hot reload/restart support.
window.\$hotReloadHook = function(modules) {
  return new Promise(function(resolve, reject) {
    if (modules == null) {
      reject();
    }
    // If no modules change, return immediately.
    if (modules.length == 0) {
      resolve();
    }
    var reloadCount = 0;
    for (var i = 0; i < modules.length; i++) {
      require.undef(modules[i]);
      require([modules[i]], function(module) {
        reloadCount += 1;
        // once we've reloaded every module, trigger the hot reload.
        if (reloadCount == modules.length) {
          require(["$entrypoint", "dart_sdk"], function(app, dart_sdk) {
            // See L81 below for an explanation.
            window.\$mainEntrypoint = app[Object.keys(app)[0]].main;
            window.\$hotReload(resolve);
          });
        }
      });
    }
  });
}
''';
}

/// Generate a synthetic main module which captures the application's main
/// method.
String generateMainModule({@required String entrypoint}) {
  return '''
// Create the main module loaded below.
define("main_module", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) {
  dart_sdk.dart.setStartAsyncSynchronously(true);
  dart_sdk._isolate_helper.startRootIsolate(() => {}, []);
  dart_sdk._debugger.registerDevtoolsFormatter();
  let voidToNull = () => (voidToNull = dart_sdk.dart.constFn(dart_sdk.dart.fnType(dart_sdk.core.Null, [dart_sdk.dart.void])))();

  // Attach the main entrypoint and hot reload functionality to the window.
  // The app module will have a single property which contains the actual application
  // code. The property name is based off of the entrypoint that is generated, for example
  // the file `foo/bar/baz.dart` will generate a property named approximately
  // `foo__bar__baz`. Rather than attempt to guess, we assume the first property of
  // this object is the module.
  window.\$mainEntrypoint = app[Object.keys(app)[0]].main;
  if (window.\$hotReload == null) {
    window.\$hotReload = function(cb) {
      dart_sdk.developer.invokeExtension("ext.flutter.disassemble", "{}").then((_) => {
        dart_sdk.dart.hotRestart();
        window.\$mainEntrypoint();
        window.requestAnimationFrame(cb);
      });
    }
  }

  window.\$mainEntrypoint();
});

// Require JS configuration.
require.config({
  waitSeconds: 0,
});
''';
}
