/* Bluefish HTML Editor
 * snr.c - search 'n replace
 *
 * Copyright (C) 1998 Olivier Sessink and Chris Mazuc
 *
 * A part of the code in this file was taken from gEdit !
 *
 * 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 <gtk/gtk.h>
#include <string.h>
#include "bluefish.h"
#include "interface.h"

typedef struct _searchwin
  {
    GtkWidget *window;
    GtkWidget *start_at_cursor;
    GtkWidget *start_at_beginning;
    GtkWidget *case_sensitive;
    GtkWidget *search_entry;
    gint replace, again, line;
    GtkWidget *replace_box;
    GtkWidget *replace_entry;
    GtkWidget *prompt_before_replacing;
    GtkWidget *search_for;
    GtkWidget *line_item, *text_item;
  }
_searchwin;

_searchwin *options;



void search_create (_searchwin * options, gint replace);
void search_start (GtkWidget * w, _searchwin * options);
void seek_to_line (gint line_number);
gint point_to_line (gint point);
void search_for_text (GtkWidget * w, _searchwin * options);
void popup_replace_window (_searchwin * options);
void search_replace_no_sel (GtkWidget * w, _searchwin * options);
void search_replace_yes_sel (GtkWidget * w, _searchwin * options);
void search_for_line (GtkWidget * w, _searchwin * options);

/* ------------------------------------------------------- */
/* ------------------------------------------------------- */

void
copy_callback (GtkWidget * w, gpointer data)
{
  gtk_editable_copy_clipboard (GTK_EDITABLE (current_document->textbox));
}


void
paste_callback (GtkWidget * w, gpointer data)
{
  gtk_editable_paste_clipboard (GTK_EDITABLE (current_document->textbox));
  current_document->modified = 1;
}

void
cut_callback (GtkWidget * w, gpointer data)
{
  gtk_editable_cut_clipboard (GTK_EDITABLE (current_document->textbox));
  current_document->modified = 1;
}

void
sel_all_callback (GtkWidget * w, gpointer data)
{
  gtk_editable_select_region (GTK_EDITABLE (current_document->textbox), 0,
			      gtk_text_get_length (GTK_TEXT (current_document->textbox)));
}

void
snr_init ()
{
  options = g_malloc0 (sizeof (_searchwin));
}

void
search_search_cmd_callback (GtkWidget * w, gpointer data)
{

  if (!options->window)
    {
      search_create (options, 0);
    }
  options->replace = 0;
  options->again = 0;
  search_for_text (NULL, options);
  gtk_window_set_title (GTK_WINDOW (options->window), ("Search"));
  gtk_container_border_width (GTK_CONTAINER (options->window), 10);
  gtk_widget_hide (options->replace_box);
  gtk_widget_show (options->window);

}

void
search_replace_cmd_callback (GtkWidget * w, gpointer data)
{

  if (!options->window)
    search_create (options, 0);

  options->replace = 1;
  options->again = 0;
  search_for_text (NULL, options);
  gtk_window_set_title (GTK_WINDOW (options->window),
			("Search and Replace"));
  gtk_container_border_width (GTK_CONTAINER (options->window), 10);
  gtk_widget_show (options->replace_box);
  gtk_widget_show (options->window);


}

void
search_again_cmd_callback (GtkWidget * w, gpointer data)
{
  if (options->window)
    {
      options->replace = 0;
      options->again = 1;
      search_start (w, options);
    }
}

/* ------------------------------------------------------- */

