#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>

#include "../include/string.h"
#include "../include/strexp.h"

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

#include "editor.h"
#include "editordnd.h"
#include "editorop.h"
#include "viewer.h"
#include "viewerfio.h"
#include "viewerdnd.h"

#include "manedit.h"
#include "config.h"


/* DND data command string parsing */
void ViewerIndexCTreeDNDParseCmd(
	const gchar *string,
	viewer_struct **viewer_ptr,
	GtkCTreeNode ***branch, gint *total_branches
);

/* DND handling */
void ViewerIndexCTreeDNDDataRequestCB(
	GtkWidget *widget, GdkDragContext *dc,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
);
void ViewerViewTextDNDDataRecievedCB(
	GtkWidget *widget,
	GdkDragContext *dc,
	gint x, gint y,
	GtkSelectionData *selection_data,
	guint info, guint t,
	gpointer data
);
void ViewerDNDDataDeleteCB(
	GtkWidget *widget, GdkDragContext *dc, 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)


/*
 *      Parses the given string which should have been recieved as
 *      a DND command.
 *
 *	The only the returned array of branches should be deleted.
 */
void ViewerIndexCTreeDNDParseCmd(
	const gchar *string,
	viewer_struct **viewer_ptr,
	GtkCTreeNode ***branch, gint *total_branches
)
{
	guint32 addr_val;
	gint i, n, strc;
	gchar **strv;

	/* Reset returns */
	if(viewer_ptr != NULL)
	    *viewer_ptr = NULL;
	if(branch != NULL)
	    *branch = NULL;
	if(total_branches != NULL)
	    *total_branches = 0;

	if(string == NULL)
	    return;

	if(g_strcasepfx(string, "error"))
	    return;

	/* Parse command, format is as follows:
	 *
	 * <viewer_ptr> <branch_ptr...>
	 */
	strv = strexp(string, &strc);
	if(strv == NULL)
	    return;

	/* Get viewer_ptr (format in hex with no "0x" prefix) */
	if(strc > 0)
	    i = sscanf(strv[0], "%x", (guint32 *)&addr_val);
	else
	    i = 0;
	if((i > 0) && (viewer_ptr != NULL))
	    (*viewer_ptr) = (viewer_struct *)addr_val;

	/* Get list of branch pointers which should be branches
	 * on the parsed viewer's index GtkCTree
	 */
	if((branch != NULL) && (total_branches != NULL))
	{
	    const gchar *s;

	    /* Start at parsed argument number 1 */
	    for(i = 1; i < strc; i++)
	    {
		s = strv[i];
		if(s == NULL)
		    continue;

		n = *total_branches;
		*total_branches = n + 1;
		*branch = (GtkCTreeNode **)realloc(
		    *branch,
		    (*total_branches) * sizeof(GtkCTreeNode *)
		);
		if(*branch == NULL)
		{
		    *total_branches = 0;
		    break;
		}
		else
		{
		    i = sscanf(s, "%x", (guint32 *)&addr_val);
		    if(i > 0)
			(*branch)[n] = (GtkCTreeNode *)addr_val;
		    else
			(*branch)[n] = NULL;
		}
	    }
	}

	/* Delete exploded strings */
	strlistfree(strv, strc);
}


/*
 *	Viewer index ctree data request callback.
 */
void ViewerIndexCTreeDNDDataRequestCB(
	GtkWidget *widget, GdkDragContext *dc,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
)
{
	gchar *buf;
	GtkCTreeNode *branch_ptr;
	viewer_struct *v = VIEWER(data);
	if((widget == NULL) || (v == NULL) || (dc == NULL))
	    return;

	if(!v->initialized)
	    return;

	if(widget != v->index_ctree)
	{
	    g_printerr(
"ViewerIndexCTreeDNDDataRequestCB(): widget != v->index_ctree\n"
	    );
	    return;
	}

	/* Get selected branch on viewer's index GtkCTree
	 *
	 * Note currently we only allow one selected branch at a time
	 */
	branch_ptr = v->selected_index_branch;

	/* No need to apply values to branch_ptr */

	/* Format buffer in the following format:
	 *
	 * <viewer_ptr> <branch_ptr...>
	 */
	if(branch_ptr != NULL)
	    buf = g_strdup_printf(
		"%.8x %.8x",
		(guint32)v,
		(guint32)branch_ptr
	    );
	else
	    buf = g_strdup_printf(
		"%.8x",
		(guint32)v
	    );

	/* Send out data */
	gtk_selection_data_set(
	    selection_data,
	    GDK_SELECTION_TYPE_STRING,
	    8,			/* 8 bits per character */
	    buf, strlen(buf)
	);

	g_free(buf);
}


