/* the main window 
 *
 * $Id: g2main.c,v 1.12 2005/04/26 23:26:41 miq Exp $
 *
 */

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

#include "g2main.h"
#include "g2mesg.h"
#include "g2status.h"
#include "g2map.h"
#include "g2menu.h"
#include "g2text.h"
#include "g2equip.h"
#include "g2i18n.h"
#include "hack.h"

#define VERSION "0.5.7"

static void g2_exit(GtkWidget *app, gpointer data);
static void g2_show_about(GtkWidget * app, gpointer data);

enum {
    META_BIT = 1 << 7,
    CTRL_BITS = 0x1f
};

GtkWidget *mainWin;
GtkWidget *mapWin;
GtkWidget *messageWin;
GtkWidget *equipWin;
GtkWidget *statusWin;
GtkWidget *mainRows;
GtkWidget *topRow;

static char* translators[] = {
    "Mihael Vrbanec <miq@users.sourceforge.net>",
    "Paolo M. <qualsiasi@qualsiasi.net>",
    "Paulo Henrique Cabral <paulo@mudvayne.com.br>",
};

static char* linguas[] = {
    N_("german"),
    N_("italian"),
    N_("brazilian portuguese"),
};

#if GTK_CHECK_VERSION(2,4,0)

typedef struct {
	gchar *name;
	gchar *label;
	gchar *tooltip;
	gchar *accel;
	gchar command;
} G2ActionEntry;

/* Normal items */
static GtkActionEntry entries[] = {
	{ "FileMenu", NULL, N_("_File") },
	{ "EditMenu", NULL, N_("_Edit") },
	{ "ApparelMenu", NULL, N_("Appa_rel") },
	{ "ActionMenu", NULL, N_("_Action") },
	{ "SpecialMenu", NULL, N_("_Special") },
	{ "MoveMenu", NULL, N_("Move") },
  	{ "HelpMenu", NULL, N_("_Help") },
  	{ "Quit", GTK_STOCK_QUIT, N_("_Quit"), NULL, N_("Quit Gtk2Hack"), G_CALLBACK(g2_exit) },
  	{ "Ride", NULL, N_("Ride"), NULL, NULL, G_CALLBACK(doride) },
  	{ "About", NULL, N_("_About"), NULL, N_("About Gtk2Hack"), G_CALLBACK(g2_show_about) },
};

