/*
 * private/ui.c - Plain TTY based user interface routines.
 */
/*
 * This file is part of Open Sound System
 *
 * Copyright (C) 4Front Technologies 1996-2007.
 *
 * This software is released under CDDL 1.0 source license for operating systems
 * that have their full source code available under the BSD or CDDL licenses.
 *
 * See the COPYING file included in the main directory of this source
 * distribution. Please contact sales@opensound.com for further info.
 */

/*
 * NOTE! This is not the development version of this file. Please download the
 * sources of the latest Open Sound System version from 
 * http://developer.opensound.com/sources if you plan to make any changes
 * to the software.
 */

#ifdef __hpux
#define G_INLINE_FUNC
#endif

#ifdef UI_DYNAMIC
#define ui_check		ui_check_X
#define ui_init			ui_init_X
#define ui_close		ui_close_X
#define ui_message		ui_message_X
#define ui_input		ui_input_X
#define ui_sel_run		ui_sel_run_X
#define ui_sel_close_window	ui_sel_close_window_X
#endif

#define PLUGIN
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#ifdef CHECK_X
#include <X11/Xlib.h>
#endif
#include <gtk/gtk.h>
#include "ui.h"

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

static int ui_opened = 0;
static GtkWidget *main_window = NULL, *topwin = NULL;
static GtkWidget *statusbar = NULL;
static int have_exited = 0;
static int dialog_exited = 0;
static int input_exited = 0;

static void
close_all (GtkWidget * window, gpointer data)
{
  if (have_exited)
    return;
  have_exited = 1;
  gtk_main_quit ();
}

static void
close_dialog (GtkWidget * window, gpointer data)
{
  if (dialog_exited)
    return;
  gtk_main_quit ();
}

static void
close_input (GtkWidget * window, gpointer data)
{
  if (input_exited)
    return;
  gtk_main_quit ();
}

static void
init_mainwin (void)
{
  topwin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size (GTK_WINDOW (topwin), 640, 480);
  gtk_window_set_position (GTK_WINDOW (topwin), GTK_WIN_POS_CENTER);
  main_window = gtk_vbox_new (FALSE, 1);
  gtk_container_add (GTK_CONTAINER (topwin), main_window);
  gtk_widget_show (main_window);
  gtk_signal_connect (GTK_OBJECT (topwin),
		      "destroy", GTK_SIGNAL_FUNC (close_all), NULL);
  statusbar = gtk_statusbar_new ();
  gtk_widget_show (statusbar);
  gtk_box_pack_end (GTK_BOX (main_window), statusbar, FALSE, FALSE, 0);
  gtk_widget_show (topwin);
}

static int status_pending = 0;

static gint
poll_status (gpointer data)
{
  if (status_pending)
    gtk_main_quit ();
  status_pending = 0;
  return FALSE;
}

static void
flash_pixmap (void)
{
#include "oss.xpm"

  GtkWidget *win;
  GtkWidget *pic;
  GdkPixmap *pixmap;
  GdkBitmap *mask;

  win = gtk_window_new (GTK_WINDOW_POPUP);
  gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER);

  pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL,
						  gtk_widget_get_default_colormap
						  (), &mask, NULL,
						  (gchar **) oss);
  pic = gtk_pixmap_new (pixmap, mask);
  gtk_container_add (GTK_CONTAINER (win), pic);

  gtk_widget_show_all (win);

  status_pending = 1;
  gtk_timeout_add (3000, poll_status, NULL);
  gtk_main ();
  status_pending = 0;
  gtk_widget_destroy (win);
}

int
ui_init (char *path)
{
/*
 * Initialize the display handling package.
 */

  ui_opened = 1;

  gtk_init (0, NULL);
  flash_pixmap ();
  init_mainwin ();
  return 0;
}

void
ui_close (void)
{
  if (!ui_opened)
    return;
  ui_opened = 0;
  have_exited = 1;
  gtk_widget_hide_all (main_window);
  gtk_widget_unrealize (main_window);
  gtk_widget_destroy (main_window);
  gtk_widget_destroy (topwin);
}

static long global_event = UI_NONEID;
static int pending_event = UI_NONEID;
static int active_event = UI_NONEID;
static char global_input[4096];

static void
handle_event (GtkWidget * window, gpointer data)
{
  global_event = (long) data;
  gtk_main_quit ();
}

static void
handle_input (GtkWidget * window, gpointer data)
{
  strcpy (global_input, gtk_entry_get_text (GTK_ENTRY (data)));
  gtk_main_quit ();
}

static gint
clear_event (gpointer data)
{
  pending_event = UI_NONEID;
  return FALSE;
}

static void
clear_status (void)
{
  guint context;

  if (statusbar == NULL)
    {
      return;
    }

  context = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "Main");
  gtk_statusbar_push (GTK_STATUSBAR (statusbar), context, "");
  gtk_widget_show_all (topwin);
  gtk_timeout_add (2000, poll_status, NULL);
}

