/*
 * Copyright (C) 2001-2017 the xine-project
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 *
 * stuff to run on first startup / setup wizards
 */

#include "globals.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>

#include "wizards.h"
#include "ui.h"
#include "utils.h"
#include "desktop_integration.h"
#include "preferences.h"

static GtkWidget *add_label (GtkBox *vbox, const char *label)
{
  GtkWidget *l = ui_label_new_with_markup (label);
  gtk_label_set_line_wrap (GTK_LABEL (l), TRUE);
#if GTK_CHECK_VERSION(3, 14, 0)
  gtk_label_set_xalign (GTK_LABEL (l), 0.0);
  gtk_label_set_yalign (GTK_LABEL (l), 0.5);
#else
  gtk_misc_set_alignment (GTK_MISC (l), 0, 0.5);
#endif
  gtk_box_pack_start (vbox, l, FALSE, FALSE, 5);
  return l;
}

static GtkWidget *add_heading (GtkBox *vbox, const char *label)
{
  gchar *markup = g_strdup_printf ("<big><b>%s</b></big>", label);
  GtkWidget *l = add_label (vbox, markup);
  g_free (markup);
  return l;
}

static gboolean do_welcome (GtkDialog *dlg, GtkBox *vbox)
{
  add_heading (vbox, _("Welcome to gxine!\n\n"));
  add_label (vbox, _("Would you like to run some setup wizards now "
		     "that will check your installation and maybe "
		     "do some adjustments for you if necessary?\n\n"
		     "If you do not want to run the wizards right now, "
		     "just click on the <i>Close</i> button and you will "
		     "not be bothered again.\n\n"
		     "You can always run the wizards (again) from the "
		     "help menu."));
  return FALSE;
};


typedef struct {
  const char *msg, *explanation;
} hc_msg_t;

static void details_cb (GtkWidget* widget, gpointer data)
{
  hc_msg_t *msg = data;
  display_info (FROM_GXINE, _("Health Check Results"), "%s\n%s",
		msg->msg, msg->explanation);
}

static void hc_update_pref (const char *pref, const char *value)
{
  xine_cfg_entry_t entry;

  if (!xine_config_lookup_entry (xine, pref, &entry))
    return;

  entry.str_value = (char *)value;
  preferences_update_entry (&entry);
}

static gboolean do_health_check (GtkDialog *dlg, GtkBox *vbox)
{
  GtkWidget           *l, *table, *b;
  xine_health_check_t  hc;
  xine_health_check_t *results;
  int                  check;
  hc_msg_t            *msg;

  table = gtk_table_new (1, 1, FALSE);

  add_heading (vbox, _("System configuration check"));
  add_label (vbox, _("The xine engine runs certain checks on your "
		     "system configuration. Results follow:"));

  /* If media.vcd.device is empty, set it to the default "/dev/cdrom" */
  hc.cdrom_dev = xine_config_register_string(xine,
      "media.vcd.device",
      "/dev/cdrom",
      _("device used for CD-ROM drive"),
      NULL, 0, NULL, NULL);

  /* Similarly for media.audio_cd.device */
  const char *audio_cd = xine_config_register_string(xine,
      "media.audio_cd.device",
      "/dev/cdrom",
      _("device used for CD-ROM drive"),
      NULL, 0, NULL, NULL);

  if (!audio_cd || !*audio_cd)
    audio_cd = hc.cdrom_dev;
  if (!audio_cd || !*audio_cd)
    audio_cd = "/dev/cdrom";
  if (!hc.cdrom_dev || !*hc.cdrom_dev)
    hc.cdrom_dev = audio_cd;

  /* If media.dvd.device is empty, set it to the default "/dev/dvd" */
  hc.dvd_dev = xine_config_register_string(xine,
      "media.dvd.device",
      "/dev/dvd",
      _("device used for DVD drive"),
      NULL, 0, NULL, NULL);

  if (!hc.dvd_dev || !*hc.dvd_dev)
    hc.dvd_dev = "/dev/dvd";

  hc_update_pref ("media.audio_cd.device", audio_cd);
  hc_update_pref ("media.vcd.device", hc.cdrom_dev);
  hc_update_pref ("media.dvd.device", hc.dvd_dev);

  /* Run tests */
  for (check = 0; ; ++check)
  {
    results = xine_health_check (&hc, check);

    if (results->status == XINE_HEALTH_CHECK_NO_SUCH_CHECK)
      break;

    gtk_table_resize (GTK_TABLE (table), 3, check+1);

    l = ui_label_new_with_xalign (results->title, 0);
    gtk_table_attach (GTK_TABLE (table), l, 0, 1, check, check+1, GTK_FILL,
		      GTK_SHRINK, 2, 5);

    switch (results->status)
    {
    case XINE_HEALTH_CHECK_UNSUPPORTED:
    case XINE_HEALTH_CHECK_OK:
      l = gtk_image_new_from_stock (GTK_STOCK_YES, GTK_ICON_SIZE_BUTTON);
      break;
    case XINE_HEALTH_CHECK_FAIL:
      msg = malloc (sizeof (hc_msg_t));

      msg->msg = results->msg;
      msg->explanation = results->explanation;

      b = gtk_button_new_with_label (_("Details"));
      g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (details_cb), msg);
      gtk_table_attach (GTK_TABLE (table), b, 2, 3, check, check+1, GTK_FILL,
			GTK_SHRINK, 2, 5);

      l = gtk_image_new_from_stock (GTK_STOCK_NO, GTK_ICON_SIZE_BUTTON);
      break;
    }

    gtk_table_attach (GTK_TABLE (table), l, 1, 2, check, check+1, GTK_FILL,
		      GTK_SHRINK, 2, 5);
  }

  gtk_box_pack_start (GTK_BOX(vbox), table, FALSE, FALSE, 5);
  return FALSE;
}

