from enum import IntEnum
from typing import TypeVar
from .pdo_private_types import FixedPdoSourceStruct, FixedPdoSinkStruct, BatteryPdoStruct, VariablePdoStruct


class PdoTypeEnum(IntEnum):
    """
    Class `PdoTypeEnum` contains all possible variants of PDO types.
    """
    Fixed = 0
    Battery = 1
    Variable = 2
    Disabled = 3
    Mandatory = 4


class PdoSide(IntEnum):
    """
    Class `PdoTypeEnum` contains all possible variants of PDO side.
    """
    Sink = 0
    Source = 1


class PeakCurrent(IntEnum):
    """
    Class `PdoTypeEnum` contains all possible variants of peak current for PDO.
    """
    Percent_100 = 0
    Percent_110 = 1
    Percent_125 = 2
    Percent_150 = 3


class FixedPdoSource:

    """
    Class `FixedPdoSource` contains information about Fixed PDO on Source side.
    - Set and get maximum current `max_current`, type `int`.
    - Set and get voltage `voltage`, type `int`.
    - Set and get peak_current `peak_current`, type `PeakCurrent`.
    - Set and get dual data role flag `dual_data_role`, type `bool`.
    - Set and get usb communication flag `usb_communication`, type `bool`.
    - Set and get unconstrained power flag `unconstrained_power`, type `bool`.
    - Set and get higher capability flag `higher_capability`, type `bool`.
    - Set and get dual power role flag `dual_power_role`, type `bool`.
    - Get PDO type `pdo_type`, type `PdoTypeEnum`.
    """

    def __init__(self, pdo: FixedPdoSourceStruct = FixedPdoSourceStruct(0), disable=False):
        self.__max_current = pdo.max_current * 10
        self.__voltage = pdo.voltage * 50
        self.__peak_current = PeakCurrent(pdo.peak_current)
        self.__dual_data_role = pdo.dual_data_role
        self.__usb_communication = pdo.usb_communication_capable
        self.__unconstrained_power = pdo.externally_powered
        self.__higher_capability = pdo.usb_suspend_supported
        self.__dual_power_role = pdo.dual_power_role_capable
        self.__type = PdoTypeEnum.Disabled if disable else PdoTypeEnum.Fixed

    @property
    def max_current(self) -> int:
        """
        Returns maximum current.

        Returns:
            object of `int` type
        """
        return self.__max_current

    @max_current.setter
    def max_current(self, max_current: int):
        self.__max_current = max_current

    @property
    def voltage(self) -> int:
        """
        Returns voltage.

        Returns:
            object of `int` type
        """
        return self.__voltage

    @voltage.setter
    def voltage(self, voltage: int):
        self.__voltage = voltage

    @property
    def peak_current(self) -> PeakCurrent:
        """
        Returns peak current.

        Returns:
            object of `PeakCurrent` type
        """
        return self.__peak_current

    @peak_current.setter
    def peak_current(self, peak_current: PeakCurrent):
        self.__peak_current = peak_current

    @property
    def dual_data_role(self) -> bool:
        """
        Returns flag of dual data role.

        Returns:
            object of `bool` type
        """
        return bool(self.__dual_data_role)

    @dual_data_role.setter
    def dual_data_role(self, dual_data_role: bool):
        self.__dual_data_role = dual_data_role

    @property
    def usb_communication(self) -> bool:
        """
        Returns flag of usb communication.

        Returns:
            object of `bool` type
        """
        return bool(self.__usb_communication)

    @usb_communication.setter
    def usb_communication(self, usb_communication: bool):
        self.__usb_communication = usb_communication

    @property
    def unconstrained_power(self) -> bool:
        """
        Returns flag of unconstrained power.

        Returns:
            object of `bool` type
        """
        return bool(self.__unconstrained_power)

    @unconstrained_power.setter
    def unconstrained_power(self, unconstrained_power: bool):
        self.__unconstrained_power = unconstrained_power

    @property
    def higher_capability(self) -> bool:
        """
        Returns flag of higher capability.

        Returns:
            object of `bool` type
        """
        return bool(self.__higher_capability)

    @higher_capability.setter
    def higher_capability(self, higher_capability: bool):
        self.__higher_capability = higher_capability

    @property
    def dual_power_role(self) -> bool:
        """
        Returns flag of dual power role.

        Returns:
            object of `bool` type
        """
        return bool(self.__dual_power_role)

    @dual_power_role.setter
    def dual_power_role(self, dual_power_role: bool):
        self.__dual_power_role = dual_power_role

    @property
    def pdo_type(self) -> PdoTypeEnum:
        """
        Returns flag of PDO type.

        Returns:
            object of `PdoTypeEnum` type
        """
        return self.__type

    def __str__(self):
        return f"Max Current: {self.__max_current}\n" \
               f"Voltage: {self.__voltage}\n" \
               f"Peak Current: {self.__peak_current.name}\n" \
               f"Dual Role: {'Yes' if self.__dual_data_role else 'No'}\n" \
               f"USB Communication: {'Yes' if self.__usb_communication else 'No'}\n" \
               f"Unconstrained Power: {'Yes' if self.__unconstrained_power else 'No'}\n" \
               f"Higher capability: {'Yes' if self.__higher_capability else 'No'}\n" \
               f"Dual Power role: {'Yes' if self.__dual_power_role else 'No'}\n"


