load("@@//build_external/android_tools_netsim:collect_files_in_dir.bzl", "collect_files_in_dir")
load("@@//build_external/android_tools_netsim:pdl_rust.bzl", "pdl_rust")
load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
load("@netsim_crates//:defs.bzl", "all_crate_deps")
load("@protobuf//bazel:cc_proto_library.bzl", "cc_proto_library")
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
load("@rules_cc//cc:defs.bzl", "cc_library")
load("@rules_proto//proto:defs.bzl", "proto_library")
load("@rules_proto_grpc_cpp//:defs.bzl", "cpp_grpc_library")
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library")

pdl_rust(
    name = "netsim_netlink_rust_gen",
    src = "pdl/netlink.pdl",
    out = "netlink_packets.rs",
)

pdl_rust(
    name = "netsim_mac80211_hwsim_rust_gen",
    src = "pdl/mac80211_hwsim.pdl",
    out = "mac80211_hwsim_packets.rs",
)

pdl_rust(
    name = "netsim_ieee80211_rust_gen",
    src = "pdl/ieee80211.pdl",
    out = "ieee80211_packets.rs",
)

pdl_rust(
    name = "netsim_llc_rust_gen",
    src = "pdl/llc.pdl",
    out = "llc_packets.rs",
)

pdl_rust(
    name = "netsim_arp_rust_gen",
    src = "pdl/arp.pdl",
    out = "arp_packets.rs",
)

# HACK to have a unified OUT_DIR
collect_files_in_dir(
    name = "libnetsim_packets_out_dir",
    srcs = [
        ":netsim_arp_rust_gen",
        ":netsim_ieee80211_rust_gen",
        ":netsim_llc_rust_gen",
        ":netsim_mac80211_hwsim_rust_gen",
        ":netsim_netlink_rust_gen",
        "@rootcanal//packets:link_layer_packets_rs",
    ],
    out = "libnetsim_packets_out_dir",
)

rust_library(
    name = "libnetsim_proto",
    srcs = glob(["rust/proto/src/**/*.rs"]),
    crate_features = ["cuttlefish"],
    crate_name = "netsim_proto",
    visibility = ["//visibility:public"],
    deps = all_crate_deps(
        package_name = "proto",
        normal = True,
    ),
)

rust_library(
    name = "libnetsim_common",
    srcs = glob(["rust/common/src/**/*.rs"]),
    crate_name = "netsim_common",
    visibility = ["//visibility:public"],
    deps = all_crate_deps(
        package_name = "common",
        normal = True,
    ),
)

rust_library(
    name = "libnetsim_cli",
    srcs = glob(["rust/cli/src/*.rs"]),
    crate_name = "netsim_cli",
    visibility = ["//visibility:public"],
    deps = all_crate_deps(
        package_name = "cli",
        normal = True,
    ) + [
        ":libnetsim_common",
        ":libnetsim_proto",
    ],
)

rust_library(
    name = "libnetsim_packets",
    srcs = glob([
        "rust/packets/src/**/*.rs",
    ]) + [
        ":libnetsim_packets_out_dir",
    ],
    crate_name = "netsim_packets",
    rustc_env = {
        "OUT_DIR": "$(execpath libnetsim_packets_out_dir)",
    },
    visibility = ["//visibility:public"],
    deps = all_crate_deps(
        package_name = "packets",
        normal = True,
    ),
)

rust_library(
    name = "libnetsim_hostapd_rs",
    srcs = glob([
        "rust/hostapd-rs/src/**/*.rs",
    ]),
    crate_name = "hostapd_rs",
    deps = [
        ":libnetsim_packets",
    ] + all_crate_deps(
        package_name = "hostapd-rs",
        normal = True,
    ),
)

rust_binary(
    name = "netsim",
    srcs = ["rust/cli/bin/netsim.rs"],
    visibility = ["//visibility:public"],
    deps = all_crate_deps(
        package_name = "cli",
        normal = True,
    ) + [
        ":libnetsim_cli",
    ],
)

proto_library(
    name = "netsim_proto",
    srcs = [
        "proto/netsim/common.proto",
        "proto/netsim/config.proto",
        "proto/netsim/frontend.proto",
        "proto/netsim/hci_packet.proto",
        "proto/netsim/model.proto",
        "proto/netsim/packet_streamer.proto",
        "proto/netsim/startup.proto",
        "proto/netsim/stats.proto",
    ],
    strip_import_prefix = "proto",
    deps = [
        "@protobuf//:empty_proto",
        "@protobuf//:timestamp_proto",
        "@rootcanal//:rootcanal_proto",
    ],
)