#ifdef USE_INTEGRATION_WIZARD
static struct {
  GtkToggleButton *moz, *gnome, *kde, *mailcap;
} cb;

static void integrate_cb (GtkButton *button, gpointer data)
{
  gboolean state = !!data;
  if (gtk_widget_is_sensitive (GTK_WIDGET(cb.mailcap)))
    gtk_toggle_button_set_active (cb.mailcap, state);
  if (gtk_widget_is_sensitive (GTK_WIDGET(cb.moz)))
    gtk_toggle_button_set_active (cb.moz, state);
  if (gtk_widget_is_sensitive (GTK_WIDGET(cb.gnome)))
    gtk_toggle_button_set_active (cb.gnome, state);
  if (gtk_widget_is_sensitive (GTK_WIDGET(cb.kde)))
    gtk_toggle_button_set_active (cb.kde, state);
}

static GtkToggleButton *
new_toggle (GtkBox *box, const char *label, gboolean active)
{
  GtkWidget *w = gtk_check_button_new_with_mnemonic (label);
  gtk_widget_set_sensitive (w, active);
  gtk_box_pack_start (box, w, FALSE, FALSE, 5);
  GtkToggleButton *b = GTK_TOGGLE_BUTTON (w);
  gtk_toggle_button_set_active (b, active);
  return b;
}

static gboolean ask_integration_wizard (GtkDialog *w, GtkBox *vbox)
{
  struct stat st;
  gchar *sname;
  GtkWidget *b, *hbox;
  GtkBox *vb = GTK_BOX (vbox);

  add_heading (vbox, _("Registration"));
  add_label (vbox, _("Register gxine with the following applications "
		     "as a media handler/helper:"));

  cb.mailcap = new_toggle (vb, "~_/.mailcap", TRUE);

  sname = g_build_filename (plugindir, "gxineplugin.so", NULL);
  cb.moz = new_toggle (vb, _("_Mozilla & Mozilla Firefox (plugin)"), !stat (sname, &st));
  g_free (sname);

  cb.gnome = new_toggle (vb, _("_GNOME, Nautilus"), gxine_vfs_init ());

  cb.kde = new_toggle (vb, _("_KDE, Konqueror"), TRUE);

  hbox = gtk_hbox_new (FALSE, 2);
  gtk_box_pack_start (vb, hbox, FALSE, FALSE, 2);

  b = ui_button_new_stock_mnemonic (GTK_STOCK_ADD, _("_All"));
  gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 2);
  g_signal_connect (G_OBJECT (b), "clicked",
		    (GCallback) integrate_cb, integrate_cb);

  b = ui_button_new_stock_mnemonic (GTK_STOCK_REMOVE, _("_None"));
  gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 2);
  g_signal_connect (G_OBJECT (b), "clicked",
		    (GCallback) integrate_cb, NULL);

  return FALSE;
}

static void desktop_integration (void)
{
  di_registration_flush ();
  if (gtk_toggle_button_get_active (cb.mailcap))
    di_register_mailcap();
  if (gtk_toggle_button_get_active (cb.gnome))
    di_register_gnome();
  if (gtk_toggle_button_get_active (cb.kde))
    di_register_kde();
  if (gtk_toggle_button_get_active (cb.moz))
    di_register_mozilla();
}

static gboolean report_integration_wizard (GtkDialog *w, GtkBox *vbox)
{
  const char *report = di_registration_report ();
  if (!report)
    return TRUE;

  add_heading (vbox, _("Registration report"));
  add_label (vbox, _("There were some problems during registration."));
  add_label (vbox, report);
  di_registration_flush ();

  return FALSE;
}
#endif

#define RESPONSE_CLOSE 0
#define RESPONSE_NEXT  1

static gboolean close_cb (GtkWidget* widget, gpointer data)
{
  int *b = data;
  *b = RESPONSE_CLOSE;
  gtk_main_quit();

  return TRUE;
}

