# Copyright 2019 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("//flutter/build/zip_bundle.gni")
import("//flutter/common/config.gni")
import("//flutter/web_sdk/web_sdk.gni")
import("//third_party/dart/build/dart/dart_action.gni")

web_ui_sources = exec_script("//third_party/dart/tools/list_dart_files.py",
                             [
                               "absolute",
                               rebase_path("//flutter/lib/web_ui/lib"),
                             ],
                             "list lines")

web_engine_libraries = [
  ":skwasm_impl_library",
  ":skwasm_stub_library",
  ":web_engine_library",
  ":web_ui_library",
  ":web_ui_ui_web",
  ":web_ui_library_sources",
  ":web_unicode_library",
  ":web_test_fonts_library",
  ":web_locale_keymap_library",
]

group("web_sdk") {
  deps = [
    ":flutter_ddc_modules",
    ":flutter_platform_dills",
  ]
}

sdk_rewriter("web_ui_library_sources") {
  ui = true
  input_dir = "//flutter/lib/web_ui/lib/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/ui/"

  # exclude everything in the engine directory, it will be a separate internal library
  exclude_patterns = [ rebase_path("//flutter/lib/web_ui/lib/src/*") ]
}

web_ui_ui_web_with_output("web_ui_ui_web") {
  output_dir = "$root_out_dir/flutter_web_sdk/lib/ui_web/"
}

sdk_rewriter("web_engine_library") {
  library_name = "engine"
  api_file = "//flutter/lib/web_ui/lib/src/engine.dart"
  input_dir = "//flutter/lib/web_ui/lib/src/engine/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_engine/"

  # exclude skwasm, it will be a separate internal library
  exclude_patterns =
      [ rebase_path("//flutter/lib/web_ui/lib/src/engine/skwasm/*") ]
}

sdk_rewriter("skwasm_stub_library") {
  library_name = "skwasm_stub"
  api_file = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart"
  input_dir = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_skwasm_stub/"
}

sdk_rewriter("skwasm_impl_library") {
  library_name = "skwasm_impl"
  api_file = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart"
  input_dir = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_skwasm_impl/"
}

sdk_rewriter("web_unicode_library") {
  library_name = "web_unicode"
  api_file = "//flutter/third_party/web_unicode/lib/web_unicode.dart"
  input_dir = "//flutter/third_party/web_unicode/lib/web_unicode/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_web_unicode/"
}

sdk_rewriter("web_test_fonts_library") {
  library_name = "web_test_fonts"
  api_file = "//flutter/third_party/web_test_fonts/lib/web_test_fonts.dart"
  input_dir = "//flutter/third_party/web_test_fonts/lib/web_test_fonts/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_web_test_fonts/"
}

sdk_rewriter("web_locale_keymap_library") {
  library_name = "web_locale_keymap"
  api_file =
      "//flutter/third_party/web_locale_keymap/lib/web_locale_keymap.dart"
  input_dir = "//flutter/third_party/web_locale_keymap/lib/web_locale_keymap/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_web_locale_keymap/"
}

copy("web_ui_library") {
  sources = [ "//flutter/web_sdk/libraries.json" ]

  outputs = [ "$root_out_dir/flutter_web_sdk/{{source_file_part}}" ]
}

