"""
This module is intended to distributed as part of automatically generated code by the PeakRDL
Python tool. It provides a set of classes used by the autogenerated code to represent register
"""
from typing import List, Union

from .base import Node
from .callbacks import CallbackSet


class Reg(Node):
    """
    base class of register wrappers

    Note:
        It is not expected that this class will be instantiated under normal
        circumstances however, it is useful for type checking
    """

    __slots__ : List[str] = ['__width', '__accesswidth']

    # pylint: disable=too-many-arguments, duplicate-code
    def __init__(self,
                 callbacks: CallbackSet,
                 address: int,
                 width: int,
                 accesswidth: int,
                 logger_handle: str,
                 inst_name: str):

        super().__init__(callbacks=callbacks,
                         address=address,
                         logger_handle=logger_handle,
                         inst_name=inst_name)

        self.__width = width
        self.__accesswidth = accesswidth
    # pylint: enable=too-many-arguments, duplicate-code

    @property
    def max_value(self) -> int:
        """maximum unsigned integer value that can be stored in the register

        For example:

        * 8-bit register returns 0xFF (255)
        * 16-bit register returns 0xFFFF (65535)
        * 32-bit register returns 0xFFFF_FFFF (4294967295)

        """
        return (2 ** self.width) - 1

    @property
    def width(self) -> int:
        """
        The width of the register in bits, this uses the `regwidth` systemRDL property

        Returns: register width

        """
        return self.__width

    @property
    def accesswidth(self) -> int:
        """
        The access width of the register in bits, this uses the `accesswidth` systemRDL property

        Returns: register access width
        """
        return self.__accesswidth


class RegReadOnly(Reg):
    """
    class for a read only register

    Args:
        callbacks: set of callback to be used for accessing the hardware or simulator
        address: address of the register
        width: width of the register in bits
        accesswidth: minimum access width of the register in bits
        logger_handle: name to be used logging messages associate with this
            object

    """

    __slots__ : List[str] = []

    def read(self) -> int:
        """Read value from the register

        Returns:
            The value from register

        """
        return self._callbacks.read_callback(addr=self.address,
                                             width=self.width,
                                             accesswidth=self.accesswidth)


class RegWriteOnly(Reg):
    """
    class for a write only register
    """

    __slots__ : List[str] = []

    def write(self, data: int) -> None:
        """Writes a value to the register

        Args:
            data: data to be written

        Raises:
            ValueError: if the value provided is outside the range of the
                permissible values for the register
            TypeError: if the type of data is wrong
        """
        if not isinstance(data, int):
            raise TypeError(f'data should be an int got {type(data)}')

        if data > self.max_value:
            raise ValueError('data out of range')

        if data < 0:
            raise ValueError('data out of range')

        self._logger.info('Writing data:%X to %X', data, self.address)

        self._callbacks.write_callback(addr=self.address,
                                       width=self.width,
                                       accesswidth=self.accesswidth,
                                       data=data)


class RegReadWrite(RegReadOnly, RegWriteOnly):
    """
    class for a read and write only register

    """
    __slots__ : List[str] = []

ReadableRegister = Union[RegReadOnly, RegReadWrite]
WriteableRegister = Union[RegWriteOnly, RegReadWrite]
