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

import bluetooth
import bluetooth._bluetooth as bluez

from blueproximity.exceptions import DeviceException
from blueproximity.log import logger

rssi_re = re.compile('^RSSI return value: (-?\d+)')


def scan():
    '''
    Scan for bluetooth-devices

    :return: list of bluetooth-devices
    :rtype: [blueproximity.device.BluetoothDevice]
    '''
    def _scan():
        for mac, name in bluetooth.discover_devices(lookup_names=True):
            yield BluetoothDevice(mac=mac, name=name)
    return list(_scan())


class BluetoothDevice(object):
    '''
    Abstract access to a bluetooth-device
    '''
    def __init__(self, mac, port=None, name=None):
        self.sock = None

        self.mac = mac
        self.port = port
        self.name = name

        self.port = self.scan_ports() if not port else port
        self.name = bluetooth.lookup_name(mac) if not name else name

    def scan_ports(self):
        '''
        Find a suitable port for connection

        :return: suitable port
        :rtype: int
        '''
        for port in range(1, 30):
            try:
                self.connect(port)
                logger.debug('Could connect on port %s', port)
                return port
            except bluetooth.btcommon.BluetoothError:
                logger.debug('Couldn\'t get connection on port %s', port)
        raise DeviceException(
            '{}: Couldn\'t find suitable port for connection'.format(self)
        )

    def connect(self, port=None):
        '''
        Connect to the device

        :param port: port used for connection
        :type port: int
        '''
        if self.connected:
            return
        if not port:
            port = self.port
        logger.debug('Connecting %s on port %s', self, port)
        self.sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM,
                                              bluez.btsocket())
        self.sock.connect((self.mac, port))

    def disconnect(self):
        '''
        Disconnect the device
        '''
        if not self.connected:
            return
        logger.debug('Disconnecting %s', self)
        if self.sock:
            self.sock.close()
            self.sock = None

    @property
    def connected(self):
        p = subprocess.run(
            ['hcitool', 'lq', self.mac],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL
        )
        return p.returncode == 0

    @property
    def distance(self):
        '''
        Determinte distance of the device

        :return: distance of the device
        :rtype: int
        '''
        if not self.connected:
            logger.debug('Device disconnected -> reconnecting')
            self.connect()
        p = subprocess.run(
            ['hcitool', 'rssi', self.mac],
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL
        )
        if p.returncode == 0:
            match = rssi_re.match(p.stdout.decode('utf-8'))
            if match:
                return abs(int(match.group(1)))
        return 255

    def __str__(self):
        return 'BluetoothDevice(mac={mac}, port={port}, name={name}, '\
            'connected={connected})'.format(
                mac=self.mac, port=self.port,
                name=self.name, connected=self.connected
            )

    def __repr__(self):
        return self.__str__()