/*
 * Copyright (C) 2007 Intel
 *
 * 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 <gdk/gdkx.h>
#include <gdk/gdk.h>

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

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include <libgnome/gnome-desktop-item.h>

#include "launcher-config.h"
#include "launcher-defines.h"
#include "launcher-sidebar.h"
#include "launcher-startup.h"
#include "launcher-menu.h"
#include "launcher-util.h"
#include "launcher-wm.h"

G_DEFINE_TYPE (LauncherStartup, launcher_startup, G_TYPE_OBJECT)

#define LAUNCHER_STARTUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE(obj, \
        LAUNCHER_TYPE_STARTUP, LauncherStartupPrivate))

#define TIMEOUT 4000 /* Miliseconds */
#define SN_TIMEOUT 10000

struct _LauncherStartupPrivate
{
  GdkWindow *root_window;
  GdkCursor *normal;
  GdkCursor *busy;

  guint tag;
  guint wm_tag;
};

/*
 * Public functions
 */

static void
load_app_settings (LauncherMenuApplication   *app, 
                   gboolean                  *sn_enable)
                   
{
  GKeyFile *key_file;
  GError *error = NULL;
  const gchar *filename;

  g_return_if_fail (app);

  key_file = g_key_file_new ();
  filename = launcher_menu_application_get_desktop_filename (app);

  if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error))
  {
    g_key_file_free (key_file);
    g_warning ("Unable to load Desktop File values: %s", error->message);
    g_error_free (error);
    return;
  }

  *sn_enable = g_key_file_get_boolean (key_file, "Desktop Entry", 
                                            "StartupNotify", NULL);

  g_key_file_free (key_file);
}

static gboolean
sn_timeout (LauncherStartup *startup)
{
  LauncherStartupPrivate *priv;

  g_return_val_if_fail (LAUNCHER_IS_STARTUP (startup), FALSE);
  priv = startup->priv;

  gdk_window_set_cursor (gdk_get_default_root_window (), priv->normal);
  
  return FALSE;
}

static void
on_window_opened (LauncherWm      *wm, 
                  WnckWindow      *window, 
                  LauncherStartup *startup)
{
  LauncherStartupPrivate *priv;

  g_return_if_fail (LAUNCHER_IS_STARTUP (startup));
  priv = startup->priv;

  gdk_window_set_cursor (gdk_get_default_root_window (), priv->normal);

  if (!wnck_window_is_active (window))
  {
    wnck_window_activate (window, CLUTTER_CURRENT_TIME);    
  }

  g_signal_handler_disconnect (wm, priv->wm_tag);
} 

gboolean
launcher_startup_launch_app (LauncherStartup          *startup, 
                             LauncherMenuApplication *app)
{
  LauncherStartupPrivate *priv;
  LauncherConfig *cfg = launcher_config_get_default ();
  GnomeDesktopItem *item;
  GError *error = NULL;
  gboolean sn_enable = FALSE;;
  gint pid;

  g_return_val_if_fail (LAUNCHER_IS_STARTUP (startup), FALSE);
  priv = startup->priv;
  
  /* Signal instance checking 
  window = launcher_menu_application_get_window (app);
  if (WNCK_IS_WINDOW (window) && !cfg->disable_single_instance)
  {
    wnck_window_activate (window, CLUTTER_CURRENT_TIME);
    return TRUE;
  }
  */

  if (app->volume)
  {
    launcher_sidebar_launch_volume (LAUNCHER_SIDEBAR 
                                            (launcher_sidebar_get_default ()), 
                                    (GVolume*)app->volume);
    return TRUE;
  }

  if (app->path == NULL)
  {
    gdk_spawn_command_line_on_screen (gdk_screen_get_default (), 
                                      app->exec, NULL);
    return TRUE;
  }

  /* Check to see if we've already opened an application, and if so, switch
   * to it
   */
  if (!cfg->disable_single_instance 
      && launcher_menu_application_get_pid (app) >= 1)
  {
    GList *windows, *w;
    gint pid;
    gboolean found = FALSE;

    windows = wnck_screen_get_windows (wnck_screen_get_default ());
    pid = launcher_menu_application_get_pid (app);
    
    for (w = windows; w; w = w->next)
    {
      WnckWindow *win = w->data;

      if (pid == wnck_window_get_pid (win))
      {
        guint32 ev_t = launcher_util_present_time (wnck_window_get_xid (win));
        wnck_window_activate_transient (win, ev_t);
        found = TRUE;
      }
    }

    if (found)
      return FALSE;
  }

  /* Load the important settings from the desktop file if not already done */
  load_app_settings (app, &sn_enable);

  item = gnome_desktop_item_new_from_file (
        launcher_menu_application_get_desktop_filename (app),
                                         GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS,
                                           &error);

  if (error)
  {
    g_warning ("Cannot launch %s: %s", 
               launcher_menu_application_get_exec (app), error->message);
    g_error_free (error);
    return FALSE;
  }
  if (!item)
  {
    g_warning ("Unable to open: %s\n", 
               launcher_menu_application_get_desktop_filename (app));
    return FALSE;
  }

  /* Execute the program */
  pid = gnome_desktop_item_launch (item, NULL, 0, &error);

  if (error)
  {
    g_warning ("Cannot launch %s: %s", 
               launcher_menu_application_get_exec (app), error->message);
    g_error_free (error);
    gnome_desktop_item_unref (item);
    return FALSE;
  }
  
  launcher_menu_application_set_pid (app, pid);
  
  /* 
   * Let the program know that the launch has started, Sn apps get a longer
   * timeout
   */
  if (sn_enable)
    priv->tag = g_timeout_add (SN_TIMEOUT, (GSourceFunc)sn_timeout, startup);
  else
  {
    gdk_window_set_cursor (gdk_get_default_root_window (), priv->busy);
    priv->tag = g_timeout_add (TIMEOUT, (GSourceFunc)sn_timeout, startup);
    priv->wm_tag = g_signal_connect (launcher_wm_get_default (), "show-windec",
                                     G_CALLBACK (on_window_opened), startup);
  }

  gnome_desktop_item_unref (item);

  return TRUE;
}

/* GObject functions */
static void
launcher_startup_dispose (GObject *object)
{
  LauncherStartup *startup = (LauncherStartup*)object;
  LauncherStartupPrivate *priv;
  
  g_return_if_fail (LAUNCHER_IS_STARTUP (startup));
  priv = LAUNCHER_STARTUP (startup)->priv;  
  
  gdk_window_set_cursor (gdk_get_default_root_window (), priv->normal);

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

static void
launcher_startup_finalize (GObject *startup)
{
  G_OBJECT_CLASS (launcher_startup_parent_class)->finalize (startup);
}


static void
launcher_startup_class_init (LauncherStartupClass *klass)
{
  GObjectClass *obj_class = G_OBJECT_CLASS (klass);

  obj_class->finalize = launcher_startup_finalize;
  obj_class->dispose = launcher_startup_dispose;

  g_type_class_add_private (obj_class, sizeof (LauncherStartupPrivate)); 
}

static void
launcher_startup_init (LauncherStartup *startup)
{
  LauncherStartupPrivate *priv;
    
  priv = startup->priv = LAUNCHER_STARTUP_GET_PRIVATE (startup);

  priv->busy = gdk_cursor_new (GDK_WATCH);
  priv->normal = gdk_cursor_new (GDK_LEFT_PTR);  
}

LauncherStartup*
launcher_startup_get_default (void)
{
  static LauncherStartup *startup = NULL;
  
  if (startup == NULL)
    startup = g_object_new (LAUNCHER_TYPE_STARTUP, 
                            NULL);

  return startup;
}