static void
update_statusbar (char *s)
{
  guint context;

  if (statusbar == NULL)
    {
      return;
    }

  context = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "Main");
  gtk_statusbar_push (GTK_STATUSBAR (statusbar), context, s);
  gtk_widget_show_all (topwin);
  gtk_timeout_add (10, poll_status, NULL);
  status_pending = 1;
  gtk_main ();
  status_pending = 0;
}

void
ui_message (char *s, int pause)
{
  GtkWidget *win;
  GtkWidget *label;
  GtkWidget *button;
  GtkWidget *dialog = NULL;

  if (!pause)
    {
      update_statusbar (s);
      return;
    }

  clear_status ();

  dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size (GTK_WINDOW (dialog), 320, 240);
  gtk_window_set_title (GTK_WINDOW (dialog), "Message");
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
  win = gtk_vbox_new (FALSE, 1);
  gtk_container_add (GTK_CONTAINER (dialog), win);
  gtk_widget_show (win);

  label = gtk_label_new (s);
  gtk_box_pack_start (GTK_BOX (win), label, TRUE, TRUE, 0);
  gtk_widget_show (label);

  button = gtk_button_new_with_label ("OK");
  gtk_box_pack_end (GTK_BOX (win), gtk_hseparator_new (), FALSE, FALSE, 10);
  gtk_box_pack_end (GTK_BOX (win), button, FALSE, FALSE, 10);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (handle_event), 0);
  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
  gtk_widget_show_all (dialog);
  gtk_signal_connect (GTK_OBJECT (dialog),
		      "destroy", GTK_SIGNAL_FUNC (close_dialog), NULL);
  dialog_exited = 0;
  gtk_main ();
  dialog_exited = 1;
  gtk_widget_hide (dialog);
}

/*
 * ui_input is a modal input dialog function. question is pointer to a
 * (multi line) prompt string. This text is displayed above the input field.
 * answer points to a string variable where the input is returned.
 */
int
ui_input (char *question, char *answer)
{
  char *s = question;

  GtkWidget *win;
  GtkWidget *label;
  GtkWidget *input;
  GtkWidget *button;
  GtkWidget *dialog = NULL;

  clear_status ();

  dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size (GTK_WINDOW (dialog), 320, 240);
  gtk_window_set_title (GTK_WINDOW (dialog), "Message");
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
  win = gtk_vbox_new (FALSE, 1);
  gtk_container_add (GTK_CONTAINER (dialog), win);
  gtk_widget_show (win);

  label = gtk_label_new (s);
  gtk_box_pack_start (GTK_BOX (win), label, TRUE, TRUE, 0);
  gtk_widget_show (label);

  input = gtk_entry_new ();
  gtk_entry_set_text (GTK_ENTRY (input), answer);
  gtk_box_pack_start (GTK_BOX (win), input, TRUE, TRUE, 0);
  gtk_widget_show (input);

  button = gtk_button_new_with_label ("OK");
  gtk_box_pack_end (GTK_BOX (win), gtk_hseparator_new (), FALSE, FALSE, 10);
  gtk_box_pack_end (GTK_BOX (win), button, FALSE, FALSE, 10);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (handle_input), input);
  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
  gtk_widget_show_all (dialog);
  gtk_signal_connect (GTK_OBJECT (dialog),
		      "destroy", GTK_SIGNAL_FUNC (close_input), NULL);
  input_exited = 0;
  *global_input = 0;
  gtk_main ();
  strcpy (answer, global_input);
  *global_input = 0;
  input_exited = 1;
  gtk_widget_hide (dialog);
}

typedef struct
{
  void *ptr;
  int id;
}
item_entry;

#define MAXITEMS 4096

static int nitems = 0;
static item_entry items[MAXITEMS];

static void
item_selected (GtkWidget * window, gpointer data)
{
  int i;

  for (i = 0; i < nitems; i++)
    if (items[i].ptr == data)
      {
	global_event = items[i].id;

	if (global_event == pending_event)
	  {
	    pending_event = UI_NONEID;
	    active_event = UI_NONEID;
	    gtk_main_quit ();
	  }

	pending_event = active_event = global_event;
	gtk_timeout_add (1000, clear_event, NULL);
	return;
      }
}

/*
 * Should not be here but don't know any better place.
 */
static void
ui_sel_setactive_byid (ui_selection * sel, int id)
{
  int i;

  for (i = 0; i < sel->nchoices; i++)
    if (sel->choices[i].id == id)
      {
	sel->active = i;
	return;
      }
}