/* ------------------------------------------------------- */
void
search_create (_searchwin * options, gint replace)
{
  GtkWidget *search_label, *replace_label, *ok, *cancel, *search_hbox,
  *replace_hbox, *hbox;
  GtkWidget *search_for_menu_items, *search_for_label;

  options->window = gtk_dialog_new ();
  gtk_window_set_policy (GTK_WINDOW (options->window), TRUE, TRUE, TRUE);
  if (!replace)
    {
      options->replace = 0;
      gtk_window_set_title (GTK_WINDOW (options->window), ("Search"));
    }
  else
    {
      options->replace = 1;
      gtk_window_set_title (GTK_WINDOW (options->window),
			    ("Search and Replace"));
    }
  hbox = gtk_hbox_new (FALSE, 1);
  search_for_label = gtk_label_new ("Search For:");
  gtk_widget_show (search_for_label);

  search_for_menu_items = gtk_menu_new ();
  options->text_item = gtk_radio_menu_item_new_with_label (NULL, "Text");
  gtk_menu_append (GTK_MENU (search_for_menu_items), options->text_item);
  gtk_widget_show (options->text_item);
  options->line_item = gtk_radio_menu_item_new_with_label (
							    gtk_radio_menu_item_group
							    (GTK_RADIO_MENU_ITEM (options->text_item)),
							    "Line Number");
  gtk_menu_append (GTK_MENU (search_for_menu_items), options->line_item);
  gtk_widget_show (options->line_item);
  options->search_for = gtk_option_menu_new ();
  gtk_option_menu_set_menu (GTK_OPTION_MENU (options->search_for), search_for_menu_items);
  gtk_widget_show (options->search_for);

  gtk_widget_show (hbox);
  gtk_signal_connect (GTK_OBJECT (options->text_item), "activate",
		      GTK_SIGNAL_FUNC (search_for_text), options);
  gtk_signal_connect (GTK_OBJECT (options->line_item), "activate",
		      GTK_SIGNAL_FUNC (search_for_line), options);

  search_hbox = gtk_hbox_new (FALSE, 1);
  options->search_entry = gtk_entry_new ();

  search_label = gtk_label_new (("Search:"));
  options->start_at_cursor = gtk_radio_button_new_with_label (NULL,
							      ("Start searching at cursor position"));
  options->start_at_beginning = gtk_radio_button_new_with_label (
								  gtk_radio_button_group
								  (GTK_RADIO_BUTTON (options->start_at_cursor)),
								  ("Start searching at beginning of the document"));
  options->case_sensitive = gtk_check_button_new_with_label
    (("Case sensitive"));

  options->replace_box = gtk_vbox_new (FALSE, 1);
  replace_hbox = gtk_hbox_new (FALSE, 1);
  replace_label = gtk_label_new (("Replace:"));
  options->replace_entry = gtk_entry_new ();
  options->prompt_before_replacing = gtk_check_button_new_with_label
    (("Prompt before replacing"));

  ok = gtk_button_new_with_label ("OK");
  cancel = gtk_button_new_with_label ("Cancel");

  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (options->window)->vbox), hbox,
		      TRUE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), search_for_label, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), options->search_for, TRUE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (options->window)->vbox),
		      search_hbox, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (search_hbox), search_label, FALSE, FALSE, 2);
  gtk_box_pack_start (GTK_BOX (search_hbox), options->search_entry, TRUE,
		      TRUE, 2);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (options->window)->vbox),
		      options->start_at_cursor, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (options->window)->vbox),
		      options->start_at_beginning, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (options->window)->vbox),
		      options->case_sensitive, TRUE, TRUE, 2);

  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (options->window)->vbox),
		      options->replace_box, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (options->replace_box), replace_hbox, TRUE,
		      TRUE, 0);
  gtk_box_pack_start (GTK_BOX (replace_hbox), replace_label, FALSE,
		      FALSE, 2);
  gtk_box_pack_start (GTK_BOX (replace_hbox), options->replace_entry, TRUE,
		      TRUE, 0);
  gtk_box_pack_start (GTK_BOX (options->replace_box),
		      options->prompt_before_replacing, TRUE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (options->window)->action_area),
		      ok, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (options->window)->action_area),
		      cancel, TRUE, TRUE, 2);

  gtk_widget_show (search_hbox);
  gtk_widget_show (options->search_entry);
  gtk_widget_show (search_label);
  gtk_widget_show (options->start_at_cursor);
  gtk_widget_show (options->start_at_beginning);
  gtk_widget_show (options->case_sensitive);

  if (replace)
    gtk_widget_show (options->replace_box);

  gtk_widget_show (replace_hbox);
  gtk_widget_show (replace_label);
  gtk_widget_show (options->replace_entry);
  gtk_widget_show (options->prompt_before_replacing);
  gtk_widget_show (ok);
  gtk_widget_show (cancel);
  /*gtk_widget_show (options->window); */

  gtk_signal_connect (GTK_OBJECT (ok), "clicked", GTK_SIGNAL_FUNC
		      (search_start), options);
  gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
			     GTK_SIGNAL_FUNC (gtk_widget_hide),
			     (gpointer) options->window);
  /*GTK_WIDGET_SET_FLAGS (ok, GTK_CAN_DEFAULT);
     gtk_widget_grab_default (ok); */
  gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
			     GTK_SIGNAL_FUNC (gtk_widget_hide),
			     (gpointer) options->window);
}