/* XXX perhaps add more commands, tooltips and memnonics; check for sanity with other options */
static G2ActionEntry commands[] = {
	{ "Version", N_("_Version"), N_("Show Nethack version"), "v", 'v' },
	{ "Slash", N_("Identify a map symbol"), NULL, NULL, '/' },
	{ "Backslash", N_("Discoveries"), NULL, NULL, GDK_backslash },
	{ "History", N_("_History"), NULL, "<shift>v", 'V' },
	{ "Ampersand", N_("Explain command"), NULL, NULL, '&' },
	{ "Smaller", N_("Go up"), NULL, NULL, '<' },
	{ "Greater", N_("Go down"), NULL, NULL, '>' },
	{ "Fight", N_("Attack <direction>"), NULL, "F", 'F' },
	{ "Underscore", N_("Travel"), NULL, NULL, '_' },
	{ "Rest", N_("Rest"), NULL, NULL, '.'},
	{ "Apply", N_("Apply"), NULL, "a", 'a'},
	{ "Close", N_("Close"), NULL, "c", 'c'},
	{ "Name", N_("Name monster"), NULL, "<shift>c", 'C'},
	{ "Drop", N_("Drop"), NULL, "d", 'd' },
	{ "DropMany", N_("Drop many"), NULL, "<shift>d", 'D' },
	{ "Kick", N_("Kick"), NULL, "<control>d", CTRL_BITS & GDK_d },
	{ "Eat", N_("Eat"), NULL, "e", 'e' },
	{ "Engrave", N_("Engrave"), NULL, "<shift>e", 'E' },
	{ "Fire", N_("Fire"), NULL, "f", 'f' },
	{ "Inventory", N_("Inventory"), NULL, "i", 'i' },
	{ "Open", N_("Open"), NULL, "o", 'o' },
	{ "Pay", N_("Pay"), NULL, "p", 'p' },
	{ "PutOn", N_("Put on accessory"), NULL, "<shift>p", 'P' },
	{ "Quaff", N_("Quaff"), NULL, "q", 'q' },
	{ "Quiver", N_("Put in quiver"), NULL, "<shift>q", 'Q' },
	{ "Read", N_("Read"), NULL, "r", 'r' },
	{ "Remove", N_("Remove accessory"), NULL, "<shift>r", 'R' },
	{ "Search", N_("Search"), NULL, "s", 's' },
	{ "Throw", N_("Throw/Shoot"), NULL, "t", 't'},
	{ "TakeOff", N_("Take off armor"), NULL, "<shift>t", 'T' },
	{ "Teleport", N_("Teleport"), NULL, "<control>t", CTRL_BITS & GDK_t },
	{ "Wield", N_("Wield weapon"), NULL, "w", 'w' },
	{ "Wear", N_("Wear armor"), NULL, "<shift>w", 'W' },
	{ "Exchange", N_("Exchange weapons"), NULL, "x", 'x' },
	{ "Explore", N_("Explore mode"), NULL, "<shift>x", 'X' },
	{ "Zap", N_("Zap a wand"), NULL, "z", 'z' },
	{ "Cast", N_("Cast a spell"), NULL, "<shift>z", 'Z' },
	{ "PickUp", N_("Pick up"), NULL, NULL, ',' },
	{ "Adjust", N_("Adjust letters"), NULL, "<alt>a", META_BIT | GDK_a },
	{ "Chat", N_("Chat"), NULL, "<alt>c", META_BIT | GDK_c },
	{ "Dip", N_("Dip"), NULL, "<alt>d", META_BIT | GDK_d },
	{ "Enhance", N_("Enhance skills"), NULL, "<alt>e", META_BIT | GDK_e },
	{ "Force", N_("Force"), NULL, "<alt>f", META_BIT | GDK_f },
	{ "Invoke", N_("Invoke"), NULL, "<alt>i", META_BIT | GDK_i },
	{ "Jump", N_("Jump"), NULL, "<alt>j", META_BIT | GDK_j },
	{ "Loot", N_("Loot"), NULL, "<alt>l", META_BIT | GDK_l },
	{ "Monster", N_("Monster action"), NULL, "<alt>m", META_BIT | GDK_m },
	{ "NameIndividual", N_("Name object"), NULL, "<alt>n", META_BIT | GDK_n },
	{ "Offer", N_("Offer"), NULL, "<alt>o", META_BIT | GDK_o },
	{ "Pray", N_("Pray"), NULL, "<alt>p", META_BIT | GDK_p },
	{ "Rub", N_("Rub"), NULL, "<alt>r", META_BIT | GDK_r },
	{ "Sit", N_("Sit"), NULL, "<alt>s", META_BIT | GDK_s },
	{ "Turn", N_("Turn undead"), NULL, "<alt>t", META_BIT | GDK_t },
	{ "Untrap", N_("Untrap"), NULL, "<alt>u", META_BIT | GDK_u },
	{ "Wipe", N_("Wipe"), NULL, "<alt>w", META_BIT | GDK_w },
	{ "Edition", N_("Show build options"), NULL, "<alt>v", META_BIT | GDK_v },
	{ "CharacterInfo", N_("Show character info"), NULL, "<control>x", CTRL_BITS & GDK_x },
};

