"""Insteon device address class."""
import logging

from .constants import HC_LOOKUP
from .utils import (
    byte_to_housecode,
    byte_to_unitcode,
    housecode_to_byte,
    unitcode_to_byte,
)

_LOGGER = logging.getLogger(__name__)


def create(housecode: str, unitcode: int):
    """Create an X10 device address."""
    if housecode.lower() in HC_LOOKUP.keys():
        byte_housecode = housecode_to_byte(housecode)
    else:
        if isinstance(housecode, str):
            str_error = "X10 house code invalid: {}".format(housecode)
        else:
            str_error = "X10 house code is not a string"
            raise ValueError(str_error)

    # 20, 21 and 22 for All Units Off, All Lights On and All Lights Off
    # 'fake' units
    if unitcode in range(1, 17) or unitcode in range(20, 23):
        byte_unitcode = unitcode_to_byte(unitcode)
    else:
        if isinstance(unitcode, int):
            str_error = "X10 unit code error: {}".format(unitcode)
        else:
            str_error = "X10 unit code is not an integer 1 - 16"
            raise ValueError(str_error)

    addr = X10Address(bytearray([byte_housecode, byte_unitcode]))
    return addr


class X10Address:
    """Datatype definition for an X10 device address."""

    def __init__(self, housecode_unitcode: bytearray):
        """Create an X10 device address."""
        if isinstance(housecode_unitcode, X10Address):
            housecode_unitcode = bytes(housecode_unitcode)
        if len(housecode_unitcode) != 2:
            raise ValueError("housecode_unitcode must be 2 bytes")
        self._housecode_byte = housecode_unitcode[0]
        self._unitcode_byte = housecode_unitcode[1]
        valid = self._check_housecode_unitcode()
        if not valid:
            raise ValueError("Invalid housecode or unitcode byte")

    def __repr__(self):
        """Return the string representation of an X10 device address."""
        return "x10{}{}".format(
            byte_to_housecode(self._housecode_byte),
            byte_to_unitcode(self._unitcode_byte),
        )

    def __str__(self):
        """Return the string representation of an X10 device address."""
        str_rep = f"X10.{self.housecode}.{self.unitcode:02d}"
        return str(str_rep)

    def __bytes__(self):
        """Return the byte representation of an X10 address."""
        return bytes(bytearray([self._housecode_byte, self._unitcode_byte]))

    def __eq__(self, other):
        """Test if two X10 addresses are equal."""
        if isinstance(other, X10Address):
            return bytes(self) == bytes(other)
        return False

    def __getitem__(self, byte):
        """Return the housecode or unitcode bytes."""
        if byte == 0:
            return self.housecode_byte
        if byte == 1:
            return self.unitcode_byte
        err = "Item index must be 0 or 1: {}".format(byte)
        raise ValueError(err)

    def __hash__(self):
        """Return a hash of the address."""
        return hash(str)

    def _check_housecode_unitcode(self):
        """Check to confirm housecode and unitcode are valid."""
        housecode = byte_to_housecode(self._housecode_byte)
        unitcode = byte_to_unitcode(self._unitcode_byte)
        if housecode and unitcode:
            return True
        return False

    @property
    def id(self):
        """Return the ID of the X10 address."""
        return repr(self)

    @property
    def housecode_byte(self):
        """Emit the X10 house code byte value."""
        return self._housecode_byte

    @property
    def unitcode_byte(self):
        """Emit the X10 unit code byte value."""
        return self._unitcode_byte

    @property
    def housecode(self):
        """Emit the X10 house code."""
        return byte_to_housecode(self._housecode_byte).upper()

    @property
    def unitcode(self):
        """Emit the X10 unit code."""
        return byte_to_unitcode(self._unitcode_byte)