class VariablePdo:

    """
    Class `VariablePdo` contains information about Fixed PDO on Sink and Source side.
    - Set and get maximum current `max_current`, type `int`.
    - Set and get minimum voltage `min_voltage`, type `int`.
    - Set and get maximum voltage `max_voltage`, type `bool`.
    - Get PDO type `pdo_type`, type `PdoTypeEnum`.
    """

    def __init__(self, pdo: VariablePdoStruct = VariablePdoStruct(0)):
        self.__max_current = pdo.max_current * 10
        self.__min_voltage = pdo.min_voltage * 50
        self.__max_voltage = pdo.max_voltage * 50
        self.__type = PdoTypeEnum.Variable

    @property
    def max_current(self) -> int:
        """
        Returns maximum current.

        Returns:
            object of `int` type
        """
        return self.__max_current

    @max_current.setter
    def max_current(self, max_current: int):
        self.__max_current = max_current

    @property
    def min_voltage(self) -> int:
        """
        Returns minimum voltage.

        Returns:
            object of `int` type
        """
        return self.__min_voltage

    @min_voltage.setter
    def min_voltage(self, min_voltage: int):
        self.__min_voltage = min_voltage

    @property
    def max_voltage(self) -> int:
        """
        Returns maximum voltage.

        Returns:
            object of `int` type
        """
        return self.__max_voltage

    @max_voltage.setter
    def max_voltage(self, max_voltage: int):
        self.__max_voltage = max_voltage

    @property
    def pdo_type(self) -> PdoTypeEnum:
        """
        Returns flag of PDO type.

        Returns:
            object of `PdoTypeEnum` type
        """
        return self.__type

    def __str__(self):
        return f"Max Current: {self.__max_current}\n" \
               f"Min Voltage: {self.__min_voltage}\n" \
               f"Max Voltage: {self.__max_voltage}"


class BatteryPdo:

    """
    Class `BatteryPdo` contains information about Fixed PDO on Sink and Source side.
    - Set and get maximum power `max_power`, type `int`.
    - Set and get minimum voltage `min_voltage`, type `int`.
    - Set and get maximum voltage `max_voltage`, type `bool`.
    - Get PDO type `pdo_type`, type `PdoTypeEnum`.
    """

    def __init__(self, pdo: BatteryPdoStruct = BatteryPdoStruct(0)):
        self.__max_power = pdo.max_power * 250
        self.__min_voltage = pdo.min_voltage * 50
        self.__max_voltage = pdo.max_voltage * 50
        self.__type = PdoTypeEnum.Battery

    @property
    def max_power(self) -> int:
        """
        Returns maximum power.

        Returns:
            object of `int` type
        """
        return self.__max_power

    @max_power.setter
    def max_power(self, max_power: int):
        self.__max_power = max_power

    @property
    def min_voltage(self) -> int:
        """
        Returns minimum voltage.

        Returns:
            object of `int` type
        """
        return self.__min_voltage

    @min_voltage.setter
    def min_voltage(self, min_voltage: int):
        self.__min_voltage = min_voltage

    @property
    def max_voltage(self) -> int:
        """
        Returns maximum voltage.

        Returns:
            object of `int` type
        """
        return self.__max_voltage

    @max_voltage.setter
    def max_voltage(self, max_voltage: int):
        self.__max_voltage = max_voltage

    @property
    def pdo_type(self) -> PdoTypeEnum:
        """
        Returns flag of PDO type.

        Returns:
            object of `PdoTypeEnum` type
        """
        return self.__type

    def __str__(self):
        return f"Max power: {self.__max_power}\n" \
               f"Min Voltage: {self.__min_voltage}\n" \
               f"Max Voltage: {self.__max_voltage}"


