load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("@rules_cc//cc:defs.bzl", "cc_library")

def _pico_generate_pio_header_impl(ctx):
    generated_headers = []
    for f in ctx.files.srcs:
        out = ctx.actions.declare_file(
            "{}_pio_generated/{}.h".format(ctx.label.name, f.basename),
        )
        generated_headers.append(out)
        ctx.actions.run(
            executable = ctx.executable._pioasm_tool,
            arguments = [
                "-o",
                "c-sdk",
                f.path,
                out.path,
            ],
            inputs = [f],
            outputs = [out],
        )

    cc_ctx = cc_common.create_compilation_context(
        headers = depset(direct = generated_headers),
        includes = depset(direct = [generated_headers[0].dirname]),
    )
    return [
        DefaultInfo(files = depset(direct = generated_headers)),
        CcInfo(compilation_context = cc_ctx),
    ]

pico_generate_pio_header = rule(
    implementation = _pico_generate_pio_header_impl,
    doc = """Generates a .h header file for each listed pio source.

Each source file listed in `srcs` will be available as `[pio file name].h` on
the include path if you depend on this rule from a `cc_library`.

pico_generate_pio_header(
    name = "my_fun_pio",
    srcs = ["my_fun_pio.pio"],
)

# This library can #include "my_fun_pio.pio.h".
cc_library(
    name = "libfoo",
    deps = [":my_fun_pio"],
    srcs = ["libfoo.c"],
)
""",
    attrs = {
        "srcs": attr.label_list(mandatory = True, allow_files = True),
        "_pioasm_tool": attr.label(
            default = "@pico-sdk//tools/pioasm:pioasm",
            cfg = "exec",
            executable = True,
        ),
    },
    provides = [CcInfo],
)

# Because the syntax for target_compatible_with when used with config_setting
# rules is both confusing and verbose, provide some helpers that make it much
# easier and clearer to express compatibility.
#
# Context: https://github.com/bazelbuild/bazel/issues/12614

def compatible_with_config(config_label):
    """Expresses compatibility with a config_setting."""
    return select({
        config_label: [],
        "//conditions:default": ["@platforms//:incompatible"],
    })

def incompatible_with_config(config_label):
    """Expresses incompatibility with a config_setting."""
    return select({
        config_label: ["@platforms//:incompatible"],
        "//conditions:default": [],
    })

def compatible_with_rp2():
    """Expresses a rule is compatible with the rp2 family."""
    return incompatible_with_config("//bazel/constraint:host")

def compatible_with_pico_w():
    """Expresses a rule is compatible a Pico W."""
    return select({
        "@pico-sdk//bazel/constraint:cyw43_wireless": [],
        "@pico-sdk//bazel/constraint:is_pico_w": [],
        "//conditions:default": ["@platforms//:incompatible"],
    })

def pico_board_config(name, platform_includes, **kwargs):
    """A helper macro for declaring a Pico board to use with PICO_CONFIG_HEADER.

    This generates pico_config_platform_headers.h using the list of
    includes provided in `platform_includes`, and the final artifact is
    a cc_library that you can configure //bazel/config:PICO_CONFIG_HEADER to
    point to.
    """
    _hdr_dir = "{}_generated_includes".format(name)
    _hdr_path = "{}/pico_config_platform_headers.h".format(_hdr_dir)
    write_file(
        name = "{}_platform_headers_file".format(name),
        out = _hdr_path,
        content = ['#include "{}"'.format(inc) for inc in platform_includes],
    )
    kwargs.setdefault("hdrs", [])
    kwargs["hdrs"].append(_hdr_path)
    kwargs.setdefault("includes", [])
    kwargs["includes"].append(_hdr_dir)
    cc_library(
        name = name,
        **kwargs
    )
