#include <sys/stat.h>
#include <gtk/gtk.h>

#include "../include/string.h"
#include "../include/disk.h"

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

#include "editor.h"
#include "viewer.h"
#include "viewerfio.h"

#include "manedit.h"
#include "maneditcb.h"
#include "maneditop.h"
#include "aboutdialog.h"
#include "config.h"


void MEditHelpManPageProceduresCB(GtkWidget *widget, gpointer data);
void MEditHelpManPageXMLRefCB(GtkWidget *widget, gpointer data);
void MEditHelpManualCB(GtkWidget *widget, gpointer data);

void MEditAboutDialogMapCB(GtkWidget *widget, gpointer data);
void MEditEditorNewCB(GtkWidget *widget, gpointer data);
void MEditViewerNewCB(GtkWidget *widget, 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)


/* Creates a new viewer window or maps an existing initialized but
 * unmapped viewer on the core structure, uses variables core_ptr,
 * last_null_num, viewer_num, and viewer
 */
#define DO_NEW_VIEWER	\
{ \
 last_null_num = -1; \
 for(viewer_num = 0; viewer_num < core_ptr->total_viewers; viewer_num++) \
 { \
  viewer = core_ptr->viewer[viewer_num]; \
  if(viewer == NULL) \
  { \
   last_null_num = viewer_num; \
   continue; \
  } \
  if(viewer->initialized && !viewer->map_state) \
   break; \
 } \
 if(viewer_num < core_ptr->total_viewers) \
 { \
  /* Found one that was initialized and unmapped */ \
  viewer = core_ptr->viewer[viewer_num]; \
  ViewerMap(viewer); \
 } \
 else \
 { \
  /* Need to create a new viewer */ \
  if(last_null_num > -1) \
  { \
   viewer_num = last_null_num; \
  } \
  else \
  { \
   viewer_num = core_ptr->total_viewers; \
   core_ptr->total_viewers = viewer_num + 1; \
   core_ptr->viewer = (viewer_struct **)g_realloc( \
    core_ptr->viewer, \
    core_ptr->total_viewers * sizeof(viewer_struct *) \
   ); \
   if(core_ptr->viewer == NULL) \
   { \
    core_ptr->total_viewers = 0; \
    return; \
   } \
   core_ptr->viewer[viewer_num] = NULL; \
  } \
  /* Allocate and create a new viewer */ \
  viewer = ViewerNew((void *)core_ptr, -1); \
  core_ptr->viewer[viewer_num] = viewer; \
  if(viewer != NULL) \
   ViewerMap(viewer); \
  else \
   viewer_num = -1; \
 } \
}


/*
 *	Help man page creation procedures.
 */