void
search_start (GtkWidget * w, _searchwin * options)
{

  gchar *search_for, *buffer, *replace_with;
  gchar bla[] = " ";
  gint len, start_pos, text_len, match, i, search_for_line, cur_line,
  end_line, oldchanged, replace_diff = 0;

  search_for = gtk_entry_get_text (GTK_ENTRY (options->search_entry));
  replace_with = gtk_entry_get_text (GTK_ENTRY (options->replace_entry));
  len = strlen (search_for);
  oldchanged = current_document->modified;

  if (options->again)
    {
      start_pos = gtk_text_get_point (GTK_TEXT (current_document->textbox));
      options->again = 0;
    }
  else if (GTK_TOGGLE_BUTTON (options->start_at_cursor)->active)
    start_pos = GTK_EDITABLE (current_document->textbox)->current_pos;
  else
    start_pos = 0;


  if ((text_len = gtk_text_get_length (GTK_TEXT
				       (current_document->textbox))) < len)
    return;



  if (GTK_CHECK_MENU_ITEM (options->line_item)->active || options->line)
    {
      start_pos = 0;
      cur_line = 1;
      sscanf (search_for, "%i", &search_for_line);
      for (i = start_pos; i < text_len; i++)
	{
	  if (cur_line == search_for_line)
	    break;
	  /*gtk_editable_get_chars allocates new memrory for buffer,
	     so we should not forget to free it afterwards */
	  buffer = gtk_editable_get_chars (GTK_EDITABLE
					   (current_document->textbox),
					   i, i
					   + 1);
	  if (strcmp (buffer, "\n") == 0)
	    cur_line++;
	  g_free (buffer);
	}
      if (i >= text_len)
	return;
      for (end_line = i; end_line < text_len; end_line++)
	{
	  buffer = gtk_editable_get_chars (GTK_EDITABLE
					   (current_document->textbox),
					   end_line, end_line + 1);
	  if (strcmp (buffer, "\n") == 0)
	    break;
	  g_free (buffer);
	}
      seek_to_line (search_for_line);
      gtk_editable_insert_text (GTK_EDITABLE (current_document->textbox),
				bla, strlen (bla), &i);
      gtk_editable_delete_text (GTK_EDITABLE (current_document->textbox), i
				- 1, i);
      current_document->modified = oldchanged;
      gtk_editable_select_region (GTK_EDITABLE (current_document->textbox),
				  i - 1, end_line);
      return;
    }
  gtk_text_freeze (GTK_TEXT (current_document->textbox));

/*      for (i = start_pos; i <= (text_len - start_pos - len - replace_diff); i++)
 */
  for (i = start_pos; i <= (text_len - len - replace_diff); i++)
    {
      buffer = gtk_editable_get_chars (GTK_EDITABLE
				       (current_document->textbox), i, i
				       + len);
      if (GTK_TOGGLE_BUTTON (options->case_sensitive)->active)
	match = strcmp (buffer, search_for);
      else
	match = strcasecmp (buffer, search_for);

      if (match == 0)
	{
	  gtk_text_thaw (GTK_TEXT (current_document->textbox));

	  /* This is so the text will scroll to the selection no matter what */
	  seek_to_line (point_to_line (i));
	  gtk_editable_insert_text (GTK_EDITABLE
				    (current_document->textbox), bla, strlen
				    (bla), &i);
	  gtk_editable_delete_text (GTK_EDITABLE
				    (current_document->textbox), i - 1, i);
	  i--;
	  current_document->modified = oldchanged;

	  gtk_text_set_point (GTK_TEXT (current_document->textbox), i + len);
	  gtk_editable_select_region (GTK_EDITABLE
				      (current_document->textbox), i, i
				      + len);
	  g_free (buffer);
	  buffer = NULL;


	  if (options->replace)
	    {
	      if (GTK_TOGGLE_BUTTON
		  (options->prompt_before_replacing)->active)
		{
		  gtk_text_set_point (GTK_TEXT (current_document->textbox),
				      i + 2);
		  popup_replace_window (options);
		  return;
		}
	      else
		{
		  gtk_editable_delete_selection (GTK_EDITABLE (current_document->textbox));
		  gtk_editable_insert_text (GTK_EDITABLE
					    (current_document->textbox),
					    replace_with, strlen
					    (replace_with), &i);
		  gtk_text_set_point (GTK_TEXT (current_document->textbox),
				      i + strlen (replace_with));
		  replace_diff = replace_diff + (strlen (search_for) -
						 strlen (replace_with));
		  gtk_text_freeze (GTK_TEXT (current_document->textbox));
		}
	    }
	  else
	    break;
	}
      g_free (buffer);
    }
  gtk_text_thaw (GTK_TEXT (current_document->textbox));

}



