from UniTAP.libs.lib_tsi.tsi_io import PortIO
from UniTAP.libs.lib_tsi.tsi import *
from .cec_types import *
from .cec_private_types import *
from typing import Union


class CecRx:

    def __init__(self, port_io: PortIO):
        self.__io = port_io
        self.__version = self.__read_version()
        self.__commands = []

    @property
    def version(self) -> (int, int):
        """
        Read CEC version from the RX side.

        Returns:
            object of tuple of `int` type
        """
        return self.__version

    def enable(self, state: bool):
        """
        Enable/Disable CEC.

        Args:
            state (`bool`)
        """
        self.__io.set(TSI_HDRX_CEC_CONTROL, state, c_uint32)

    def is_enabled(self) -> bool:
        """
        Return state of CEC (enabled or not).

        Returns:
            object of `bool` type
        """
        return self.__io.get(TSI_HDRX_CEC_CONTROL, c_uint32)[1] & 0x1 == 1

    @property
    def logical_address(self) -> LogicalAddressEnum:
        """
        Set/Get CEC logical address.

        Returns:
            object of `LogicalAddressEnum` type
        """
        return LogicalAddressEnum(self.__io.get(TSI_HDRX_CEC_LOGICAL_ADDRESS, c_uint32)[1])

    @logical_address.setter
    def logical_address(self, address: LogicalAddressEnum):
        self.__io.set(TSI_HDRX_CEC_LOGICAL_ADDRESS, address.value, c_uint32)

    @property
    def destination(self) -> LogicalAddressEnum:
        """
        Set/Get CEC destination address.

        Returns:
            object of `LogicalAddressEnum` type
        """
        return LogicalAddressEnum(self.__io.get(TSI_HDRX_CEC_DESTINATION, c_uint32)[1])

    @destination.setter
    def destination(self, address: LogicalAddressEnum):
        self.__io.set(TSI_HDRX_CEC_DESTINATION, address.value, c_uint32)

    @property
    def phy_address(self) -> int:
        """
        Set/Get CEC physical address.

        Returns:
            object of `int` type
        """
        return self.__io.get(TSI_HDRX_CEC_PHYSICAL_ADDRESS, c_uint32)[1]

    @phy_address.setter
    def phy_address(self, address: int):
        self.__io.set(TSI_HDRX_CEC_PHYSICAL_ADDRESS, address, c_uint32)

    @property
    def op_code(self) -> int:
        """
        Set/Get CEC operation code.

        Returns:
            object of `int` type
        """
        return self.__io.get(TSI_HDRX_CEC_OP_CODE, c_uint32)[1]

    @op_code.setter
    def op_code(self, code: int):
        self.__io.set(TSI_HDRX_CEC_OP_CODE, code, c_uint32)

    @property
    def op_code_param(self) -> Union[int, list]:
        """
        Set/Get CEC additional parameter for operation code.

        Returns:
            object of `int` type
        """
        return self.__io.get(TSI_HDRX_CEC_OP_CODE_PARAM, c_uint32)[1]

    @op_code_param.setter
    def op_code_param(self, code_param: Union[int, list]):
        data_count = len(code_param) if isinstance(code_param, list) else 1
        self.__io.set(TSI_HDRX_CEC_OP_CODE_PARAM, code_param, c_uint32, data_count=data_count)

    @property
    def device_type(self) -> DeviceType:
        """
        Set/Get CEC Device type.

        Returns:
            object of `DeviceType` type
        """
        data = self.__io.get(TSI_HDRX_CEC_DEVICE_TYPE, DeviceTypePrivate)[1]
        return DeviceType(data.cecSwitch, data.audioSystem, data.pbDevice, data.tuner, data.recDev, data.tv)

    @device_type.setter
    def device_type(self, dev_type: Union[DeviceTypeEnum, DeviceType, int]):
        if isinstance(dev_type, DeviceType):
            dev_type_value = DeviceTypePrivate()
            dev_type_value.value = dev_type
            value = dev_type_value.value
        elif isinstance(dev_type, DeviceTypeEnum):
            value = dev_type.value
        elif isinstance(dev_type, int):
            value = dev_type & 0xFF
        else:
            raise TypeError(f"Unsupported type: {type(dev_type)}")
        self.__io.set(TSI_HDRX_CEC_DEVICE_TYPE, value, c_uint32)

    @property
    def status(self) -> CECStatus:
        """
        Get CEC status.

        Returns:
            object of `CECStatus` type
        """
        data = self.__io.get(TSI_HDRX_CEC_STATUS_R, CECStatusPrivate)[1]
        return CECStatus(data.bufferOverflowed)

    @property
    def command_number(self) -> int:
        """
        Get CEC command number.

        Returns:
            object of `int` type
        """
        return self.__io.get(TSI_HDRX_CEC_RECEIVED_CNT_R, c_uint32)[1]

    @property
    def data(self) -> list:
        """
        Get CEC raw data.

        Returns:
            object of `list` type
        """
        data_size = self.__io.get(TSI_HDRX_CEC_DATA_SIZE_R, c_uint32)[1]
        return self.__io.get(TSI_HDRX_CEC_DATA_RECIEVED, c_uint32, data_size)[1]

    def reset_data(self, command: CecResetDataCommand):
        """
        Get CEC reset data.

        Returns:
            object of `CecResetDataCommand` type
        """
        self.__io.set(TSI_HDRX_CEC_DATA_RESET_W, command.value, c_uint32)

    def __read_version(self) -> (int, int):
        version = self.__io.get(TSI_HDRX_CEC_VERSION_R, CECVersion)[1]
        return version.minor, version.major

    def add_command(self, command: CECCommand = CECCommand()):
        """
        Add CEC command in list.

        Args:
            command (`CECCommand`)
        """
        self.__commands.append(command)

    def delete_command(self, index: int = 0):
        """
        Delete CEC command in list by index.

        Args:
            index (`int`)
        """
        self.__commands.pop(index)

    def send_command(self, logical_address: LogicalAddressEnum = LogicalAddressEnum.Tv,
                     destination: LogicalAddressEnum = LogicalAddressEnum.Broadcast,
                     phy_address: int = 0x0, op_code: int = 0x0, op_code_param: Union[int, list] = 0x0,
                     device_type: Union[DeviceTypeEnum, DeviceType, int] = DeviceTypeEnum.TV):
        """
        Send CEC command with the transferred parameters.

        Args:
            logical_address (`LogicalAddressEnum`)
            destination (`LogicalAddressEnum`)
            phy_address (`int`)
            op_code (`int`)
            op_code_param (`int`)
            device_type (`DeviceTypeEnum` | `DeviceType`)
        """
        self.enable(True)
        self.logical_address = logical_address
        self.phy_address = phy_address
        self.op_code = op_code
        self.op_code_param = op_code_param
        self.device_type = device_type
        self.destination = destination
        self.__send_cec_command()

    def send_command_by_index(self, index: int):
        """
        Send CEC command from the list by index.

        Args:
            index (`int`)
        """
        if len(self.__commands) > 0 and 0 <= index < len(self.__commands):
            self.__send_command(self.__commands[index])

    def send_commands(self):
        """
        Send all saved commands.
        """
        for command in self.__commands:
            self.__send_command(command)

    def __send_command(self, command: CECCommand = CECCommand()):
        self.logical_address = command.logical_address
        self.destination = command.destination
        self.phy_address = command.phy_address
        self.op_code = command.op_code
        self.op_code_param = command.op_code_param
        self.device_type = command.device_type
        self.__send_cec_command()

    def __send_cec_command(self):
        self.__io.set(TSI_HDRX_CEC_COMMAND, 1, c_uint32)

    @property
    def commands(self) -> list:
        """
        Get CEC command list.

        Returns:
            object of `list` type
        """
        return self.__commands

    def clear_commands(self):
        """
        Clear saved commands.
        """
        self.__commands.clear()
