from __future__ import annotations

from enum import Enum
from typing import Union


class InfoDatasetBase(Enum):
    """
    This enum class is the base class for the UnitInfo, BuildingInfo, HeroInfo and OtherInfo datasets. It provides the
    properties and functions common to all the mentioned classes

    **Methods**
    
    >>> InfoDatasetBase.from_id()
    >>> InfoDatasetBase.from_dead_id()
    >>> InfoDatasetBase.from_icon_id()
    >>> InfoDatasetBase.from_hotkey_id()
    >>> InfoDatasetBase.gaia_only()
    >>> InfoDatasetBase.non_gaia()
    """

    @staticmethod
    def _id_map() -> dict:
        """
        **Private Method**

        Returns:
            A dict that maps the names of the properties to their index in the info tuple
        """
        return {'id': 0, 'icon_id': 1, 'dead_id': 2, 'hotkey_id': 3, 'gaia_only': 4}

    def _get_property(self, name: str) -> Union[int, bool]:
        """
        **Private Method**

        Get the specified property by its name from an object of this (or its derived) class

        Args:
            name: the name of the property to get

        Returns:
        """
        return self.value[self._id_map()[name]]

    @property
    def ID(self) -> int:
        """
        Returns:
            The ID of the specified unit
        """
        return self._get_property('id')

    @property
    def ICON_ID(self) -> int:
        """
        Returns:
            The Icon ID of the specified unit
        """
        return self._get_property('icon_id')

    @property
    def DEAD_ID(self) -> int:
        """
        Returns:
            The Dead Unit ID of the specified unit
        """
        return self._get_property('dead_id')

    @property
    def HOTKEY_ID(self) -> int:
        """
        Returns:
            The HotKey ID of the specified unit
        """
        return self._get_property('hotkey_id')

    @property
    def IS_GAIA_ONLY(self) -> bool:
        """
        Returns:
            A boolean value indicating if the specified unit is a gaia only unit (eg. Deer)
        """
        return self._get_property('gaia_only')

    @classmethod
    def _from_id(cls, id_type: str, value: int) -> InfoDatasetBase:
        """
        **Private Method**

        This function finds and returns the member object that uses the given value for the specified property (id_type)

        Args:
            id_type: the property of member objects that should match the value specified
            value: the value of the property to search for

        Returns:
            An InfoDatasetBase member object which uses the given value for the specified property (id_type
        """
        index = cls._id_map()[id_type]

        if type(value) is not int:
            raise TypeError(f"from_id expected int, got {type(value)}")
        if value < 0:
            raise ValueError(f"{value} is not a valid id value")

        for member in cls._member_map_.values():
            if member.value[index] == value:
                return member

        raise KeyError(f"A unit with {id_type} = {value} was not found in the dataset")

    @classmethod
    def from_id(cls, unit_id: int) -> InfoDatasetBase:
        """
        This function finds and returns the unit with the given unit ID

        Args:
            unit_id: the unit ID to search for

        Returns:
            A unit with the given unit I
        """
        return cls._from_id('id', unit_id)

    @classmethod
    def from_icon_id(cls, icon_id: int) -> InfoDatasetBase:
        """
        This function finds and returns the unit with the given icon ID

        Args:
            icon_id: the icon ID to search for

        Returns:
            A unit with the given icon ID
        """
        return cls._from_id('icon_id', icon_id)

    @classmethod
    def from_dead_id(cls, dead_id: int) -> InfoDatasetBase:
        """
        This function finds and returns the unit with the given dead unit ID

        Args:
            dead_id: the dead unit ID to search for

        Returns:
            A unit with the given dead unit ID
        """
        return cls._from_id('dead_id', dead_id)

    @classmethod
    def from_hotkey_id(cls, hotkey_id: int) -> InfoDatasetBase:
        """
        This function finds and returns the unit with the given hotkey ID. Note that there may be multiple units that
        use the same hotkey ID, currently only one is returned!

        Args:
            hotkey_id: the hotkey ID to search for

        Returns:
            A unit with the given hotkey ID
        """
        return cls._from_id('hotkey_id', hotkey_id)

    @classmethod
    def gaia_only(cls) -> list[InfoDatasetBase]:
        """
        Returns:
            A list of all the gaia only units (eg. Deer)
        """
        return cls._gaia_filter(gaia_only=True)

    @classmethod
    def non_gaia(cls) -> list[InfoDatasetBase]:
        """
        Returns:
            A list of all the units excluding gaia only units (eg. Deer)
        """
        return cls._gaia_filter(gaia_only=False)

    @classmethod
    def _gaia_filter(cls, gaia_only: bool) -> list[InfoDatasetBase]:
        """
        **Private Method**

        Args:
            gaia_only: if set to true, lists all gaia only units. If set to false, lists all units except gaia only units

        Returns:
            A list of either all gaia only units or a list of all units excluding gaia only units
        """
        result = []
        for x in cls:
            if x.IS_GAIA_ONLY == gaia_only:
                result.append(x)
        return result