# Compiles a Dart program with dartdevc
#
# Parameters:
#
#   inputs (required): The inputs to dartdevc
#
#   outputs (required): The files generated by dartdevc
#
#   args (required): The arguments to pass to dartdevc
template("_dartdevc") {
  if (flutter_prebuilt_dart_sdk) {
    action(target_name) {
      not_needed(invoker, [ "packages" ])
      deps = web_engine_libraries
      script = "//build/gn_run_binary.py"

      inputs = invoker.inputs
      outputs = invoker.outputs

      pool = "//flutter/build/dart:dart_pool"

      ext = ""
      if (is_win) {
        ext = ".exe"
      }
      dart = rebase_path("$host_prebuilt_dart_sdk/bin/dart$ext", root_out_dir)
      dartdevc = rebase_path(
              "$host_prebuilt_dart_sdk/bin/snapshots/dartdevc.dart.snapshot")
      args = [
               dart,
               dartdevc,
             ] + invoker.args
    }
  } else {
    prebuilt_dart_action(target_name) {
      forward_variables_from(invoker, "*")

      deps = web_engine_libraries + [
               "//third_party/dart:create_sdk",
               "//third_party/dart/pkg:pkg_files_stamp",
               "//third_party/dart/utils/dartdevc:dartdevc_files_stamp",
               "//third_party/dart/utils/dartdevc:dartdevc_sdk_patch_stamp",
             ]

      script = "//third_party/dart/pkg/dev_compiler/bin/dartdevc.dart"

      pool = "//flutter/build/dart:dart_pool"
    }
  }
}

# Compiles a Dart SDK with the kernel_worker
#
# Parameters:
#
#   inputs (required): The inputs to the kernel_worker
#
#   outputs (required): The files generated by the kernel_worker
#
#   args (required): The arguments to pass to the kernel_worker
template("_kernel_worker") {
  if (flutter_prebuilt_dart_sdk) {
    action(target_name) {
      deps = web_engine_libraries
      script = "//build/gn_run_binary.py"

      inputs = invoker.inputs
      outputs = invoker.outputs

      pool = "//flutter/build/dart:dart_pool"

      ext = ""
      if (is_win) {
        ext = ".exe"
      }
      dart = rebase_path("$host_prebuilt_dart_sdk/bin/dart$ext", root_out_dir)
      kernel_worker = rebase_path(
              "$host_prebuilt_dart_sdk/bin/snapshots/kernel_worker.dart.snapshot")

      args = [
               dart,
               kernel_worker,
             ] + invoker.args
    }
  } else {
    prebuilt_dart_action(target_name) {
      forward_variables_from(invoker, "*")

      deps = web_engine_libraries + [
               "//third_party/dart:create_sdk",
               "//third_party/dart/pkg:pkg_files_stamp",
               "//third_party/dart/utils/dartdevc:dartdevc_files_stamp",
               "//third_party/dart/utils/dartdevc:dartdevc_sdk_patch_stamp",
             ]

      script = "//third_party/dart/utils/bazel/kernel_worker.dart"

      pool = "//flutter/build/dart:dart_pool"
    }
  }
}

template("_compile_platform") {
  assert(defined(invoker.sound_null_safety),
         "sound_null_safety must be defined for $target_name")
  assert(defined(invoker.kernel_target),
         "kernel_target must be defined for $target_name")
  assert(defined(invoker.summary_only),
         "summary_only must be defined for $target_name")
  assert(defined(invoker.output_dill),
         "output_dill must be defined for $target_name")
  _kernel_worker(target_name) {
    inputs = [ "sdk_rewriter.dart" ] + web_ui_sources

    outputs = [ invoker.output_dill ]

    if (invoker.sound_null_safety) {
      args = [ "--sound-null-safety" ]
    } else {
      args = [ "--no-sound-null-safety" ]
    }

    if (invoker.summary_only) {
      args += [ "--summary-only" ]
    } else {
      args += [ "--no-summary-only" ]
    }
    if (defined(invoker.null_environment) && invoker.null_environment) {
      args += [ "--null-environment" ]
    }
    args += [
      "--target",
      "${invoker.kernel_target}",
      "--packages-file",
      "file:///" + rebase_path(dart_sdk_package_config),
      "--multi-root-scheme",
      "org-dartlang-sdk",
      "--multi-root",
      "file:///" + rebase_path("$root_out_dir/flutter_web_sdk"),
      "--libraries-file",
      "org-dartlang-sdk:///libraries.json",
      "--output",
      rebase_path(invoker.output_dill),
      "--source",
      "dart:core",

      # Additional Flutter web dart libraries
      "--source",
      "dart:ui",
      "--source",
      "dart:ui_web",
      "--source",
      "dart:_engine",
      "--source",
      "dart:_skwasm_stub",
      "--source",
      "dart:_web_unicode",
      "--source",
      "dart:_web_locale_keymap",
    ]
    if (flutter_prebuilt_dart_sdk) {
      args += [
        "--multi-root",
        "file:///" + rebase_path("$host_prebuilt_dart_sdk/.."),
      ]
    } else {
      args += [
        "--multi-root",
        "file:///" + rebase_path("$root_out_dir"),
      ]
    }
  }
}

