/*
    ragmaan/find.c            (C) 2003 Raymond (zandbergen@home.nl)

    "ragmaan" is an anagram generator

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <regex.h>
#include <glib.h>
#include <gtk/gtk.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ragmaan.h"
#include "gettext.h"
#include "find.h"

enum
{ FIND_CANCEL, FIND_BACK, FIND_FORWARD, FIND_NEW };

static gboolean case_sensitive = FALSE;

static void find_dialog (void);

/* action for find button/menu entry */
gint
find_cb (GtkWidget * unused1, gpointer unused2)
{
  /* find dialog already running? */
  if (!global_widgets.find_window)
    find_dialog ();
  return FALSE;
}

/* hook when find dialog is destroyed */
static gint
find_cleanup (void)
{
  global_widgets.find_window = NULL;
  return FALSE;
}

static gint
find_toggle_case_sensitive (GtkWidget * check, gpointer unused)
{
  case_sensitive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
  return FALSE;
}

static void
find_and_select_string (const char *target, int direction)
{
  GtkTreeIter iter;
  GtkTreeSelection *sel;
  GtkTreeModel *m;
  gchar *subword;
  GtkTreePath *path;
  gboolean match = FALSE;
  regex_t comp_expr;
  gint reg_flags = REG_EXTENDED | REG_NOSUB;
  gint rcerr;

  /* compile and check regex */

  if (!case_sensitive)
    reg_flags |= REG_ICASE;

  rcerr = regcomp (&comp_expr, target, reg_flags);
  if (rcerr)
    {				/* regex error: report and leave */
      GtkWidget *dialog;
      gchar err_string[200];
      regerror (rcerr, &comp_expr, err_string, sizeof (err_string));
      print_to_statusbar (_("invalid regex"));
      dialog =
	gtk_message_dialog_new (GTK_WINDOW (global_widgets.find_window),
				GTK_DIALOG_DESTROY_WITH_PARENT,
				GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
				err_string);
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (GTK_WIDGET (dialog));
      regfree (&comp_expr);
      return;
    }
  /* get selection */
  m =
    gtk_tree_view_get_model (GTK_TREE_VIEW (global_widgets.result_tree_view));
  sel =
    gtk_tree_view_get_selection (GTK_TREE_VIEW
				 (global_widgets.result_tree_view));
  if (!gtk_tree_selection_get_selected (sel, &m, &iter))
    /* no selection - start at the top */
    if (!gtk_tree_model_iter_nth_child (m, &iter, NULL, 1))
      /* no top ... */
      return;

  /* match loops:
   * these suck! loop back==tree_path, loop forward==iter */

  if (direction == FIND_BACK)
    {
      path = gtk_tree_model_get_path (m, &iter);
      while (!match && gtk_tree_path_prev (path))
	{
	  gtk_tree_model_get_iter (m, &iter, path);
	  gtk_tree_model_get (m, &iter, COLUMN_SUBWORD, &subword, -1);
	  match = !regexec (&comp_expr, subword, 0, NULL, 0);
	  g_free (subword);
	}
      gtk_tree_path_free (path);
    }
  else				/* direction == FIND_FORWARD */
    while (!match && gtk_tree_model_iter_next (m, &iter))
      {
	gtk_tree_model_get (m, &iter, COLUMN_SUBWORD, &subword, -1);
	match = !regexec (&comp_expr, subword, 0, NULL, 0);
	g_free (subword);
      }
  /* end match loop */

  regfree (&comp_expr);
  if (match)
    {
      /* select and center the match */
      print_to_statusbar (_("match"));
      path = gtk_tree_model_get_path (m, &iter);
      gtk_tree_selection_select_path (sel, path);
      gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW
				    (global_widgets.result_tree_view),
				    path, NULL, TRUE, 0.5, 0.5);
      gtk_tree_path_free (path);
    }
  else
    print_to_statusbar (_("no match"));

  /* update combobox history */

  global_widgets.find_history =
    history_update (global_widgets.find_history, target);
  gtk_combo_set_popdown_strings (GTK_COMBO (global_widgets.find_entry),
				 (global_widgets.find_history));
}

/* action for find dialog buttons / enter key */

static gint
find_dialog_cb (GtkWidget * w, gpointer data)
{
  gint response = GPOINTER_TO_INT (data);
  const gchar *p;

  switch (response)
    {
    case FIND_CANCEL:
      gtk_widget_destroy (global_widgets.find_window);
      global_widgets.find_window = NULL;
      return TRUE;
    case FIND_FORWARD:
      ;
    case FIND_BACK:
      p =
	gtk_entry_get_text (GTK_ENTRY
			    (GTK_COMBO (global_widgets.find_entry)->entry));
      find_and_select_string (p, response);
      break;
    default:
      ;
    }
  return FALSE;
}

static void
find_dialog (void)
{
  GtkWidget *dialog, *label, *combo, *table, *check;

  dialog = gtk_dialog_new_with_buttons (_("Find"),
					GTK_WINDOW (global_widgets.
						    main_window),
					GTK_DIALOG_DESTROY_WITH_PARENT,
					GTK_STOCK_CANCEL, FIND_CANCEL,
					GTK_STOCK_GO_BACK, FIND_BACK,
					GTK_STOCK_GO_FORWARD, FIND_FORWARD,
					NULL);

  table = gtk_table_new (4, 5, FALSE);
  gtk_container_set_border_width (GTK_CONTAINER (table), 10);
  gtk_table_set_row_spacings (GTK_TABLE (table), 10);
  gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), table);
  label = gtk_label_new (_("regexp:"));
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, 0,
		    0, 0, 0);
  combo = gtk_combo_new ();
  gtk_combo_disable_activate (GTK_COMBO (combo));
  g_signal_connect (G_OBJECT (GTK_COMBO (combo)->entry),
		    "activate", G_CALLBACK (find_dialog_cb),
		    GINT_TO_POINTER (FIND_FORWARD));
  if (global_widgets.find_history)
    gtk_combo_set_popdown_strings (GTK_COMBO (combo),
				   global_widgets.find_history);
  gtk_table_attach (GTK_TABLE (table), combo, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL,
		    GTK_EXPAND|GTK_FILL, 0, 0);

  check = gtk_check_button_new_with_label (_("case sensitive"));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), case_sensitive);
  g_signal_connect (G_OBJECT (check), "toggled",
		    G_CALLBACK (find_toggle_case_sensitive), NULL);
  gtk_table_attach (GTK_TABLE (table), check, 1, 2, 1, 2, GTK_FILL,
		    GTK_FILL, 0, 0);

  g_signal_connect_swapped (GTK_OBJECT (dialog),
			    "response",
			    G_CALLBACK (find_dialog_cb), GTK_OBJECT (dialog));
  global_widgets.find_window = dialog;
  global_widgets.find_entry = combo;
  g_signal_connect (G_OBJECT (global_widgets.find_window), "delete_event",
		    G_CALLBACK (find_cleanup), NULL);
  gtk_widget_show_all (dialog);
}
