# build-aux/measurestack/app_main.py - Application-specific wrapper around analyze.py
#
# Copyright (C) 2024-2025  Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later

import os.path
import typing

from . import analyze, app_output, app_plugins, util
from .analyze import BaseName, QName

# pylint: disable=unused-variable
__all__ = [
    "main",
]


def main(
    *,
    arg_pico_platform: str,
    arg_base_dir: str,
    arg_ci_fnames: typing.Collection[str],
    arg_c_fnames: typing.Collection[str],
) -> None:

    plugins: list[util.Plugin] = []

    # sbc-harness ####################################################

    libobj_plugin = app_plugins.LibObjPlugin(arg_c_fnames)
    lib9p_plugin = app_plugins.Lib9PPlugin(arg_base_dir, arg_c_fnames, libobj_plugin)

    def sbc_is_thread(name: QName) -> int:
        if str(name).endswith("_cr") and name.base() != BaseName("lib9p_srv_read_cr"):
            if "9p" in str(name.base()) or "lib9p/tests/test_server/main.c:" in str(
                name
            ):
                return lib9p_plugin.thread_count(name)
            return 1
        if name.base() == (
            BaseName("_entry_point")
            if arg_pico_platform == "rp2040"
            else BaseName("main")
        ):
            return 1
        return 0

    plugins += [
        app_plugins.CmdPlugin(),
        libobj_plugin,
        app_plugins.PicoFmtPlugin(arg_pico_platform),
        app_plugins.LibHWPlugin(arg_pico_platform, libobj_plugin),
        app_plugins.LibCRPlugin(),
        app_plugins.LibCRIPCPlugin(),
        lib9p_plugin,
        app_plugins.LibMiscPlugin(),
    ]

    # pico-sdk #######################################################

    if arg_pico_platform == "rp2040":

        def get_init_array() -> typing.Collection[QName]:
            ret: list[QName] = []
            for plugin in plugins:
                ret.extend(plugin.init_array())
            return ret

        plugins += [
            app_plugins.PicoSDKPlugin(
                get_init_array=get_init_array,
            ),
            app_plugins.TinyUSBDevicePlugin(arg_c_fnames),
            app_plugins.NewlibPlugin(),
            app_plugins.LibGCCPlugin(),
        ]

    # Tie it all together ############################################

    def thread_filter(name: QName) -> tuple[int, bool]:
        return sbc_is_thread(name), True

    def intrhandler_filter(name: QName) -> tuple[int, bool]:
        for plugin in plugins:
            if plugin.is_intrhandler(name):
                return 1, True
        return 0, False

    def misc_filter(name: QName) -> tuple[int, bool]:
        if name in [
            QName("__assert_msg_fail"),
            QName("__lm_printf"),
            QName("__lm_light_printf"),
            QName("fmt_vfctprintf"),
            QName("fmt_vsnprintf"),
        ]:
            return 1, False
        return 0, False

    extra_includes: list[BaseName] = []
    for plugin in plugins:
        extra_includes.extend(plugin.extra_includes())

    def extra_filter(name: QName) -> tuple[int, bool]:
        nonlocal extra_includes
        if name.base() in extra_includes:
            return 1, True
        return 0, False

    def _str_location_xform(loc: str) -> str:
        if not loc.startswith("/"):
            return loc
        parts = loc.split(":", 1)
        parts[0] = "./" + os.path.relpath(parts[0], arg_base_dir)
        return ":".join(parts)

    def location_xform(_loc: QName) -> str:
        return _str_location_xform(str(_loc))

    result = analyze.analyze(
        ci_fnames=arg_ci_fnames,
        app_func_filters={
            "Threads": thread_filter,
            "Interrupt handlers": intrhandler_filter,
            "Misc": misc_filter,
            "Extra": extra_filter,
        },
        app=util.PluginApplication(_str_location_xform, plugins),
        cfg_max_call_depth=100,
    )

    app_output.print_c(result, location_xform)