/* Binding the nethack engine to the gtk2 port 
 *
 * $Id: g2bind.c,v 1.2 2004/06/27 00:17:32 miq Exp $
 *
 */



#include <gdk/gdkkeysyms.h>

#include "g2bind.h"
#include "g2main.h"
#include "g2player.h"
#include "g2text.h"
#include "func_tab.h"

struct window_procs Gtk2_procs = {
    "Gtk2",
    WC_COLOR | WC_HILITE_PET | WC_INVERSE,
    0L,
    gtk2_init_nhwindows,
    gtk2_player_selection,
    gtk2_askname,
    gtk2_get_nh_event,
    gtk2_exit_nhwindows,
    gtk2_suspend_nhwindows,
    gtk2_resume_nhwindows,
    gtk2_create_nhwindow,
    gtk2_clear_nhwindow,
    gtk2_display_nhwindow,
    gtk2_destroy_nhwindow,
    gtk2_curs,
    gtk2_putstr,
    gtk2_display_file,
    gtk2_start_menu,
    gtk2_add_menu,
    gtk2_end_menu,
    gtk2_select_menu,
    genl_message_menu,          /* no need for X-specific handling */
    gtk2_update_inventory,
    gtk2_mark_synch,
    gtk2_wait_synch,
#ifdef CLIPPING
    gtk2_cliparound,
#endif
#ifdef POSITIONBAR
    donull,
#endif
    gtk2_print_glyph,
    gtk2_raw_print,
    gtk2_raw_print_bold,
    gtk2_nhgetch,
    gtk2_nh_poskey,
    gtk2_nhbell,
    gtk2_doprev_message,
    gtk2_yn_function,
    gtk2_getlin,
    gtk2_get_ext_cmd,
    gtk2_number_pad,
    gtk2_delay_output,
#ifdef CHANGE_COLOR             /* only a Mac option currently */
    donull,
    donull,
#endif
    /* other defs that really should go away (they're tty specific) */
    gtk2_start_screen,
    gtk2_end_screen,
    gtk2_outrip,
    genl_preference_update,
};

#define G2_OUTPUT_DELAY 40	/* milliseconds to wait between delayed outputs */
static gboolean inventoryReady = FALSE;
static gboolean delayDone = FALSE;

void gtk2_init_nhwindows(int *argcp, char **argv)
{
/*    g_print("Init nhwindows\n");*/
    g2_init_main_window(argcp, argv);
    iflags.window_inited = TRUE;
}

void gtk2_player_selection(void)
{
    g2_do_player_selection(g2_get_main_window());
}

void gtk2_askname(void)
{
    g_print("askname\n");
    g_print("currently unimplemented\n");
}

void gtk2_get_nh_event(void)
{
/* XXX: We do our own eventhandling ?! */
}

void gtk2_exit_nhwindows(const char *string)
{
    g2_exit_windows(string);
}

void gtk2_suspend_nhwindows(const char *string)
{
    g_print("suspend_nhwindows\n");
    g_print("currently unimplemented\n");
}

void gtk2_resume_nhwindows(void)
{
    g_print("resume_nhwindows\n");
    g_print("currently unimplemented\n");
}

winid gtk2_create_nhwindow(int type)
{
    /*g_print("Create nhwindow type=%d\n", type);*/
    return g2_create_window(type);
}

void gtk2_clear_nhwindow(winid wid)
{
    g_signal_emit_by_name(G_OBJECT(wid), "clear", NULL);
}

void gtk2_display_nhwindow(winid wid, BOOLEAN_P block)
{
    /*g_print("Show nhwindow=%x, block=%d\n", wid, block);*/
    g_signal_emit_by_name(G_OBJECT(wid), "display", block, NULL);
	/* show the map and wait until user presses a key. This causes the output to
	 * block when detecting monsters, gold, food or finding a trap hidden under
	 * some item.
	 */
	if (block && wid == WIN_MAP) {
		gtk2_nhgetch();
	}
}

void gtk2_destroy_nhwindow(winid wid)
{
    /*g_print("destroy_nhwindow winid=%x\n", wid);*/
    /* XXX prevent our main windows from being destroyed! */
    if (wid != WIN_MAP && wid != WIN_STATUS && wid != WIN_MESSAGE) {
        gtk_widget_destroy(GTK_WIDGET(wid));
    }
}