# lib-netsimd-proto
cc_proto_library(
    name = "netsim_cc_proto",
    deps = [":netsim_proto"],
)

cpp_grpc_library(
    name = "netsim_cc_grpc",
    protos = [":netsim_proto"],
    deps = [":netsim_cc_proto"],
)

# netsim_daemon_cc, netsim_daemon_h
run_binary(
    name = "netsim_daemon_ffi",
    srcs = [
        "rust/daemon/src/ffi.rs",
    ],
    outs = [
        "netsim-daemon/src/ffi.rs.cc",
        "netsim-daemon/src/ffi.rs.h",
    ],
    args = [
        "$(execpath rust/daemon/src/ffi.rs)",
        "--cfg",
        'feature="cuttlefish"',
        "-o",
        "$(execpath netsim-daemon/src/ffi.rs.h)",
        "-o",
        "$(execpath netsim-daemon/src/ffi.rs.cc)",
    ],
    tool = "@cxx.rs//:codegen",
)

cc_library(
    name = "lib-netsim",
    srcs = [
        "src/backend/grpc_client.cc",
        "src/hci/bluetooth_facade.cc",
        "src/hci/hci_packet_transport.cc",
        "src/hci/rust_device.cc",
        "src/util/crash_report.cc",
        "src/util/ini_file.cc",
        "src/util/log.cc",
        "src/util/os_utils.cc",
        "src/util/string_utils.cc",
        ":netsim_daemon_ffi",
    ] + glob([
        "src/**/*.h",
    ]),
    copts = [
        "-Wno-unused-parameter",
        "-g",  # Produce debugging information.
    ],
    defines = [
        # We use NETSIM_ANDROID_EMULATOR here to temporarily simplify crash
        # reporting infra.
        # TODO: Properly add breakpad, libunwindstack, etc.
        "NETSIM_ANDROID_EMULATOR",
    ],
    includes = [
        "src",
    ],
    linkopts = ["-rdynamic"],  # Allow obtaining backtraces for debugging.
    linkstatic = True,
    deps = [
        ":netsim_cc_grpc",
        ":netsim_cc_proto",
        "@@//build_external/cxxbridge:cxx-bridge-header",
        "@grpc//:grpc++",
        "@jsoncpp",
        "@protobuf",
        "@rootcanal",
    ],
)

cc_library(
    name = "netsim_daemon_ffi_cc_library",
    srcs = ["netsim-daemon/src/ffi.rs.cc"],
    hdrs = ["netsim-daemon/src/ffi.rs.h"],
    deps = [
        ":lib-netsim",
    ],
    alwayslink = True,
    linkstatic = True,
)

rust_library(
    name = "libnetsim_daemon",
    srcs = glob([
        "rust/daemon/src/**/*.rs",
    ]),
    compile_data = glob([
        "rust/daemon/src/**/*.csv",
    ]),
    crate_features = ["cuttlefish"],
    crate_name = "netsim_daemon",
    visibility = ["//visibility:public"],
    deps = [
        ":libnetsim_common",
        ":libnetsim_hostapd_rs",
        ":libnetsim_packets",
        ":libnetsim_proto",
        # pica is manually excluded from Cargo.toml due to missing required
        # version on crates.io, so we need to manually add it here.
        "@netsim_crates//:pica",
        # This is missing from Cargo.toml, so manually add it.
        "@netsim_crates//:libc",
        "@netsim_crates//:openssl",
    ] + all_crate_deps(
        package_name = "daemon",
        normal = True,
    ),
)

cc_binary(
    name = "netsimd",
    srcs = ["rust/netsimd.cc"],
    copts = [
        "-Wno-unused-parameter",
        "-g",  # Produce debugging information.
    ],
    includes = ["src"],
    linkopts = ["-rdynamic"],  # Allow obtaining backtraces for debugging.
    visibility = ["//visibility:public"],
    deps = [
        ":lib-netsim",
        ":libnetsim_daemon",
        ":netsim_daemon_ffi_cc_library",
        ":netsim_cc_proto",
        "@grpc//:grpc++",
        "@netsim_crates//:grpcio-sys",
        "@rootcanal//:librootcanal",
    ],
)
