/*
 * GTK frontend for TBE
 * Provides an interface to add/remove loadable modules, switch models, etc.
 */

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

#include "utils.h"

static int gtk_ok;
static GdkImage *img;

static int the_timeout = -1;

GtkWidget *main_window, *main_vbox;
GtkWidget *statusbar, *menubar, *drawarea;

void gtkfe_init (int *argc, char ***argv)
{
	if (!gtk_init_check (argc, argv))
		gtk_ok = 0;
	else
		gtk_ok = 1;
}

void gtkfe_quit (void)
{
	gdk_key_repeat_restore ();
	gtk_main_quit ();
}

int paused_key;

void gtkfe_pause_emul (void)
{
	char buf[256];
	
	gtk_timeout_remove (the_timeout);
	the_timeout = -1;
	paused_key++;
	if (paused_key > 1)
		sprintf (buf, "Paused (%d).", paused_key);
	else
		strcpy (buf, "Paused.");
	gtk_statusbar_push (GTK_STATUSBAR (statusbar), 1, buf);
}

void gtkfe_resume_emul (void)
{
	if (!paused_key || !--paused_key)
		the_timeout = gtk_timeout_add (50, (GtkFunction) ExecThread, NULL);
	gtk_statusbar_pop (GTK_STATUSBAR (statusbar), 1);
}


// Main window events
static gint delete_event (GtkWidget *widget,
	GdkEvent *event, gpointer data)
{
	return FALSE;
}

static void destroy_event (GtkWidget *widget, gpointer data)
{
	printf ("Destroy OK.\n");
	gtkfe_quit ();
}

// Drawing-area events
static gint gtkfe_button_press (GtkWidget *widget, GdkEventButton *ev)
{
	if (ev->button >= 1 && ev->button <= 3)
	{
		mousebtns[ev->button - 1] = 1;
		// FIXME: should call some sort of MouseEvent handler
	}
	return TRUE;
}

static gint gtkfe_button_release (GtkWidget *widget, GdkEventButton *ev)
{
	if (ev->button >= 1 && ev->button <= 3)
	{
		mousebtns[ev->button - 1] = 0;
		// FIXME: should call some sort of MouseEvent handler
	}
	return TRUE;
}

static gint gtkfe_motion (GtkWidget *widget, GdkEventMotion *ev)
{
	static int oldx = 0, oldy = 0;
	
	mousemotx += (ev->x - oldx);
	mousemoty += (ev->x - oldy);
	oldx = ev->x;
	oldy = ev->y;
	// FIXME: should call some sort of MouseEvent handler
	return TRUE;
}