static const char *uiDescription =
	"<ui>"
	"  <menubar name='MainMenu'>"
	"    <menu action='FileMenu'>"
	"      <menuitem action='Save'/>"
	"      <separator/>"
	"      <menuitem action='Quit'/>"
	"    </menu>"
	"    <menu action='EditMenu'>"
	"      <menuitem action='Options'/>"
	"      <menuitem action='Explore'/>"
	"      <separator/>"
	"      <menuitem action='Adjust'/>"
	"      <menuitem action='Inventory'/>"
	"      <separator/>"
	"      <menuitem action='CharacterInfo'/>"
	"      <menuitem action='Enhance'/>"
	"      <menuitem action='Name'/>"
	"      <menuitem action='NameIndividual'/>"
	"      <menuitem action='Backslash'/>"
	"    </menu>"
	"    <menu action='ApparelMenu'>"
	"      <menuitem action='Wield'/>"
	"      <menuitem action='Exchange'/>"
	"      <menuitem action='Quiver'/>"
	"      <separator/>"
	"      <menuitem action='Wear'/>"
	"      <menuitem action='TakeOff'/>"
	"      <menuitem action='PutOn'/>"
	"      <menuitem action='Remove'/>"
	"    </menu>"
	"    <menu action='ActionMenu'>"
	"      <menuitem action='Fight'/>"
	"      <menuitem action='Throw'/>"
	"      <menuitem action='Fire'/>"
	"      <separator/>"
	"      <menuitem action='Force'/>"
	"      <menuitem action='Untrap'/>"
	"      <menuitem action='Kick'/>"
	"      <menuitem action='Apply'/>"
	"      <menuitem action='Engrave'/>"
	"      <menuitem action='Search'/>"
	"      <menuitem action='Eat'/>"
	"      <menuitem action='Open'/>"
	"      <menuitem action='Close'/>"
	"      <separator/>"
	"      <menuitem action='Loot'/>"
	"      <menuitem action='PickUp'/>"
	"      <menuitem action='Drop'/>"
	"      <menuitem action='DropMany'/>"
	"    </menu>"
	"    <menu action='SpecialMenu'>"
	"      <menuitem action='Quaff'/>"
	"      <menuitem action='Rub'/>"
	"      <menuitem action='Dip'/>"
	"      <separator/>"
	"      <menuitem action='Zap'/>"
	"      <menuitem action='Cast'/>"
	"      <menuitem action='Invoke'/>"
	"      <menuitem action='Read'/>"
	"      <menuitem action='Teleport'/>"
	"      <separator/>"
	"      <menuitem action='Offer'/>"
	"      <menuitem action='Pray'/>"
	"      <menuitem action='Wipe'/>"
	"      <menuitem action='Turn'/>"
	"      <menuitem action='Monster'/>"
	"      <separator/>"
	"      <menuitem action='Pay'/>"
	"      <menuitem action='Chat'/>"
	"    </menu>"
	"    <menu action='MoveMenu'>"
	"      <menuitem action='Jump'/>"
	"      <menuitem action='Ride'/>"
	"      <menuitem action='Sit'/>"
	"      <menuitem action='Smaller'/>"
	"      <menuitem action='Greater'/>"
	"      <menuitem action='Underscore'/>"
	"      <menuitem action='Rest'/>"
	"    </menu>"
	"    <menu action='HelpMenu'>"
	"      <menuitem action='About'/>"
	"      <separator/>"
	"      <menuitem action='Version'/>"
	"      <menuitem action='Edition'/>"
	"      <menuitem action='History'/>"
	"      <menuitem action='Help'/>"
	"      <separator/>"
	"      <menuitem action='Slash'/>"
	"      <menuitem action='Ampersand'/>"
	"    </menu>"
	"  </menubar>"
	"</ui>";


static void command_activated(GtkWidget *action, gpointer command)
{
	keyBuffer = g_slist_append(keyBuffer, GINT_TO_POINTER(command));
}

static void add_new_action_to_group(GtkActionGroup *group, const gchar *name,
		const gchar *label, const gchar *tooltip, const gchar *accel,
		const gchar command, const gchar *stock_id)
{
	GtkAction* action;

	action = gtk_action_new(name, label, tooltip, stock_id);
	g_signal_connect(G_OBJECT(action), "activate", G_CALLBACK(command_activated),
			GINT_TO_POINTER((gint) command));
	gtk_action_group_add_action_with_accel(group, action, accel);
}
#endif

void g2_exit_windows(const gchar* string)
{
    g_print("%s\n", string);
    gtk_widget_destroy(g2_get_main_window());
    exit(0);
}

static void g2_exit(GtkWidget * app, gpointer data)
{
    gint response;
    GtkWidget *saveDialog;
    
    saveDialog = gtk_message_dialog_new(GTK_WINDOW(mainWin), GTK_DIALOG_MODAL,
            GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("Do you want to save the "
            "current game?\nIf not your current game will be lost!"));
	gtk_dialog_set_default_response (GTK_DIALOG(saveDialog), GTK_RESPONSE_YES);
	response = gtk_dialog_run(GTK_DIALOG(saveDialog));
    if (response == GTK_RESPONSE_YES) {
        dosave0();
    }
    g2_exit_windows(_("Exiting...\n"));
}

