/* vim: set sw=3 ts=3 sts=3 expandtab: */
#include "ecdb.h"

void _ecdb_hal_device_all_cb(void *data, void *reply_data, DBusError *error);
void _ecdb_hal_volume_all_cb(void *data, void *reply_data, DBusError *error);
void _ecdb_hal_device_handle(void *data, void *reply_data, DBusError *error);
void _ecdb_hal_volume_removed_cb(void *data, DBusMessage *msg);
void _ecdb_hal_volume_added_cb(void *data, DBusMessage *msg);
void _ecdb_hal_volume_prop_modified(void *data, DBusMessage *msg);
void _ecdb_hal_volume_cb(void *data, void *reply_data, DBusError *error);
void _ecdb_hal_volume_handle(void *data, void *reply_data, DBusError *error);
void _ecdb_hal_unmount_cb(void *data, void *reply_data, DBusError *error);
static void _ecdb_hal_volumes_init(void);

void
_ecdb_hal_device_handle(void *data, void *reply_data, DBusError *error)
{
   E_Hal_Device_Get_All_Properties_Return *ret;
   Eina_List *l;
   Ecdb_Drive_Info *drive;
   char *udi, *str;
   int err = 0;

   ret = reply_data;
   udi = data;

   if ((!ret) || (!udi))
   {
      EINA_ERROR_PWARN("NULL udi or reply!\n");
      return;
   }

   if (dbus_error_is_set(error))
   {
      EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message);
      dbus_error_free(error);
      return;
   }

   str = e_hal_property_string_get(ret, "block.device", &err);

   if (!str)
      return;

   if (!err)
   {
      EINA_LIST_FOREACH(em->drives, l, drive)
      {
         if (!strcmp(drive->location, str))
         {
            if (!ecdb_update_drive_info(drive, ret))
            {
               EINA_ERROR_PWARN("Couldn't update old drive info!\n");
            }

            ecdb_print_drive_info();
            return;
         }
      }

      /*
       * Create the drive information here, as we will have already
       * returned if it previously exists
       */
      if (!ecdb_aquire_drive_info(ret, str, udi))
      {
         EINA_ERROR_PWARN("Couldn't create new drive info!\n");
         return;
      }

      ecdb_print_drive_info();

      /* Check to see if we have acquired all of the drives */
      if (eina_list_count(em->drives) >= em->init_count)
      {
         em->init_count = 0;
         _ecdb_hal_volumes_init();
      }
   }
}

void
_ecdb_hal_volume_removed_cb(void *data __UNUSED__, DBusMessage *msg)
{
   DBusError err;
   Eina_List *l;
   Ecdb_Drive_Info *drive;
   char *udi;

   dbus_error_init(&err);
   dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &udi,
                                                      DBUS_TYPE_INVALID);

   EINA_LIST_FOREACH(em->drives, l, drive)
   {
      if (!strcmp(drive->disc_udi, udi))
      {
         drive->fresh_info = FALSE;
         drive->status = 0;
         FREE(drive->type);
      }
   }
}

void
_ecdb_hal_volume_added_cb(void *data __UNUSED__, DBusMessage *msg)
{
   DBusError err;
   char *udi;

   dbus_error_init(&err);
   dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &udi,
                                                      DBUS_TYPE_INVALID);

   /* XXX Do something with the error above? */
   e_hal_device_query_capability(em->conn, udi, "volume",
                                       _ecdb_hal_volume_cb,
                                       (void *)strdup(udi));
}

void
_ecdb_hal_volume_cb(void *data, void *reply_data, DBusError *error)
{
   char *udi;
   E_Hal_Device_Query_Capability_Return *ret;

   udi = data;
   ret = reply_data;

   if (!ret)
   {
      EINA_ERROR_PWARN("No DBus data!\n");
      return;
   }

   if (dbus_error_is_set(error))
   {
      EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message);
      dbus_error_free(error);
      return;
   }

   if (ret->boolean)
   {
      e_hal_device_get_all_properties(em->conn, udi, _ecdb_hal_volume_handle,
                                                            udi);
   }
}

void
_ecdb_hal_device_all_cb(void *data __UNUSED__, void *reply_data,
                                                         DBusError *error)
{
   char *udi;
   Eina_List *l;
   E_Hal_Manager_Find_Device_By_Capability_Return *ret;

   ret = reply_data;

   if ((!ret) || (!ret->strings))
   {
      EINA_ERROR_PWARN("No DBus data!\n");
      return;
   }

   if (dbus_error_is_set(error))
   {
      EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message);
      dbus_error_free(error);
      return;
   }

   em->init_count = eina_list_count(ret->strings);
   EINA_LIST_FOREACH(ret->strings, l, udi)
   {
      e_hal_device_get_all_properties(em->conn, udi, _ecdb_hal_device_handle,
                                       (void *)strdup(udi));
   }
}

