/**
 * hildon_helloworld-9.c
 *
 * This maemo code example is licensed under a MIT-style license,
 * that can be found in the file called "License" in the same
 * directory as this file.
 * Copyright (c) 2007-2008 Nokia Corporation. All rights reserved.
 *
 * We'll store the color that the user selects into a GConf
 * preference. In fact, we'll have three settings, one for each
 * channel of the color (red, green and blue).
 *
 * Look for lines with "NEW" or "MODIFIED" in them.
 */

#include <stdlib.h>
#include <hildon/hildon-program.h>
#include <hildon/hildon-color-button.h>
#include <hildon/hildon-find-toolbar.h>
#include <hildon/hildon-file-chooser-dialog.h>
#include <hildon/hildon-banner.h>
#include <libgnomevfs/gnome-vfs.h>
/* Include the prototypes for GConf client functions (NEW). */
#include <gconf/gconf-client.h>

/* The application name -part of the GConf namespace (NEW). */
#define APP_NAME "hildon_hello"
/* This will be the root "directory" for our preferences (NEW). */
#define GC_ROOT  "/apps/Maemo/" APP_NAME "/"

  /*... Listing cut for brevity ...*/

/**
 * NEW
 *
 * Utility function to store the given color into our application
 * preferences. We could use a list of integers as well, but we'll
 * settle for three separate properties; one for each of RGB
 * channels.
 *
 * The config keys that will be used are 'red', 'green' and 'blue'.
 *
 * NOTE:
 *   We're doing things very non-optimally. If our application would
 *   have multiple preference settings, and we would like to know
 *   when someone will change them (external program, another
 *   instance of our program, etc), we'd have to keep a reference to
 *   the GConf client connection. Listening for changes in
 *   preferences would also require a callback registration, but this
 *   is covered in the "maemo Platform Development" material.
 */
static void confStoreColor(const GdkColor* color) {

  /* We'll store the pointer to the GConf connection here. */
  GConfClient* gcClient = NULL;

  /* Make sure that no NULLs are passed for the color. GdkColor is
     not a proper GObject, so there is no GDK_IS_COLOR macro. */
  g_assert(color);

  g_print("confStoreColor: invoked\n");

  /* Open a connection to gconfd-2 (via D-Bus in maemo). The GConf
     API doesn't say whether this function can ever return NULL or
     how it will behave in error conditions. */
  gcClient = gconf_client_get_default();
  /* We make sure that it's a valid GConf-client object. */
  g_assert(GCONF_IS_CLIENT(gcClient));

  /* Store the values. */
  if (!gconf_client_set_int(gcClient, GC_ROOT "red", color->red,
                            NULL)) {
    g_warning(" failed to set %s/red to %d\n", GC_ROOT, color->red);
  }
  if (!gconf_client_set_int(gcClient, GC_ROOT "green", color->green,
                            NULL)) {
    g_warning(" failed to set %s/green to %d\n", GC_ROOT,
              color->green);
  }
  if (!gconf_client_set_int(gcClient, GC_ROOT "blue", color->blue,
                            NULL)) {
    g_warning(" failed to set %s/blue to %d\n", GC_ROOT,
              color->blue);
  }

  /* Release the GConf client object (with GObject-unref). */
  g_object_unref(gcClient);
  gcClient = NULL;
}

/**
 * NEW
 *
 * An utility function to get an integer but also return the status
 * whether the requested key existed or not.
 *
 * NOTE:
 *   It's also possible to use gconf_client_get_int(), but it's not
 *   possible to then know whether they key existed or not, because
 *   the function will return 0 if the key doesn't exist (and if the
 *   value is 0, how could you tell these two conditions apart?).
 *
 * Parameters:
 * - GConfClient: the client object to use
 * - const gchar*: the key
 * - gint*: the address to store the integer to if the key exists
 *
 * Returns:
 * - TRUE: if integer has been updated with a value from GConf.
 *   FALSE: there was no such key or it wasn't an integer.
 */
static gboolean confGetInt(GConfClient* gcClient, const gchar* key,
                           gint* number) {

  /* This will hold the type/value pair at some point. */
  GConfValue* val = NULL;
  /* Return flag (tells the caller whether this function wrote behind
     the 'number' pointer or not). */
  gboolean hasChanged = FALSE;

  /* Try to get the type/value from the GConf DB.
     NOTE:
       We're using a version of the getter that will not return any
       defaults (if a schema would specify one). Instead, it will
       return the value if one has been set (or NULL).

     We're not really interested in errors as this will return a NULL
     in case of missing keys or errors and that is quite enough for
     us. */
  val = gconf_client_get_without_default(gcClient, key, NULL);
  if (val == NULL) {
    /* Key wasn't found, no need to touch anything. */
    g_warning("confGetInt: key %s not found\n", key);
    return FALSE;
  }

  /* Check whether the value stored behind the key is an integer. If
     it is not, we issue a warning, but return normally. */
  if (val->type == GCONF_VALUE_INT) {
    /* It's an integer, get it and store. */
    *number = gconf_value_get_int(val);
    /* Mark that we've changed the integer behind 'number'. */
    hasChanged = TRUE;
  } else {
    g_warning("confGetInt: key %s is not an integer\n", key);
  }

  /* Free the type/value-pair. */
  gconf_value_free(val);
  val = NULL;

  return hasChanged;
}

