/*
 * Copyright (C) 2008 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as 
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authored by Neil Jagdish Patel <neil.patel@canonical.com>
 *
 */

#include <glib.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <gio/gio.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-bindings.h>

#include <gconf/gconf.h>
#include <gconf/gconf-client.h>

#include "launcher-config.h"
#include "launcher-defines.h"

G_DEFINE_TYPE (LauncherConfig, launcher_config, G_TYPE_OBJECT)

#define LAUNCHER_CONFIG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE(obj, \
        LAUNCHER_TYPE_CONFIG, LauncherConfigPrivate))

#define LANDSCAPE_CATS 13
#define PORTRAIT_CATS  20

#define FONT_DPI "/desktop/gnome/font_rendering/dpi"

struct _LauncherConfigPrivate
{
  GdkScreen   *screen;  
  GtkSettings *settings;
  GConfClient *client;

  GFile        *watch_dir;
  GFileMonitor *dir_monitor;

  DBusGConnection *connection;
  DBusGProxy      *proxy;
};

enum
{
  CONFIG_CHANGED,
  RESUME_EVENT,

  LAST_SIGNAL
};
static guint _config_signals[LAST_SIGNAL] = { 0 };


static gint
get_rows_for_height (gint height)
{
  gint rows = 3;

  if (height < 600)
    rows = 3;
  else if (height < 1025)
    rows = 4;
  else
    rows = 5;

  return rows;
}

static gint
get_cols_for_width (gint width)
{
  gint cols = 3;

  if (width < 801)
    cols = 3;
  else if (width < 1025)
    cols = 4;
  else
    cols = 5;

  return cols;
}

static void
calculate_sizes (LauncherConfig *cfg)
{
#define LAUNCHER_DIR "/apps/netbook-launcher"
#define TABLET_MODE LAUNCHER_DIR "/tablet_mode"
  GConfClient *client = gconf_client_get_default ();
  gint width;
  gint height;
  gint n_cols;     
  gint n_cats;
  gint padding;

  width = CSW();
  height = CSH();

  if (width < height)
  {
    cfg->is_portrait = TRUE;
    n_cols = 4;
    n_cats = PORTRAIT_CATS + 1;
    padding = 0;
  }
  else
  {
    cfg->is_portrait = FALSE;
    n_cols = 5;
    n_cats = LANDSCAPE_CATS;
    padding = PADDING;
  }

  /* tablet mode checking */
  cfg->tablet_mode = gconf_client_get_bool (client, TABLET_MODE, NULL);

  if (cfg->tablet_mode && !cfg->is_portrait)
  {
    n_cats = LANDSCAPE_CATS - 1;
  }

  cfg->win_width = width - 2* padding;
  cfg->win_height = CSH () - PANEL_HEIGHT - (2*padding);
  cfg->bar_width = cfg->win_width/n_cols;
  cfg->n_cats = n_cats;
  
  cfg->iconview_rows = cfg->is_portrait ? 7 : get_rows_for_height (height);
  cfg->iconview_cols = get_cols_for_width (width);
  cfg->iconview_width = cfg->bar_width * 3;
  cfg->iconview_height = cfg->win_height-(PANEL_HEIGHT*1.3) - PADDING;
  cfg->iconview_padding = 0;
 
  cfg->icon_width = (cfg->iconview_width - ((cfg->iconview_cols+1)*cfg->iconview_padding)) / cfg->iconview_cols;
  
  cfg->icon_height = (cfg->iconview_height - ((cfg->iconview_rows+1)*cfg->iconview_padding))/cfg->iconview_rows;

  cfg->shortcut_height = (cfg->win_height/n_cats);

  if (cfg->is_portrait)
    g_debug ("CONFIG: Running in portrait mode");

  if (cfg->tablet_mode)
    g_debug ("CONFIG: Running in tablet mode");

  g_object_unref (client);
}


static void
set_dpi (LauncherConfig *config)
{
  ClutterBackend *backend = clutter_get_default_backend ();
  gfloat dpi;
  cairo_font_options_t *options;

  dpi = gconf_client_get_float (config->priv->client, FONT_DPI, NULL);

  if (dpi < 2)
    dpi = 96.0;
  
  clutter_backend_set_resolution (backend, (gint)dpi);

  options = clutter_backend_get_font_options (backend);

  cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_SUBPIXEL);

  cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_FULL);

  cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);

  cairo_font_options_set_subpixel_order (options, CAIRO_SUBPIXEL_ORDER_RGB);
  
  clutter_backend_set_font_options (backend, options);
  
  g_debug ("CONFIG: Font dpi: %f", dpi);}