int
ecdb_hal_init(void)
{
   em->conn = e_dbus_bus_get(DBUS_BUS_SYSTEM);
   if (!em->conn)
   {
      EINA_ERROR_PWARN("Error connecting to system bus!\n");
      return 0;
   }

   e_hal_manager_find_device_by_capability(em->conn, "storage.cdrom",
                                       _ecdb_hal_device_all_cb, NULL);

   return 1;
}

void
ecdb_hal_shutdown(void)
{
   e_dbus_signal_handler_del(em->conn, em->dev_added);
   e_dbus_signal_handler_del(em->conn, em->dev_removed);
   e_dbus_connection_close(em->conn);
}

void
ecdb_hal_request_unmount(Ecdb_Drive_Info *drive, E_DBus_Callback_Func cb,
                                                                  void *data)
{
   e_hal_device_volume_unmount(em->conn, drive->disc_udi, NULL,
                                                cb, data);
}

static void
_ecdb_hal_volumes_init()
{

   em->dev_added = e_dbus_signal_handler_add(em->conn, "org.freedesktop.Hal",
                                        "/org/freedesktop/Hal/Manager",
                                        "org.freedesktop.Hal.Manager",
                                        "DeviceAdded",
                                        _ecdb_hal_volume_added_cb, NULL);

   em->dev_removed = e_dbus_signal_handler_add(em->conn,
                                       "org.freedesktop.Hal",
                                       "/org/freedesktop/Hal/Manager",
                                       "org.freedesktop.Hal.Manager",
                                       "DeviceRemoved",
                                       _ecdb_hal_volume_removed_cb, NULL);

   e_hal_manager_find_device_by_capability(em->conn, "volume.disc",
                                       _ecdb_hal_volume_all_cb, NULL);
}

void
_ecdb_hal_volume_all_cb(void *data __UNUSED__, void *reply_data,
                                                         DBusError *error)
{
   char *udi;
   Eina_List *l;
   E_Hal_Manager_Find_Device_By_Capability_Return *ret;

   ret = reply_data;

   if ((!ret) || (!ret->strings))
   {
      EINA_ERROR_PWARN("No DBus data!\n");
      return;
   }

   if (dbus_error_is_set(error))
   {
      EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message);
      dbus_error_free(error);
      return;
   }

   EINA_LIST_FOREACH(ret->strings, l, udi)
   {
      e_hal_device_get_all_properties(em->conn, udi, _ecdb_hal_volume_handle,
                                                         (void *)strdup(udi));
   }
}

void
_ecdb_hal_volume_handle(void *data, void *reply_data, DBusError *error)
{
   E_Hal_Device_Get_All_Properties_Return *ret;
   Ecdb_Drive_Info *drive;
   Eina_List *l;
   char *udi, *disc_udi;
   int err;

   ret = reply_data;
   disc_udi = data;

   if ((!ret) || (!disc_udi))
   {
      EINA_ERROR_PWARN("No DBus data or volume data!\n");
      return;
   }

   if (dbus_error_is_set(error))
   {
      EINA_ERROR_PWARN("DBus error: %s %s\n", error->name, error->message);
      dbus_error_free(error);
      return;
   }

   /* Ignore volumes that are not discs */
   if ((!e_hal_property_bool_get(ret, "volume.is_disc", &err)) || (err))
      return;

   /* Get the parent udi */
   udi = e_hal_property_string_get(ret, "info.parent", NULL);

   /* Find the drive this volume belongs to */
   EINA_LIST_FOREACH(em->drives, l, drive)
   {
      if (!strcmp(udi, drive->udi))
      {
         drive->fresh_info = TRUE;
         drive->disc_udi = disc_udi;
         drive->capacity = e_hal_property_uint64_get(ret,
                                                "volume.disc.capacity", NULL);

         if (e_hal_property_bool_get(ret, "volume.disc.is_blank", NULL))
            drive->status |= ECDB_DISC_BLANK;

         if (e_hal_property_bool_get(ret, "volume.disc.is_appendable", NULL))
            drive->status |= ECDB_DISC_APPENDABLE;

         if (e_hal_property_bool_get(ret, "volume.disc.is_rewritable", NULL))
            drive->status |= ECDB_DISC_REWRITABLE;

         drive->type = e_hal_property_string_get(ret, "volume.disc.type",
                                                                        NULL);

         /* Also refresh the drive, for write speeds */
         e_hal_device_get_all_properties(em->conn, udi,
                                          _ecdb_hal_device_handle, udi);
         break;
      }
   }

   FREE(udi);
}

