# Copyright (c) Advanced Micro Devices, Inc.
# SPDX-License-Identifier:  MIT

import argparse
import os
import shlex
from pathlib import Path
from typing import Optional, Union

from rocprof_compute_profile.profiler_base import RocProfCompute_Base
from rocprof_compute_soc.soc_base import OmniSoC_Base
from utils.logger import console_error, console_log, demarcate
from utils.utils_common import resolve_rocm_library_path


class rocprofiler_sdk_profiler(RocProfCompute_Base):
    def __init__(
        self,
        profiling_args: argparse.Namespace,
        profiler_mode: str,
        soc: OmniSoC_Base,
    ) -> None:
        super().__init__(profiling_args, profiler_mode, soc)

    def get_profiler_options(
        self, native_tool_path: Optional[str] = None
    ) -> dict[str, Union[str, list[str]]]:
        args = self.get_args()
        app_cmd = shlex.split(args.remaining)

        # Build LD_PRELOAD: preserve user's existing, then append our libs
        # Order: [user's existing LD_PRELOAD] : [our profiler libs]
        ld_preload_parts = [
            os.environ.get("LD_PRELOAD"),  # User's existing LD_PRELOAD (if any)
            args.rocprofiler_sdk_tool_path,  # Our rocprofiler-sdk tool
            native_tool_path,  # Native tool (if provided)
        ]
        # Filter out None and empty string values and join with ':'
        ld_preload_value = ":".join(part for part in ld_preload_parts if part)

        if native_tool_path:
            # Use native tool to collect counters
            options = {"ROCPROF_COUNTER_COLLECTION": "0"}
            console_log(
                f"Using native counter collection tool: {str(native_tool_path)}"
            )
        else:
            options = {"ROCPROF_COUNTER_COLLECTION": "1"}

        options.update({
            "LD_PRELOAD": ld_preload_value,
            "ROCPROF_KERNEL_TRACE": "1",
            "ROCPROF_OUTPUT_FORMAT": args.format_rocprof_output,
            "ROCPROF_OUTPUT_PATH": f"{args.path}/out/pmc_1",
        })

        if getattr(args, "torch_trace", False):
            options["ROCPROF_MARKER_API_TRACE"] = "1"
        # Create folder pointed by ROCPROF_OUTPUT_PATH
        Path(options["ROCPROF_OUTPUT_PATH"]).mkdir(parents=True, exist_ok=True)

        if args.iteration_multiplexing:
            options.update({
                "ROCPROF_ITERATION_MULTIPLEXING": args.iteration_multiplexing,
            })

        if args.attach_pid:
            # In attach mode, tools are provided using ROCPROF_ATTACH_TOOL_LIBRARY
            # instead of LD_PRELOAD.
            # Build attach tool list (only our tools, not user's LD_PRELOAD)
            attach_tools = [args.rocprofiler_sdk_tool_path]
            if native_tool_path:
                attach_tools.append(native_tool_path)
            options.update({
                "ROCPROF_ATTACH_TOOL_LIBRARY": ":".join(attach_tools),
            })
            options.pop("LD_PRELOAD", None)

            rocprofiler_attach_library_path = resolve_rocm_library_path(
                str(
                    Path(args.rocprofiler_sdk_tool_path).parent.parent
                    / "librocprofiler-sdk-rocattach.so"
                )
            )
            options.update({
                "ROCPROF_ATTACH_LIBRARY": rocprofiler_attach_library_path,
                "ROCPROF_ATTACH_PID": args.attach_pid,
            })

            if args.attach_duration_msec:
                options.update({
                    "ROCPROF_ATTACH_DURATION": args.attach_duration_msec,
                })

        if args.kokkos_trace:
            # NOTE: --kokkos-trace feature is incomplete and is disabled for now.
            console_error(
                "The option '--kokkos-trace' is not supported in the current "
                "version of rocprof-compute. This functionality is planned for a "
                "future release. Please adjust your profiling options accordingly."
            )
        # Kernel filtering
        if args.kernel:
            options["ROCPROF_KERNEL_FILTER_INCLUDE_REGEX"] = "|".join(args.kernel)

        # Dispatch filtering
        dispatch = []
        # rocprof sdk dispatch indexing is inclusive and starts from 1
        if args.dispatch:
            for dispatch_id in args.dispatch:
                if ":" in dispatch_id:
                    # 4:7 -> 4-7
                    start, end = dispatch_id.split(":")
                    dispatch.append(f"{start}-{end}")
                else:
                    # 4 -> 4
                    dispatch.append(f"{dispatch_id}")
        if dispatch:
            options["ROCPROF_KERNEL_FILTER_RANGE"] = f"[{','.join(dispatch)}]"
        if not args.attach_pid:
            options["APP_CMD"] = app_cmd
        return options

    # -----------------------
    # Required child methods
    # -----------------------
    @demarcate
    def pre_processing(self) -> None:
        """Perform any pre-processing steps prior to profiling."""
        super().pre_processing()

    @demarcate
    def run_profiling(self, version: str, prog: str) -> None:
        """Run profiling."""
        if self.get_args().roof_only:
            console_log("roofline", "Profiling roofline counters only.")

        # Log profiling options and setup filtering
        super().run_profiling(version, prog)

    @demarcate
    def post_processing(self) -> None:
        """Perform any post-processing steps prior to profiling."""
        super().post_processing()