static void
set_font (LauncherConfig *config)
{
  gchar *temp;

  g_object_get (config->priv->settings, "gtk-font-name", &temp, NULL);

  memcpy (config->font_name, temp, strlen (temp)+1);

  g_debug ("CONFIG: Font changed: %s\n", config->font_name);

  g_free (temp);
}

/* Callbacks */
static void
on_font_changed (GtkSettings    *settings,
                 GParamSpec     *spec,
                 LauncherConfig *config)
{
  set_font (config);

  g_signal_emit (config, _config_signals[CONFIG_CHANGED], 0);
}

static void
on_font_dpi_changed (GConfClient    *client,
                     guint           cnxn_id,
                     GConfEntry     *entry,
                     LauncherConfig *config)
{
  set_dpi (config);


  g_signal_emit (config, _config_signals[CONFIG_CHANGED], 0);
}

static void
on_single_instance_changed (GConfClient    *client,
                            guint           cnxn_id,
                            GConfEntry     *entry,
                            LauncherConfig *config)
{
  config->disable_single_instance = gconf_client_get_bool (client,
                              "/apps/netbook-launcher/disable_single_instance",
                                                            NULL);
}
/*
 * Session switching signals
 */
static gboolean
clean_up_dbus (LauncherConfig *cfg)
{
  g_object_unref (cfg->priv->proxy);
  dbus_g_connection_unref (cfg->priv->connection);

  return FALSE;
}

static void
on_session_active_changed (DBusGProxy     *proxy, 
                           gboolean        active, 
                           LauncherConfig *cfg)
{
  g_debug ("CONFIG: ActiveChanged: %s", active ? "Active" : "Inactive");
  
  if (active)
  {
    dbus_g_proxy_disconnect_signal (proxy, "ActiveChanged", 
                                    G_CALLBACK (on_session_active_changed), 
                                    cfg);
    g_signal_emit (cfg, _config_signals[RESUME_EVENT], 0);
    g_idle_add ((GSourceFunc)clean_up_dbus, cfg);
  }
}

/*
 * Low graphics mode
 */
static gboolean
get_low_graphics_mode (LauncherConfig *config)
{
  gchar    *argv[] = { "glxinfo", NULL };
  gchar    *output = NULL;
  gchar    *error = NULL;
  gint      exit_status = 0;
  gboolean  low_graphics = FALSE;
      
  g_spawn_sync (NULL, argv, NULL, 
                G_SPAWN_SEARCH_PATH, NULL, NULL, 
                &output, &error, &exit_status, NULL);
  if (output)
  {
    GInputStream     *istream;
    GDataInputStream *dstream;
    gchar            *line = NULL;
    gsize             len = 0;

    istream = g_memory_input_stream_new_from_data (output,
                                                   g_utf8_strlen (output, -1),
                                                   g_free);
    dstream = g_data_input_stream_new (istream);
    while ((line = g_data_input_stream_read_line (dstream, &len, NULL, NULL))
           != NULL)
    {
      if (g_strstr_len (line, len, "direct rendering"))
      {
        if (g_strstr_len (line, len, "No"))
          low_graphics = TRUE;
        break;
      }

    }
    g_object_unref (dstream);
    g_object_unref (istream);
  }

  if (gconf_client_get_bool (config->priv->client, 
                             "/apps/netbook-launcher/force_low_graphics", 
                             NULL))
  {
    g_debug ("CONFIG: Forcing low graphics mode from GConf");
    low_graphics = TRUE;
  }

  g_debug ("CONFIG: Low graphics mode: %s", low_graphics ? "True" : "False");
  return low_graphics;
}