void gtk2_curs(winid wid, int x, int y)
{
    g_signal_emit_by_name(G_OBJECT(wid), "curs", x, y, NULL);
}

void gtk2_putstr(winid wid, int attr, const char *text)
{
    /*g_printf("pustr: %s\n", text);*/
    g_signal_emit_by_name(G_OBJECT(wid), "putstr", attr, text, NULL);
}

void gtk2_display_file(const char *filename, BOOLEAN_P must_exist)
{
	dlb *file;
	
    g_print("display_file, filename=%s, must exist=%d\n", filename, must_exist);
       
    file = dlb_fopen(filename, "r");
    if (file) {
		GtkWidget *textWindow;
		gchar line[128];
		
		/* read the contents and display it */
		textWindow = g2_text_new();
	  	while (dlb_fgets(line, 128, file)) {
			/* XXX strip newline from buffer, think of changing putstr to not add \n */
			line[strlen(line) -1] = '\0';
	  		g_signal_emit_by_name(G_OBJECT(textWindow), "putstr", 0, line, NULL);
	  	}
	  	dlb_fclose(file);
		g_signal_emit_by_name(G_OBJECT(textWindow), "display", TRUE, NULL);
		gtk_widget_destroy(textWindow);
	} else if (!file && must_exist) {
		/* show error */
    	GtkWidget *errorDialog;
        gchar message[128];
		
        g_snprintf(message, 128, "Error! Could not find file: %s\n",filename);
    	errorDialog = gtk_message_dialog_new(GTK_WINDOW(g2_get_main_window()),
				GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, message);
    	gtk_dialog_run(GTK_DIALOG(errorDialog));
		gtk_widget_destroy(errorDialog);
	}
}

void gtk2_start_menu(winid wid)
{
/*    g_print("start_menu window=%x\n", wid);*/
    g_signal_emit_by_name(G_OBJECT(wid),
                          "start_menu", NULL);
}

void gtk2_add_menu(winid wid, int glyph, const ANY_P * identifier,
                     CHAR_P accelerator, CHAR_P group_accel, int attr,
                     const char *str, BOOLEAN_P presel)
{
    g_signal_emit_by_name(G_OBJECT(wid),
                          "add_menu", glyph, identifier, accelerator,
                          group_accel, attr, str, presel, NULL);
}

void gtk2_end_menu(winid wid, const char *prompt)
{
/*    g_print("end_menu\n");*/
    g_signal_emit_by_name(G_OBJECT(wid),
                          "end_menu", prompt, NULL);
}

int gtk2_select_menu(winid wid, int how, MENU_ITEM_P ** selected)
{
    gint returnValue;

    g_signal_emit_by_name(G_OBJECT(wid),
                          "select_menu", how, selected, &returnValue);

    return returnValue;
}

void gtk2_update_inventory(void)
{
    /* XXX we need to investigate segfault when restoring game and not delaying inventory update */
    if(flags.perm_invent && inventoryReady) {
        /*g_print("update_inventory, call display inventory for debugging and perm invent\n");*/
        display_inventory(NULL, 0);
    }
}

void gtk2_mark_synch(void)
{
    /* XXX: do we have to do to something? */
	/*g_print("mark_synch, unimplemented\n");*/
}

void gtk2_wait_synch(void)
{
    /* XXX: do we have to do to something? */
	g_print("wait_synch, unimplemented\n");
}

void gtk2_cliparound(int x, int y)
{
    /* XXX: Currently we assume that only the map can be clipped */
    g_signal_emit_by_name(G_OBJECT(WIN_MAP), "cliparound",
                          x, y, NULL);
}

void gtk2_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph)
{
	/*g_print("Print glyph\n");*/
    g_signal_emit_by_name(G_OBJECT(wid), "print_glyph",
                          x, y, glyph, NULL);
}

void gtk2_raw_print(const char *str)
{
    g_print("%s\n", str);
}

void gtk2_raw_print_bold(const char *str)
{
    g_print("%s\n", str);
    /*tty_raw_print_bold(str);*/
}