// Main window events -- Keypress/release handling stuff
static struct
{
	guint keyval;
	int col, row;
} keymapping[] =
{
	{ GDK_Escape, 7, 0 },
	//{ GDK_, 5, 0 },		// Key for shift lock??
	{ GDK_Caps_Lock, 4, 0 },
	{ GDK_Shift_L, 0, 0 }, { GDK_Shift_R, 0, 0 },
	{ GDK_Control_L, 0, 1}, { GDK_Control_R, 0, 1 },
	{ GDK_1, 3, 0 }, { GDK_exclam, 3, 0 },
	{ GDK_2, 3, 1 }, { GDK_quotedbl, 3, 1 },
	{ GDK_3, 1, 1 }, { GDK_numbersign, 1, 1 },
	{ GDK_4, 1, 2 }, { GDK_dollar, 1, 2 },
	{ GDK_5, 1, 3 }, { GDK_percent, 1, 3 },
	{ GDK_6, 3, 4 }, { GDK_asciicircum, 3, 4 },
	{ GDK_7, 2, 4 }, { GDK_ampersand, 2, 4 },
	{ GDK_8, 1, 5 }, { GDK_asterisk, 1, 5 },
	{ GDK_9, 2, 6 }, { GDK_parenleft, 2, 6 },
	{ GDK_0, 2, 7 }, { GDK_parenright, 2, 7 },
	{ GDK_at, 4, 7 }, { GDK_quoteright, 4, 7 },
	{ GDK_minus, 1, 7 },
	
	{ GDK_Tab, 6, 0 },
	{ GDK_q, 1, 0 }, { GDK_Q, 1, 0 },
	{ GDK_w, 2, 1 }, { GDK_W, 2, 1 },
	{ GDK_e, 2, 2 }, { GDK_E, 2, 2 },
	{ GDK_r, 3, 3 }, { GDK_R, 3, 3 },
	{ GDK_t, 2, 3 }, { GDK_T, 2, 3 },
	{ GDK_y, 4, 4 }, { GDK_Y, 4, 4 },
	{ GDK_u, 3, 5 }, { GDK_U, 3, 5 },
	{ GDK_i, 2, 5 }, { GDK_I, 2, 5 },
	{ GDK_o, 3, 6 }, { GDK_O, 3, 6 },
	{ GDK_p, 3, 7 }, { GDK_P, 3, 7 },
	{ GDK_equal, 5, 7 }, { GDK_plus, 5, 7 },
	{ GDK_bracketleft, 3, 8 }, { GDK_braceleft, 3, 8 },
	{ GDK_bracketright, 5, 8 }, { GDK_braceright, 5, 8 },
	
	{ GDK_a, 4, 1 }, { GDK_A, 4, 1 },
	{ GDK_s, 5, 1 }, { GDK_S, 5, 1 },
	{ GDK_d, 3, 2 }, { GDK_D, 3, 2 },
	{ GDK_f, 4, 3 }, { GDK_F, 4, 3 },
	{ GDK_g, 5, 3 }, { GDK_G, 5, 3 },
	{ GDK_h, 5, 4 }, { GDK_H, 5, 4 },
	{ GDK_j, 4, 5 }, { GDK_J, 4, 5 },
	{ GDK_k, 4, 6 }, { GDK_K, 4, 6 },
	{ GDK_l, 5, 6 }, { GDK_L, 5, 6 },
	{ GDK_semicolon, 4, 8 }, { GDK_colon, 4, 8 },
	{ GDK_Return, 4, 9 },
	
	{ GDK_z, 6, 1 }, { GDK_Z, 6, 1 },
	{ GDK_x, 4, 2 }, { GDK_X, 4, 2 },
	{ GDK_c, 5, 2 }, { GDK_C, 5, 2 },
	{ GDK_v, 6, 3 }, { GDK_V, 6, 3 },
	{ GDK_b, 6, 4 }, { GDK_B, 6, 4 },
	{ GDK_n, 5, 5 }, { GDK_N, 5, 5 },
	{ GDK_m, 6, 5 }, { GDK_M, 6, 5 },
	{ GDK_comma, 6, 6 },
	{ GDK_period, 6, 7 },
	{ GDK_slash, 6, 8 }, { GDK_question, 6, 8 },
	{ GDK_BackSpace, 5, 9 }, { GDK_Delete, 5, 9 },
	
	{ GDK_space, 6, 2 },
	
	{ GDK_F1, 7, 1 },
	{ GDK_F2, 7, 2 },
	{ GDK_F3, 7, 3 },
	{ GDK_F4, 1, 4 },
	{ GDK_F5, 7, 4 },
	{ GDK_F6, 7, 5 },
	{ GDK_F7, 1, 6 },
	{ GDK_F8, 7, 6 },
	{ GDK_F9, 7, 7 },
	{ GDK_F10, 2, 0 },
	{ GDK_backslash, 7, 8 },
	
	{ GDK_Left, 1, 9 },
	{ GDK_Right, 7, 9 },
	{ GDK_Down, 2, 9 },
	{ GDK_Up, 3, 9 },
	{ GDK_End, 6, 9 },
	
	{ GDK_F12, 0, -1 },	// Break
};
#define NR_KEYMAPPING (sizeof (keymapping) / sizeof (keymapping[0]))