class FixedPdoSink:

    """
    Class `FixedPdoSink` contains information about Fixed PDO on Sink side.
    - Set and get maximum current `max_current`, type `int`.
    - Set and get voltage `voltage`, type `int`.
    - Set and get dual data role flag `dual_data_role`, type `bool`.
    - Set and get usb communication flag `usb_communication`, type `bool`.
    - Set and get unconstrained power flag `unconstrained_power`, type `bool`.
    - Set and get higher capability flag `higher_capability`, type `bool`.
    - Set and get dual power role flag `dual_power_role`, type `bool`.
    - Get PDO type `pdo_type`, type `PdoTypeEnum`.
    """

    def __init__(self, pdo: FixedPdoSinkStruct = FixedPdoSinkStruct(0), disable=False):
        self.__oper_current = pdo.oper_current * 10
        self.__voltage = pdo.voltage * 50
        self.__dual_data_role = bool(pdo.dual_data_role)
        self.__usb_communication = bool(pdo.usb_communication_capable)
        self.__unconstrained_power = bool(pdo.externally_powered)
        self.__higher_capability = bool(pdo.usb_suspend_supported)
        self.__dual_power_role = bool(pdo.dual_power_role_capable)
        self.__type = PdoTypeEnum.Disabled if disable else PdoTypeEnum.Fixed

    @property
    def oper_current(self) -> int:
        """
        Returns operation current.

        Returns:
            object of `int` type
        """
        return self.__oper_current

    @oper_current.setter
    def oper_current(self, oper_current: int):
        self.__oper_current = oper_current

    @property
    def voltage(self) -> int:
        """
        Returns voltage.

        Returns:
            object of `int` type
        """
        return self.__voltage

    @voltage.setter
    def voltage(self, voltage: int):
        self.__voltage = voltage

    @property
    def dual_data_role(self) -> bool:
        """
        Returns flag of dual data role.

        Returns:
            object of `bool` type
        """
        return bool(self.__dual_data_role)

    @dual_data_role.setter
    def dual_data_role(self, dual_data_role: bool):
        self.__dual_data_role = dual_data_role

    @property
    def usb_communication(self) -> bool:
        """
        Returns flag of usb communication.

        Returns:
            object of `bool` type
        """
        return bool(self.__usb_communication)

    @usb_communication.setter
    def usb_communication(self, usb_communication: bool):
        self.__usb_communication = usb_communication

    @property
    def unconstrained_power(self) -> bool:
        """
        Returns flag of unconstrained power.

        Returns:
            object of `bool` type
        """
        return bool(self.__unconstrained_power)

    @unconstrained_power.setter
    def unconstrained_power(self, unconstrained_power: bool):
        self.__unconstrained_power = unconstrained_power

    @property
    def higher_capability(self) -> bool:
        """
        Returns flag of higher capability.

        Returns:
            object of `bool` type
        """
        return bool(self.__higher_capability)

    @higher_capability.setter
    def higher_capability(self, higher_capability: bool):
        self.__higher_capability = higher_capability

    @property
    def dual_power_role(self) -> bool:
        """
        Returns flag of dual power role.

        Returns:
            object of `bool` type
        """
        return bool(self.__dual_power_role)

    @dual_power_role.setter
    def dual_power_role(self, dual_power_role: bool):
        self.__dual_power_role = dual_power_role

    @property
    def pdo_type(self) -> PdoTypeEnum:
        """
        Returns flag of PDO type.

        Returns:
            object of `PdoTypeEnum` type
        """
        return self.__type

    def __str__(self):
        return f"Oper Current: {self.__oper_current}\n" \
               f"Voltage: {self.__voltage}\n" \
               f"Dual Role: {'Yes' if self.__dual_data_role else 'No'}\n" \
               f"USB Communication: {'Yes' if self.__usb_communication else 'No'}\n" \
               f"Unconstrained Power: {'Yes' if self.__unconstrained_power else 'No'}\n" \
               f"Higher capability: {'Yes' if self.__higher_capability else 'No'}\n" \
               f"Dual Power role: {'Yes' if self.__dual_power_role else 'No'}\n"


PdoType = TypeVar("PdoType",
                  FixedPdoSink,
                  FixedPdoSource,
                  BatteryPdo,
                  VariablePdo)