static void g2_show_about(GtkWidget * app, gpointer data)
{
    gint response;
    gint i;
    gchar* trans = "";
    GtkWidget *aboutDialog;
	GtkWidget *vbox;
	GtkWidget *tabContent;
	GtkWidget *notebook;
	GtkWidget *label;
    
    aboutDialog = gtk_dialog_new_with_buttons(_("About Gtk2Hack"), 
			GTK_WINDOW(mainWin), GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR,
			GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
	vbox = gtk_vbox_new(FALSE, 5);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
	notebook = gtk_notebook_new();
	label = gtk_label_new(NULL);
	gtk_label_set_markup(GTK_LABEL(label), "<b><big>Gtk2Hack " VERSION "</big></b>");
	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 10);
	label = gtk_label_new(_("Gtk2Hack was developed by Mihael Vrbanec.\n"
			"Window icon and gold icon by David Theis.\n"
			"Other artwork is borrowed from the Qt, X11 and Gnome window ports.\n\n"
			"Thanks for trying Gtk2Hack."));
	tabContent = gtk_vbox_new(FALSE, 5);
	gtk_container_set_border_width(GTK_CONTAINER(tabContent), 5);
	gtk_box_pack_start(GTK_BOX(tabContent), label, FALSE, FALSE, 0);
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), tabContent, gtk_label_new(_("Credits")));
    tabContent = gtk_vbox_new(FALSE, 5);
    gtk_container_set_border_width(GTK_CONTAINER(tabContent), 5);
	for (i = 0; linguas[i] != NULL; i++) {
        gchar* temp = g_strconcat(trans, translators[i], " (", _(linguas[i]), ")\n", NULL);
        g_free(trans);
        trans = temp;
    }
    label = gtk_label_new(trans);
    gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.0f);
    gtk_box_pack_start(GTK_BOX(tabContent), label, FALSE, FALSE, 0);
    g_free(trans);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), tabContent, gtk_label_new(_("Translations")));

	gtk_box_pack_start(GTK_BOX(vbox), notebook, FALSE, FALSE, 10);
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(aboutDialog)->vbox), vbox);
	gtk_widget_show_all(aboutDialog);
    response = gtk_dialog_run(GTK_DIALOG(aboutDialog));
	gtk_widget_destroy(aboutDialog);
}

static gboolean g2_move_keys_with_numpad(guint keyval, gint* key)
{
    switch (keyval) {
    case GDK_Right:
    case GDK_rightarrow:
    case GDK_KP_Right:    
    case GDK_KP_6:
        *key = '6';
        break;
    case GDK_Left:
    case GDK_leftarrow:
    case GDK_KP_Left:    
    case GDK_KP_4:
        *key = '4';
        break;
    case GDK_Up:
    case GDK_uparrow:
    case GDK_KP_Up:    
    case GDK_KP_8:
        *key = '8';
        break;
    case GDK_Down:
    case GDK_downarrow:
    case GDK_KP_Down:    
    case GDK_KP_2:
        *key = '2';
        break;
    case GDK_Home:
    case GDK_KP_Home:    
    case GDK_KP_7:
        *key = '7';
        break;
    case GDK_End:
    case GDK_KP_End:    
    case GDK_KP_1:
        *key = '1';
        break;
    case GDK_Page_Down:
    case GDK_KP_Page_Down:    
    case GDK_KP_3:
        *key = '3';
        break;
    case GDK_Page_Up:
    case GDK_KP_Page_Up:    
    case GDK_KP_9:
        *key = '9';
        break;
    default:
        return FALSE;
        break;
    }
    return TRUE;
}

static gboolean g2_key_is_valid_input(guint keyval)
{
    switch (keyval) {
	case GDK_Return:
	case GDK_KP_Enter:
	case GDK_Escape:
	case GDK_BackSpace:
		return TRUE;
		break;
    default:
        return FALSE;
    }
}

static gboolean g2_main_key_press_event(GtkWidget *widget, GdkEventKey *event,
                                          gpointer data)
{
    gint key;
	
	key = event->keyval;
	/* convert movement keys */
    if (iflags.num_pad && g2_move_keys_with_numpad(event->keyval, &key)) {
		keyBuffer = g_slist_append(keyBuffer, GINT_TO_POINTER(key));
	} else if (g_ascii_isprint(key) || g2_key_is_valid_input(key)) {
		if (event->state & GDK_MOD1_MASK) {
			/*g_print("modifier <ALT> pressed\n");*/
			key = key | META_BIT;
		} else if (event->state & GDK_CONTROL_MASK) {
			/*g_print("modifier <CONTROL> pressed\n");*/
			/* do not handle some menu accelerators which are not needed by nethack */
			switch (key) {
			case GDK_q:
				return FALSE;
			default:
				break;
			}
			key = CTRL_BITS & key;
		}
		keyBuffer = g_slist_append(keyBuffer, GINT_TO_POINTER(key));
	}
	return TRUE;
}