/* XXX: This is our event-loop thingy, that checks the keybuffer */
int gtk2_nhgetch(void)
{
    int key;
    
    while (keyBuffer == NULL) {
        gtk_main_iteration();
    }
    key = GPOINTER_TO_INT(keyBuffer->data);
    keyBuffer = g_slist_delete_link(keyBuffer, keyBuffer);

    return key;
}

int gtk2_nh_poskey(int *x, int *y, int *mod)
{
    gint key;
    
    /* XXX hack: we call set a flag for perm_invent and call display_inventory the first time */
    if (!inventoryReady && flags.perm_invent) {
        inventoryReady = TRUE;
        display_inventory(NULL, 0);
    }
    
    /*g_print("nh_poskey:x=%d, y=%d, mod=%d\n", *x, *y, *mod);*/
    g_signal_emit_by_name(G_OBJECT(g2_get_equipment_window()), "update" , NULL);
    while (keyBuffer == NULL) {
        gtk_main_iteration();
    }
    g_signal_emit_by_name(G_OBJECT(WIN_STATUS), "player_acted" , NULL);
    key = GPOINTER_TO_INT(keyBuffer->data);
    keyBuffer = g_slist_delete_link(keyBuffer, keyBuffer);
    *x = clickX;
    *y = clickY;
    *mod = clickMod;
    return key;
}

void gtk2_nhbell(void)
{
    g_print("nhbell\n");
    g_print("currently weakly implemented\n");
    gdk_beep();
}

int gtk2_doprev_message(void)
{
    /* do nothing, we have a nice textarea with a scrollbar */
    return 0;
}

/* XXX think of repeating question if input not valid...*/
gchar gtk2_yn_function(const gchar *question, const gchar *choices, CHAR_P def)
{
    int key;
    gchar *message;
    gchar charString[2];
    
    charString[0] = def;
    charString[1] = '\0';
    if (choices != NULL) {
        message = g_strconcat(question, " [", choices, "] def=", charString, NULL);
    } else {
        message = (gchar*) question;
    }
    g_signal_emit_by_name(G_OBJECT(WIN_MESSAGE), "putstr",
                                  0, message, NULL);
    while (keyBuffer == NULL) {
        gtk_main_iteration();
    }
    key = GPOINTER_TO_INT(keyBuffer->data);
    keyBuffer = g_slist_delete_link(keyBuffer, keyBuffer);
	if (choices == NULL) {
		return key;
    } else {
		switch (key) {
		case GDK_Escape:
			if (g_strrstr(choices, "q") != NULL) {
				return 'q';
			} else if (g_strrstr(choices, "n") != NULL) {
				return 'n';
			} else {
				return def;
			}
			break;
		case GDK_KP_Enter:
		case GDK_Return:
		case GDK_space:
			return def;
		}
		charString[0] = (gchar) key;
		charString[1] = '\0';
		if (g_strrstr(choices, charString) != NULL) {
			return key;
		}
	}
	
	return def;
}

void gtk2_getlin(const char *question, char *input)
{
    GtkWidget *inputDialog;
	GtkWidget *spacerVbox;
	GtkWidget *questionLabel;
	GtkWidget *inputEntry;
	gint response;
	
	inputDialog = gtk_dialog_new_with_buttons("Gtk2Hack - Question",
			GTK_WINDOW(g2_get_main_window()),GTK_DIALOG_MODAL,
			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
	spacerVbox = gtk_vbox_new(FALSE, 6);
    gtk_container_set_border_width(GTK_CONTAINER(spacerVbox), 5);
	questionLabel = gtk_label_new(question);
	gtk_misc_set_alignment(GTK_MISC(questionLabel), 0.0, 0.5);
	inputEntry = gtk_entry_new();
	gtk_entry_set_max_length(GTK_ENTRY(inputEntry), BUFSIZ);
	gtk_entry_set_activates_default(GTK_ENTRY(inputEntry), TRUE);
	gtk_box_pack_start(GTK_BOX(spacerVbox), questionLabel, TRUE, FALSE, 5);
	gtk_box_pack_start(GTK_BOX(spacerVbox), inputEntry,	TRUE, FALSE, 5);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(inputDialog)->vbox), spacerVbox, TRUE, TRUE, 0);
	gtk_dialog_set_default_response(GTK_DIALOG(inputDialog), GTK_RESPONSE_OK);
	gtk_widget_show_all(inputDialog);
	response = gtk_dialog_run(GTK_DIALOG(inputDialog));
	if (response == GTK_RESPONSE_OK) {
		g_strlcpy(input, gtk_entry_get_text(GTK_ENTRY(inputEntry)), BUFSIZ);
	} else {
		g_strlcpy(input, "\033\000", 2);
	}
	gtk_widget_destroy(inputDialog);
}