static void response_cb (GtkDialog *dbox, int response, gpointer data)
{
  int *b = data;
  switch (response)
  {
  case GTK_RESPONSE_ACCEPT:
    *b = RESPONSE_NEXT;
    break;
  default:
    *b = RESPONSE_CLOSE;
  }
  gtk_main_quit();
}

#define WIZARDS_LEVEL 6

void run_wizards (gboolean requested)
{
  static GtkDialog *dlg = NULL;

  if (dlg)
  {
    gtk_window_set_modal (GTK_WINDOW (dlg), FALSE);
    gtk_window_present (GTK_WINDOW(dlg));
    return;
  }

  if (requested
      || (xine_config_register_num (xine, "misc.wizards_shown", 0,
				    _("Keep track of whether user has seen wizards yet"),
				    NULL, 32767, NULL, NULL) < WIZARDS_LEVEL))
  {
    xine_cfg_entry_t  entry;
    int b, state;

    static const struct {
      gboolean (*create) (GtkDialog *, GtkBox *);  /* TRUE to skip a stage */
      void     (*action) (void);
    } wizards[] = {
      { do_welcome, NULL },
      { do_health_check, NULL },
#ifdef USE_INTEGRATION_WIZARD
      { ask_integration_wizard, desktop_integration },
      { report_integration_wizard, NULL },
#endif
      { NULL }
    };

    if (xine_config_lookup_entry (xine, "misc.wizards_shown", &entry))
    {
      entry.num_value = WIZARDS_LEVEL;
      preferences_update_entry (&entry);
    }

    /* set up dialog which all wizards will use */

    dlg = GTK_DIALOG (gtk_dialog_new_with_buttons (_("gxine setup wizards"), NULL, 0,
				       GTK_STOCK_GO_FORWARD, GTK_RESPONSE_ACCEPT,
				       GTK_STOCK_CLOSE, GTK_RESPONSE_DELETE_EVENT,
				       NULL));
    gtk_dialog_set_default_response (dlg, GTK_RESPONSE_ACCEPT);
    gtk_window_set_default_size (GTK_WINDOW (dlg), -1, 400);
    gtk_window_set_modal (GTK_WINDOW (dlg), !requested);
    g_object_connect (G_OBJECT (dlg),
	"signal::delete-event", G_CALLBACK (close_cb), &b,
	"signal::response", G_CALLBACK (response_cb), &b,
	NULL);

    /*
     * contents: headline, separator, wizard-specific part
     */

    /* headline */
    {
      GtkWidget *w, *b = gtk_hbox_new (0, 0);
      gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (dlg)),
                          b, FALSE, FALSE, 2);
      gtk_box_pack_start (GTK_BOX (b),
			  gtk_image_new_from_stock (GXINE_LOGO, icon_size_logo),
			  FALSE, FALSE, 2);
      w = ui_label_new_with_markup (_("<span face='serif' size='xx-large' weight='bold'>Welcome!</span>"));
      gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 2);
    }

    gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (dlg)),
#if GTK_CHECK_VERSION(3, 2, 0)
                        gtk_separator_new (GTK_ORIENTATION_HORIZONTAL),
#else
                        gtk_hseparator_new (),
#endif
                        FALSE, FALSE, 5);
    window_show (GTK_WIDGET (dlg), NULL);

    state = -1;
    while (1)
    {
      GtkWidget *content = gtk_vbox_new (0, 0);
      gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (dlg)),
                          content, TRUE, TRUE, 0);

      if (wizards[++state].create)
      {
	if (wizards[state].create (dlg, GTK_BOX (content)))
	  goto next;
      }
      else
      {
	gtk_dialog_set_response_sensitive (dlg, GTK_RESPONSE_ACCEPT, FALSE);
	add_heading (GTK_BOX (content), _("Setup completed."));
      }

      gtk_widget_show_all (content);
      gtk_main ();

      if (b == RESPONSE_CLOSE)
	break;

      if (wizards[state].action)
	wizards[state].action ();

      next:
      gtk_container_remove (GTK_CONTAINER (gtk_dialog_get_content_area (dlg)),
                            content);
    }

    gtk_widget_destroy (GTK_WIDGET (dlg));
    dlg = NULL;
  }
}

static JSBool js_run_wizards (JSContext *cx, uintN argc, jsval *vp)
{
  /* se_t *se = (se_t *) JS_GetContextPrivate(cx); */
  se_log_fncall_checkinit ("run_wizards");
  run_wizards (TRUE);
  JS_SET_RVAL (cx, vp, JSVAL_VOID);
  return JS_TRUE;
}

void wizards_init (void)
{
  se_defun (gse, NULL, "run_wizards", js_run_wizards, 0, JSFUN_FAST_NATIVE,
	    SE_GROUP_HIDDEN, NULL, NULL);
}
