# -*- coding: utf-8 -*-
import asyncio
import re

from bleak.uuids import uuidstr_to_str

from bleak.backends.bluezdbus import defs
from bleak.exc import BleakError

_mac_address_regex = re.compile("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$")
_hci_device_regex = re.compile("^hci(\\d+)$")


def validate_mac_address(address):
    return _mac_address_regex.match(address) is not None


def validate_hci_device(hci_device):
    return _hci_device_regex.match(hci_device) is not None


def get_device_object_path(hci_device, address):
    """Get object path for a Bluetooth device.

    Service         org.bluez
    Interface       org.bluez.Device1
    Object path     [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX

    Args:
        hci_device (str): Which bluetooth adapter to connect with.
        address (str): The Bluetooth address of the bluetooth device.

    Returns:
        String representation of device object path on format
        `/org/bluez/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX`.

    """
    if not validate_mac_address(address):
        raise BleakError("{0} is not a valid Bluetooth address.".format(address))

    if not validate_hci_device(hci_device):
        raise BleakError("{0} is not a valid HCI device.".format(hci_device))

    # TODO: Join using urljoin? Or pathlib?
    return "/org/bluez/{0}/dev_{1}".format(
        hci_device, "_".join(address.split(":")).upper()
    )


def get_gatt_service_path(hci_device, address, service_id):
    """Get object path for a GATT Service for a Bluetooth device.

    Service         org.bluez
    Service         org.bluez
    Interface       org.bluez.GattService1
    Object path     [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX

    Args:
        hci_device (str): Which bluetooth adapter to connect with.
        address (str): The Bluetooth address of the bluetooth device.
        service_id (int):

    Returns:
        String representation of GATT service object path on format
        `/org/bluez/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX`.

    """
    base = get_device_object_path(hci_device, address)
    return base + "{0}/service{1:02d}".format(base, service_id)


async def get_managed_objects(bus, object_path_filter=None):
    objects = await bus.callRemote(
        "/",
        "GetManagedObjects",
        interface="org.freedesktop.DBus.ObjectManager",
        destination="org.bluez",
    ).asFuture(asyncio.get_event_loop())
    if object_path_filter:
        return dict(
            filter(lambda i: i[0].startswith(object_path_filter), objects.items())
        )

    else:
        return objects


def format_GATT_object(object_path, interfaces):
    if defs.GATT_SERVICE_INTERFACE in interfaces:
        props = interfaces.get(defs.GATT_SERVICE_INTERFACE)
        _type = "{0} Service".format("Primary" if props.get("Primary") else "Secondary")
    elif defs.GATT_CHARACTERISTIC_INTERFACE in interfaces:
        props = interfaces.get(defs.GATT_CHARACTERISTIC_INTERFACE)
        _type = "Characteristic"
    elif defs.GATT_DESCRIPTOR_INTERFACE in interfaces:
        props = interfaces.get(defs.GATT_DESCRIPTOR_INTERFACE)
        _type = "Descriptor"
    else:
        return None

    _uuid = props.get("UUID")
    return "\n{0}\n\t{1}\n\t{2}\n\t{3}".format(
        _type, object_path, _uuid, uuidstr_to_str(_uuid)
    )
