"""Sensor Read Sensors request message."""

from typing import List, Optional, Union
from iqrfpy.enums.commands import SensorRequestCommands
from iqrfpy.enums.peripherals import Standards
from iqrfpy.exceptions import RequestParameterInvalidValueError
from iqrfpy.utils.common import Common
import iqrfpy.utils.dpa as dpa_constants
from iqrfpy.irequest import IRequest
from iqrfpy.peripherals.sensor.requests.sensor_written_data import SensorWrittenData

__all__ = [
    'ReadSensorsRequest',
]


class ReadSensorsRequest(IRequest):
    """Sensor Read Sensors request class."""

    __slots__ = '_sensors', '_written_data'

    def __init__(self, nadr: int, sensors: Optional[List[int]] = None,
                 written_data: Optional[List[SensorWrittenData]] = None, hwpid: int = dpa_constants.HWPID_MAX,
                 dpa_rsp_time: Optional[float] = None, dev_process_time: Optional[float] = None,
                 msgid: Optional[str] = None):
        """Read Sensors request constructor.

        Args:
            nadr (int): Device address.
            sensors (List[int], optional): List of sensor indexes to read. If left empty, only the first sensor is read.
            written_data (List[SensorWrittenData], optional): Data to write to sensors.
            hwpid (int, optional): Hardware profile ID. Defaults to 65535 (Ignore HWPID check).
            dpa_rsp_time (float, optional): DPA request timeout in seconds. Defaults to None.
            dev_process_time (float, optional): Device processing time. Defaults to None.
            msgid (str, optional): JSON API message ID. Defaults to None. If the parameter is not specified, a random
                UUIDv4 string is generated and used.
        """
        self._validate_sensors(sensors)
        super().__init__(
            nadr=nadr,
            pnum=Standards.SENSOR,
            pcmd=SensorRequestCommands.READ_SENSORS,
            m_type=None,
            hwpid=hwpid,
            dpa_rsp_time=dpa_rsp_time,
            dev_process_time=dev_process_time,
            msgid=msgid
        )
        self._sensors = sensors
        self._written_data = written_data

    @staticmethod
    def _validate_sensors(sensors: Optional[List[int]] = None):
        """Validate sensors parameter.

        Args:
            sensors (List[int], optional): List of sensor indexes to read. If left empty, only the first sensor is read.
        Raises:
            RequestParameterInvalidValueError: If sensors contains more than 32 values or if values are not
                in range from 0 to 31.
        """
        if sensors is not None:
            if len(sensors) == 0:
                return
            if len(sensors) > 32:
                raise RequestParameterInvalidValueError('Sensors length should be at most 32 values.')
            if min(sensors) < 0 or max(sensors) > 31:
                raise RequestParameterInvalidValueError('Sensors values should be between 0 and 31.')

    @property
    def sensors(self) -> Optional[List[int]]:
        """:obj:`list` of :obj:`int` or :obj:`None`: List of sensor indexes to read.
            If left empty, only the first sensor is read.

        Getter and setter.
        """
        return self._sensors

    @sensors.setter
    def sensors(self, value: Optional[List[int]] = None):
        self._validate_sensors(value)
        self._sensors = value

    @property
    def written_data(self) -> Optional[List[SensorWrittenData]]:
        """:obj:`list` of :obj:`SensorWrittenData` or :obj:`None`: Data to write to sensors.

        Getter and setter.
        """
        return self._written_data

    @written_data.setter
    def written_data(self, value: List[SensorWrittenData]):
        self._written_data = value

    def to_dpa(self, mutable: bool = False) -> Union[bytes, bytearray]:
        """DPA request serialization method.

        Args:
            mutable (bool, optional): Serialize into mutable byte representation of DPA request packet.
                Defaults to False.

        Returns:
            :obj:`bytes`: Immutable byte representation of DPA request packet.\n
            :obj:`bytearray`: Mutable byte representation of DPA request packet (if argument mutable is True).
        """
        if self._sensors is not None:
            written_data = []
            if self._written_data is not None:
                for data in self._written_data:
                    written_data += data.to_pdata()
            self._pdata = Common.indexes_to_4byte_bitmap(self._sensors) + written_data
        else:
            self._pdata = None
        return super().to_dpa(mutable=mutable)

    def to_json(self) -> dict:
        """JSON API request serialization method.

        Returns:
            :obj:`dict`: JSON API request object.
        Raises:
            NotImplementedError: Message type not implemented.
        """
        raise NotImplementedError('JSON API request not implemented.')
