#import "JubGtkChatUI.h"

struct add_text_params {
	OFString *name;
	OFString *text;
	GtkTextBuffer *buffer;
	BOOL first;
};

static gboolean add_text(gpointer data)
{
	GtkTextIter endIter;
	struct add_text_params *params = data;

	if (OF_LIKELY(!params->first))
		gtk_text_buffer_insert_at_cursor(params->buffer, "\n", 1);

	gtk_text_buffer_get_end_iter(params->buffer, &endIter);
	gtk_text_buffer_insert_with_tags_by_name(params->buffer, &endIter,
	    [params->name UTF8String], [params->name UTF8StringLength], "bold",
	    NULL);
	gtk_text_buffer_get_end_iter(params->buffer, &endIter);
	gtk_text_buffer_insert_with_tags_by_name(params->buffer, &endIter,
	    ": ", 2, "bold", NULL);

	gtk_text_buffer_insert_at_cursor(params->buffer,
	    [params->text UTF8String], [params->text UTF8StringLength]);

	[params->name release];
	[params->text release];
	g_object_unref(params->buffer);
	free(params);

	return FALSE;
}

struct call_send_block_params {
	jub_send_block_t block;
	JubGtkChatUI *chat;
};

static gboolean call_send_block(GtkEntry *entry, GdkEventKey *event,
    gpointer data)
{
	struct call_send_block_params *params = data;
	if (event->keyval != GDK_KEY_Return &&
	    event->keyval != GDK_KEY_KP_Enter) return TRUE;

	OFString *text =
	    [[OFString alloc] initWithUTF8String: gtk_entry_get_text(entry)];
	gtk_entry_set_text(entry, "");
	params->block(text);
	[params->chat addMessage: text
			  sender: @"You"];
	[text release];

	return TRUE;
}

static gboolean call_close_block(GtkWidget *object, gpointer data)
{
	jub_close_block_t block = data;
	block();

	return FALSE;
}

struct init_params {
	OFString *title;
	JubGtkChatUI *object;
	volatile BOOL *initialized;
};

static gboolean init(gpointer data)
{
	struct init_params *params = data;

	GtkTextView *chat_view;
	GtkEntry *chat_entry;

	GtkBuilder *builder = gtk_builder_new();
	gtk_builder_add_from_file(builder, "data/gtk/chat.ui", NULL);

	params->object.chat_window = GTK_WIDGET(
	    gtk_builder_get_object(builder, "ChatWindow"));
	g_signal_connect(params->object.chat_window, "destroy",
	    G_CALLBACK(call_close_block), params->object.closeBlock);


	chat_view = GTK_TEXT_VIEW(
	    gtk_builder_get_object(builder, "ChatTextView"));
	params->object.chat_buffer = gtk_text_view_get_buffer(chat_view);
	gtk_text_buffer_create_tag(params->object.chat_buffer, "bold",
	    "weight", PANGO_WEIGHT_BOLD, NULL);

	chat_entry = GTK_ENTRY(
	    gtk_builder_get_object(builder, "ChatEntry"));
	struct call_send_block_params *send_params =
	    [params->object allocMemoryWithSize: sizeof(*send_params)];
	send_params->block = params->object.sendBlock;
	send_params->chat = params->object;
	g_signal_connect(chat_entry, "key_release_event",
	    G_CALLBACK(call_send_block), send_params);

	gtk_window_set_title(GTK_WINDOW(params->object.chat_window),
	    [params->title UTF8String]);
	gtk_widget_show(params->object.chat_window);

	*params->initialized = YES;

	g_object_unref(builder);
	[params->title release];
	free(params);

	return FALSE;
}

@implementation JubGtkChatUI
@synthesize chat_window;
@synthesize chat_buffer;
@synthesize sendBlock;
@synthesize closeBlock;

- initWithTitle: (OFString*)title
     closeBlock: (jub_close_block_t)closeBlock_
      sendBlock: (jub_send_block_t)sendBlock_
{
	volatile BOOL initialized = NO;
	self = [super init];

	@try {

		closeBlock = [closeBlock_ copy];
		sendBlock = [sendBlock_ copy];
		bufferEmpty = YES;

		struct init_params *params = malloc(sizeof(*params));
		params->initialized = &initialized;
		params->title = [title retain];
		params->object = self;
		g_idle_add(init, params);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	while (!initialized);

	return self;
}

- (void)dealloc
{
	gtk_widget_destroy(chat_window);
	[sendBlock release];
	[closeBlock release];

	[super dealloc];
}

- (void)addMessage: (OFString*)text
	    sender: (OFString*)sender;
{
	struct add_text_params *params = malloc(sizeof(*params));
	params->name = [sender retain];
	params->text = [text retain];
	params->buffer = g_object_ref(chat_buffer);
	params->first = bufferEmpty;
	g_idle_add(add_text, params);
	if (OF_UNLIKELY(bufferEmpty)) bufferEmpty = NO;
}
@end
