#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals

from optparse import OptionParser
import sys
sys.path.append('/usr/lib/bluez/test/')
import dbus
import dbus.service
import dbus.mainloop.glib
from gi.repository import GLib
import bluezutils
import re

BUS_NAME = 'org.bluez'
AGENT_INTERFACE = 'org.bluez.Agent1'
AGENT_PATH = '/test/agent'

bus = None
device_obj = None
dev_path = None


def ask(prompt):
    try:
        return raw_input(prompt)
    except:
        return input(prompt)


def set_trusted(path):
    props = dbus.Interface(bus.get_object('org.bluez', path),
                           'org.freedesktop.DBus.Properties')
    props.Set('org.bluez.Device1', 'Trusted', True)


def dev_connect(path):
    dev = dbus.Interface(bus.get_object('org.bluez', path),
                         'org.bluez.Device1')
    dev.Connect()


class Rejected(dbus.DBusException):

    _dbus_error_name = 'org.bluez.Error.Rejected'


class Agent(dbus.service.Object):

    exit_on_release = True

    def timeout(self, loop, time):
        self.mainloop = loop
        GLib.timeout_add(time, self.timeout_handler)

    def timeout_handler(self):
        self.mainloop.quit()

    def set_exit_on_release(self, exit_on_release):
        self.exit_on_release = exit_on_release

    @dbus.service.method(AGENT_INTERFACE, in_signature='',
                         out_signature='')
    def Release(self):
        print('Release')
        if self.exit_on_release:
            mainloop.quit()

    @dbus.service.method(AGENT_INTERFACE, in_signature='os',
                         out_signature='')
    def AuthorizeService(self, device, uuid):
        print('AuthorizeService (%s, %s)' % (device, uuid))

# ........authorize = ask("Authorize connection (yes/no): ")

        authorize = 'yes'
        if authorize == 'yes':
            return
        raise Rejected('Connection rejected by user')

    @dbus.service.method(AGENT_INTERFACE, in_signature='o',
                         out_signature='s')
    def RequestPinCode(self, device):
        print('RequestPinCode (%s)' % device)
        set_trusted(device)
        return get_pin()

    @dbus.service.method(AGENT_INTERFACE, in_signature='o',
                         out_signature='u')
    def RequestPasskey(self, device):
        print('RequestPasskey (%s)' % device)
        set_trusted(device)
        passkey = ask('Enter passkey: ')
        return dbus.UInt32(passkey)

    @dbus.service.method(AGENT_INTERFACE, in_signature='ouq',
                         out_signature='')
    def DisplayPasskey(
        self,
        device,
        passkey,
        entered,
        ):
        print('DisplayPasskey (%s, %06u entered %u)' % (device,
              passkey, entered))

    @dbus.service.method(AGENT_INTERFACE, in_signature='os',
                         out_signature='')
    def DisplayPinCode(self, device, pincode):
        print('DisplayPinCode (%s, %s)' % (device, pincode))

    @dbus.service.method(AGENT_INTERFACE, in_signature='ou',
                         out_signature='')
    def RequestConfirmation(self, device, passkey):
        print('RequestConfirmation (%s, %06d)' % (device, passkey))

# ........confirm = ask("Confirm passkey (yes/no): ")

        confirm = 'yes'
        if confirm == 'yes':
            set_trusted(device)
            return
        raise Rejected("Passkey doesn't match")

    @dbus.service.method(AGENT_INTERFACE, in_signature='o',
                         out_signature='')
    def RequestAuthorization(self, device):
        print('RequestAuthorization (%s)' % device)

# ........auth = ask("Authorize? (yes/no): ")

        auth = 'yes'
        if auth == 'yes':
            return
        raise Rejected('Pairing rejected')

    @dbus.service.method(AGENT_INTERFACE, in_signature='',
                         out_signature='')
    def Cancel(self):
        print('Cancel')


def pair_reply():
    print('Device paired')
    set_trusted(dev_path)
    dev_connect(dev_path)
    mainloop.quit()


def pair_error(error):
    err_name = error.get_dbus_name()
    if err_name == 'org.freedesktop.DBus.Error.NoReply' and device_obj:
        print('Timed out. Cancelling pairing')
        device_obj.CancelPairing()
    else:
        print('Creating device failed: %s' % error)
    mainloop.quit()


def get_pin():
    f = open('/factory/serial_number', 'r')
    serial = f.readline()
    pin = ''
    for c in serial:
        if c.isdigit():
            pin = pin + c
    f.close()
    return pin[-6:]


if __name__ == '__main__':
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    bus = dbus.SystemBus()

    capability = 'KeyboardDisplay'

    parser = OptionParser()
    parser.add_option(
        '-i',
        '--adapter',
        action='store',
        type='string',
        dest='adapter_pattern',
        default=None,
        )
    parser.add_option('-c', '--capability', action='store',
                      type='string', dest='capability')
    parser.add_option(
        '-t',
        '--timeout',
        action='store',
        type='int',
        dest='timeout',
        default=60000,
        )
    (options, args) = parser.parse_args()
    if options.capability:
        capability = options.capability

    if options.timeout:
        timeout = options.timeout * 1000
    else:
        timeout = 60 * 1000

    path = '/test/agent'
    agent = Agent(bus, path)

    pin = get_pin()

# we use the last 6 digits from the factory serial number as pin

    print('Requesting pin: ' + pin + ' from peer')

    mainloop = GLib.MainLoop()

    agent.timeout(mainloop, timeout)

    obj = bus.get_object(BUS_NAME, '/org/bluez')
    manager = dbus.Interface(obj, 'org.bluez.AgentManager1')
    manager.RegisterAgent(path, capability)

    print('Agent registered')

    # Fix-up old style invocation (BlueZ 4)

    if len(args) > 0 and args[0].startswith('hci'):
        options.adapter_pattern = args[0]
        del args[:1]

    if len(args) > 0:
        device = bluezutils.find_device(args[0],
                options.adapter_pattern)
        dev_path = device.object_path
        agent.set_exit_on_release(False)
        device.Pair(reply_handler=pair_reply, error_handler=pair_error,
                    timeout=60000)
        device_obj = device
    else:
        manager.RequestDefaultAgent(path)

    mainloop.run()

    manager.UnregisterAgent(path)
    print('Agent unregistered')