/**
 * NEW
 *
 * Utility function to change the given color into the one that is
 * specified in application preferences.
 *
 * If some key is missing, that channel is left untouched. The
 * function also checks for proper values for the channels so that
 * invalid values are not accepted (guint16 range of GdkColor).
 *
 * Parameters:
 * - GdkColor*: the color structure to modify if changed from prefs.
 *
 * Returns:
 * - TRUE if the color was been changed by this routine.
 *   FALSE if the color wasn't changed (there was an error or the
 *   color was already exactly the same as in the preferences).
 */
static gboolean confLoadCurrentColor(GdkColor* color) {

  GConfClient* gcClient = NULL;
  /* Temporary holders for the pref values. */
  gint red = -1;
  gint green = -1;
  gint blue = -1;
  /* Temp variable to hold whether the color has changed. */
  gboolean hasChanged = FALSE;

  g_assert(color);

  g_print("confLoadCurrentColor: invoked\n");

  /* Open a connection to gconfd-2 (via d-bus). */
  gcClient = gconf_client_get_default();
  /* Make sure that it's a valid GConf-client object. */
  g_assert(GCONF_IS_CLIENT(gcClient));

  if (confGetInt(gcClient, GC_ROOT "red", &red)) {
    /* We got the value successfully, now clamp it. */
    g_print(" got red = %d, ", red);
    /* We got a value, so let's limit it between 0 and 65535 (the
       legal range for guint16). We use the CLAMP macro from GLib for
       this. */
    red = CLAMP(red, 0, G_MAXUINT16);
    g_print("after clamping = %d\n", red);
    /* Update & mark that at least this component changed. */
    color->red = (guint16)red;
    hasChanged = TRUE;
  }
  /* Repeat the same logic for the green component. */
  if (confGetInt(gcClient, GC_ROOT "green", &green)) {
    g_print(" got green = %d, ", green);
    green = CLAMP(green, 0, G_MAXUINT16);
    g_print("after clamping = %d\n", green);
    color->green = (guint16)green;
    hasChanged = TRUE;
  }
  /* Repeat the same logic for the last component (blue). */
  if (confGetInt(gcClient, GC_ROOT "blue", &blue)) {
    g_print(" got blue = %d, ", blue);
    blue = CLAMP(blue, 0, G_MAXUINT16);
    g_print("after clamping = %d\n", blue);
    color->blue = (guint16)blue;
    hasChanged = TRUE;
  }

  /* Release the client object (with GObject-unref). */
  g_object_unref(gcClient);
  gcClient = NULL;

  /* Return status if the color was been changed by this routine. */
  return hasChanged;
}

/**
 * MODIFIED
 *
 * Invoked when the user selects a color (or will cancel the dialog).
 *
 * Will also write the color to preferences (GConf) each time the
 * color changes. We'll compare whether it has really changed (to
 * avoid writing to GConf is nothing really changed).
 */
static void cbActionColorChanged(HildonColorButton* colorButton,
                                 ApplicationState* app) {

  /* Local variables that we'll need to handle the change (NEW). */
  gboolean hasChanged = FALSE;
  GdkColor newColor = {};
  GdkColor* curColor = NULL;

  g_assert(app != NULL);

  g_print("cbActionColorChanged invoked\n");
  /* Retrieve the new color from the color button (NEW). */
  hildon_color_button_get_color(colorButton, &newColor);
  /* Just an alias to save some typing (could also use
     app->currentColor) (NEW). */
  curColor = &app->currentColor;

  /* Check whether the color really changed (NEW). */
  if ((newColor.red   != curColor->red) ||
      (newColor.green != curColor->green) ||
      (newColor.blue  != curColor->blue)) {
    hasChanged = TRUE;
  }
  if (!hasChanged) {
    g_print(" color not really changed\n");
    return;
  }
  /* Color really changed, store to preferences (NEW). */
  g_print(" color changed, storing into preferences.. \n");
  confStoreColor(&newColor);
  g_print(" done.\n");

  /* Update the changed color into the application state. */
  app->currentColor = newColor;
}

  /*... Listing cut for brevity ...*/

/**
 * MODIFIED
 *
 * The color of the color button will be loaded from the application
 * preferences (or keep the default if preferences have no setting).
 */
static GtkWidget* buildToolbar(ApplicationState* app) {

  GtkToolbar*  toolbar = NULL;
  GtkToolItem* tbOpen = NULL;
  GtkToolItem* tbSave = NULL;
  GtkToolItem* tbSep = NULL;
  GtkToolItem* tbFind = NULL;
  GtkToolItem* tbColorButton = NULL;
  GtkWidget*   colorButton = NULL;

  g_assert(app != NULL);

  tbOpen = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
  tbSave = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
  tbSep  = gtk_separator_tool_item_new();
  tbFind = gtk_tool_button_new_from_stock(GTK_STOCK_FIND);

  tbColorButton = gtk_tool_item_new();
  colorButton = hildon_color_button_new();
  /* Copy the color from the color button into the application state.
     This is done to detect whether the color in preferences matches
     the default color or not (NEW). */
  hildon_color_button_get_color(HILDON_COLOR_BUTTON(colorButton),
                                &app->currentColor);
  /* Load preferences and change the color if necessary. */
  g_print("buildToolbar: loading color pref.\n");
  if (confLoadCurrentColor(&app->currentColor)) {
    g_print(" color not same as default one\n");
    hildon_color_button_set_color(HILDON_COLOR_BUTTON(colorButton),
                                  &app->currentColor);
  } else {
    g_print(" loaded color same as default\n");
  }
  gtk_container_add(GTK_CONTAINER(tbColorButton), colorButton);

  /*... Listing cut for brevity ...*/

}
