load("//tools/install:install.bzl", "install")
load("//tools/lint:lint.bzl", "add_lint_tests")
load(
    "//tools/skylark:drake_cc.bzl",
    "drake_cc_googletest",
    "drake_cc_library",
    "drake_transitive_installed_hdrs_filegroup",
)
load(
    "//tools/skylark:drake_java.bzl",
    "drake_java_binary",
)
load(
    "//tools/skylark:drake_lcm.bzl",
    "drake_lcm_cc_library",
    "drake_lcm_java_library",
    "drake_lcm_py_library",
)
load(
    "//tools/skylark:drake_py.bzl",
    "drake_py_library",
    "drake_py_test",
)
load("//tools/workspace:generate_file.bzl", "generate_file")
load(":defs.bzl", "ALL_LCM_SRCS")

package(default_visibility = ["//visibility:public"])

# Search for all *.lcm files.
_GLOB_ALL_LCM_SRCS = sorted(glob(["*.lcm"]))

# The list of files in the glob, but not in defs.bzl.
_MISSING_ALL = [
    x
    for x in _GLOB_ALL_LCM_SRCS
    if x not in ALL_LCM_SRCS
]

# Fail with an error message if the two lists disagree.
# TODO(jwnimmer-tri) Ideally, we'd fail at test-time, not build-time.
(len(_MISSING_ALL) == 0) or fail(
    "Please update lcmtypes/defs.bzl to add {}".format(
        _MISSING_ALL,
    ),
)

# The list of files in defs.bzl, but not in the glob.
_EXTRA_ALL = [
    x
    for x in ALL_LCM_SRCS
    if x not in _GLOB_ALL_LCM_SRCS
]

# Fail with an error message if the two lists disagree.
# TODO(jwnimmer-tri) Ideally, we'd fail at test-time, not build-time.
(len(_EXTRA_ALL) == 0) or fail(
    "Please update lcmtypes/defs.bzl remove {}".format(
        _EXTRA_ALL,
    ),
)

drake_lcm_cc_library(
    name = "acrobot",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_acrobot_u.lcm",
        "lcmt_acrobot_x.lcm",
        "lcmt_acrobot_y.lcm",
    ],
)

drake_lcm_cc_library(
    name = "call_python",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_call_python.lcm",
        "lcmt_call_python_data.lcm",
    ],
)

drake_lcm_cc_library(
    name = "contact_results_for_viz",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_contact_results_for_viz.lcm"],
    deps = [
        ":hydroelastic_contact_surface_for_viz",
        ":point_pair_contact_info_for_viz",
    ],
)

drake_lcm_cc_library(
    name = "experimental_deformable_mesh",
    lcm_package = "drake",
    lcm_srcs = [
        "experimental_lcmt_deformable_tri.lcm",
        "experimental_lcmt_deformable_tri_mesh_init.lcm",
        "experimental_lcmt_deformable_tri_meshes_init.lcm",
        "experimental_lcmt_deformable_tri_mesh_update.lcm",
        "experimental_lcmt_deformable_tri_meshes_update.lcm",
    ],
)

drake_lcm_cc_library(
    name = "header",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_header.lcm"],
)

drake_lcm_cc_library(
    name = "hydroelastic_contact_surface_for_viz",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_hydroelastic_contact_surface_for_viz.lcm"],
    deps = [
        ":hydroelastic_quadrature_per_point_data_for_viz",
        ":point",
    ],
)

drake_lcm_cc_library(
    name = "hydroelastic_quadrature_per_point_data_for_viz",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_hydroelastic_quadrature_per_point_data_for_viz.lcm"],
)

drake_lcm_cc_library(
    name = "image",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_image.lcm"],
    deps = [":header"],
)

drake_lcm_cc_library(
    name = "image_array",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_image_array.lcm"],
    deps = [
        ":header",
        ":image",
    ],
)

drake_lcm_cc_library(
    name = "point",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_point.lcm"],
)

drake_lcm_cc_library(
    name = "point_cloud",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_point_cloud.lcm",
        "lcmt_point_cloud_field.lcm",
    ],
)

drake_lcm_cc_library(
    name = "point_pair_contact_info_for_viz",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_point_pair_contact_info_for_viz.lcm"],
)

drake_lcm_cc_library(
    name = "planar_manipuland_status",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_planar_manipuland_status.lcm"],
)

drake_lcm_cc_library(
    name = "planar_gripper",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_planar_gripper_command.lcm",
        "lcmt_planar_gripper_status.lcm",
        "lcmt_planar_gripper_finger_command.lcm",
        "lcmt_planar_gripper_finger_face_assignment.lcm",
        "lcmt_planar_gripper_finger_face_assignments.lcm",
        "lcmt_planar_gripper_finger_status.lcm",
        "lcmt_planar_plant_state.lcm",
        "lcmt_force_torque.lcm",
    ],
)

drake_lcm_cc_library(
    name = "quaternion",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_quaternion.lcm"],
)

drake_lcm_cc_library(
    name = "robot_plan",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_robot_plan.lcm",
        "lcmt_robot_state.lcm",
    ],
)

drake_lcm_cc_library(
    name = "scope",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_scope.lcm"],
)

drake_lcm_cc_library(
    name = "drake_signal",
    lcm_package = "drake",
    lcm_srcs = ["lcmt_drake_signal.lcm"],
)

drake_lcm_cc_library(
    name = "viewer",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_viewer_command.lcm",
        "lcmt_viewer_draw.lcm",
        "lcmt_viewer_geometry_data.lcm",
        "lcmt_viewer_link_data.lcm",
        "lcmt_viewer_load_robot.lcm",
    ],
)

