#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "guiutils.h"
#include "cdialog.h"
#include "clipboard.h"

#include "editor.h"
#include "editorfip.h"
#include "editorfipcb.h"
#include "editorfipop.h"
#include "manedit.h"


void EditorFIPItemDestroyCB(gpointer data);

void EditorFIPDestroyCB(GtkObject *object, gpointer data);
gint EditorFIPCloseCB(GtkWidget *widget, GdkEvent *event, gpointer data);
void EditorFIPCloseMCB(GtkWidget *widget, gpointer data);

void EditorFIPFindCB(GtkWidget *widget, gpointer data);
void EditorFIPFindActivateCB(GtkWidget *widget, gpointer data);
void EditorFIPClearCB(GtkWidget *widget, gpointer data);
void EditorFIPStopCB(GtkWidget *widget, gpointer data);
void EditorFIPGotoCB(GtkWidget *widget, gpointer data);

gint EditorFIPMenuMapCB(GtkWidget *widget, GdkEvent *event, gpointer data);

void EditorFIPResultsListSelectCB(
	GtkWidget *widget, gint row, gint column,
	GdkEventButton *event, gpointer data
);
void EditorFIPResultsListUnselectCB(
	GtkWidget *widget, gint row, gint column,
	GdkEventButton *event, gpointer data
);
void EditorFIPResultsListColumnClickCB(
	GtkWidget *widget, gint column, gpointer data
);


#define ATOI(s)		(((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)		(((s) != NULL) ? atol(s) : 0)
#define ATOF(s)		(((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)	(((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)	(((a) > (b)) ? (a) : (b))
#define MIN(a,b)	(((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)	(MIN(MAX((a),(l)),(h)))
#define STRLEN(s)	(((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)	(((s) != NULL) ? (*(s) == '\0') : TRUE)


/*
 *	Item destroy callback.
 */
void EditorFIPItemDestroyCB(gpointer data)
{
	editor_fip_item_struct *item = EDITOR_FIP_ITEM(data);
	if(item == NULL)
	    return;

	EditorFIPItemDelete(item);
}

/*
 *	Destroy callback.
 */
void EditorFIPDestroyCB(GtkObject *object, gpointer data)
{

}

/*
 *	Close callback.
 */
gint EditorFIPCloseCB(GtkWidget *widget, GdkEvent *event, gpointer data)
{
	static gboolean reenterant = FALSE;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if(fip == NULL)
	    return(TRUE);

	if(reenterant)
	    return(TRUE);
	else
	    reenterant = TRUE;

	if(!fip->initialized)
	{
	    reenterant = FALSE;
	    return(TRUE);
	}

	/* Cannot close while processing */
	if(fip->processing)
	    return(TRUE);

	/* Unmap dialog (do not reset it) */
	EditorFIPUnmap(fip);

	reenterant = FALSE;
	return(TRUE);
}

/*
 *	Close callback for menus.
 */
void EditorFIPCloseMCB(GtkWidget *widget, gpointer data)
{
	EditorFIPCloseCB(widget, NULL, data);
}


/*
 *	Find button callback.
 */
void EditorFIPFindCB(GtkWidget *widget, gpointer data)
{
	static gboolean reenterant = FALSE;
	gchar *value;
	GtkCombo *find_combo;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if(fip == NULL)
	    return;

	if(reenterant)
	    return;
	else
	    reenterant = TRUE;

	find_combo = (GtkCombo *)fip->find_combo;
	if(find_combo == NULL)
	    return;

	/* Add item to list */
	value = gtk_entry_get_text(
	    GTK_ENTRY(find_combo->entry)
	);

	/* Make a copy of value if possible */
	if(value != NULL)
	    value = g_strdup(value);

	/* Call combo widget to set new value and activate, this will
	 * then call EditorFIPFindActivateCB()
	 */
	GUIComboActivateValue(GTK_WIDGET(find_combo), value);

	/* Free coppied value */
	g_free(value);

	reenterant = FALSE;
}

/*
 *	Find activate (from find_combo) callback.
 */