static gint gtkfe_keypress (GtkWidget *widget, GdkEventKey *ev)
{
	int i;
	
	for (i = 0; i < NR_KEYMAPPING; i++)
		if (keymapping[i].keyval == ev->keyval)
			break;
	if (i < NR_KEYMAPPING)
	{
		if (keymapping[i].row >= 0 && keymapping[i].col >= 0)
			kbmap[keymapping[i].row][keymapping[i].col] = 1;
		if (KeyboardEvent)
			KeyboardEvent (keymapping[i].row, keymapping[i].col,
				KeyEventData);
	}
	else
		printf ("Unknown keysym %x\n", ev->keyval);
	return TRUE;
}

static gint gtkfe_keyrelease (GtkWidget *widget, GdkEventKey *ev)
{
	int i;
	
	for (i = 0; i < NR_KEYMAPPING; i++)
		if (keymapping[i].keyval == ev->keyval)
			break;
	if (i < NR_KEYMAPPING)
	{
		if (keymapping[i].row >= 0 && keymapping[i].col >= 0)
			kbmap[keymapping[i].row][keymapping[i].col] = 0;
		if (KeyboardEvent && keymapping[i].row >= 0)
			KeyboardEvent (keymapping[i].row, keymapping[i].col,
				KeyEventData);
	}
	return TRUE;
}

// Drawing-area DRAWING events
// Should call X11 backend drawing stuff
static gint gtkfe_expose (GtkWidget *widget, GdkEventExpose *ev)
{
	gdk_window_clear_area (widget->window,
		ev->area.x, ev->area.y, ev->area.width, ev->area.height);
	gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state],
		&ev->area);
	gdk_draw_image (widget->window,
		widget->style->fg_gc[widget->state],
		img, 0, 0, 0, 0, 640, 512);
	gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], NULL);
	return TRUE;
}

static gint gtkfe_configure (GtkWidget *widget, GdkEventConfigure *ev)
{
	return TRUE;
}

// Menu events
extern void gtkfe_loaddisc (GtkWidget *w, gpointer data);

static void gtkfe_pref (GtkWidget *w, gpointer data)
{

}

static void gtkfe_model (GtkWidget *w, gpointer data)
{

}

static void gtkfe_mods (GtkWidget *w, gpointer data)
{

}

static GtkWidget *pause_menu;

static void gtkfe_pause (GtkWidget *w, gpointer data)
{
	if (GTK_CHECK_MENU_ITEM (pause_menu)->active)
		gtkfe_pause_emul ();
	else
		gtkfe_resume_emul ();
}

static GtkItemFactoryEntry menu_items[] = {
 { "/_File",		NULL,		NULL,		0, "<Branch>" },
 { "/File/Load _ROM...",	NULL,		NULL,		0, NULL },
 { "/File/Load _disc image...", NULL,	gtkfe_loaddisc,	0, NULL },
 { "/File/Sep1",	NULL,		NULL,		0, "<Separator>" },
 { "/File/E_xit",	NULL,		gtkfe_quit,	0, NULL },
 { "/_Hardware",	NULL,		NULL,		0, "<Branch>" },
 { "/Hardware/_Model...", NULL,		gtkfe_model,	0, NULL },
 { "/Hardware/Modu_les...", NULL,	gtkfe_mods,	0, NULL },
 { "/Hardware/Reset",	NULL,		NULL,		0, NULL },
 { "/Hardware/Sound emulation", NULL,	NULL,		0, "<ToggleItem>" },
 { "/Hardware/Pause",	NULL,		gtkfe_pause,	0, "<ToggleItem>" },
 { "/_Options",		NULL,		NULL,		0, "<Branch>" },
 { "/Options/_Preferences...", NULL,	gtkfe_pref,	0, NULL },
 { "/_Help",		NULL,		NULL,		0, "<LastBranch>" },
 { "/Help/About...",	NULL,		NULL,		0, NULL },
};

GtkWidget *gtkfe_main_menu (GtkWidget *window)
{
	GtkItemFactory *fac;
	GtkAccelGroup *acc;
	gint nitems = sizeof (menu_items) / sizeof (menu_items[0]);
	
	acc = gtk_accel_group_new ();
	fac = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", acc);
	gtk_item_factory_create_items
		(fac, nitems, menu_items, NULL);
	pause_menu = gtk_item_factory_get_widget (fac, "<main>/Hardware/Pause");
	gtk_window_add_accel_group (GTK_WINDOW (window), acc);
	return gtk_item_factory_get_widget (fac, "<main>");
}

