#include "gego_global_notify.h"

static GegoGlobalNotify * _gego_global_notify_instance = NULL;


#define GEGO_GLOBAL_NOTIFY_GET_PRIVATE(o)\
  G_TYPE_INSTANCE_GET_PRIVATE (o, GEGO_TYPE_GLOBEL_NOTIFY, GegoGlobalNotifyPrivate)

/**
 * section: gego_global_notify
 * @title: GegoGlobalNotify
 * @short_description: subscriber based application wide notifications.
 *
 * The #GegoGlobalNotify tries to solve a probrem that occurs in many peaces of
 * software: How to notify many parts, or components, of changes, or how to
 * send information from one part of a program to many other parts the same
 * program
 **/

/**
 * GegoGlobalNotify:
 * @short_description:
 * 
 */


////////////////////////////////////////////////////////////////////////////////

/**
 * GegoGlobalNotifyPrivate: (skip)
 *
 * @hash_table:
 * GHashTable<gchar *><GLinkedList<GegoCallbackItem*>*>*
 */
struct
_GegoGlobalNotifyPrivate {
  /* < private > */
  GHashTable * hash_table;
};


/**
 * GegoCallbackItem: (skip)
 * 
 * @subscription_id: The id of the subscriber.
 * @key_id: The key id of the callback.
 * @subscriber_data: The data provided by the subscriber_data.
 * @callback: The callback to be run.
 */
struct
_GegoCallbackItem {
  /* < private > */
  guint subscription_id;
  GQuark key_id;
  gpointer subscriber_data;
  GCallback callback;
};

G_DEFINE_TYPE_WITH_PRIVATE (GegoGlobalNotify, gego_global_notify, G_TYPE_OBJECT)

/**
 * gego_global_notify_new: (skip)
 *
 * @void: (skip)
 */
GegoGlobalNotify *
gego_global_notify_new (void) {
  GegoGlobalNotify * self = g_object_new (GEGO_TYPE_GLOBEL_NOTIFY, NULL, NULL);
  
  return self;
}


/**
 * gego_global_notify_constructor: (skip)
 * gtype: (skip)
 * n_properties: (skip)
 * properties: (skip)
 */
static GObject *
gego_global_notify_constructor (GType                   gtype,
                                guint                   n_properties,
                                GObjectConstructParam * properties) {
  static GObject * self = NULL;
  
  
  if (self == NULL) {
    self = G_OBJECT_CLASS (gego_global_notify_parent_class
                           )->constructor (gtype, n_properties, properties);
    g_object_add_weak_pointer (G_OBJECT (self), (gpointer) &self);
    return G_OBJECT (self);
  }
  
  return g_object_ref (G_OBJECT (self));
}

/**
 * gego_global_notify_class_init: (skip)
 *
 * klass: (skip)
 */
static void
gego_global_notify_class_init (GegoGlobalNotifyClass * klass) {
  GObjectClass * object_class = G_OBJECT_CLASS (klass);
  
  object_class->constructor = gego_global_notify_constructor;
  
}


/**
 * gego_global_notify_init: (skip)
 *
 * @self: (skip)
 */
static void
gego_global_notify_init (GegoGlobalNotify * self) {
  
  GegoGlobalNotifyPrivate * priv = GEGO_GLOBAL_NOTIFY_GET_PRIVATE (self);
  
  /* Note that the hash table should contain linked lists of
   * GegoGlobalNotifyCallbackItems.
   * In template speak:
   * GHashTable<gchar *><GLinkedList<GegoGlobalNotifyCallbackItem *> *>*
   */
  priv->hash_table = g_hash_table_new (g_str_hash, g_str_equal);
  
}

////////////////////////////////////////////////////////////////////////////////

/**
 * gego_global_notify_initalize:
 * @short_description: Should be run at the start of the program.
 *
 * @err: (allow-none): NULL'ed errer to be passed to function.
 */
gboolean
gego_global_notify_initalize (GError ** err) {
  if (_gego_global_notify_instance != NULL) {
    g_set_error (err, GEGO_GLOBAL_NOTIFY_ERROR,
                 GEGO_GLOBAL_NOTIFY_ERROR_ALREADY_INITALIZED,
                 "The Global Notify system is already initialized.");
    return FALSE;
  }
  _gego_global_notify_instance = gego_global_notify_new ();
  return TRUE;
}

/**
 * gego_global_notify_unsubscribe:
 * @short_description: Sholud only be run when the program quits.
 *
 * Calling callbacks after this point is an undefined behaviour.
 *
  * @err: (allow-none):NULL'ed errer to be passed to function.
 */
