# SPDX-License-Identifier: MIT

import os
from pyudev import Context

from amd_debug.common import read_file


class WakeGPIO:
    """Class for wake GPIOs"""

    def __init__(self, num):
        self.num = int(num)
        self.name = ""

    def __str__(self):
        if self.name:
            return f"{self.num} ({self.name})"
        return f"{self.num}"


class WakeIRQ:
    """Class for wake IRQs"""

    def __init__(self, num, context=Context()):
        self.num = num
        p = os.path.join("/", "sys", "kernel", "irq", str(num))
        try:
            self.chip_name = read_file(os.path.join(p, "chip_name"))
        except (PermissionError, FileNotFoundError):
            self.chip_name = ""
        try:
            self.actions = read_file(os.path.join(p, "actions"))
        except (PermissionError, FileNotFoundError):
            self.actions = ""
        self.driver = ""
        self.name = ""
        try:
            wakeup = read_file(os.path.join(p, "wakeup"))
        except (PermissionError, FileNotFoundError):
            wakeup = ""

        # This is an IRQ tied to _AEI
        if self.chip_name == "amd_gpio":
            try:
                hw_gpio = read_file(os.path.join(p, "hwirq"))
                self.name = f"GPIO {hw_gpio}"
            except (PermissionError, FileNotFoundError):
                self.name = "GPIO (unknown)"
        # legacy IRQs
        elif "IR-IO-APIC" in self.chip_name:
            if self.actions == "acpi":
                self.name = "ACPI SCI"
            elif self.actions == "i8042":
                self.name = "PS/2 controller"
            elif self.actions == "pinctrl_amd":
                self.name = "GPIO Controller"
            elif self.actions == "rtc0":
                self.name = "RTC"
            elif self.actions == "timer":
                self.name = "Timer"
            self.actions = ""
        elif "PCI-MSI" in self.chip_name:
            bdf = self.chip_name.split("-")[-1]
            for dev in context.list_devices(subsystem="pci"):
                if dev.device_path.endswith(bdf):
                    vendor = dev.properties.get("ID_VENDOR_FROM_DATABASE")
                    desc = dev.properties.get("ID_PCI_CLASS_FROM_DATABASE")
                    if not desc:
                        desc = dev.properties.get("ID_PCI_INTERFACE_FROM_DATABASE")
                    name = dev.properties.get("PCI_SLOT_NAME")
                    self.driver = dev.properties.get("DRIVER")
                    self.name = f"{vendor} {desc} ({name})"
                    break

        # "might" look like an ACPI device, try to follow it
        if not self.name and self.actions:
            # self.actions is a free-form string supplied to request_irq() by
            # the kernel, so restrict it to a single path component to avoid
            # escaping the ACPI device directory via "/" or "..".
            comp = os.path.basename(self.actions)
            if comp and comp not in (".", ".."):
                p = os.path.join("/", "sys", "bus", "acpi", "devices", comp)
                if os.path.exists(p):
                    for directory in os.listdir(p):
                        if "physical_node" not in directory:
                            continue

                        for root, _dirs, files in os.walk(
                            os.path.join(p, directory), followlinks=False
                        ):
                            if "name" in files:
                                try:
                                    self.name = read_file(os.path.join(root, "name"))
                                except (PermissionError, FileNotFoundError):
                                    pass
                                t = os.path.join(root, "driver")
                                if os.path.exists(t):
                                    try:
                                        self.driver = os.path.basename(
                                            os.readlink(t)
                                        )
                                    except (PermissionError, FileNotFoundError, OSError):
                                        pass
                                break
                        if self.name:
                            break

        # If the name isn't descriptive try to guess further
        if self.driver and self.actions == self.name:
            if self.driver == "i2c_hid_acpi":
                self.name = f"{self.name} I2C HID device"

        # check if it's disabled
        if not self.name and wakeup == "disabled":
            self.name = "Disabled interrupt"

    def __str__(self):
        actions = f" ({self.actions})" if self.actions else ""
        return f"{self.name}{actions}"


if __name__ == "__main__":
    from tabulate import tabulate

    pyudev = Context()

    p = os.path.join("/sys", "kernel", "irq")
    irqs = []
    for d in os.listdir(p):
        if os.path.isdir(os.path.join(p, d)):
            w = WakeIRQ(d, pyudev)
            irqs.append([int(d), str(WakeIRQ(d, pyudev))])
    irqs.sort()
    print(tabulate(irqs, tablefmt="fancy_grid"))
