/*
 * Copyright (c) 2002, 2003, 2004 Jean-Yves Lefort
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdarg.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <string.h>
#include "sg-util.h"
#include "sgtk-auth-dialog.h"
#include "sgtk-dialog.h"
#include "sgtk-message-dialog.h"
#include "st-thread.h"
#include "st-search-dialog.h"
#include "st-dialog-api.h"
#include "st-shell.h"
#include "st-util.h"

/*** function declarations ***************************************************/

static void	st_dialog_internal	(const char	*stock_id,
					 const char	*primary,
					 const char	*secondary);

/*** API implementation ******************************************************/

/**
 * st_notice:
 * @format: the message format. See the printf() documentation.
 * @...: the parameters to insert into the format string.
 *
 * Outputs a formatted streamtuner notice to the standard error output.
 *
 * If you need to output a notice regarding a specific handler, use
 * st_handler_notice() instead of this function.
 **/
void
st_notice (const char *format, ...)
{
  va_list args;
  char *message;

  va_start(args, format);
  message = g_strdup_vprintf(format, args);
  va_end(args);

  g_printerr("streamtuner: %s\n", message);
  g_free(message);
}

/*
 * Deprecated.
 */
void
st_warning (const char *format, ...)
{
  va_list args;
  char *secondary;

  va_start(args, format);
  secondary = g_strdup_vprintf(format, args);
  va_end(args);

  st_error_dialog(NULL, "%s", secondary);
  g_free(secondary);
}

/*
 * Deprecated.
 */
void
st_error (const char *format, ...)
{
  va_list args;
  char *secondary;

  va_start(args, format);
  secondary = g_strdup_vprintf(format, args);
  va_end(args);

  st_error_dialog(NULL, "%s", secondary);
  g_free(secondary);

  exit(1);
}

/**
 * st_info_dialog:
 * @primary: the primary text, or %NULL.
 * @format: the secondary text format, or %NULL. See the printf()
 * documentation.
 * @...: the parameters to insert into the format string.
 *
 * Displays an informational dialog.
 **/
void
st_info_dialog (const char *primary,
		const char *format,
		...)
{
  char *secondary = NULL;
  
  if (format)
    {
      va_list args;
      
      va_start(args, format);
      secondary = g_strdup_vprintf(format, args);
      va_end(args);
    }
  
  st_dialog_internal(GTK_STOCK_DIALOG_INFO, primary, secondary);
  g_free(secondary);
}

/**
 * st_error_dialog:
 * @primary: the primary text, or %NULL.
 * @format: the secondary text format, or %NULL. See the printf()
 * documentation.
 * @...: the parameters to insert into the format string.
 *
 * Displays an error dialog.
 **/
void
st_error_dialog (const char *primary,
		 const char *format,
		 ...)
{
  char *secondary = NULL;
  
  if (format)
    {
      va_list args;
      
      va_start(args, format);
      secondary = g_strdup_vprintf(format, args);
      va_end(args);
    }
  
  st_dialog_internal(GTK_STOCK_DIALOG_ERROR, primary, secondary);
  g_free(secondary);
}

/**
 * st_search_dialog:
 *
 * Prompts the user for a search string.
 *
 * Return value: a search string which should be freed after use, or
 * %NULL if the user pressed the cancel button.
 **/
char *
st_search_dialog (void)
{
  GtkWidget *dialog;
  char *str = NULL;

  if (st_thread_get())
    GDK_THREADS_ENTER();

  dialog = st_search_dialog_new(st_shell ? st_shell_get_window(st_shell) : NULL);

  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
    str = g_strdup(st_search_dialog_get_text(ST_SEARCH_DIALOG(dialog)));

  gtk_widget_destroy(dialog);
  
  if (st_thread_get())
    {
      gdk_flush();
      GDK_THREADS_LEAVE();
    }

  return str;
}

/**
 * st_auth_dialog:
 * @default_name: a default name, or %NULL.
 * @default_password: a default password, or %NULL.
 * @name: a location to return the entered name, or %NULL.
 * @password: a location to return the entered password.
 * @primary: the primary text, or %NULL.
 * @format: the secondary text format, or %NULL. See the printf()
 * documentation.
 * @...: the parameters to insert into the format string.
 *
 * Prompts the user for a name and password. The name and password
 * entries will be initialized to @default_name and @default_password,
 * if provided.
 *
 * The entered name and password will be stored in @name and
 * @password, and should be freed when no longer needed. If @name is
 * %NULL, the name entry will not be made sensitive.
 *
 * Return value: %TRUE if the user pressed the "Authenticate" button,
 * %FALSE otherwise. If %FALSE is returned, @name and @password will
 * not be set.
 **/
gboolean
st_auth_dialog (const char *default_name,
		const char *default_password,
		char **name,
		char **password,
		const char *primary,
		const char *format,
		...)
{
  GtkWidget *dialog;
  GtkWindow *parent = NULL;
  va_list args;
  char *secondary;
  gboolean status = FALSE;

  g_return_val_if_fail(password != NULL, FALSE);
  g_return_val_if_fail(format != NULL, FALSE);

  if (st_thread_get())
    GDK_THREADS_ENTER();

  if (st_shell)
    parent = st_shell_get_window(st_shell);

  va_start(args, format);
  secondary = g_strdup_vprintf(format, args);
  va_end(args);

  dialog = sgtk_auth_dialog_new(parent,
				default_name,
				default_password,
				primary,
				"%s",
				secondary);
  g_free(secondary);

  if (! parent)
    st_window_set_icon(GTK_WINDOW(dialog));

  sgtk_auth_dialog_set_name_sensitive(SGTK_AUTH_DIALOG(dialog), name != NULL);

  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
    {
      if (name)
	*name = g_strdup(sgtk_auth_dialog_get_name(SGTK_AUTH_DIALOG(dialog)));
      *password = g_strdup(sgtk_auth_dialog_get_password(SGTK_AUTH_DIALOG(dialog)));
      status = TRUE;
    }

  gtk_widget_destroy(dialog);

  if (st_thread_get())
    {
      gdk_flush();
      GDK_THREADS_LEAVE();
    }

  return status;
}