drake_lcm_cc_library(
    name = "allegro",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_allegro_command.lcm",
        "lcmt_allegro_status.lcm",
    ],
)

drake_lcm_cc_library(
    name = "iiwa",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_iiwa_command.lcm",
        "lcmt_iiwa_status.lcm",
        "lcmt_iiwa_status_telemetry.lcm",
    ],
)

drake_lcm_cc_library(
    name = "schunk",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_schunk_wsg_command.lcm",
        "lcmt_schunk_wsg_status.lcm",
    ],
)

drake_lcm_cc_library(
    name = "jaco",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_jaco_command.lcm",
        "lcmt_jaco_status.lcm",
    ],
)

drake_lcm_cc_library(
    name = "panda",
    lcm_package = "drake",
    lcm_srcs = [
        "lcmt_panda_command.lcm",
        "lcmt_panda_status.lcm",
    ],
)

# Generate the *.py sources, but keep __init__.py separate, so that it doesn't
# get installed.  The install will use this (private) library.  Users should
# depend on ":lcmtypes_drake_py", immediately below.
drake_lcm_py_library(
    name = "_generated_lcmtypes_drake_py",
    # We'll rely on the //:module_py path munging instead.
    add_current_package_to_imports = False,
    lcm_package = "drake",
    lcm_srcs = ALL_LCM_SRCS,
    visibility = ["//visibility:private"],
)

drake_py_library(
    name = "lcmtypes_drake_py",
    srcs = ["__init__.py"],
    deps = [
        ":_generated_lcmtypes_drake_py",
    ],
)

drake_lcm_java_library(
    name = "lcmtypes_drake_java",
    lcm_package = "drake",
    lcm_srcs = ALL_LCM_SRCS,
)

# This should list every LCM type that is known to Drake.
LCMTYPES_CC = [
    ":acrobot",
    ":allegro",
    ":call_python",
    ":experimental_deformable_mesh",
    ":point",
    ":point_cloud",
    ":header",
    ":quaternion",
    ":image",
    ":image_array",
    ":contact_results_for_viz",
    ":drake_signal",
    ":hydroelastic_contact_surface_for_viz",
    ":iiwa",
    ":jaco",
    ":panda",
    ":planar_manipuland_status",
    ":planar_gripper",
    ":point_pair_contact_info_for_viz",
    ":robot_plan",
    ":schunk",
    ":scope",
    ":viewer",
]

# This rule should list every LCM type that is known to Drake or its external
# dependencies.
drake_java_binary(
    name = "drake-lcm-spy",
    main_class = "lcm.spy.Spy",
    visibility = ["//visibility:private"],
    runtime_deps = [
        ":lcmtypes_drake_java",
    ],
)

drake_cc_library(
    name = "lcmtypes_drake_cc",
    deps = LCMTYPES_CC,
)

drake_transitive_installed_hdrs_filegroup(
    name = "lcmtypes_drake_cc_headers",
    visibility = ["//visibility:private"],
    deps = LCMTYPES_CC,
)

# The drake-lcmtypes-cpp library is distinct from (but required by) the drake
# library; refer to `tools/install/libdrake/drake.cps` for library structure.
# Therefore, we install Drake's lcmtypes C++ headers to a different directory
# than the drake library's include path; this ensures that downstream code is
# using the correct include paths for the libraries they need.  The path to
# `include/drake_lcmtypes` is provided to downstream code via `drake.cps` and
# its generated cmake config `lib/cmake/drake/drake-config.cmake.
install(
    name = "install_drake_cc_headers",
    hdrs = [":lcmtypes_drake_cc_headers"],
    hdr_dest = "include/drake_lcmtypes",
    visibility = ["//visibility:private"],
)

install(
    name = "install",
    install_tests = select({
        "@lcm//:lcm_install_java_is_off": [],
        "//conditions:default": [
            ":test/drake-lcm-spy_install_test.py",
        ],
    }),
    targets = [
        ":_generated_lcmtypes_drake_py",
    ] + select({
        "@lcm//:lcm_install_java_is_off": [],
        "//conditions:default": [
            ":drake-lcm-spy",
            ":drake-lcm-spy-launcher",
            ":lcmtypes_drake_java",
        ],
    }),
    rename = {
        "share/java/liblcmtypes_drake_java.jar": "lcmtypes_drake.jar",
        "bin/drake-lcm-spy-launcher.sh": "drake-lcm-spy",
    },
    allowed_externals = [
        "@com_jidesoft_jide_oss//jar",
        "@commons_io//jar",
        "@lcm//:lcm-java",
        "@net_sf_jchart2d//jar",
        "@org_apache_xmlgraphics_commons//jar",
    ],
    deps = [
        ":install_drake_cc_headers",
        "@lcm//:install",
    ],
)

# === test/ ===

# TODO(jwnimmer-tri) We use this to test the `use_new_lcm_gen = True` flag.
# Once we turn on that flag by default, we can probably ditch this test.
generate_file(
    name = "test_message.lcm",
    content = """
package drake;
struct test_message { double value; }
""",
    visibility = ["//visibility:private"],
)

drake_lcm_cc_library(
    name = "test_message",
    lcm_package = "drake",
    lcm_srcs = ["test_message.lcm"],
    use_new_lcm_gen = True,
    visibility = ["//visibility:private"],
)

drake_cc_googletest(
    name = "test_message_test",
    deps = [
        ":test_message",
        "//lcm:lcm_messages",
    ],
)

drake_py_test(
    name = "nested_types_test",
    deps = [
        ":lcmtypes_drake_py",
    ],
)

add_lint_tests(
    python_lint_extra_srcs = [
        ":test/drake-lcm-spy_install_test.py",
    ],
)