static gint g2_get_command_index(gchar* cmd)
{
	gint possibleCommands = 0;
	gint commandIndex = -1;
	gint i = 0;
	
	for (i = 0; extcmdlist[i].ef_txt; i++) {
		/*g_print("commandname:%s, prefix=%s\n", extcmdlist[i].ef_txt, cmd);*/
		if (g_str_has_prefix(extcmdlist[i].ef_txt, cmd)) {
			commandIndex = i;
			possibleCommands++;
		}
	}
	if (possibleCommands == 1) {
		return commandIndex;
	} else {
		return -1;
	}
}

static gint g2_complete_ext_cmd(gchar* commandBuffer)
{
	gint commandIndex;
	
	commandIndex = g2_get_command_index(commandBuffer);
	if (commandIndex == -1) {
		g_signal_emit_by_name(G_OBJECT(WIN_MESSAGE), "show_ext_command",
				commandBuffer, NULL);
	} else {
		g_signal_emit_by_name(G_OBJECT(WIN_MESSAGE), "show_ext_command",
				extcmdlist[commandIndex].ef_txt, NULL);
	}
	return commandIndex;
}

int gtk2_get_ext_cmd(void)
{
    gint key;
	gchar commandBuffer[10];
	gint bufferIndex = 0;
	gint commandIndex = -1;
	
/*    g_print("get_ext_cmd\n");*/
	g_signal_emit_by_name(G_OBJECT(WIN_MESSAGE), "show_ext_command", "", NULL);
	do {    
		while (keyBuffer == NULL) {
			gtk_main_iteration();
		}
		key = GPOINTER_TO_INT(keyBuffer->data);
		keyBuffer = g_slist_delete_link(keyBuffer, keyBuffer);
		if (key == GDK_Escape) {
			commandIndex = -1;
		} else if (bufferIndex > 0 && key == GDK_BackSpace) {
			bufferIndex--;
			commandBuffer[bufferIndex] = '\0';
			commandIndex = g2_complete_ext_cmd(commandBuffer);
		} else if (bufferIndex < 9 && g_ascii_isalnum((guchar) key)) {
			commandBuffer[bufferIndex] = (gchar) key;
			commandBuffer[bufferIndex + 1] = '\0';
			bufferIndex++;
			commandIndex = g2_complete_ext_cmd(commandBuffer);
		}
    } while (key != GDK_Return && key != GDK_KP_Enter && key != GDK_Escape);
	g_signal_emit_by_name(G_OBJECT(WIN_MESSAGE), "putstr", 0, "", NULL);
	
    return commandIndex;
}

void gtk2_number_pad(int state)
{
    g_print("number_pad\n");
    /* XXX: do we have to do to something? */
    g_print("currently unimplemented\n");
}

static gint timeout(gpointer data)
{
    delayDone = TRUE;
    return FALSE;
}

void gtk2_delay_output(void)
{
    delayDone = FALSE;
    g_timeout_add(G2_OUTPUT_DELAY, timeout, NULL);
    while(delayDone == FALSE) {
		gtk_main_iteration();
	}
}

void gtk2_start_screen(void)
{
    /* XXX: do we have to do to something? */
}

void gtk2_end_screen(void)
{
    /* XXX: do we have to do to something? */
}

void gtk2_outrip(winid wid, int how)
{
    /*g_print("outrip wid=%x, how=%d\n", wid, how);*/
	g_signal_emit_by_name(G_OBJECT(wid), "outrip", how, NULL);
    g_print("weakly implemented\n");
}

void gtk2_delete_nhwindow_by_reference(GtkWidget * menuWin)
{
    g_print("delete_nhwindow_by_reference\n");
    g_print("currently unimplemented\n");
}