/*
 *	Viewer view text widget drop recieved callback.
 */
void ViewerViewTextDNDDataRecievedCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
)
{
	gboolean need_delete = FALSE;
	gint viewer_num;
	gboolean same;
	GtkWidget *source_widget;
	GtkEditable *editable;
	medit_core_struct *core_ptr;
	viewer_struct *v = VIEWER(data);
	if((widget == NULL) || (v == NULL) || (dc == NULL))
		return;

	if(!v->initialized)
	    return;

	/* Important, check if we got data */
	if(selection_data == NULL)
	    return;
	if(selection_data->length < 0)
	    return;

	/* Source and target same? */
	source_widget = gtk_drag_get_source_widget(dc);
	same = ((source_widget == widget) ? TRUE : FALSE);

	/* Check if data needs to be deleted by testing if this
	 * drag operation is not a copy.
	 */
	if(dc->action != GDK_ACTION_COPY)
	    need_delete = TRUE;

	/* Get viewer view text widget */
	editable = (GtkEditable *)v->view_text;

	/* Check if viewer view text widget matches the destination
	 * widget. If it does not then set editable to NULL so no further
	 * processing is done.
	 */
	if((void *)editable != (void *)widget)
	    editable = NULL;

	/* Get pointer to core structure */
	core_ptr = (medit_core_struct *)v->core_ptr;
	if(core_ptr == NULL)
	    return;

	/* Get viewer number */
	for(viewer_num = 0; viewer_num < core_ptr->total_viewers; viewer_num++)
	{
	    if(v == core_ptr->viewer[viewer_num])
		break;
	}
	if(viewer_num >= core_ptr->total_viewers)
	    viewer_num = -1;


	if(editable != NULL)
	{
	    /* Editor layout ctree branch node transfer command? */
	    if(info == MEDIT_DND_TYPE_INFO_EDITOR_BRANCH_CMD)
	    {
		gint i;
		editor_struct *src_editor = NULL;
		GtkCTreeNode **branch = NULL;
		gint total_branches = 0;
		gboolean	is_branch_file = FALSE,
				is_branch_header = FALSE,
				is_branch_section = FALSE;


		/* Parse command string */
		EditorDNDParseCmd(
		    (const gchar *)selection_data->data,
		    &src_editor,
		    &branch, &total_branches
		);

		/* Is source editor valid? */
		if(src_editor != NULL)
		{
		    editor_item_struct *item;

		    /* Check what types of branch item data types we
		     * just got
		     */
		    for(i = 0; i < total_branches; i++)
		    {
			item = EditorBranchGetData(
			    (GtkCTree *)src_editor->layout_ctree, branch[i]
			);
			if(item == NULL)
			    continue;

			/* Check item type */
			switch(item->type)
			{
			  case EditorItemTypeFile:
			    is_branch_file = TRUE;
			    break;
		
			  case EditorItemTypeHeader:
			    is_branch_header = TRUE;
			    break;

			 case EditorItemTypeSection:  
			    is_branch_section = TRUE;
			    break;
			}
		    }   
		    /* `No operation' check */
		    if(same)
		    {
			/* No such check possible when droping to the
			 * view text widget on a viewer.
			 */
		    }

		    /* ************************************************ */
		    /* Handle by branch item data type */
		    /* File (which implies trunk)? */
		    if(is_branch_file)
		    {
			/* Unset delete, we don't want to delete anything
			 * for this operation.
			 */
			need_delete = FALSE;

			/* Handle only the first branch */
			if(total_branches > 0)
			{
			    /* Call editor's preview procedure on source
			     * editor so that it first saves the loaded
			     * data before loading it into the viewer.
			     */
			    EditorDoPreview(
				src_editor,
				branch[0],
				viewer_num
			    );
			}
		    }
		    /* ************************************************ */
		    /* Header or section? */
		    else if(is_branch_header || is_branch_section)
		    {
			/* Unset delete, we don't want to delete anything
			 * for this operation.
			 */
			need_delete = FALSE;

			/* Handle only the first branch */
			if(total_branches > 0)
			{
			    GtkCTreeNode *trunk;


			    /* Get toplevel trunk branch for first
			     * transfered branch.
			     */
			    trunk = EditorItemGetToplevel(
				src_editor, branch[0]
			    );

			    /* Call editor's preview procedure on source
			     * editor so that it first saves the loaded
			     * data before loading it into the viewer.
			     */
			    EditorDoPreview(
				src_editor,
				trunk,
				viewer_num
			    );
			}
		    }
		    /* ************************************************ */
		    /* Add support for other types here */
		}

		/* Free only list of branches from source editor */
		g_free(branch);
		branch = NULL;
		total_branches = 0;
	    }
	    /* ******************************************************** */
	    /* Viewer index ctree branch node transfer command? */
	    else if(info == MEDIT_DND_TYPE_INFO_VIEWER_BRANCH_CMD)
	    {
		viewer_struct *src_viewer = NULL;
		GtkCTreeNode **branch = NULL;
		gint total_branches = 0;


		/* Parse command string. Value of src_viewer points to
		 * the source viewer and branch and total_branches are
		 * the selected branch(es) on the source viewer's index
		 * ctree.
		 */   
		ViewerIndexCTreeDNDParseCmd(
		    (const gchar *)selection_data->data,
		    &src_viewer,
		    &branch, &total_branches
		);

		/* Is source viewer valid? */
		if((src_viewer != NULL) ?
		    (src_viewer->index_ctree != NULL) : FALSE
		)
		{
		    GtkCTree *ctree = GTK_CTREE(src_viewer->index_ctree);
		    viewer_index_item_struct *item;

		    /* Handle only the first branch */
		    if(total_branches > 0)
		    {
			/* Get index item */
			if(branch[0] != NULL)
			    item = VIEWER_INDEX_ITEM(
				gtk_ctree_node_get_row_data(ctree, branch[0])
			    );
			else
			    item = NULL;

			/* Check if source viewer's index item is valid
			 * and of the currect type
			 */
			if((item != NULL) ?
			    (item->type == ViewerIndexItemTypeManualPage) : FALSE
			)
			{
			    /* Load manual page into target viewer's
			     * view text
			     */
			    ViewerSetBusy(v);
			    ViewerViewTextRecordScrollPositions(v);
			    ViewerTextDelete(v, 0, -1);
			    ViewerTextInsertPosition(v, 0);
			    ViewerLoadFile(
				v,
				item->full_path,
				item->full_path
			    );
			    ViewerSetReady(v);
			}
		    } 

	     
		    /* Delete branches that came from source editor
		     * as needed
		     */
		    if(need_delete)
		    {
			/* Do not delete branches on source viewer's
			 * index GtkCTree
			 */
		    }
		}

		/* Delete only list of branches from source viewer's
		 * index GtkCTree
		 */
		g_free(branch);
		branch = NULL;  
		total_branches = 0;
 	    }
	    /* Other standard DND target types? */
	    else if((info == MEDIT_DND_TYPE_INFO_TEXT_PLAIN) ||
		    (info == MEDIT_DND_TYPE_INFO_TEXT_URI_LIST) ||
		    (info == MEDIT_DND_TYPE_INFO_STRING)
	    )
	    {
		gchar *path = EditorDNDParseURL(
		    selection_data->data, selection_data->length
		);

		/* Load manual page into target viewer's view text */
		ViewerSetBusy(v);
		ViewerViewTextRecordScrollPositions(v);
		ViewerTextDelete(v, 0, -1);
		ViewerTextInsertPosition(v, 0);
		ViewerLoadFile(v, path, path);
		ViewerSetReady(v);

		g_free(path);
	    }

	    /* ******************************************************** */
	    /* Add support for other types of DND here */


	}
}

/*
 *      Viewer DND data delete callback.
 *
 *      This deletes the selected item on viewer's layout ctree.
 */
void ViewerDNDDataDeleteCB(
	GtkWidget *widget, GdkDragContext *dc, gpointer data
)
{
	/* Function no longer used */
	return;
}