void MEditHelpManPageProceduresCB(GtkWidget *widget, gpointer data)
{
	gint last_null_num, viewer_num;
	const gchar *s;
	gchar *help_path, *help_file;
	struct stat stat_buf;
	viewer_struct *viewer;
	medit_core_struct *core_ptr = (medit_core_struct *)data;
	if(core_ptr == NULL)
	    return;
		
	/* Get template path */
	s = PrefParmGetValueP(
	    core_ptr->pref, MEDIT_PREF_PARM_LOCATIONS_GLOBAL_DIR
	);
	if(s == NULL)
	    s = MEDIT_GLOBAL_DIR;
	if(s != NULL)
	{
	    s = PrefixPaths(s, MEDIT_HELP_DIR);
	    if(s == NULL)
		s = "/";
	    help_path = STRDUP(s);
	}
	else
	{
	    help_path = STRDUP("/");
	}

	/* Get help file path */
	s = PrefixPaths(  
	    help_path, MEDIT_MANPAGE_WRITING_FILE
	);
	if(s == NULL)
	    s = MEDIT_MANPAGE_WRITING_FILE;
	help_file = STRDUP(s);

	/* Check if help file exists */
	if(stat(help_file, &stat_buf))
	{
	    gchar *s = g_strdup_printf(
"Unable to find Manual Page file\n\n    %s",
		help_file
	    );
	    CDialogGetResponse(
"Cannot find file!",
		s,
"The specified Manual Page file could not be found\n\
please check the specified paths in the preferences\n\
(go to edit->preferences). Also make sure that the\n\
program is installed properly.",
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    g_free(s);
	    g_free(help_path);
	    g_free(help_file);
	    return;
	}


	/* Create a new viewer */
	DO_NEW_VIEWER

	ViewerSetBusy(viewer);
	ViewerViewTextRecordScrollPositions(viewer);
	ViewerTextDelete(viewer, 0, -1);
	ViewerTextInsertPosition(viewer, 0);
	ViewerLoadFile(
	    viewer, help_file, MEDIT_MANPAGE_WRITING_FILE
	);
	ViewerSetReady(viewer);

	g_free(help_path);
	g_free(help_file);
}

/*
 *	Help man page XML references.
 */
void MEditHelpManPageXMLRefCB(GtkWidget *widget, gpointer data)
{
	const gchar *s;
	gint last_null_num, viewer_num;
	viewer_struct *viewer;
	gchar *help_path, *help_file;
	struct stat stat_buf;
	medit_core_struct *core_ptr = (medit_core_struct *)data;
	if(core_ptr == NULL)
	    return;

	/* Get template path */
	s = PrefParmGetValueP(
	    core_ptr->pref, MEDIT_PREF_PARM_LOCATIONS_GLOBAL_DIR
	);
	if(s == NULL)
	    s = MEDIT_GLOBAL_DIR;
	if(s != NULL)
	{
	    s = PrefixPaths(s, MEDIT_HELP_DIR);
	    if(s == NULL)
		s = "/";
	    help_path = STRDUP(s);
	}
	else
	{
	    help_path = STRDUP("/");
	}

	/* Get help file path */
	s = PrefixPaths(
	    help_path, MEDIT_MANPAGE_XML_REFERENCE_FILE
	);
	if(s == NULL)
	    s = MEDIT_MANPAGE_XML_REFERENCE_FILE;
	help_file = STRDUP(s);

	/* Check if help file exists */
	if(stat(help_file, &stat_buf))
	{
	    gchar *s = g_strdup_printf(
"Cannot find Manual Page file\n\n    %s",
		help_file
	    );
	    CDialogGetResponse(
"Cannot find file!",
		s,
"The specified Manual Page file could not be found\n\
please check the specified paths in the preferences\n\
(go to edit->preferences). Also make sure that the\n\
program is installed properly.",
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    g_free(s);
	    g_free(help_path);
	    g_free(help_file);
	    return;
	}


	/* Create a new viewer */
	DO_NEW_VIEWER

	ViewerSetBusy(viewer);
	ViewerViewTextRecordScrollPositions(viewer);
	ViewerTextDelete(viewer, 0, -1);
	ViewerTextInsertPosition(viewer, 0);
	ViewerLoadFile(
	    viewer, help_file, MEDIT_MANPAGE_XML_REFERENCE_FILE
	);
	ViewerSetReady(viewer);

	g_free(help_path);
	g_free(help_file);
}

/*
 *	Help manual callback.
 */
void MEditHelpManualCB(GtkWidget *widget, gpointer data)
{
	gint last_null_num, viewer_num;
	viewer_struct *viewer;
	medit_core_struct *core_ptr = (medit_core_struct *)data;
	if(core_ptr == NULL)
	    return;

	/* Create a new viewer */
	DO_NEW_VIEWER

	ViewerSetBusy(viewer);
	ViewerViewTextRecordScrollPositions(viewer);
	ViewerTextDelete(viewer, 0, -1);
	ViewerTextInsertPosition(viewer, 0);
	ViewerLoadFile(viewer, "manedit", "manedit");
	ViewerSetReady(viewer);
}




#undef DO_NEW_VIEWER

/*
 *	Maps the program's about dialog.
 */
void MEditAboutDialogMapCB(GtkWidget *widget, gpointer data)
{
	about_dialog_struct *about;
	medit_core_struct *core_ptr = (medit_core_struct *)data;
	if(core_ptr == NULL)
	    return;

	/* Get pointer to about window structure and initialize as
	 * needed
	 */
	about = core_ptr->about;
	if(about == NULL)
	{
	    about = AboutDialogNew(core_ptr);
	    core_ptr->about = about;
	}
	if(about == NULL)
	    return;

	AboutDialogMap(about);
}

/*
 *	Creates a new editor or maps an existing initialized and
 *	unmapped editor if possible.
 */
void MEditEditorNewCB(GtkWidget *widget, gpointer data)
{
	gint i, last_null_num = -1;
	editor_struct *editor;
	medit_core_struct *core_ptr = (medit_core_struct *)data;
	if(core_ptr == NULL)
	    return;

	/* Search for existing initialized but unmapped editor on
	 * the core structure.
	 */
	for(i = 0; i < core_ptr->total_editors; i++)
	{
	    editor = core_ptr->editor[i];
	    if(editor == NULL)
	    {
		last_null_num = i;
		continue;
	    }

	    if(editor->initialized && !editor->map_state)
		break;
	}
	if(i < core_ptr->total_editors)
	{
	    /* Found one that was initialized and unmapped */
	    EditorMap(core_ptr->editor[i]);
	}
	else
	{
	    /* Need to create a new editor */
	    if(last_null_num > -1)
	    {
		i = last_null_num;
	    }
	    else
	    {
		i = core_ptr->total_editors;
		core_ptr->total_editors = i + 1;
		core_ptr->editor = (editor_struct **)g_realloc(
		    core_ptr->editor,
		    core_ptr->total_editors * sizeof(editor_struct *)
		);
		if(core_ptr->editor == NULL)
		{
		    core_ptr->total_editors = 0;
		    return;
		}
		core_ptr->editor[i] = NULL;
	    }

	    /* Allocate and create a new editor */
	    editor = EditorNew((void *)core_ptr);
	    core_ptr->editor[i] = editor;

	    if(editor != NULL)
	    {
		EditorMap(editor);
	    }
	}

	return;
}


/*
 *	Creates a new viewer or maps an existing initialized and
 *	unmapped viewer if possible.
 */
void MEditViewerNewCB(GtkWidget *widget, gpointer data)
{
	gint i, last_null_num = -1;
	viewer_struct *viewer;
	medit_core_struct *core_ptr = (medit_core_struct *)data;
	if(core_ptr == NULL)
	    return;

	/* Search for existing initialized but unmapped viewer on
	 * the core structure.
	 */
	for(i = 0; i < core_ptr->total_viewers; i++)
	{
	    viewer = core_ptr->viewer[i];
	    if(viewer == NULL)
	    {
		last_null_num = i;
		continue;
	    }
		
	    if(viewer->initialized && !viewer->map_state)
		break;
	}
	if(i < core_ptr->total_viewers)
	{
	    /* Found one that was initialized and unmapped */
	    ViewerMap(core_ptr->viewer[i]);
	}
	else
	{
	    /* Need to create a new viewer */
	    if(last_null_num > -1)
	    {
		i = last_null_num;
	    }
	    else
	    {
		i = core_ptr->total_viewers;
		core_ptr->total_viewers = i + 1;
		core_ptr->viewer = (viewer_struct **)g_realloc(
		    core_ptr->viewer,
		    core_ptr->total_viewers * sizeof(viewer_struct *)
		);
		if(core_ptr->viewer == NULL)
		{
		    core_ptr->total_viewers = 0;
		    return;
		}
		core_ptr->viewer[i] = NULL;
	    }

	    /* Allocate and create a new viewer */
	    viewer = ViewerNew((void *)core_ptr, -1);
	    core_ptr->viewer[i] = viewer;

	    if(viewer != NULL)
	    {
		ViewerMap(viewer);
	    }
	}

	return;
}