void
seek_to_line (gint line_number)
{
  gfloat value, ln, tl;
  gchar *c;
  gint i, total_lines = 0;

/*    c = g_malloc0(4); */
  for (i = 0; i < gtk_text_get_length (GTK_TEXT
				       (current_document->textbox)); i++)
    {
      c = gtk_editable_get_chars (GTK_EDITABLE (current_document->textbox),
				  i, i + 1);
      if (strcmp (c, "\n") == 0)
	total_lines++;
      g_free (c);
    }


  if (total_lines < 3)
    return;
  if (line_number > total_lines)
    return;
  tl = total_lines;
  ln = line_number;
  value = (ln * GTK_ADJUSTMENT (GTK_TEXT
				(current_document->textbox)->vadj)->upper)
    / tl - GTK_ADJUSTMENT
    (GTK_TEXT
     (current_document->textbox)->vadj)->page_increment;
#ifdef DEBUG
  g_print ("seek_to_line, %i\n", total_lines);
  g_print ("seek_to_line, %f\n", value);
  g_print ("seek_to_line, %f, %f\n", GTK_ADJUSTMENT (GTK_TEXT
						     (current_document->textbox)->vadj)->lower, GTK_ADJUSTMENT
	   (GTK_TEXT (current_document->textbox)->vadj)->upper);
#endif


  gtk_adjustment_set_value (GTK_ADJUSTMENT (GTK_TEXT
					    (current_document->textbox)->vadj), value);
}

gint
point_to_line (gint point)
{
  gint i, lines;
  gchar *c;			/* = g_malloc0(3); */

  lines = 0;
  i = point;
  for (i = point; i > 1; i--)
    {
      c = gtk_editable_get_chars (GTK_EDITABLE (current_document->textbox),
				  i - 1, i);
      if (strcmp (c, "\n") == 0)
	lines++;
      g_free (c);
    }

/*    g_free(c); */
  return lines;
}