void EditorFIPFindActivateCB(GtkWidget *widget, gpointer data)
{
	static gboolean reenterant = FALSE;
	gchar *s, *search_string;
	GtkWidget *w;
	GtkCombo *find_combo;
	GtkCList *results_clist;
	gint matches;
	gint throughness = 0;
	gboolean case_sensitive = FALSE;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if(fip == NULL)
	    return;

	if(reenterant)
	    return;
	else
	    reenterant = TRUE;

	/* Get pointer to results clist */
	results_clist = (GtkCList *)fip->results_clist;
	if(results_clist == NULL)
	{
	    reenterant = FALSE;
	    return;
	}

	/* Get find combo widget pointer */
	find_combo = (GtkCombo *)fip->find_combo;
	if(find_combo == NULL)
	{   
	    reenterant = FALSE;
	    return;
	}

	/* Get search string and make a copy of it */
	search_string = gtk_entry_get_text(GTK_ENTRY(find_combo->entry));
	if(search_string != NULL)
	    search_string = g_strdup(search_string);

	if(search_string == NULL)
	{   
	    reenterant = FALSE;
	    return;
	}


	/* Get search options */

	/* Throughness */
	w = fip->match_all_radio;
	if(w != NULL)
	{
	    if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)))
		throughness = 0;
	}
	w = fip->match_any_radio;
	if(w != NULL)
	{
	    if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)))
		throughness = 1;
	}
	w = fip->match_phrase_radio;
	if(w != NULL)
	{
	    if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)))
		throughness = 2;
	}

	/* Case sensitive */
	w = fip->case_sensitive_toggle;
	if(w != NULL)
	    case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));


	/* Begin finding */

	EditorFIPSetBusy(fip);

	EditorFIPSetStatusMessage(fip, "Preparing search...");
	EditorFIPSetStatusProgress(fip, 0.0);

	gtk_clist_freeze(results_clist);

	/* Clear clist */
	gtk_clist_clear(results_clist);

	/* Do find */
	matches = EditorFIPDoFind(
	    fip, (editor_struct *)fip->editor_ptr,
	    throughness, search_string, case_sensitive
	);


	/* Delete search string */
	g_free(search_string);
	search_string = NULL;


	/* Done finding */

	gtk_clist_thaw(results_clist);

	/* Print match results */
	s = g_strdup_printf(
	    "Search done: %i results",
	    matches
	);
	EditorFIPSetStatusMessage(fip, s);
	g_free(s);
	EditorFIPSetStatusProgress(fip, 0.0f);


	/* Update menus */
	EditorFIPUpdateMenus(fip);

	EditorFIPSetReady(fip);

	reenterant = FALSE;
}

/*
 *	Clear button callback.
 */
void EditorFIPClearCB(GtkWidget *widget, gpointer data)
{
	GtkCList *clist;
	GtkCombo *combo;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if(fip == NULL)
	    return;

	clist = (GtkCList *)fip->results_clist;
	if(clist == NULL)
	    return;

	combo = (GtkCombo *)fip->find_combo;
	if(combo == NULL)
	    return;

	EditorFIPSetBusy(fip);

	gtk_entry_set_text(GTK_ENTRY(combo->entry), "");

#if 0
/* Just clear the find combo's entry widget */
	gtk_clist_freeze(clist);
	gtk_clist_clear(clist);
	gtk_clist_thaw(clist);
#endif

	EditorFIPUpdateMenus(fip);
	EditorFIPSetReady(fip);
}

/*
 *	Stop button callback.
 */
void EditorFIPStopCB(GtkWidget *widget, gpointer data)
{
	static gboolean reenterant = FALSE;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if(fip == NULL)
	    return;

	if(!fip->initialized)
	    return;

	if(reenterant)
	    return;
	else
	    reenterant = TRUE;

	fip->stop_find_count++;
	if(fip->stop_find_count < 1)
	    fip->stop_find_count = 1;

	reenterant = FALSE;
}

/*
 *	Goto button callback.
 */
