#include "WatchUdev.h"
#include <QSocketNotifier>
#include <QDebug>
#include "libudev.h"

WatchUdev::WatchUdev()
 : udev(NULL),
   udev_monitor(NULL),
   monitor_fd(-1),
   notice_monitor(NULL) {

   udev = udev_new();
   if (udev != NULL) {
      udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
      if (udev_monitor != NULL) {
         udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024);
         if (udev_monitor_enable_receiving(udev_monitor) >= 0) {
            monitor_fd = udev_monitor_get_fd(udev_monitor);
            if (monitor_fd >= 0) {
               notice_monitor = new QSocketNotifier(monitor_fd,
                                                    QSocketNotifier::Read);
               connect(notice_monitor, SIGNAL(activated(int)),
                       this,           SLOT(SeeMonitor(int)));
            }
         }
      }
   }
}

WatchUdev::~WatchUdev() {
   if (udev != NULL) {
      udev_unref(udev);
      if (udev_monitor != NULL) {
         delete notice_monitor;
         udev_monitor_unref(udev_monitor);
      }
   }
}

void
WatchUdev::IncludeProperty(QString propname) {
   included_properties.insert(propname);
}

void
WatchUdev::ScanDevices(QStringList subsystems, QStringList properties) {
   struct udev * scud = udev_new();
   if (scud == NULL) {
      return;
   }
   struct udev_enumerate *udev_enumerate;
   udev_enumerate = udev_enumerate_new(scud);
   if (udev_enumerate != NULL) {
      QStringList::const_iterator it;
      for (it = subsystems.constBegin();
           it != subsystems.constEnd();
           ++it) {
         udev_enumerate_add_match_subsystem(udev_enumerate,
                                            (*it).toAscii().constData());
      }
      for (it = properties.constBegin();
           it != properties.constEnd();
           ++it) {
         QString keyval = *it;
         int eql = keyval.indexOf('=');
         QString key = keyval.left(eql);
         QString val = keyval.right(keyval.size() - (eql + 1));
         udev_enumerate_add_match_property(udev_enumerate,
                                           key.toAscii().constData(),
                                           val.toAscii().constData());
      }
      udev_enumerate_scan_devices(udev_enumerate);
      struct udev_list_entry *entry;
      udev_list_entry_foreach(entry,
                              udev_enumerate_get_list_entry(udev_enumerate)) {
         const char * path = udev_list_entry_get_name(entry);
         struct udev_device *device;
         device = udev_device_new_from_syspath(udev, path);
         if (device != NULL) {
            HandleNewDevice(device);
         }
      }
      udev_enumerate_unref(udev_enumerate);
   }
   udev_unref(scud);
}

void
WatchUdev::SeeMonitor(int /* fd */) {
   struct udev_device *device;

   device = udev_monitor_receive_device(udev_monitor);
   HandleNewDevice(device);
}

void
WatchUdev::HandleNewDevice(struct udev_device *device) {
   if (device != NULL) {
      QString udev_action(udev_device_get_action(device));
      QString udev_path(udev_device_get_devpath(device));
      QString udev_subsystem(udev_device_get_subsystem(device));
      QMap<QString, QString> propmap;
      QSet<QString>::const_iterator it = included_properties.constBegin();
      while (it != included_properties.constEnd()) {
         QString propname = *it;
         QByteArray rawname(propname.toAscii());
         const char * charname = rawname.constData();
         const char * propval = udev_device_get_property_value(device, charname);
         if (propval != NULL) {
            QString qval(propval);
            propmap.insert(propname, qval);
         }
         ++it;
      }
      udev_device_unref(device);
      emit seeDevice(udev_action, udev_path, udev_subsystem, propmap);
   }
}
