/// QasTools: Desktop toolset for the Linux sound system ALSA.
/// \copyright See COPYING file.

#include "udev_device_lookout.hpp"
#include <QSocketNotifier>
#include <iostream>
#include <sstream>

namespace QSnd
{

UDev_Device_Lookout::UDev_Device_Lookout ( QObject * parent_n )
: QObject ( parent_n )
{
  // Init udev
  _udev = ::udev_new ();
  if ( _udev == nullptr ) {
    {
      std::ostringstream msg;
      msg << "Could not create udev object" << std::endl;
      std::cout << msg.str () << std::flush;
    }
    return;
  }

  // Monitor
  _udev_mon = ::udev_monitor_new_from_netlink ( _udev, "udev" );
  if ( _udev_mon == nullptr ) {
    {
      std::ostringstream msg;
      msg << "Could not create udev monitor object";
      msg << std::endl;
      std::cout << msg.str () << std::flush;
    }
    // Close
    ::udev_unref ( _udev );
    _udev = nullptr;
    return;
  }
  ::udev_monitor_filter_add_match_subsystem_devtype ( _udev_mon, "sound", 0 );
  ::udev_monitor_enable_receiving ( _udev_mon );

  // Socket notifier
  {
    QSocketNotifier * socknot = new QSocketNotifier (
        ::udev_monitor_get_fd ( _udev_mon ), QSocketNotifier::Read, this );
    connect ( socknot,
              &QSocketNotifier::activated,
              this,
              &UDev_Device_Lookout::udev_process );
  }
}

UDev_Device_Lookout::~UDev_Device_Lookout ()
{
  if ( _udev == nullptr ) {
    return;
  }
  if ( _udev_mon != nullptr ) {
    ::udev_monitor_unref ( _udev_mon );
    _udev_mon = nullptr;
  }
  ::udev_unref ( _udev );
  _udev = nullptr;
}

void
UDev_Device_Lookout::udev_process ()
{
  bool any_change = false;
  while ( true ) {
    // Read all device device changes from udev
    ::udev_device * dev = ::udev_monitor_receive_device ( _udev_mon );
    if ( dev != nullptr ) {
#ifdef QASTOOLS_UDEV_DEBUG
      {
        std::string devnode;
        std::string subsystem;
        std::string devtype;
        std::string action;
        {
          const char * nullstr = "(null)";
          const char * cstr = nullptr;
          cstr = ::udev_device_get_devnode ( dev );
          devnode = ( ( cstr != nullptr ) ? cstr : nullstr );
          cstr = ::udev_device_get_subsystem ( dev );
          subsystem = ( ( cstr != nullptr ) ? cstr : nullstr );
          cstr = ::udev_device_get_devtype ( dev );
          devtype = ( ( cstr != nullptr ) ? cstr : nullstr );
          cstr = ::udev_device_get_action ( dev );
          action = ( ( cstr != nullptr ) ? cstr : nullstr );
        }
        {
          std::ostringstream msg;
          msg << "UDev action" << std::endl;
          msg << "  Node:      " << devnode << std::endl;
          msg << "  Subsystem: " << subsystem << std::endl;
          msg << "  Devtype:   " << devtype << std::endl;
          msg << "  Action:    " << action << std::endl;
          std::cout << msg.str () << std::flush;
        }
      }
#endif
      ::udev_device_unref ( dev );
      any_change = true;
    } else {
      break;
    }
  }

  if ( any_change ) {
    Q_EMIT sig_change ();
  }
}

} // namespace QSnd
