/*
 * notification.c - A gobject subclass to represent a org.freedesktop.Notification.Notify message.
 */

#include <string.h>
#include "notification.h"

#define COLUMN_APP_NAME       0
#define COLUMN_REPLACES_ID    1
#define COLUMN_APP_ICON       2
#define COLUMN_SUMMARY        3
#define COLUMN_BODY           4
#define COLUMN_ACTIONS        5
#define COLUMN_HINTS          6
#define COLUMN_EXPIRE_TIMEOUT 7

#define COLUMN_COUNT 8

#define X_CANONICAL_PRIVATE_SYNCHRONOUS "x-canonical-private-synchronous"

static void notification_class_init(NotificationClass *klass);
static void notification_init(Notification *self);
static void notification_dispose(GObject *object);

G_DEFINE_TYPE_WITH_PRIVATE(Notification, notification, G_TYPE_OBJECT);

static void
notification_class_init(NotificationClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
  object_class->dispose = notification_dispose;
}

static void
notification_init(Notification *self)
{
  self->priv = notification_get_instance_private(self);

  self->priv->app_name = NULL;
  self->priv->replaces_id = 0;
  self->priv->app_icon = NULL;
  self->priv->summary = NULL;
  self->priv->body = NULL;
  self->priv->expire_timeout = 0;
  self->priv->timestamp = NULL;
  self->priv->is_private = FALSE;
}

static void
notification_dispose(GObject *object)
{
  Notification *self = NOTIFICATION(object);

  if(self->priv->app_name != NULL) {
    g_free(self->priv->app_name);
    self->priv->app_name = NULL;
  }

  if(self->priv->app_icon != NULL) {
    g_free(self->priv->app_icon);
    self->priv->app_icon = NULL;
  }

  if(self->priv->summary != NULL) {
    g_free(self->priv->summary);
    self->priv->summary = NULL;
  }

  if(self->priv->body != NULL) {
    g_free(self->priv->body);
    self->priv->body = NULL;
  }

  if(self->priv->timestamp != NULL) {
    g_date_time_unref(self->priv->timestamp);
    self->priv->timestamp = NULL;
  }

  G_OBJECT_CLASS(notification_parent_class)->dispose(object);
}

Notification*
notification_new(void)
{
  return NOTIFICATION(g_object_new(NOTIFICATION_TYPE, NULL));
}

Notification*
notification_new_from_dbus_message(GDBusMessage *message)
{
  Notification *self = notification_new();

  /* timestamp */
  self->priv->timestamp = g_date_time_new_now_local();

  GVariant *body = g_dbus_message_get_body(message);
  GVariant *child = NULL, *value = NULL;
  g_assert(g_variant_is_of_type(body, G_VARIANT_TYPE_TUPLE));
  g_assert(g_variant_n_children(body) == COLUMN_COUNT);

  /* app_name */
  child = g_variant_get_child_value(body, COLUMN_APP_NAME);
  g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING));
  self->priv->app_name = g_variant_dup_string(child,
      &(self->priv->app_name_length));
  g_variant_unref(child);

  /* replaces_id */
  child = g_variant_get_child_value(body, COLUMN_REPLACES_ID);
  g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_UINT32));
  self->priv->replaces_id = g_variant_get_uint32(child);
  g_variant_unref(child);

  /* app_icon */
  child = g_variant_get_child_value(body, COLUMN_APP_ICON);
  g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING));
  self->priv->app_icon = g_variant_dup_string(child,
      &(self->priv->app_icon_length));
  g_variant_unref(child);

  /* summary */
  child = g_variant_get_child_value(body, COLUMN_SUMMARY);
  g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING));
  self->priv->summary = g_variant_dup_string(child,
      &(self->priv->summary_length));
  g_strstrip(self->priv->summary);
  self->priv->summary_length = strlen(self->priv->summary);
  g_variant_unref(child);

  /* body */
  child = g_variant_get_child_value(body, COLUMN_BODY);
  g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING));
  self->priv->body = g_variant_dup_string(child,
      &(self->priv->body_length));
  g_strstrip(self->priv->body);
  self->priv->body_length = strlen(self->priv->body);
  g_variant_unref(child);

  /* hints */
  child = g_variant_get_child_value(body, COLUMN_HINTS);
  g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_DICTIONARY));

  /* check for volume hint */
  value = g_variant_lookup_value(child, X_CANONICAL_PRIVATE_SYNCHRONOUS, G_VARIANT_TYPE_STRING);
  if(value != NULL) {
    const gchar *private_string = g_variant_get_string(value, NULL);
    g_variant_unref(value);
    value = NULL;

    if((g_strcmp0(private_string, "volume") == 0) ||
       (g_strcmp0(private_string, "brightness") == 0) ||
       (g_strcmp0(private_string, "indicator-sound") == 0)) {
      self->priv->is_private = TRUE;
    }
  }

  g_variant_unref(child);
  child = NULL;

  return self;
}

const gchar*
notification_get_app_name(Notification *self)
{
  return self->priv->app_name;
}

const gchar*
notification_get_app_icon(Notification *self)
{
  return self->priv->app_icon;
}

const gchar*
notification_get_summary(Notification *self)
{
  return self->priv->summary;
}

const gchar*
notification_get_body(Notification *self)
{
  return self->priv->body;
}

gint64
notification_get_timestamp(Notification *self)
{
  return g_date_time_to_unix(self->priv->timestamp);
}

gchar*
notification_timestamp_for_locale(Notification *self)
{
  return g_date_time_format(self->priv->timestamp, "%X %x");
}

gboolean
notification_is_private(Notification *self)
{
  return self->priv->is_private;
}

/**
 * A notification is considered empty if both the summary and body do not
 * contain any text.
 */
gboolean
notification_is_empty(Notification *self)
{
  return (self->priv->summary_length == 0) && (self->priv->body_length == 0);
}

void
notification_print(Notification *self)
{
  g_print("app_name = %s\n", self->priv->app_name);
  g_print("app_icon = %s\n", self->priv->app_icon);
  g_print("summary = %s\n", self->priv->summary);
  g_print("body = %s\n", self->priv->body);
}