void EditorFIPGotoCB(GtkWidget *widget, gpointer data)
{
	static gboolean reenterant = FALSE;
	GtkCList *results_clist;	/* Dialog's results clist */
	gint row;			/* Row on dialog's result clist */
	GtkCTree *layout_ctree;
	GtkCTreeNode *branch;
	editor_item_struct *item;
	editor_struct *editor;
	editor_fip_item_struct *result_ptr;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if(fip == NULL)
	    return;

	if(!fip->initialized)
	    return;

	if(reenterant)
	    return;
	else
	    reenterant = TRUE;

	/* Get pointer to the dialog's editor */
	editor = (editor_struct *)fip->editor_ptr;
	if((editor == NULL) ? 1 : !editor->initialized)
	{
	    reenterant = FALSE;
	    return;
	}

	/* Get pointer to editor's layout ctree */
	layout_ctree = (GtkCTree *)editor->layout_ctree;
	if(layout_ctree == NULL)
	{
	    reenterant = FALSE;
	    return;
	}


	/* Get pointer to dialog's results clist */
	results_clist = (GtkCList *)fip->results_clist;
	if(results_clist == NULL)
	{
	    reenterant = FALSE;
	    return;
	}

	/* Get selected row on results clist? */
	row = (gint)fip->selected_result;
	if((row < 0) || (row >= results_clist->rows))
	{
	    reenterant = FALSE;
	    return;
	}

	/* Get results item structure pointer from row data */
	result_ptr = (editor_fip_item_struct *)gtk_clist_get_row_data(
	    results_clist, row
	);
	if(result_ptr == NULL)
	{
	    reenterant = FALSE;
	    return;
	}

	/* Get branch item pointer from result structure's branch_data */
	item = (editor_item_struct *)result_ptr->branch_data;
	if(item == NULL)
	{
	    CDialogGetResponse(
"Non-Critical Internal error!",
"Result item structure's pointer to the branch's\n\
item data is NULL.",
"The goto procedure could not be completed due to\n\
a missing pointer to the editor layour tree branch\n\
item data.",
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    reenterant = FALSE;
	    return;
	}


	/* Match the branch on the editor's layout ctree with the
	 * branch item pointer which is suppose to be a unique
	 * pointer on one of the branches.
	 */
	branch = gtk_ctree_find_by_row_data(
	    layout_ctree, NULL, (gpointer)item
	);
	if(branch == NULL)
	{
	    CDialogGetResponse(
"Non-Critical Internal error!",
"Tree branch on the editor's layout tree could not be\n\
found with the result's branch item pointer.",
"The goto procedure could not be completed due to\n\
an unfindable reference to the layout tree's branch\n\
by branch item pointer. It is possible that the\n\
branch and corresponding item data pointer no longer\n\
exist on the layout tree.",
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    reenterant = FALSE;
	    return;
	}

	/* Select the matched branch on the layout ctree */
	EditorBranchSelect(editor, branch);

	/* Flush events to make sure selecting is done properly */
	while(gtk_events_pending() > 0)
	    gtk_main_iteration();

	/* Now handle by item type, note that item is valid since
	 * branch is not NULL according to gtk_ctree_find_by_row_data().
	 */
	switch(item->type)
	{
	  case EditorItemTypeHeader:
	    if((result_ptr->text_sel_start >= 0) &&
	       (result_ptr->text_sel_end >= 0)
	    )
	    {
		GtkText *text = (GtkText *)editor->header_text;
		if(text != NULL)
		{
		    gtk_text_set_point(
			text, (guint)result_ptr->text_sel_start
		    );
		    gtk_editable_set_position(
			GTK_EDITABLE(text), result_ptr->text_sel_start
		    );
		    gtk_editable_select_region(
			GTK_EDITABLE(text),
			result_ptr->text_sel_start,
			result_ptr->text_sel_end
		    );
		}
	    }
	    break;

	  case EditorItemTypeSection:
	    if((result_ptr->text_sel_start >= 0) &&
	       (result_ptr->text_sel_end >= 0)
	    )
	    {
		GtkText *text = (GtkText *)editor->section_text;
		if(text != NULL)
		{
		    gtk_text_set_point(
			text,
			(guint)result_ptr->text_sel_start
		    );
		    gtk_editable_set_position(
			GTK_EDITABLE(text),
			result_ptr->text_sel_start
		    );
		    gtk_editable_select_region(
			GTK_EDITABLE(text),
			result_ptr->text_sel_start,
			result_ptr->text_sel_end
		    );
		}
	    }
	    break;

	  case EditorItemTypeFile:
	    break;
	}


	/* Unmap the Find In Pages dialog */
	EditorFIPUnmap(fip);

	reenterant = FALSE;
}


/*
 *	Menu map callback.
 */
gint EditorFIPMenuMapCB(GtkWidget *widget, GdkEvent *event, gpointer data)
{
	static gboolean reenterant = FALSE;
	GdkEventButton *button;
	GtkWidget *w;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if((widget == NULL) || (fip == NULL))
	    return(TRUE);

	if(reenterant)
	    return(TRUE);
	else
	    reenterant = TRUE;

	/* Is event type button press? If so then get pointer to button
	 * event structure.
	 */
	if((event->type == GDK_BUTTON_PRESS) ||
	   (event->type == GDK_BUTTON_RELEASE)
	)
	    button = (GdkEventButton *)event;
	else
	    button = NULL;

	/* Results clist? */
	if(widget == fip->results_clist)
	{
	    w = fip->results_menu;
	    if((button != NULL) && (w != NULL))
	    {
		if(button->button == 3)
		{
		    gint row, column;


		    /* Try to select branch first */
		    if(gtk_clist_get_selection_info(
			GTK_CLIST(widget),
			button->x, button->y,
			&row, &column
		    ))
		    {
			gtk_clist_select_row(
			    GTK_CLIST(widget), row, column
			);
		    }

		    /* Map menu */
		    gtk_menu_popup(
			GTK_MENU(w),
			NULL, NULL, NULL, (gpointer)fip,
			button->button, button->time
		    );
		}
	    }
	}

	reenterant = FALSE;
	return(TRUE);
}

/*
 *	Results clist select callback.
 */
void EditorFIPResultsListSelectCB(
	GtkWidget *widget, gint row, gint column,
	GdkEventButton *event, gpointer data
)
{
	static gboolean reenterant = FALSE;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if((widget == NULL) || (fip == NULL))
	    return;

	if(reenterant)
	    return;
	else
	    reenterant = TRUE;

	/* Results clist? */
	if(widget == fip->results_clist)
	{
	    fip->selected_result = row;

	    /* Scroll if row not completely visable */
	    if(gtk_clist_row_is_visible(GTK_CLIST(widget), row) !=
		GTK_VISIBILITY_FULL
	    )
		gtk_clist_moveto(
		    GTK_CLIST(widget),
		    row, 0,	/* Row, column */
		    0.5, 0.0	/* Row, column */
		);

	    /* Check for double click */
	    if((event == NULL) ? 0 : (event->type == GDK_2BUTTON_PRESS))
	    {
		/* Call goto callback */
		EditorFIPGotoCB(widget, data);
		reenterant = FALSE;
		return;
	    }

	    /* Update menus */
	    EditorFIPUpdateMenus(fip);
	}

	reenterant = FALSE;
}

/*
 *	Results clist unselect callback.
 */
void EditorFIPResultsListUnselectCB(
	GtkWidget *widget, gint row, gint column,
	GdkEventButton *event, gpointer data
)
{
	static gboolean reenterant = FALSE;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if((widget == NULL) || (fip == NULL))
	    return;

	if(reenterant)
	    return;
	else
	    reenterant = TRUE;





	reenterant = FALSE;
}


/*
 *	Results clist column click callback.
 */
void EditorFIPResultsListColumnClickCB(
	GtkWidget *widget, gint column, gpointer data
)
{
	static gboolean reenterant = FALSE;
	editor_fip_struct *fip = EDITOR_FIP(data);
	if((widget == NULL) || (fip == NULL))
	    return;
	
	if(reenterant)
	    return;
	else
	    reenterant = TRUE;

	gtk_clist_set_sort_column(GTK_CLIST(widget), column);
	gtk_clist_sort(GTK_CLIST(widget));

	reenterant = FALSE;
}
