/* logjam - a GTK client for LiveJournal.
 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
 *
 * vim: tabstop=4 shiftwidth=4 noexpandtab :
 */

#include "config.h"
#include "gtk-all.h"

#include "util.h"
#include "network.h"
#include "network-internal.h"
#include "network-throbber.h"

struct _NetWindow {
	GtkWindow win;
	GtkWidget *parent, *box, *titlelabel, *label, *progress;
	NetMainloopHandle nethandle;
	gboolean showing_error;
	GtkWidget *throbber;
};

void net_window_set_title(NetWindow *nw, const char *title);

void 
net_window_show_error(NetWindow *nw, const char *fmt, ...) {
	char buf[1024];
	va_list ap;

	va_start(ap, fmt);
	g_vsnprintf(buf, 1024, fmt, ap);
	va_end(ap);

	if (nw) {
		net_window_set_title(nw, _("Error"));
		gtk_label_set_text(GTK_LABEL(nw->label), buf);

		net_throbber_stop(NET_THROBBER(nw->throbber));
		gtk_image_set_from_stock(GTK_IMAGE(nw->throbber),
				GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG);
		gtk_widget_hide(nw->progress);
		nw->showing_error = TRUE;
		gtk_main();
	} else {
		g_print(_("Error: %s\n"), buf);
	}
}

static void 
cancel_cb(NetWindow *nw) {
	if (nw->nethandle)
		net_mainloop_cancel(nw->nethandle);
	else if (nw->showing_error)
		gtk_main_quit();
}

static void
destroy_cb(GtkWidget *w, NetWindow *nw) {
	net_throbber_stop(NET_THROBBER(nw->throbber));
	if (nw->parent)
		g_signal_handlers_disconnect_matched(nw->parent,
				G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, nw);
}

static gboolean
delete_event_cb(GtkWidget *w, GdkEvent *e, NetWindow *nw) {
	cancel_cb(nw);
	return TRUE; /* don't destroy us. */
}

static void
reposition(NetWindow *nw, gint w, gint h) {
	gint ox, oy, px, py, x, y;

	if (!nw->parent)
		return;
	gdk_window_get_origin(nw->parent->window, &px, &py);
	if (w == -1)
		w = GTK_WIDGET(nw)->allocation.width;
	if (h == -1)
		h = GTK_WIDGET(nw)->allocation.height;
	x = px + (nw->parent->allocation.width  - w) / 2;
	y = py + (nw->parent->allocation.height - h) / 2;

	gdk_window_get_origin(GTK_WIDGET(nw)->window, &ox, &oy);
	if (x != ox || y != oy) {
		gtk_window_move(GTK_WINDOW(nw), x, y);
	}
}

static gboolean
parent_configure_cb(GtkWidget *w, GdkEvent *e, NetWindow *nw) {	 
	reposition(nw, -1, -1);
	return FALSE;	 
}

static gboolean
configure_cb(GtkWidget *w, GdkEventConfigure *e, NetWindow *nw) {	 
	if (GTK_WIDGET(nw)->allocation.width != e->width ||
		GTK_WIDGET(nw)->allocation.height != e->height)
		reposition(nw, e->width, e->height);
	return FALSE;	 
}	 


static void
net_window_set_parent(NetWindow *nw, GtkWindow *parent) {
	nw->parent = GTK_WIDGET(parent);
	gtk_window_set_transient_for(GTK_WINDOW(nw), parent);
	g_signal_connect(G_OBJECT(parent), "configure-event",
			G_CALLBACK(parent_configure_cb), nw);
}