#if GTK_CHECK_VERSION(2,4,0)
static GtkWidget *create_menubar()
{
	GtkWidget *menubar;
	GtkActionGroup *actionGroup;
	GtkUIManager *uiManager;
	GtkAccelGroup *accelGroup;
	GError* error;
	gint i;

	actionGroup = gtk_action_group_new("MenuActions");
	gtk_action_group_set_translation_domain(actionGroup, "gtk2hack");
	gtk_action_group_add_actions(actionGroup, entries, G_N_ELEMENTS(entries), mainWin);
	for (i = 0; i < G_N_ELEMENTS(commands); i++) {
		add_new_action_to_group(actionGroup, commands[i].name, _(commands[i].label),
				_(commands[i].tooltip), commands[i].accel, commands[i].command, NULL);
	}
	/* special-cases for entries with stock ids */
	add_new_action_to_group(actionGroup, "Save", _("Save game"), NULL,	"<shift>s",
			GDK_S, GTK_STOCK_SAVE);
	add_new_action_to_group(actionGroup, "Options", _("Options"), NULL, "<shift>o",
			GDK_O, GTK_STOCK_PREFERENCES);
	add_new_action_to_group(actionGroup, "Help", _("Show Help Menu"), NULL, NULL,
			GDK_question, GTK_STOCK_HELP);
	
	uiManager = gtk_ui_manager_new();
	gtk_ui_manager_insert_action_group(uiManager, actionGroup, 0);

	accelGroup = gtk_ui_manager_get_accel_group(uiManager);
	gtk_window_add_accel_group(GTK_WINDOW(mainWin), accelGroup);

	error = NULL;
	if (!gtk_ui_manager_add_ui_from_string(uiManager, uiDescription, -1, &error)) {
		g_message("building menus failed: %s", error->message);
		g_error_free (error);
		exit(EXIT_FAILURE);
  	}

	menubar = gtk_ui_manager_get_widget(uiManager, "/MainMenu");
	return menubar;
}
#endif

void g2_init_main_window(int *argcp, char **argv)
{
    GtkWidget *menubar;
    GdkPixbuf *icon;
    GError *error = NULL;
	int i = 0;

	setlocale(LC_ALL, "");
	bindtextdomain("gtk2hack", HACKDIR "/locale");
	bind_textdomain_codeset("gtk2hack", "UTF-8");
	textdomain("gtk2hack");
    g_print("Gtk2Hack %s\n", VERSION);
    for (i = 0; i < *argcp; i++) {
        g_print("Arg[%d]:%s\n", i, argv[i]);
    }
    /* TODO: learn how to drop privileges when running as suid or sgid */
    gtk_init(argcp, &argv);

    mainWin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(mainWin), "Gtk2Hack " VERSION);
    icon = gdk_pixbuf_new_from_file("gtk2hack.png", &error);
    if (icon != NULL) {
    	g_print("Setting icon\n");
    	gtk_window_set_icon(GTK_WINDOW(mainWin), icon);
    }
    g_signal_connect(G_OBJECT(mainWin), "delete_event",
                     G_CALLBACK(g2_exit), NULL);

    mainRows = gtk_vbox_new(FALSE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(mainRows), 0);

    topRow = gtk_hbox_new(FALSE, 1);

    mapWin = g2_map_new();
    messageWin = g2_message_new();
	equipWin = g2_equipment_new();
    statusWin = g2_status_new();
    
#if GTK_CHECK_VERSION(2,4,0)
	g_print("Creating gtk2.4-style menubar!\n");	
	menubar = create_menubar();
	gtk_box_pack_start(GTK_BOX(mainRows), menubar, FALSE, FALSE, 0);
#else
	g_print("No pre-gtk2.4 menubar implemented (yet)!\n");
#endif
    gtk_box_pack_start(GTK_BOX(topRow), messageWin, TRUE, TRUE, 2);
    gtk_box_pack_start(GTK_BOX(topRow), equipWin, FALSE, FALSE, 2);
    gtk_box_pack_start(GTK_BOX(topRow), statusWin, FALSE, FALSE, 2);
    gtk_box_pack_start(GTK_BOX(mainRows), topRow, TRUE, TRUE, 2);
    gtk_box_pack_start(GTK_BOX(mainRows), mapWin, FALSE, FALSE, 0);
    g_signal_connect(G_OBJECT(mainWin), "key_press_event",
                     G_CALLBACK(g2_main_key_press_event), NULL);

	gtk_container_add(GTK_CONTAINER(mainWin), mainRows);
    gtk_widget_show_all(mainWin);
}

GtkWidget *g2_get_main_window()
{
    return mainWin;
}

GtkWidget *g2_get_equipment_window()
{
    return equipWin;
}

gint g2_create_window(gint type)
{
    GtkWidget* win;

    switch (type) {
    case NHW_MAP:
        win = mapWin;
        break;
    case NHW_MESSAGE:
        win = messageWin;
        break;
    case NHW_STATUS:
        win = statusWin;
        break;
    case NHW_MENU:
        win = g2_menu_new();
        break;
    case NHW_TEXT:
        win = g2_text_new();
        break;
    default:
        g_print("create unknown window=%d\n", type);
    }

    return (int) win;
}