gboolean
gego_global_notify_uninitalize (GError ** err) {
  gego_global_notify_return_not_initialized(err);
  g_object_unref (_gego_global_notify_instance);
}

/**
 * gego_global_notify_add_notification:
 * 
 * Add a notifications to the system.
 * 
 * @name The name of the notification to be added.
 * @err: (allow-none):NULL'ed errer to be passed to function.
 * 
 * return: GUI for that callbacks. negative value on fail.
 */
gint
gego_global_notify_add_notification (gchar * name, GError ** err) {
  gego_global_notify_return_not_initialized(err);
  //TODO
  return 0;
}

/**
 * gego_global_notify_remove_notification:
 * @short_description: removes a notification from the system.
 * 
 * 
 * @name: The name of the notification to be removed from the system.
 * @notification_id: The notification ID that was given when the notification
 * was added to the system.
 * @err: (allow-none):A NULL'ed error to be passed to the...
 */
void
gego_global_notify_remove_notification (gchar * name,
                                        gint notification_id,
                                        GError ** err) {
  gego_global_notify_return_not_initialized(err);
  //TODO
}

/**
 * gego_global_notify_subscribe:
 * @short_description: hook up a callback to a signal name.
 *
 * @name: The name of the callback to attash to.
 * @callback: the callback to hookup to the signal.
 *
 *
 * This function is used to hook up a callback to a globla signal. It is
 * is recomended to save the retuned value for when a callback should be
 * unsubscribed to, using the gego_global_notify_unsubscribe() function.
 * 
 * Here is an example of a callback.
 * \[<!-- language="C" -->
 * 
 * \]
 * 
 * returns: the subscription id of the signal.
 */
gint
gego_global_notify_subscribe (gchar * name,
                              GCallback callback,
                              gpointer subscriber_data,
                              GError ** err) {
  gego_global_notify_return_not_initialized(err);
  // TODO
}

/**
 * gego_global_notify_unsubscribe:
 * @short_description: removes a callback from a notify.
 *
 * @name: The name of the notify to unsubscribed to.
 * @subscription_id: The Subscription ID that was got when the subscription was.
 *                   made.
 * @err: (out)(allow-none): Error to be filled with an error if such has occured.
 */
void
gego_global_notify_unsubscribe (gchar * name,
                                gint subscription_id,
                                GError ** err) {
  gego_global_notify_return_not_initialized(err);
  // TODO
}

/**
 * gego_global_notify_call:
 * @short_description: Calls all subscribers to a notify.
 *
 * @name: The name of the notify to call.
 * @caller_data: The data to be passed to the callback.
 * @err: (out)(allow-none): Error to be filled with an error if such has occured.
 */
gint
gego_global_notify_call (gchar * name,
                         gpointer caller_data,
                         GError ** err) {
  gego_global_notify_return_not_initialized(err);
  // TODO
}

/**
 * gego_global_notify_is_initialized:
 * @short_description: Check if Global Notify is initialized.
 *
 * return: #TRUE if Global Notify is initialized, otherwise #FALSE
 */
gboolean
gego_global_notify_is_initialized (void) {
  if (_gego_global_notify_instance == 0) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/**
 * gego_global_notify_return_not_initialized:
 * @short_description: convinience macro; returns with an error if Global Notify
 * is not initialized.
 *
 * @err: (type GError) An #GError to be passed to the macro.
 */



/**
 * gego_global_notify_error_quark: (skip)
 */
GQuark
gego_global_notify_error_quark (void) {
  return g_quark_from_static_string ("gego-global-notify-error-quark");
}

/**
 * GEGO_GLOBAL_NOTIFY_ERROR:
 * @short_description: A convinience wrapper around #gego_global_notify_error_quark()
 */

/**
 * GegoGlobalNotifyErrorName:
 * @short_description: Get the name of the error.
 */

/**
 * gego_global_notify_error_get_name:
 * @short_description: Get the string name of the #GegoGlobalNotifyError.
 * 
 * @k: The error to get the name of.
 * 
 * Anagalius to GegoGlobalNotifyErrorName[k], but included for binding
 * purposus.
 *
 * return: The string containing the name of the error. Or NULL on fail.
 */
gchar *
gego_global_notify_error_get_name (GegoGlobalNotifyError k) {
  if (k > GEGO_GLOBAL_NOTIFY_ERROR_LAST) {
    return NULL;
  }
  return GegoGlobalNotifyErrorName[k];
}