void
search_for_text (GtkWidget * w, _searchwin * options)
{
  gtk_widget_show (options->start_at_cursor);
  gtk_widget_show (options->start_at_beginning);
  gtk_widget_show (options->case_sensitive);
  options->line = 0;
  if (options->replace)
    gtk_widget_show (options->replace_box);
  /*gtk_menu_item_select (GTK_MENU_ITEM (options->text_item));    */
  gtk_option_menu_set_history (GTK_OPTION_MENU (options->search_for), 0);
  gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (options->text_item), 1);
  gtk_editable_delete_text (GTK_EDITABLE (options->search_entry), 0, -1);
}

void
search_for_line (GtkWidget * w, _searchwin * options)
{
  gtk_widget_hide (options->start_at_cursor);
  gtk_widget_hide (options->start_at_beginning);
  gtk_widget_hide (options->case_sensitive);
  options->line = 1;
  if (options->replace)
    {
      gtk_widget_hide (options->replace_box);
    }
  gtk_option_menu_set_history (GTK_OPTION_MENU (options->search_for), 1);
  gtk_editable_delete_text (GTK_EDITABLE (options->search_entry), 0, -1);

}

void
search_replace_yes_sel (GtkWidget * w, _searchwin * options)
{
  gchar *replace_with;

  gint i;

  replace_with = gtk_entry_get_text (GTK_ENTRY (options->replace_entry));
  i = gtk_text_get_point (GTK_TEXT (current_document->textbox)) - 2;
  gtk_editable_delete_selection (GTK_EDITABLE (current_document->textbox));
  gtk_editable_insert_text (GTK_EDITABLE (current_document->textbox),
			    replace_with, strlen (replace_with), &i);
  gtk_text_set_point (GTK_TEXT (current_document->textbox), i + strlen (replace_with));
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (options->start_at_cursor), TRUE);
  search_start (w, options);
}

void
search_replace_no_sel (GtkWidget * w, _searchwin * options)
{
  /*gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(data->window->search->start_at_cursor), TRUE); */
  options->again = 1;
  search_start (w, options);
}


void
popup_replace_window (_searchwin * options)
{
  GtkWidget *window, *yes, *no, *cancel, *label;
  window = gtk_dialog_new ();
  gtk_window_set_title (GTK_WINDOW (window), ("Replace?"));
  gtk_widget_set_usize (GTK_WIDGET (window), 280, 90);
  label = gtk_label_new (("Are you sure you want to replace this?"));

  yes = gtk_button_new_with_label ("Yes");
  no = gtk_button_new_with_label ("No");
  cancel = gtk_button_new_with_label ("Cancel");
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE,
		      FALSE, 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), yes, TRUE,
		      TRUE, 2);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), no, TRUE,
		      TRUE, 2);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), cancel,
		      TRUE, TRUE, 2);
  gtk_widget_show (label);
  gtk_widget_show (yes);
  gtk_widget_show (no);
  gtk_widget_show (cancel);
  gtk_widget_show (window);
  gtk_signal_connect (GTK_OBJECT (yes), "clicked", GTK_SIGNAL_FUNC
		      (search_replace_yes_sel), options);
  gtk_signal_connect (GTK_OBJECT (no), "clicked", GTK_SIGNAL_FUNC
		      (search_replace_no_sel), options);
  gtk_signal_connect_object (GTK_OBJECT (yes), "clicked", GTK_SIGNAL_FUNC
			     (gtk_widget_destroy), (gpointer) window);
  gtk_signal_connect_object (GTK_OBJECT (no), "clicked", GTK_SIGNAL_FUNC
			     (gtk_widget_destroy), (gpointer) window);
  gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked", GTK_SIGNAL_FUNC
			     (gtk_widget_destroy), (gpointer) window);
  gtk_grab_add (window);
}