# Compile the unsound DDC SDK's summary.
_compile_platform("flutter_dartdevc_kernel_sdk_outline_unsound") {
  sound_null_safety = false
  kernel_target = "ddc"
  summary_only = true
  output_dill = "$root_out_dir/flutter_web_sdk/kernel/ddc_outline.dill"
}

# Compile the sound DDC SDK's summary.
_compile_platform("flutter_dartdevc_kernel_sdk_outline_sound") {
  sound_null_safety = true
  kernel_target = "ddc"
  summary_only = true
  output_dill = "$root_out_dir/flutter_web_sdk/kernel/ddc_outline_sound.dill"
}

_compile_platform("flutter_dart2js_kernel_sdk_full_unsound") {
  sound_null_safety = false
  kernel_target = "dart2js"
  summary_only = false
  output_dill =
      "$root_out_dir/flutter_web_sdk/kernel/dart2js_platform_unsound.dill"
  null_environment = true
}

_compile_platform("flutter_dart2js_kernel_sdk_full_sound") {
  sound_null_safety = true
  kernel_target = "dart2js"
  summary_only = false
  output_dill = "$root_out_dir/flutter_web_sdk/kernel/dart2js_platform.dill"
  null_environment = true
}

# TODO(jacksongardner): remove these once they are no longer used by the flutter tool
copy("flutter_dartdevc_kernel_sdk_outline_unsound_legacy") {
  sources = get_target_outputs(":flutter_dartdevc_kernel_sdk_outline_unsound")

  outputs = [ "$root_out_dir/flutter_web_sdk/kernel/flutter_ddc_sdk.dill" ]

  deps = [ ":flutter_dartdevc_kernel_sdk_outline_unsound" ]
}

copy("flutter_dartdevc_kernel_sdk_outline_sound_legacy") {
  sources = get_target_outputs(":flutter_dartdevc_kernel_sdk_outline_sound")

  outputs =
      [ "$root_out_dir/flutter_web_sdk/kernel/flutter_ddc_sdk_sound.dill" ]

  deps = [ ":flutter_dartdevc_kernel_sdk_outline_sound" ]
}

group("flutter_platform_dills") {
  public_deps = [
    ":flutter_dart2js_kernel_sdk_full_sound",
    ":flutter_dart2js_kernel_sdk_full_unsound",
    ":flutter_dartdevc_kernel_sdk_outline_sound",
    ":flutter_dartdevc_kernel_sdk_outline_unsound",

    # TODO(jacksongardner): remove these once they are no longer used by the flutter tool
    # https://github.com/flutter/flutter/issues/113303
    ":flutter_dartdevc_kernel_sdk_outline_sound",
    ":flutter_dartdevc_kernel_sdk_outline_unsound",
  ]
}

