/* search and replace functions */

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <gtk/gtk.h>
#include <regex.h>
#include "main.h"


typedef struct {
	opt_s *o ;
	GtkWidget *searchw ;
	GtkWidget *sentry ;
	GtkWidget *sreplace ;
	char *needle ;
	char *replacement ;
	char *haystack ;
	char *posinhay ;
	int repl_l ;
	int mode ;
} search_d ;


/* replace "\\t" with "\t" etc. in the search string */
char *search_string_special(char *string) {
	char c, *p = string, *res ;
	int i, l ;

	l = strlen(string) ;
	res = my_strdup(string) ;

	for(i = 0 ; p < string + l ; i++, p++) {
		if(*p != '\\') res[i] = *p ;
		else {
			c = p[1] ;

			switch(c){
				case 'n':
					res[i] = '\n' ;
					break ;
				case 't':
					res[i] = '\t' ;
					break ;
				default:
					res[i] = c ;
					break ;
			}

			p++ ;
		}

	}

	res[i] = '\0' ;
	say(0, "got: %s, made: %s", string, res) ;
	return res ;
}


/* here the actual searching is done */
int search_real(search_d *sd) {
	GtkEditable *ed ;
	regex_t reg ;
	regmatch_t pmatch[1] ;
	int i, offset, start, end, nmatch = 1 ;

	/* compile the regexp */
	i = regcomp(&reg, sd->needle, 0) ;
	if(regexec(&reg, sd->posinhay, nmatch, pmatch, 0)) return EXIT_FAILURE ;

	ed = GTK_EDITABLE(sd->o->panels[0].tekst) ;

	offset = sd->posinhay - sd->haystack ;
	start = offset + pmatch[0].rm_so ;
	end = offset + pmatch[0].rm_eo ;

	/* if we are in search & replace mode, do the replacement */
	if(sd->mode) {
		gtk_editable_delete_text(ed, start, end) ;
		end = start ;
		gtk_editable_insert_text(ed, sd->replacement, sd->repl_l, &end) ;
		sd->o->panels[0].changed = TRUE ;
		g_free(sd->haystack) ;
		sd->haystack = gtk_editable_get_chars(ed, 0, -1) ;
		sd->posinhay = sd->haystack + end ;
	} else sd->posinhay += pmatch[0].rm_so + 1 ;

	gtk_editable_select_region(ed, start, end) ;

	return EXIT_SUCCESS ;
}


/* start the search */
void search_start(GtkWidget *kontr, gpointer dane) {
	search_d *sd = (search_d *) dane ;
	char *tmp ;

	say(0, "Starting search") ;

	tmp = gtk_editable_get_chars(GTK_EDITABLE(sd->sentry), 0, -1) ;
	sd->needle = search_string_special(tmp) ;
	g_free(tmp) ;

	/* deal with the replacement string if we are replacing */
	if(sd->mode) {
		tmp = gtk_editable_get_chars(GTK_EDITABLE(sd->sreplace), 0, -1) ;
		sd->replacement = search_string_special(tmp) ;
		g_free(tmp) ;
		sd->repl_l = strlen(sd->replacement) ;
	}

	say(0, "searching for %s", sd->needle) ;
	if(search_real(sd) == EXIT_FAILURE) {
		komunikat("Search string not found in\n%s", sd->o->panels[0].title) ;
		sd->posinhay = sd->haystack ;
	}

}


/* replace every occurence of needle in the whole window */
void search_replace_all(GtkWidget *kontr, gpointer dane) {
	search_d *sd = (search_d *) dane ;
	char *tmp ;
	int i = 0 ;

	say(0, "Replacing all") ;
	/*gtk_text_freeze(GTK_TEXT(sd->o->panels[0].tekst)) ;*/
	tmp = gtk_editable_get_chars(GTK_EDITABLE(sd->sentry), 0, -1) ;
	sd->needle = search_string_special(tmp) ;
	g_free(tmp) ;

	tmp = gtk_editable_get_chars(GTK_EDITABLE(sd->sreplace), 0, -1) ;
	sd->replacement = search_string_special(tmp) ;
	g_free(tmp) ;

	sd->repl_l = strlen(sd->replacement) ;

	while(search_real(sd) != EXIT_FAILURE) i++ ;

	/*gtk_text_thaw(GTK_TEXT(sd->o->panels[0].tekst)) ;*/

	switch(i){
		case 0:
			komunikat("Search string not found") ;
			break ;
		case 1:
			komunikat("One occurence found") ;
			break ;
		default:
			komunikat("%i occurencies found", i) ;
			break ;
	}

	sd->posinhay = sd->haystack ;

}


/* close the search & replace dialog and exit */
void search_close(GtkWidget *kontr, gpointer dane) {
	search_d *sd = (search_d *) dane ;

	gtk_widget_destroy(sd->searchw) ;

}


/* function called from the main menu setting up the search dialog */
void search(gpointer dane, guint signal, GtkWidget *kontrolka) {
	GtkWidget *ypole, *hpole, *button ;
	opt_s *o = (opt_s *) dane ;
	search_d sd ;

	debug = TRUE ;

	if(o->search_and_replace_dialog_opened) return ;
	else o->search_and_replace_dialog_opened = TRUE ;

	say(0, "starting search and replace dialog") ;

	sd.o = o ;
	sd.mode = signal ; /* mode=FALSE: search ; mode=TRUE: replace */

	sd.haystack = gtk_editable_get_chars(GTK_EDITABLE(o->panels[0].tekst), 0, -1) ;
	sd.posinhay = sd.haystack ;

	/* main window */
	sd.searchw = gtk_window_new(GTK_WINDOW_DIALOG) ;
	gtk_signal_connect(GTK_OBJECT(sd.searchw), "destroy",
		GTK_SIGNAL_FUNC(zakoncz_dialog), sd.searchw) ;

	gtk_window_set_title(GTK_WINDOW(sd.searchw), "Search and replace") ;

	ypole = gtk_vbox_new(FALSE, 5) ;
	gtk_container_add(GTK_CONTAINER(sd.searchw), ypole) ;

	/* entry fields */
	sd.sentry = create_option_widget(ypole, STRING, " string to search for") ;
	if(sd.mode)
		sd.sreplace = create_option_widget(ypole, STRING, " string to replace with") ;

	create_label(ypole, "Note: you can use any regular expressions") ;
	
	/* buttons */
	hpole = create_hbox(ypole) ;
	button = create_button(hpole, search_start, " Next ", &sd) ;
	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT) ;
	gtk_widget_grab_default(button) ;

	if(sd.mode)
		button = create_button(hpole, search_replace_all, " Replace all ", &sd) ;
	button = create_button(hpole, search_close, " Close ", &sd) ;

	gtk_widget_show_all(sd.searchw) ;
	gtk_main() ;

	debug = FALSE ;
	o->search_and_replace_dialog_opened = FALSE ;
}