void gtkfe_main (void)
{
	if (!gtk_ok)
		fatal ("GTK failed to initialize");
	
	// Start creating the user-interface
	main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title (GTK_WINDOW (main_window), "TBE");
	gtk_window_set_policy (GTK_WINDOW (main_window), FALSE, FALSE, TRUE);
	gtk_signal_connect (GTK_OBJECT (main_window), "delete_event",
			GTK_SIGNAL_FUNC (delete_event), NULL);
	gtk_signal_connect (GTK_OBJECT (main_window), "destroy",
			GTK_SIGNAL_FUNC (destroy_event), NULL);
	
	main_vbox = gtk_vbox_new (FALSE, 0);
	
	menubar = gtkfe_main_menu (main_window);
	statusbar = gtk_statusbar_new ();
	drawarea = gtk_drawing_area_new ();
	
	// Set up the drawing area stuff: we need mouse and keyboard events
	// from here, among other things
	gtk_drawing_area_size (GTK_DRAWING_AREA (drawarea), 640, 512);
	gtk_widget_set_events (drawarea, GDK_BUTTON_PRESS_MASK |
		GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
	gtk_signal_connect (GTK_OBJECT (drawarea), "button_press_event",
		(GtkSignalFunc) gtkfe_button_press, NULL);
	gtk_signal_connect (GTK_OBJECT (drawarea), "button_release_event",
		(GtkSignalFunc) gtkfe_button_release, NULL);
	gtk_signal_connect (GTK_OBJECT (drawarea), "motion_notify_event",
		(GtkSignalFunc) gtkfe_motion, NULL);
	
	gdk_key_repeat_disable ();
	gtk_widget_set_events (main_window, GDK_KEY_PRESS_MASK |
		GDK_KEY_RELEASE_MASK);
	gtk_signal_connect (GTK_OBJECT (main_window), "key_press_event",
		(GtkSignalFunc) gtkfe_keypress, NULL);
	gtk_signal_connect (GTK_OBJECT (main_window), "key_release_event",
		(GtkSignalFunc) gtkfe_keyrelease, NULL);
	
	// Set up the drawing functions
	gtk_signal_connect (GTK_OBJECT (drawarea), "expose_event",
			(GtkSignalFunc) gtkfe_expose, NULL);
	gtk_signal_connect (GTK_OBJECT (drawarea), "configure_event",
			(GtkSignalFunc) gtkfe_configure, NULL);
	{
		GList *l;
		
		l = gdk_list_visuals ();
		for (; l != NULL; l = l->next)
			if (((GdkVisual *)(l->data))->depth == 8)
				break;
		if (!l)
			fatal ("No 8bpp visual!\n");
		img = gdk_image_new (GDK_IMAGE_FASTEST, (GdkVisual *)l->data,
			640, 512);
	}
	gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, FALSE, 0);
	gtk_box_pack_start (GTK_BOX (main_vbox), drawarea, TRUE, TRUE, 0);
	gtk_box_pack_end (GTK_BOX (main_vbox), statusbar, FALSE, FALSE, 0);
	
	gtk_widget_show (menubar);
	gtk_widget_show (drawarea);
	gtk_widget_show (statusbar);
	
	gtk_statusbar_push (GTK_STATUSBAR (statusbar), 1, "Running.");
	
	gtk_container_add (GTK_CONTAINER (main_window), main_vbox);
	gtk_widget_show (main_vbox);
	gtk_widget_show (main_window);
	
	the_timeout = gtk_timeout_add (50, (GtkFunction) ExecThread, NULL);
	
	gtk_main ();
}

void gtkfe_events (void)
{
	GdkEvent *e;
	
	while (gdk_events_pending ())
	{
		if (!(e = gdk_event_get ()))
			break;
		gtk_main_do_event (e);
	}
}

/* End of file. */