/**
 * st_dialog_normalize:
 * @str: a string to normalize.
 *
 * Normalizes a string so that it can be used stand-alone as the
 * secondary string of a dialog.
 *
 * Normalization will remove trailing newlines and convert the string
 * to a sentence.
 *
 * Return value: a normalized string, which should be freed after use.
 **/
char *
st_dialog_normalize (const char *str)
{
  char *copy;
  int i;
  char *normalized;
  gboolean attn1 = FALSE;
  gboolean attn2 = FALSE;

  g_return_val_if_fail(str != NULL, NULL);

  copy = g_strdup(str);

  /* remove trailing newline(s) */
  for (i = strlen(copy) - 1; i >= 0; i--)
    if (copy[i] == '\n')
      copy[i] = 0;
    else
      break;

  normalized = sg_str_sentencize(copy);
  g_free(copy);

  for (i = 0; normalized[i]; i++)
    {
      if (attn1 && attn2)
	{
	  /* make sure we didn't find a capital word */
	  if (g_ascii_islower(normalized[i + 1]))
	    normalized[i] = g_ascii_tolower(normalized[i]);

	  attn1 = attn2 = FALSE;
	}
      else if (attn1)
	{
	  if (normalized[i] == ' ')
	    attn2 = TRUE;
	}
      else
	{
	  if (normalized[i] == ':')
	    attn1 = TRUE;
	  else if (normalized[i] == '(')
	    attn1 = attn2 = TRUE;
	}
    }

  return normalized;
}

/*
 * Deprecated.
 */
int
st_question (const char *format, ...)
{
  va_list args;
  char *message;
  GtkWidget *dialog;
  int response;

  va_start(args, format);
  message = g_strdup_vprintf(format, args);
  va_end(args);

  if (st_thread_get())
    GDK_THREADS_ENTER();

  dialog = gtk_message_dialog_new(st_shell ? st_shell_get_window(st_shell) : NULL,
				  GTK_DIALOG_DESTROY_WITH_PARENT,
				  GTK_MESSAGE_QUESTION,
				  GTK_BUTTONS_NONE,
				  "%s", message);
  gtk_window_set_title(GTK_WINDOW(dialog), "");
  gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);

  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
			 GTK_STOCK_YES, GTK_RESPONSE_YES,
			 GTK_STOCK_NO, GTK_RESPONSE_NO,
			 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			 NULL);

  response = gtk_dialog_run(GTK_DIALOG(dialog));
  gtk_widget_destroy(dialog);

  if (st_thread_get())
    {
      gdk_flush();
      GDK_THREADS_LEAVE();
    }

  g_free(message);

  return response;
}

/*
 * Deprecated.
 */
char
*st_prompt (gboolean hidden,
	    const char *init,
	    const char *format,
	    ...)
{
  va_list args; 
  char *message;
  GtkWidget *dialog;
  GtkWidget *ok;
  GtkWidget *entry;
  char *str = NULL;
  
  va_start(args, format); 
  message = g_strdup_vprintf(format, args);
  va_end(args);

  /* kludge to make old plugins use the new search dialog */
  if (! hidden && g_str_has_prefix(message, "Enter a search string"))
    {
      g_free(message);
      return st_search_dialog();
    }
  
  if (st_thread_get())
    GDK_THREADS_ENTER();

  dialog = gtk_message_dialog_new(st_shell ? st_shell_get_window(st_shell) : NULL,
				  GTK_DIALOG_DESTROY_WITH_PARENT,
				  GTK_MESSAGE_QUESTION,
				  GTK_BUTTONS_NONE,
				  "%s", message);
  gtk_window_set_title(GTK_WINDOW(dialog), "");
  gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);

  gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
  ok = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
  
  gtk_window_set_default(GTK_WINDOW(dialog), ok);

  entry = gtk_entry_new();

  gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);

  if (hidden)
    gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
  if (init)
    gtk_entry_set_text(GTK_ENTRY(entry), init);

  gtk_widget_show(entry);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, TRUE, TRUE, 0);

  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
    str = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
 
  gtk_widget_destroy(dialog);

  if (st_thread_get())
    {
      gdk_flush();
      GDK_THREADS_LEAVE();
    }

  g_free(message);
 
  return str;
}

/*** private implementation **************************************************/

static void
st_dialog_internal (const char *stock_id,
		    const char *primary,
		    const char *secondary)
{
  GtkWidget *dialog;
  GtkWindow *parent = NULL;

  if (st_thread_get())
    GDK_THREADS_ENTER();
  
  if (st_shell)
    parent = st_shell_get_window(st_shell);

  dialog = sgtk_message_dialog_new(parent, stock_id, primary, secondary);
  
  if (! parent)
    st_window_set_icon(GTK_WINDOW(dialog));
  
  gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);

  gtk_dialog_run(GTK_DIALOG(dialog));
  gtk_widget_destroy(dialog);

  if (st_thread_get())
    {
      gdk_flush();
      GDK_THREADS_LEAVE();
    }
}
