# CHIPSEC: Platform Security Assessment Framework
# Copyright (c) 2024, Intel Corporation
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; Version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# Contact information:
# chipsec@intel.com
#

"""
Device interface module.

This module provides functionality to access and manage device configurations
in the CHIPSEC framework, including PCI devices and I/O spaces.
"""

from typing import Any, List, Optional, Tuple
from chipsec.cfg.parsers.ip.pci_device import PCIConfig
from chipsec.library.register import ObjList


class Device:
    """
    Device interface for platform device definitions.

    Provides methods to access and query device configurations,
    particularly PCI devices and I/O space configurations.
    """

    def __init__(self, cs: Any) -> None:
        """
        Initialize the Device interface.

        Args:
            cs: Chipset interface object
        """
        self.cs = cs

    def get_instance_by_name(self, device_name: str,
                             instance: Any) -> Optional[PCIConfig]:
        """
        Get a specific device instance by name and instance identifier.

        Args:
            device_name: Name of the device
            instance: Instance identifier to retrieve

        Returns:
            Device instance if found, None otherwise
        """
        devlist = self.get_list_by_name(device_name)
        for dev in devlist:
            if instance in dev.instances:
                return dev.instances[instance]
        return None

    def get_list_by_name(self, device_name: str) -> List[Any]:
        """
        Get list of enabled device objects by name.

        Args:
            device_name: Name of the device to retrieve objects for

        Returns:
            List of enabled device objects matching the name
        """
        objList = self._get_defined_list(device_name)
        return objList.filter_enabled()

    def _get_defined_list(self, device_name: str) -> List[Any]:
        """
        Get list of all defined device objects by name.

        Args:
            device_name: Name of the device to retrieve objects for

        Returns:
            List of all defined device objects matching the name
        """
        devices = self.cs.Cfg.get_objlist(device_name)
        objlist = ObjList()
        [objlist.extend(ip.obj) for ip in devices]
        return objlist

    def is_defined(self, device_name: str) -> bool:
        """
        Check if a device is defined in the configuration.

        Args:
            device_name: Name of the device to check

        Returns:
            True if device is defined, False otherwise
        """
        return len(self._get_defined_list(device_name)) > 0

    def get_bus(self, device_name: str) -> List[int]:
        """
        Retrieve bus value(s) from PCI device instances.

        Args:
            device_name: Name of the PCI device

        Returns:
            List of bus numbers for all instances of the device
        """
        dev_list = self.get_list_by_name(device_name)
        buses = []
        for dev in dev_list:
            for instance_key in dev.instances.keys():
                buses.append(dev.instances[instance_key].bus)
        return buses

    def get_IO_space(self, io_name: str) -> Tuple[Optional[str],
                                                  Optional[str]]:
        """
        Retrieve BAR values for given I/O range.

        Args:
            io_name: Name of the I/O space

        Returns:
            Tuple of (register_name, base_field_name) if found,
            (None, None) otherwise
        """
        if io_name in self.cs.Cfg.IO_BARS:
            reg = self.cs.Cfg.IO_BARS[io_name].get('register')
            bf = self.cs.Cfg.IO_BARS[io_name].get('base_field')
            return (reg, bf)
        else:
            return (None, None)
