import os
from subprocess import Popen, PIPE, DEVNULL
import fcntl
import pathlib


def create_usb_list():
    device_list = list()
    lsusb_out = Popen('lsusb -v', shell=True, bufsize=64, 
                      stdin=PIPE, stdout=PIPE, stderr=DEVNULL, 
                      close_fds=True).stdout.read().strip().decode('utf-8')
    usb_devices = lsusb_out.split('%s%s' % (os.linesep, os.linesep))
    for device_categories in usb_devices:
        if not device_categories:
            continue
        categories = device_categories.split(os.linesep)
        device_stuff = categories[0].strip().split()
        bus = device_stuff[1]
        device = device_stuff[3][:-1]
        device_dict = {'bus': bus, 'device': device}
        device_info = ' '.join(device_stuff[6:])
        device_dict['description'] = device_info
        for category in categories:
            if not category:
                continue
            categoryinfo = category.strip().split()
            if categoryinfo[0] == 'iManufacturer':
                manufacturer_info = ' '.join(categoryinfo[2:])
                device_dict['manufacturer'] = manufacturer_info
            if categoryinfo[0] == 'iProduct':
                device_info = ' '.join(categoryinfo[2:])
                device_dict['device'] = device_info
        path = '/dev/bus/usb/%s/%s' % (bus, device)
        device_dict['path'] = path

        device_list.append(device_dict)
    return device_list

def reset_usb_device(dev_path):
    USBDEVFS_RESET = 21780
    try:
        f = open(dev_path, 'w', os.O_WRONLY)
        fcntl.ioctl(f, USBDEVFS_RESET, 0)
        print('Successfully reset %s' % dev_path)
    except PermissionError as ex:
        raise PermissionError('Try running "sudo chmod 777 {}"'.format(dev_path))

def reset_all_elgato_devices():
    """
    Find and reset all Elgato capture cards.
    Required to workaround a firmware bug.
    """
    
    # enumerate UBS device to find Elgato Capture Card
    device_list = create_usb_list()
    
    for dev in device_list:
        if 'Elgato' in dev['description']:
            dev_usb_path = dev['path']
            reset_usb_device(dev_usb_path)

def get_sorted_v4l_paths(by_id=True):
    """
    If by_id, sort devices by device name + serial number (preserves device order)
    else, sort devices by usb bus id (preserves usb port order)
    """
    
    dirname = 'by-id'
    if not by_id:
        dirname = 'by-path'
    v4l_dir = pathlib.Path('/dev/v4l').joinpath(dirname)

    valid_paths = list()
    for dev_path in sorted(v4l_dir.glob("*video*")):
        name = dev_path.name

        # only keep devices ends with "index0"
        # since they are the only valid video devices
        index_str = name.split('-')[-1]
        assert index_str.startswith('index')
        index = int(index_str[5:])
        if index == 0:
            valid_paths.append(dev_path)
    result = [str(x.absolute()) for x in valid_paths]

    return result