int
ui_sel_run (ui_selection * sel)
{
#include "oss_small.xpm"

  int i, saved_items;

  GtkWidget *win = NULL, *scrollwin, *tmpbox, *tmpframe, *headbox;
  GtkWidget *listbox, *listitem, *buttonbox, *button;
  GtkWidget *pic;
  GdkPixmap *pixmap;
  GdkBitmap *mask;
  GtkWidget *label;
  char labels[8000];

  pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL,
						  gtk_widget_get_default_colormap
						  (), &mask, NULL,
						  (gchar **) oss_small);
  pic = gtk_pixmap_new (pixmap, mask);

  clear_status ();
  win = gtk_vbox_new (FALSE, 1);
  tmpbox = gtk_hbox_new (FALSE, 1);
  headbox = gtk_vbox_new (FALSE, 1);
  gtk_box_pack_start (GTK_BOX (win), tmpbox, FALSE, FALSE, 0);
  if (*sel->hdrdesc)
    {
      tmpframe = gtk_frame_new (sel->hdrdesc);
      /* gtk_frame_set_shadow_type(GTK_FRAME(tmpframe), GTK_SHADOW_NONE); */
      gtk_box_pack_start (GTK_BOX (tmpbox), tmpframe, FALSE, TRUE, 0);
      gtk_container_add (GTK_CONTAINER (tmpframe), headbox);
    }
  else
    {
      gtk_box_pack_start (GTK_BOX (tmpbox), headbox, FALSE, FALSE, 0);
    }

  gtk_box_pack_end (GTK_BOX (tmpbox), pic, FALSE, FALSE, 5);
  gtk_container_add (GTK_CONTAINER (main_window), win);

  *labels = 0;
  for (i = 0; i < sel->nhdrs; i++)
    {
      if (*labels)
	strcat (labels, "\n");
      strcat (labels, sel->hdrs[i]);
      strcat (labels,
	      "                                                     ");
    }

  label = gtk_label_new (labels);
  gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
  gtk_box_pack_start (GTK_BOX (headbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  scrollwin =
    gtk_scrolled_window_new (GTK_ADJUSTMENT
			     (gtk_adjustment_new (0, 0, 0, 0, 0, 0)),
			     GTK_ADJUSTMENT (gtk_adjustment_new
					     (0, 0, 0, 0, 0, 0)));
  listbox = gtk_list_new ();
  gtk_box_pack_start (GTK_BOX (win), scrollwin, TRUE, TRUE, 0);

  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrollwin),
					 listbox);

  gtk_signal_connect (GTK_OBJECT (listbox), "select_child",
		      GTK_SIGNAL_FUNC (item_selected), items);

  saved_items = nitems;		/* PUSH */

  for (i = 0; i < sel->nchoices; i++)
    {
      char *item;
      item = sel->choices[i].txt;
      if (*item == 0)
	item = " ";
      listitem = gtk_list_item_new_with_label (item);
      gtk_container_add (GTK_CONTAINER (listbox), listitem);

      if (i == sel->active && sel->choices[i].id != UI_NONEID)
	{
	  gtk_list_select_item (GTK_LIST (listbox), i);
	}

      if (sel->choices[i].id != UI_NONEID)
	{
	  items[nitems].ptr = listitem;
	  items[nitems++].id = sel->choices[i].id;
	}
    }

  if (sel->nbuttons > 0)
    {
      buttonbox = gtk_hbox_new (FALSE, 5);
      if (sel->nbuttons == 1)
	gtk_box_pack_start (GTK_BOX (win), buttonbox, FALSE, FALSE, 4);
      else
	gtk_box_pack_start (GTK_BOX (win), buttonbox, FALSE, TRUE, 10);

      for (i = 0; i < sel->nbuttons; i++)
	{
	  button = gtk_button_new_with_label (sel->tbuttons[i]);
	  gtk_box_pack_start (GTK_BOX (buttonbox), button, TRUE, TRUE, 0);
	  gtk_widget_show (button);

	  if (sel->buttonidents[i] != UI_NONEID)
	    gtk_signal_connect (GTK_OBJECT (button), "clicked",
				GTK_SIGNAL_FUNC (handle_event),
				(void *) sel->buttonidents[i]);
	}
    }

  if (*sel->title)
    gtk_window_set_title (GTK_WINDOW (topwin), sel->title);
  else
    gtk_window_set_title (GTK_WINDOW (topwin),
			  "Open Sound System (C) 4Front Technologies");

  gtk_widget_show_all (main_window);
  global_event = UI_NONEID;
  pending_event = UI_NONEID;
  active_event = UI_NONEID;
  gtk_main ();
  if (active_event != UI_NONEID)
    ui_sel_setactive_byid (sel, active_event);
  active_event = UI_NONEID;
  nitems = saved_items;		/* POP */
  if (have_exited)
    exit (0);
  gtk_widget_destroy (win);
  return global_event;
}

/* 
 * ui_sel_close_window() is called when deleting a selection object.
 * Not reguired in the curses version.
 */
void
ui_sel_close_window (ui_selection * sel)
{
}

int
ui_check (void)
{
#ifdef CHECK_X
  Display *display;

  if ((display = XOpenDisplay (NULL)) == NULL)
    {
      fprintf (stderr, "Can't connect to display: %s\n", XDisplayName (NULL));
      printf ("Can't connect to display: %s\n", XDisplayName (NULL));
      return 0;
    }
  XCloseDisplay (display);
#endif
  return 1;
}