void 
net_window_init(NetWindow *nw) {
	GtkWidget *frame;
	GtkWidget *vbox;
	GtkWidget *hbox, *align, *bbox, *button, *lbox;

	gtk_window_set_decorated(GTK_WINDOW(nw), FALSE);
	gtk_window_set_type_hint(GTK_WINDOW(nw), GDK_WINDOW_TYPE_HINT_DIALOG);
	gtk_window_set_modal(GTK_WINDOW(nw), TRUE);
	gtk_window_set_default_size(GTK_WINDOW(nw), 250, -1);
	g_signal_connect(G_OBJECT(nw), "destroy", G_CALLBACK(destroy_cb), nw);
	g_signal_connect(G_OBJECT(nw), "delete-event",
			G_CALLBACK(delete_event_cb), nw);
	gtk_window_set_position(GTK_WINDOW(nw), GTK_WIN_POS_CENTER_ON_PARENT);
	g_signal_connect(G_OBJECT(nw), "configure-event",
			G_CALLBACK(configure_cb), nw);

	vbox = gtk_vbox_new(FALSE, 5); 
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);

	hbox = gtk_hbox_new(FALSE, 5); 

	nw->throbber = net_throbber_new();
	align = gtk_alignment_new(0.5, 0, 0, 0);
	gtk_container_add(GTK_CONTAINER(align), nw->throbber);
	gtk_box_pack_start(GTK_BOX(hbox), align, FALSE, FALSE, 0);
	net_throbber_start(NET_THROBBER(nw->throbber));

	nw->box = lbox = gtk_vbox_new(FALSE, 5);
	nw->titlelabel = gtk_label_new(NULL);
	gtk_box_pack_start(GTK_BOX(lbox), nw->titlelabel, TRUE, TRUE, 0);

	nw->label = gtk_label_new(NULL);
	gtk_label_set_line_wrap(GTK_LABEL(nw->label), TRUE);
	gtk_box_pack_start(GTK_BOX(lbox), nw->label, TRUE, TRUE, 0);

	nw->progress = gtk_progress_bar_new();
	gtk_box_pack_end(GTK_BOX(lbox), nw->progress, FALSE, FALSE, 0);

	gtk_box_pack_start(GTK_BOX(hbox), lbox, TRUE, TRUE, 0);

	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);

	bbox = gtk_hbutton_box_new();
	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
	g_signal_connect_swapped(G_OBJECT(button), "clicked",
			G_CALLBACK(cancel_cb), nw);
	gtk_box_pack_end(GTK_BOX(bbox), button, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);

	frame = gtk_frame_new(NULL);	 
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);	 
	gtk_container_add(GTK_CONTAINER(frame), vbox);	 
	gtk_widget_show_all(frame);	 
	gtk_widget_hide(nw->progress);
	gtk_container_add(GTK_CONTAINER(nw), frame);

	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
	gtk_widget_grab_default(button);
}

void
net_window_pack(NetWindow *nw, GtkWidget *contents) {
	gtk_box_pack_start(GTK_BOX(nw->box), contents, FALSE, FALSE, 0);
}

void
net_window_set_title(NetWindow *nw, const char *title) {
	char *t;
	gtk_window_set_title(GTK_WINDOW(nw), title);
	t = g_strdup_printf("<b>%s</b>", title);
	gtk_label_set_markup(GTK_LABEL(nw->titlelabel), t);
	g_free(t);
}

GtkWidget* 
net_window_new(GtkWindow *parent, const char *title) {
	NetWindow *nw = NET_WINDOW(g_object_new(net_window_get_type(), NULL));
	if (parent)
		net_window_set_parent(nw, parent);
	net_window_set_title(nw, title);
	return GTK_WIDGET(nw);
}

GType
net_window_get_type(void) {
	static GType nw_type = 0;
	if (!nw_type) {
		static const GTypeInfo nw_info = {
			sizeof (GtkWindowClass),
			NULL,
			NULL,
			NULL, /*(GClassInitFunc) net_window_class_init,*/
			NULL,
			NULL,
			sizeof (NetWindow),
			0,
			(GInstanceInitFunc) net_window_init,
		};
		nw_type = g_type_register_static(GTK_TYPE_WINDOW, "NetWindow",
				&nw_info, 0);
	}
	return nw_type;
}