/* GObject functions */
static void
launcher_config_dispose (GObject *object)
{
  LauncherConfig *config = LAUNCHER_CONFIG (object);
  LauncherConfigPrivate *priv;
  
  g_return_if_fail (LAUNCHER_IS_CONFIG (config));
  priv = LAUNCHER_CONFIG (config)->priv;

  g_object_unref (priv->dir_monitor);
  g_object_unref (priv->watch_dir);

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

static void
launcher_config_class_init (LauncherConfigClass *klass)
{
  GObjectClass *obj_class = G_OBJECT_CLASS (klass);

  obj_class->dispose = launcher_config_dispose;

	_config_signals[CONFIG_CHANGED] =
		g_signal_new ("config-changed",
			      G_OBJECT_CLASS_TYPE (obj_class),
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (LauncherConfigClass, config_changed),
			      NULL, NULL,
			      g_cclosure_marshal_VOID__VOID, 
			      G_TYPE_NONE, 0);

	_config_signals[RESUME_EVENT] =
		g_signal_new ("resume-event",
        G_OBJECT_CLASS_TYPE (obj_class),
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (LauncherConfigClass, resume_event),
			      NULL, NULL,
			      g_cclosure_marshal_VOID__VOID, 
			      G_TYPE_NONE, 0);


 
  g_type_class_add_private (obj_class, sizeof (LauncherConfigPrivate)); 
}

static void
launcher_config_init (LauncherConfig *config)
{
  LauncherConfigPrivate *priv;
  DBusGConnection       *connection;
  DBusGProxy            *proxy;
  GError                *error = NULL;
  const gchar           *restartenv;
  gboolean               norestart = FALSE;
         
  priv = config->priv = LAUNCHER_CONFIG_GET_PRIVATE (config);

  priv->client = gconf_client_get_default ();

  priv->settings = gtk_settings_get_default ();

  config->low_graphics = get_low_graphics_mode (config);
  
  set_dpi (config);
  gconf_client_notify_add (priv->client, FONT_DPI,
                           (GConfClientNotifyFunc)on_font_dpi_changed,
                           config, NULL, NULL);

  set_font (config);
  g_signal_connect (priv->settings, "notify::gtk-font-name",
                    G_CALLBACK (on_font_changed), config);

  config->disable_single_instance = gconf_client_get_bool (priv->client,
                               "/apps/netbook-launcher/disable_single_instance",
                                                         NULL);
  gconf_client_notify_add (priv->client,
                           "/apps/netbook-launcher/disable_single_instance",
                           (GConfClientNotifyFunc)on_single_instance_changed,
                           config, NULL, NULL);
   
  /* Now load in all the calculations */
  calculate_sizes (config);

  restartenv = g_getenv ("LAUNCHER_NORESTART");
  norestart = restartenv ? atoi (restartenv) : FALSE;

  if (norestart)
  {
    g_debug ("CONFIG: Not connecting to ConsoleKit as \"--no-restart\" requested");
    return;
  }
  g_debug ("CONFIG: Connecting to ConsoleKit for suspend/resume restarting");

  /* Session switch watching */
  priv->connection = connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);

  if (error)
  {
    g_warning ("%s", error->message);
    g_error_free(error);
    return;
  }
  /* Get the current session object */
  proxy = priv->proxy = dbus_g_proxy_new_for_name(connection,
                                    "org.freedesktop.ConsoleKit",
                                    "/org/freedesktop/ConsoleKit/Manager",
                                    "org.freedesktop.ConsoleKit.Manager");

  if (!proxy)
  {
    g_warning ("Unable to get the ConsoleKit manager.");
    dbus_g_connection_unref (connection);
    return;
  }
  if (!dbus_g_proxy_call (proxy, "GetCurrentSession", &error, 
                          G_TYPE_INVALID, 
                          DBUS_TYPE_G_PROXY, &priv->proxy,
                          G_TYPE_INVALID))
  {
    g_warning ("Unable to GetCurrentSession from ConsoleKit.Manager");
    g_object_unref (proxy);
    dbus_g_connection_unref (connection);
    return;
  }
  
  dbus_g_proxy_set_interface (priv->proxy, 
                              "org.freedesktop.ConsoleKit.Session");
  dbus_g_proxy_add_signal (priv->proxy, "ActiveChanged",
                           G_TYPE_BOOLEAN, G_TYPE_INVALID);
  dbus_g_proxy_connect_signal (priv->proxy, "ActiveChanged", 
                               G_CALLBACK (on_session_active_changed), config, 
                               NULL);

  g_object_unref (proxy);
}

LauncherConfig*
launcher_config_get_default (void)
{
  static LauncherConfig *config = NULL;
  
  if (config == NULL)
    config = g_object_new (LAUNCHER_TYPE_CONFIG, 
                            NULL);

  return config;
}

gint 
launcher_config_get_n_cats (LauncherConfig *cfg)
{
  return cfg->n_cats;
}
