# Copyright (c) 2023 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//build/config/clang/clang.gni")
import("//build/config/ohos/config.gni")
import("//build/ohos/notice/notice.gni")
import("//build/ohos_var.gni")
import("//build/templates/common/check_target.gni")
import("//build/templates/common/collect_target.gni")
import("//build/templates/metadata/module_info.gni")

template("ohos_rust_library") {
  assert(!defined(invoker.output_dir),
         "output_dir is not allowed to be defined.")
  _test_target = defined(invoker.testonly) && invoker.testonly
  if (defined(invoker.subsystem_name) && defined(invoker.part_name)) {
    subsystem_name = invoker.subsystem_name
    part_name = invoker.part_name
  } else if (defined(invoker.part_name)) {
    part_name = invoker.part_name
    _part_subsystem_info_file =
        "$root_build_dir/build_configs/parts_info/part_subsystem.json"
    _arguments = [
      "--part-name",
      part_name,
      "--part-subsystem-info-file",
      rebase_path(_part_subsystem_info_file, root_build_dir),
    ]
    get_subsystem_script = "//build/templates/common/get_subsystem_name.py"
    subsystem_name =
        exec_script(get_subsystem_script, _arguments, "trim string")
    if (is_use_check_deps && !_test_target) {
      skip_check_subsystem = true
    }
  } else if (defined(invoker.subsystem_name)) {
    subsystem_name = invoker.subsystem_name
    part_name = subsystem_name
  } else {
    subsystem_name = "build"
    part_name = "build_framework"
  }
  assert(subsystem_name != "")
  assert(part_name != "")
  if (is_use_check_deps && !_test_target) {
    _check_target = "${target_name}__check"
    target_path = get_label_info(":${target_name}", "label_no_toolchain")
    check_target(_check_target) {
      module_deps = []
      module_ex_deps = []
      if (defined(invoker.deps)) {
        module_deps += invoker.deps
      }
      if (defined(invoker.public_deps)) {
        module_deps += invoker.public_deps
      }
      if (defined(invoker.external_deps)) {
        module_ex_deps += invoker.external_deps
      }
      if (defined(invoker.public_external_deps)) {
        module_ex_deps += invoker.public_external_deps
      }
    }
  }

  if (check_deps) {
    deps_data = {
    }
    module_label = get_label_info(":${target_name}", "label_with_toolchain")
    module_deps = []
    if (defined(invoker.deps)) {
      foreach(dep, invoker.deps) {
        module_deps += [ get_label_info(dep, "label_no_toolchain") ]
      }
    }
    module_ex_deps = []
    if (defined(invoker.external_deps) && invoker.external_deps != []) {
      module_ex_deps = invoker.external_deps
    }
    deps_data = {
      part_name = part_name
      module_label = module_label
      deps = module_deps
      external_deps = module_ex_deps
    }
    write_file("${root_out_dir}/deps_files/${part_name}__${target_name}.json",
               deps_data,
               "json")
  }

  if (is_standard_system) {
    output_dir = "${root_out_dir}/${subsystem_name}/${part_name}"
  } else {
    output_dir = "${root_out_dir}"
  }

  if (!_test_target) {
    module_label = get_label_info(":${target_name}", "label_with_toolchain")
    _collect_target = "${target_name}__collect"
    collect_module_target(_collect_target) {
      forward_variables_from(invoker, [ "install_images" ])
    }

    _notice_target = "${target_name}__notice"
    _main_target_name = target_name
    collect_notice(_notice_target) {
      forward_variables_from(invoker,
                             [
                               "testonly",
                               "license_as_sources",
                               "license_file",
                             ])
      module_type = "rust_library"

      module_name = _main_target_name
      if (defined(invoker.crate_name)) {
        module_name = invoker.crate_name
      }
      module_source_dir = get_label_info(":${_main_target_name}", "dir")
    }
  }
  
  # auto set auto_relative_install_dir by innerapi_tags
  if (defined(invoker.innerapi_tags)) {
    is_chipsetsdk = false
    is_platformsdk = false
    is_platformsdk_indirect = false
    is_passthrough = false
    is_passthrough_indirect = false
    is_llndk = false
    is_chipsetsdk_indirect = false
    is_chipsetsdk_sp = false
    is_chipsetsdk_sp_indirect = false

    foreach(tag, filter_include(invoker.innerapi_tags, [ "llndk" ])) {
      is_llndk = true
    }
    foreach(tag, filter_include(invoker.innerapi_tags, [ "chipsetsdk" ])) {
      is_chipsetsdk = true
    }
    foreach(tag,
            filter_include(invoker.innerapi_tags, [ "chipsetsdk_indirect" ])) {
      is_chipsetsdk_indirect = true
    }
    foreach(tag, filter_include(invoker.innerapi_tags, [ "chipsetsdk_sp" ])) {
      is_chipsetsdk_sp = true
    }
    foreach(
        tag,
        filter_include(invoker.innerapi_tags, [ "chipsetsdk_sp_indirect" ])) {
      is_chipsetsdk_sp_indirect = true
    }
    foreach(tag, filter_include(invoker.innerapi_tags, [ "platformsdk" ])) {
      is_platformsdk = true
    }
    foreach(tag,
            filter_include(invoker.innerapi_tags, [ "platformsdk_indirect" ])) {
      is_platformsdk_indirect = true
    }
    foreach(tag, filter_include(invoker.innerapi_tags, [ "passthrough" ])) {
      is_passthrough = true
    }
    foreach(tag,
            filter_include(invoker.innerapi_tags, [ "passthrough_indirect" ])) {
      is_passthrough_indirect = true
    }

    if (is_chipsetsdk && (is_chipsetsdk_indirect || is_chipsetsdk_sp || is_chipsetsdk_sp_indirect)) {
      assert(false, "chipsetsdk tags cannot coexist with [chipsetsdk_indirect,chipsetsdk_sp,chipsetsdk_sp_indirect]")
    } else if (is_chipsetsdk_indirect && (is_chipsetsdk || is_chipsetsdk_sp || is_chipsetsdk_sp_indirect)) {
      assert(false, "chipsetsdk_indirect tags cannot coexist with [chipsetsdk,chipsetsdk_sp,chipsetsdk_sp_indirect]")
    } else if (is_chipsetsdk_sp && (is_chipsetsdk || is_chipsetsdk_indirect || is_chipsetsdk_sp_indirect)) {
      assert(false, "chipsetsdk_sp tags cannot coexist with [chipsetsdk,chipsetsdk_indirect,chipsetsdk_sp_indirect]")
    } else if (is_chipsetsdk_sp_indirect && (is_chipsetsdk || is_chipsetsdk_sp || is_chipsetsdk_indirect)) {
      assert(false, "chipsetsdk_sp_indirect tags cannot coexist with [chipsetsdk,chipsetsdk_indirect,chipsetsdk_sp]")
    }

    if (is_platformsdk) {
      auto_relative_install_dir = platformsdk_dir
    }

    if (is_platformsdk_indirect) {
      auto_relative_install_dir = platformsdk_dir
    }

    if (is_chipsetsdk) {
      auto_relative_install_dir = chipset_sdk_dir
      if (is_platformsdk || is_platformsdk_indirect) {
        softlink_path = platformsdk_dir
      }
    }

    if (is_chipsetsdk_indirect) {
      auto_relative_install_dir = chipset_sdk_dir
      if (is_platformsdk || is_platformsdk_indirect) {
        softlink_path = platformsdk_dir
      }
    }

    if (is_chipsetsdk_sp) {
      auto_relative_install_dir = chipset_sdk_sp_dir
      if (is_platformsdk || is_platformsdk_indirect) {
        softlink_path = platformsdk_dir
      }
    }

    if (is_chipsetsdk_sp_indirect) {
      auto_relative_install_dir = chipset_sdk_sp_dir
      if (is_platformsdk || is_platformsdk_indirect) {
        softlink_path = platformsdk_dir
      }
    }

    if (is_passthrough) {
      auto_relative_install_dir = passthrough_dir
    }
    if (is_passthrough_indirect) {
      auto_relative_install_dir = passthrough_indirect_dir
    }

    if (is_llndk) {
      auto_relative_install_dir = llndk_dir
    }
    is_ndk = false
    foreach(tag, filter_include(invoker.innerapi_tags, [ "ndk" ])) {
      is_ndk = true
    }
    if (is_ndk) {
      auto_relative_install_dir = ndk_dir
    }
  }

  target_label = get_label_info(":${target_name}", "label_with_toolchain")
  target_toolchain = get_label_info(target_label, "toolchain")

  if (target_toolchain == "${current_toolchain}") {
    ohos_module_name = target_name
    _module_info_target = "${target_name}_info"
    generate_module_info(_module_info_target) {
      module_name = ohos_module_name
      module_type = "lib"
      module_source_dir = "$root_out_dir"
      if (defined(output_dir)) {
        module_source_dir = output_dir
      }

      module_install_name = ohos_module_name
      if (defined(invoker.output_name)) {
        module_install_name = invoker.output_name
      }

      module_install_images = [ "system" ]
      if (defined(invoker.install_images)) {
        module_install_images = []
        module_install_images += invoker.install_images
      }

      module_output_extension = shlib_extension
      if (defined(invoker.module_output_extension)) {
        module_output_extension = invoker.module_output_extension
      }

      install_enable = true
      if (defined(invoker.install_enable)) {
        install_enable = invoker.install_enable
      }

      if (defined(invoker.module_install_dir)) {
        module_install_dir = invoker.module_install_dir
      }

      # update relative_install_dir if auto_relative_install_dir defined
      if (defined(auto_relative_install_dir) && auto_relative_install_dir != "") {
        relative_install_dir = auto_relative_install_dir
      }

      if (defined(invoker.relative_install_dir)) {
        relative_install_dir = invoker.relative_install_dir
      }

      if (defined(invoker.innerapi_tags)) {
        if (defined(softlink_path) && softlink_path != "") {
          softlink_create_path = softlink_path
        }
      }

      if (defined(invoker.symlink_target_name)) {
        symlink_target_name = invoker.symlink_target_name
      }

      if (defined(invoker.output_prefix_override)) {
        output_prefix_override = invoker.output_prefix_override
      }
      notice = "$target_out_dir/$ohos_module_name.notice.txt"

      if (defined(invoker.innerapi_tags)) {
        invalid = false
        valid_tags = [
          "ndk",
          "llndk",
          "chipsetsdk",
          "chipsetsdk_indirect",
          "chipsetsdk_sp",
          "chipsetsdk_sp_indirect",
          "platformsdk",
          "platformsdk_indirect",
          "passthrough",
          "passthrough_indirect",
          "sasdk",
        ]
        foreach(tag, filter_exclude(invoker.innerapi_tags, valid_tags)) {
          if (tag != "") {
            invalid = true
          }
        }
        innerapi_tags = invoker.innerapi_tags
        assert(
            invalid != true,
            "$target_label has invalid innerapi_tags $innerapi_tags, allowed values: $valid_tags")
      }
    }
  }

  rust_library("${target_name}") {
    forward_variables_from(invoker,
                           "*",
                           [
                             "configs",
                             "remove_configs",
                             "no_default_deps",
                             "install_images",
                             "module_install_dir",
                             "relative_install_dir",
                             "symlink_target_name",
                             "output_dir",
                             "install_enable",
                             "version_script",
                             "license_file",
                             "license_as_sources",
                             "use_exceptions",
                             "stl",

                             # Sanitizer variables
                             "sanitize",
                           ])
    output_dir = output_dir

    if (!defined(inputs)) {
      inputs = []
    }

    if (!defined(ldflags)) {
      ldflags = []
    }

    if (defined(invoker.configs)) {
      configs += invoker.configs
    }
    if (defined(invoker.remove_configs)) {
      configs -= invoker.remove_configs
    }

    if (!defined(output_name)) {
      output_name = target_name
    }

    if (defined(invoker.no_default_deps)) {
      no_default_deps = invoker.no_default_deps
    }

    if (!defined(ldflags)) {
      ldflags = []
    }
    if (!defined(libs)) {
      libs = []
    }
    if (!defined(cflags_cc)) {
      cflags_cc = []
    }
    if (!defined(deps)) {
      deps = []
    }
    if (is_use_check_deps && !_test_target) {
      deps += [ ":$_check_target" ]
    }
    if (target_toolchain == "${current_toolchain}" && !skip_gen_module_info) {
      deps += [ ":$_module_info_target" ]
    }

    if (is_ohos) {
      if (defined(invoker.stl)) {
        cflags_cc += [
          "-nostdinc++",
          "-I" + rebase_path(
                  "${}toolchains_dir/${host_platform_dir}/llvm_ndk/include/c++/v1",
                  root_build_dir),
        ]
        ldflags += [
          "-nostdlib++",
          "-L" + rebase_path("${clang_stl_path}/${abi_target}/c++",
                             root_build_dir),
        ]

        libs += [ invoker.stl ]
      } else {
        if (current_cpu == "arm" || current_cpu == "arm64") {
          libs += [ "unwind" ]
        }
        ldflags += [ "-L" + rebase_path("${clang_stl_path}/${abi_target}/c++",
                                        root_build_dir) ]

        libs += [ "c++" ]
      }
    }

    if (defined(visibility) && visibility != []) {
      visibility += [ "//build/*" ]
      if (defined(build_ext_path)) {
        visibility += [ "${build_ext_path}/*" ]
      }
    }

    if (!_test_target) {
      deps += [
        ":$_notice_target",
        ":${_collect_target}",
      ]
    }
    if (!defined(include_dirs)) {
      include_dirs = []
    }

    install_module_info = {
      module_def = target_label
      module_info_file =
          rebase_path(get_label_info(module_def, "target_out_dir"),
                      root_build_dir) + "/${target_name}_module_info.json"
      subsystem_name = subsystem_name
      part_name = part_name
      toolchain = current_toolchain
      toolchain_out_dir = rebase_path(root_out_dir, root_build_dir)
    }
    metadata = {
      install_modules = [ install_module_info ]
    }
    if (defined(is_debug) && !is_debug && enable_debug_components != "") {
      foreach(component_name, debug_components) {
        if (part_name == component_name) {
          configs -= default_opt_configs
          configs += debug_level_configs
        }
      }
    }
  }
}