template("_compile_ddc_modules") {
  assert(defined(invoker.sound_null_safety),
         "sound_null_safety must be defined for $target_name")
  assert(defined(invoker.use_skia),
         "use_skia must be defined  for $target_name")
  assert(defined(invoker.auto_detect),
         "auto_detect must be defined for $target_name")

  _dartdevc(target_name) {
    inputs = [ "sdk_rewriter.dart" ] + web_ui_sources

    packages = dart_sdk_package_config

    name_suffix = ""
    if (invoker.use_skia) {
      name_suffix += "-canvaskit"
    }
    if (invoker.auto_detect) {
      name_suffix += "-html"
    }
    if (invoker.sound_null_safety) {
      name_suffix += "-sound"
    }

    amd_js_path =
        "$root_out_dir/flutter_web_sdk/kernel/amd${name_suffix}/dart_sdk.js"
    legacy_js_path =
        "$root_out_dir/flutter_web_sdk/kernel/legacy${name_suffix}/dart_sdk.js"

    outputs = [
      amd_js_path,
      amd_js_path + ".map",
      legacy_js_path,
      legacy_js_path + ".map",
    ]

    if (invoker.sound_null_safety) {
      args = [ "--sound-null-safety" ]
    } else {
      args = [ "--no-sound-null-safety" ]
    }

    args += [
      "--compile-sdk",
      "dart:core",

      # Additional Flutter web dart libraries
      "dart:ui",
      "dart:ui_web",
      "dart:_engine",
      "dart:_skwasm_stub",
      "dart:_web_unicode",
      "dart:_web_locale_keymap",
      "--no-summarize",
      "--packages",
      "file:///" + rebase_path(dart_sdk_package_config),
      "--multi-root-scheme",
      "org-dartlang-sdk",
      "--multi-root",
      "file:///" + rebase_path("$root_out_dir/flutter_web_sdk"),
      "--multi-root-output-path",
      rebase_path("$root_out_dir/"),
      "--libraries-file",
      "org-dartlang-sdk:///libraries.json",
      "--inline-source-map",
      "-DFLUTTER_WEB_USE_SKIA=${invoker.use_skia}",
      "-DFLUTTER_WEB_AUTO_DETECT=${invoker.auto_detect}",
      "--modules",
      "amd",
      "-o",
      rebase_path(amd_js_path),
      "--modules",
      "legacy",
      "-o",
      rebase_path(legacy_js_path),
    ]
    if (flutter_prebuilt_dart_sdk) {
      args += [
        "--multi-root",
        "file:///" + rebase_path("$host_prebuilt_dart_sdk/.."),
      ]
    } else {
      args += [
        "--multi-root",
        "file:///" + rebase_path("$root_out_dir"),
      ]
    }
  }
}

# Compiles the unsound html only renderer.
_compile_ddc_modules("flutter_dartdevc_kernel_sdk") {
  sound_null_safety = false
  use_skia = false
  auto_detect = false
}

# Compiles the unsound canvaskit only renderer.
_compile_ddc_modules("flutter_dartdevc_canvaskit_kernel_sdk") {
  sound_null_safety = false
  use_skia = true
  auto_detect = false
}

# Compiles the unsound autodetect renderer.
_compile_ddc_modules("flutter_dartdevc_canvaskit_html_kernel_sdk") {
  sound_null_safety = false
  use_skia = true
  auto_detect = true
}

# Compiles the sound html only renderer.
_compile_ddc_modules("flutter_dartdevc_kernel_sdk_sound") {
  sound_null_safety = true
  use_skia = false
  auto_detect = false
}

# Compiles the sound canvaskit only renderer.
_compile_ddc_modules("flutter_dartdevc_canvaskit_kernel_sdk_sound") {
  sound_null_safety = true
  use_skia = true
  auto_detect = false
}

# Compiles the sound autodetect renderer.
_compile_ddc_modules("flutter_dartdevc_canvaskit_html_kernel_sdk_sound") {
  sound_null_safety = true
  use_skia = true
  auto_detect = true
}

