import logging

from enum import Enum

log = logging.getLogger(__name__)

DEFAULT_CONNECT_TIMEOUT_S = 5.0

BLEAddressType = Enum('BLEAddressType', 'public random')


class BLEBackend(object):
    """Abstract base class representing a Bluetooth adapter backend. See the
    `pygatt.backends` module for available implementations.
    """

    def start(self):
        """Initialize and resource required to run the backend, e.g. background
        threads, USB device connections, etc.
        """
        raise NotImplementedError()

    def stop(self):
        """Stop and free any resources required while the backend is running.
        """
        raise NotImplementedError()

    def supports_unbonded(self):
        """Return True if the backend supports unbonded communication - this is
        to make detecting the GATTTool backend easier, which at the moment is
        auto-upgrading to a bonded connection even if not requested.
        """
        return True

    def connect(self, address, timeout=DEFAULT_CONNECT_TIMEOUT_S, **kwargs):
        """Return a BLEDevice for the connection if connected, otherwise raise
        an exception.
        """
        raise NotImplementedError()

    def scan(self, *args, **kwargs):
        """
        Performs a BLE scan.

        Returns a list of BLE devices found.
        """
        raise NotImplementedError()

    def filtered_scan(self, name_filter="", *args, **kwargs):
        """
        Scan for BLE devices and filter the list to include only with a name
        that includes the given filter.

        Returns a list of BLE devices found.
        """
        devices = self.scan(*args, **kwargs)
        return [device for device in devices
                if name_filter in (device['name'] or '')]

    def clear_bond(self, address=None):
        raise NotImplementedError()


class Characteristic(object):
    """
    A GATT characteristic, including it handle value and associated descriptors.
    Only valid for the lifespan of a BLE connection, since the handle values are
    dynamic.
    """
    def __init__(self, uuid, handle):
        """
        Sets the characteritic uuid and handle.

        handle - a bytearray
        """
        self.uuid = uuid
        self.handle = handle
        self.descriptors = {
            # uuid_string: handle
        }

    def add_descriptor(self, uuid, handle):
        """
        Add a characteristic descriptor to the dictionary of descriptors.
        """
        self.descriptors[uuid] = handle

    def __str__(self):
        return "<%s uuid=%s handle=%d>" % (self.__class__.__name__,
                                           self.uuid, self.handle)