from __future__ import absolute_import

from yubikit.core.otp import OtpConnection
from .base import OtpYubiKeyDevice, YUBICO_VID, USAGE_OTP

import glob
import fcntl
import struct
import logging

logger = logging.getLogger(__name__)


GET_REPORT = 0xC0094807
SET_REPORT = 0xC0094806
GET_INFO = 0x80084803
GET_DESC_SIZE = 0x80044801
GET_DESC = 0x90044802


class HidrawConnection(OtpConnection):
    def __init__(self, path):
        self.handle = open(path, "wb")

    def close(self):
        self.handle.close()

    def receive(self):
        buf = bytearray(1 + 8)
        fcntl.ioctl(self.handle, GET_REPORT, buf, True)
        return buf[1:]

    def send(self, data):
        buf = bytearray([9])
        buf.extend(data)
        fcntl.ioctl(self.handle, SET_REPORT, buf, True)


def get_info(dev):
    buf = bytearray(4 + 2 + 2)
    fcntl.ioctl(dev, GET_INFO, buf, True)
    return struct.unpack("<IHH", buf)


def get_descriptor(dev):
    buf = bytearray(4)
    fcntl.ioctl(dev, GET_DESC_SIZE, buf, True)
    size = struct.unpack("<I", buf)[0]
    buf += bytearray(size)
    fcntl.ioctl(dev, GET_DESC, buf, True)
    return buf[4:]


def get_usage(dev):
    buf = get_descriptor(dev)
    usage, usage_page = (None, None)
    while buf:
        head, buf = buf[0], buf[1:]
        typ, size = 0xFC & head, 0x03 & head
        value, buf = buf[:size], buf[size:]
        if typ == 4:  # Usage page
            usage_page = struct.unpack("<I", value.ljust(4, b"\0"))[0]
            if usage is not None:
                return usage_page, usage
        elif typ == 8:  # Usage
            usage = struct.unpack("<I", value.ljust(4, b"\0"))[0]
            if usage_page is not None:
                return usage_page, usage


def list_devices():
    devices = []
    for hidraw in glob.glob("/dev/hidraw*"):
        usage = None
        try:
            with open(hidraw, "rb") as f:
                bustype, vid, pid = get_info(f)
                if vid == YUBICO_VID:
                    usage = get_usage(f)
        except Exception as e:
            logger.debug("Failed opening HID device", exc_info=e)
            continue

        if usage == USAGE_OTP:
            devices.append(OtpYubiKeyDevice(hidraw, pid, HidrawConnection))

    return devices

@@ -228,17 +228,34 @@
 ame__)%0A%0A
-%0A
+# usb_ioctl.h%0AUSB_
 GET_REPO
@@ -270,16 +270,20 @@
 0094807%0A
+USB_
 SET_REPO
@@ -298,20 +298,38 @@
 0094806%0A
-GET_
+%0A# hidraw.h%0AHIDIOCGRAW
 INFO = 0
@@ -338,25 +338,28 @@
 0084803%0A
-GET_
+HIDIOCGR
 DESC
-_
 SIZE = 0
@@ -368,20 +368,24 @@
 0044801%0A
-GET_
+HIDIOCGR
 DESC = 0
@@ -642,16 +642,20 @@
 handle, 
+USB_
 GET_REPO
@@ -806,16 +806,20 @@
 handle, 
+USB_
 SET_REPO
@@ -906,20 +906,26 @@
 tl(dev, 
-GET_
+HIDIOCGRAW
 INFO, bu
@@ -1046,17 +1046,20 @@
 ev, 
-GET_
+HIDIOCGR
 DESC
-_
 SIZE
@@ -1158,20 +1158,24 @@
 tl(dev, 
-GET_
+HIDIOCGR
 DESC, bu

Use contants named from hidraw.h