static void
statuscb(NetStatusType status, gpointer statusdata, gpointer data) {
	NetWindow *nw = data;
	NetStatusProgress *progress;
	float fraction = 0;

	switch (status) {
	case NET_STATUS_BEGIN:
		nw->nethandle = statusdata;
		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(nw->progress), 0);
		break;
	case NET_STATUS_PROGRESS:
		progress = (NetStatusProgress*)statusdata;
		if (progress->current > 0 && progress->total > 0) {
			fraction = (float)progress->current / progress->total;
			gtk_widget_show(nw->progress);
			gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(nw->progress),
					fraction);
		}
		break;
	case NET_STATUS_DONE:
		gtk_widget_hide(nw->progress);
		nw->nethandle = NULL;
		break;
	default:
		g_warning("unhandled network status %d\n", status);
	}
}

typedef struct {
	NetContext ctx;
	GtkWindow *parent;
	GtkWidget *nw;
} NetContextGtk;

static void
statuscb_ctx(NetStatusType status, gpointer statusdata, gpointer data) {
	NetContextGtk *ctx = data;
	statuscb(status, statusdata, ctx->nw);
}

static void
ctx_gtk_title(NetContext *ctx, const char *title) {
	NetWindow *nw = (NetWindow*) ((NetContextGtk*)ctx)->nw;
	net_window_set_title(nw, title);
}
static void
ctx_gtk_progress_str(NetContext *ctx, const char *str) {
	NetWindow *nw = (NetWindow*) ((NetContextGtk*)ctx)->nw;
	gtk_label_set_text(GTK_LABEL(nw->label), str);
}

static void 
ctx_gtk_error(NetContext *ctx, GError *err) {
	NetWindow *nw = (NetWindow*) ((NetContextGtk*)ctx)->nw;
	if (!g_error_matches(err, NET_ERROR, NET_ERROR_CANCELLED))
		net_window_show_error(nw, err->message);
}

static GString*
ctx_gtk_http(NetContext *ctx, const char *url, GSList *headers,
		GString *post, GError **err) {
	if (conf.options.nofork)
		return net_post_blocking(url, NULL, post, statuscb_ctx, ctx, err);
	else
		return net_post_mainloop(url, NULL, post, statuscb_ctx, ctx, err);
}

NetContext*
net_ctx_gtk_new(GtkWindow *parent, const char *title) {
	NetContext *ctx = (NetContext*)g_new0(NetContextGtk, 1);
	NetContextGtk *gtkctx = (NetContextGtk*)ctx;
	ctx->title = ctx_gtk_title;
	ctx->progress_str = ctx_gtk_progress_str;
	//XXX ctx->progress_int = ctx_cmdline_print_string;
	ctx->http = ctx_gtk_http;
	ctx->error = ctx_gtk_error;
	gtkctx->nw = net_window_new(parent, title);
	gtk_widget_show(gtkctx->nw);
	return ctx;
}

void
net_ctx_gtk_free(NetContext *ctx) {
	NetContextGtk *gtkctx = (NetContextGtk*)ctx;
	gtk_widget_destroy(gtkctx->nw);
	g_free(ctx);
}

gboolean
net_window_run_verb(NetWindow *nw, LJVerb *verb) {
	GError *err = NULL;
	gboolean ret;

	gtk_widget_show(GTK_WIDGET(nw));
	ret = net_verb_run_internal(verb, statuscb, nw, &err);

	if (!ret) {
		if (!g_error_matches(err, NET_ERROR, NET_ERROR_CANCELLED))
			net_window_show_error(nw, err->message);
		g_error_free(err);
		return FALSE;
	}

	return TRUE;
}

gboolean
net_window_run_post(NetWindow *nw, const char *url, GSList *headers,
                    GString *request, GString *response,
					GError **err) {
	GString *res;
	gboolean ret;

	gtk_widget_show(GTK_WIDGET(nw));
	res = net_post_mainloop(url, headers, request, statuscb, nw, err);
	if (res) {
		g_string_append_len(response, res->str, res->len);
		ret = TRUE;
	} else {
		ret = FALSE;
	}
	g_string_free(res, TRUE);

	return ret;
}

gboolean
net_verb_run(LJVerb *verb, const char *title, GtkWindow *parent) {
	GtkWidget *nw;
	gboolean ret;

	nw = net_window_new(parent, title);
	ret = net_window_run_verb(NET_WINDOW(nw), verb);
	gtk_widget_destroy(nw);

	return ret;
}