group("flutter_ddc_modules") {
  public_deps = [
    ":flutter_dartdevc_canvaskit_html_kernel_sdk",
    ":flutter_dartdevc_canvaskit_html_kernel_sdk_sound",
    ":flutter_dartdevc_canvaskit_kernel_sdk",
    ":flutter_dartdevc_canvaskit_kernel_sdk_sound",
    ":flutter_dartdevc_kernel_sdk",
    ":flutter_dartdevc_kernel_sdk_sound",

    # TODO(jacksongardner): remove these once they are no longer used by the flutter tool
    # https://github.com/flutter/flutter/issues/113303
    ":flutter_dartdevc_kernel_sdk_outline_sound_legacy",
    ":flutter_dartdevc_kernel_sdk_outline_unsound_legacy",
  ]
}

# Archives Flutter Web SDK
if (!is_fuchsia) {
  zip_bundle_from_file("flutter_web_sdk_archive") {
    if (target_os == "wasm") {
      output = "flutter-web-sdk.zip"
    } else if (is_win) {
      output = "flutter-web-sdk-${full_target_platform_name}.zip"
    } else {
      # TODO(jacksongardner): remove this once we stop making platform-specific
      # flutter_web_sdk archives.
      # https://github.com/flutter/flutter/issues/113303
      output = "flutter-web-sdk-${full_platform_name}.zip"
    }
    deps = [
             ":flutter_ddc_modules",
             ":flutter_platform_dills",
           ] + web_engine_libraries

    if (is_wasm) {
      deps += [
        "//flutter/third_party/canvaskit:canvaskit_chromium_group",
        "//flutter/third_party/canvaskit:canvaskit_group",
        "//flutter/third_party/canvaskit:skwasm_group",
      ]
    }

    # flutter_ddc_modules
    sources = get_target_outputs(":flutter_dartdevc_kernel_sdk")
    sources += get_target_outputs(":flutter_dartdevc_canvaskit_kernel_sdk")
    sources += get_target_outputs(":flutter_dartdevc_canvaskit_html_kernel_sdk")
    sources += get_target_outputs(":flutter_dartdevc_kernel_sdk_sound")
    sources +=
        get_target_outputs(":flutter_dartdevc_canvaskit_kernel_sdk_sound")
    sources +=
        get_target_outputs(":flutter_dartdevc_canvaskit_html_kernel_sdk_sound")

    # flutter_platform_dills
    sources +=
        get_target_outputs(":flutter_dartdevc_kernel_sdk_outline_unsound")
    sources += get_target_outputs(":flutter_dartdevc_kernel_sdk_outline_sound")
    sources += get_target_outputs(":flutter_dart2js_kernel_sdk_full_unsound")
    sources += get_target_outputs(":flutter_dart2js_kernel_sdk_full_sound")
    if (is_wasm) {
      sources += [
        "$root_out_dir/flutter_web_sdk/canvaskit/canvaskit.js",
        "$root_out_dir/flutter_web_sdk/canvaskit/canvaskit.wasm",
        "$root_out_dir/flutter_web_sdk/canvaskit/chromium/canvaskit.js",
        "$root_out_dir/flutter_web_sdk/canvaskit/chromium/canvaskit.wasm",
        "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.js",
        "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.wasm",
        "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.worker.js",
      ]
    }

    # TODO(jacksongardner): remove these once they are no longer used by the flutter tool
    # https://github.com/flutter/flutter/issues/113303
    sources += get_target_outputs(
            ":flutter_dartdevc_kernel_sdk_outline_unsound_legacy")
    sources +=
        get_target_outputs(":flutter_dartdevc_kernel_sdk_outline_sound_legacy")

    foreach(web_engine_library, web_engine_libraries) {
      sources += get_target_outputs(web_engine_library)
    }

    tmp_files = []
    web_sdk_files =
        filter_include(sources, [ "$root_build_dir/flutter_web_sdk/**" ])
    foreach(source, web_sdk_files) {
      tmp_files += [
        {
          source = rebase_path(source)
          destination = rebase_path(source, "$root_build_dir/flutter_web_sdk")
        },
      ]
    }
    files = tmp_files
  }
}
