/* vim: colorcolumn=80 ts=4 sw=4
 */
#include "assistant.h"

static GtkBuilder *builder;
static GtkWidget *assistant;
static GtkWidget *confirm_box;
static GtkWidget *progress_box;
static GtkWidget *success_box;
static GtkWidget *progress_cancel_button;
static GtkWidget *progress_close_button;
static GtkWidget *confirm_close_dialog;

/* Yes, I've looked at putting some of these into progress-page.c - it just
 * wasn't worth it due to GtkAssistant's awful api. If this source file
 * starts to get unruly I'll consider trying to move these, but for now
 * they'll just stick around in here.
 */
static void
progress_close_button_clicked_cb (GtkButton *button, gpointer user_data)
{
	gtk_window_destroy (GTK_WINDOW(assistant));
}

static void
progress_cancel_button_clicked_cb (GtkButton *button, gpointer user_data)
{
	GfiProgress *progress = GFI_PROGRESS(user_data);
	GtkWidget *progress_heading;
	GtkWidget *progress_text;
	GCancellable *cancellable;

	g_return_if_fail (GFI_IS_PROGRESS (progress));

	GET_WIDGET (builder, progress_heading);
	GET_WIDGET (builder, progress_text);

	gtk_widget_hide (progress_cancel_button);
	gtk_widget_show (progress_close_button);

	set_heading_text (GTK_LABEL(progress_heading),
			"Operation Cancelled");
	gtk_label_set_markup (GTK_LABEL(progress_text), 
			"The requested operation has been cancelled.");

	cancellable = gfi_progress_get_cancellable (progress);

	if (cancellable)
		g_cancellable_cancel (cancellable);
}

static void
setup_progress_page_buttons (GtkAssistant *a,
		GfiProgress *progress)
{
	progress_cancel_button = gtk_button_new_with_mnemonic ("_Cancel");
	gtk_widget_set_halign (progress_cancel_button, GTK_ALIGN_END);
	gtk_widget_set_sensitive (progress_cancel_button, FALSE);
	g_signal_connect (progress_cancel_button, "clicked",
			G_CALLBACK(progress_cancel_button_clicked_cb), progress);

	progress_close_button = gtk_button_new_with_mnemonic ("_Close");
	gtk_widget_set_halign (progress_close_button, GTK_ALIGN_END);
	g_signal_connect (progress_close_button, "clicked",
			G_CALLBACK(progress_close_button_clicked_cb), NULL);

	gtk_assistant_add_action_widget (a, progress_cancel_button);
	gtk_assistant_add_action_widget (a, progress_close_button);
	gtk_widget_hide (progress_close_button);
}

static void
prepare_cb (GtkAssistant *a,
		GtkWidget *page,
		gpointer user_data)
{
	GfiProgress *progress = GFI_PROGRESS(user_data);

	g_return_if_fail (GFI_IS_PROGRESS (progress));
	g_return_if_fail (GTK_IS_BUILDER (builder));

	if (page == confirm_box)
	{
		GtkWidget *confirm_heading;
		GtkWidget *confirm_text;

		GET_WIDGET (builder, confirm_heading);
		GET_WIDGET (builder, confirm_text);

		/* Set default text for confirm heading label, in case all else fails
		 */
		set_heading_text (GTK_LABEL(confirm_heading), "Flatpak Install Wizard");
		gtk_label_set_markup (GTK_LABEL(confirm_text),
				"Installation did not proceed. You may close this window now.");
	}
	else if (page == progress_box)
	{
		setup_progress_page_buttons (a, progress);
	}
	else if (page == success_box)
	{
		if (confirm_close_dialog)
			gtk_window_destroy (GTK_WINDOW(confirm_close_dialog));
		gtk_widget_hide (progress_cancel_button);
		setup_success_page (builder, progress);
	}
	else
	{
		g_error ("%s: programmer error: invalid page: %p",
				__func__, (void *)page);
	}
}

static void
apply_cb (GtkAssistant *a, gpointer user_data)
{
	GfiProgress *progress = GFI_PROGRESS(user_data);

	setup_progress_page (builder, progress);
	gfi_progress_confirm (progress);
}

static void
close_cb (GtkAssistant *a, gpointer user_data)
{
	gtk_window_destroy (GTK_WINDOW(a));
}

/* Not to be confused with progress_cancel_button_clicked_cb !  Man, the
 * GtkAssistant API kinda sux, dawg. This is handled when you hit
 * GtkAssistant's overarching Cancel button. And to *greater* serve to
 * confuse matters...
 *
 * You can't connect to the "escape" signal - so sometimes hitting Esc
 * will actually forward to the cancel signal unless you're on a "progress"
 * type of GtkAssistant page. Since we use a custom progress page, this will
 * not be caught and will just automatically close the window.
 * So we handle it here.
 */
static void
cancel_cb (GtkAssistant *a, gpointer user_data)
{
	int page_num;
	GtkWidget *page;

	page_num = gtk_assistant_get_current_page (a);
	page = gtk_assistant_get_nth_page (a, page_num);

	/* If Escape sends the cancel signal and we're on the progress page,
	 * ignore.
	 */
	if (page == progress_box)
	{
		g_debug ("%s: hit escape during progress page; ignoring.", __func__);
	}
	else
	{
		g_debug ("%s: destroying assistant.", __func__);
		gtk_window_destroy (GTK_WINDOW(a));
	}
}

static void
confirm_close_dialog_response_cb (GtkDialog *dialog,
		int response_id,
		gpointer user_data)
{
	switch (response_id)
	{
		/* yes, exit the installer. */
		case GTK_RESPONSE_ACCEPT:
			gtk_window_destroy (GTK_WINDOW(assistant));
			break;

		default:
			gtk_window_destroy (GTK_WINDOW(dialog));
			break;
	}
}

static void
setup_confirm_close_dialog (void)
{
	if (confirm_close_dialog) return;

	confirm_close_dialog = gtk_message_dialog_new (GTK_WINDOW(assistant),
			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_MESSAGE_QUESTION,
			GTK_BUTTONS_NONE,
			"Are you sure you want to exit at this time?\n\n"
			"The installation process will be aborted.");
	gtk_dialog_add_buttons (GTK_DIALOG(confirm_close_dialog),
			"_Exit Installation", GTK_RESPONSE_ACCEPT,
			"_Continue Installation", GTK_RESPONSE_REJECT,
			NULL);

	/* We want to be able to easily tell if the dialog has been destroyed
	 * or not */
	g_object_add_weak_pointer (G_OBJECT(confirm_close_dialog),
			(gpointer *)&confirm_close_dialog);

	g_signal_connect (confirm_close_dialog, "response",
			G_CALLBACK(confirm_close_dialog_response_cb), NULL);
}

static gboolean
close_request_cb (GtkWindow *self, gpointer user_data)
{
	int page_num;
	GtkWidget *page;

	page_num = gtk_assistant_get_current_page (GTK_ASSISTANT(assistant));
	page = gtk_assistant_get_nth_page (GTK_ASSISTANT(assistant), page_num);

	if (page == progress_box)
	{
		setup_confirm_close_dialog ();
		gtk_widget_show (confirm_close_dialog);
	}
	else
	{
		g_debug ("%s: destroying assistant.", __func__);
		gtk_window_destroy (GTK_WINDOW(assistant));
	}

	return GDK_EVENT_STOP;
}

static void
new_transaction_cb (GfiProgress *progress,
		gpointer user_data)
{
	gtk_widget_set_sensitive (progress_cancel_button, TRUE);
}

static void
ready_to_confirm_cb (GfiProgress *progress,
		gpointer user_data)
{
	setup_confirm_page (builder, progress);
	gtk_assistant_set_page_complete (GTK_ASSISTANT(assistant),
			confirm_box, TRUE);
}

static void
ready_to_proceed_cb (GfiProgress *progress,
		gpointer user_data)
{
	gfi_progress_proceed (progress);
}


static void
transaction_done_cb (GfiProgress *progress,
		gpointer user_data)
{
	gtk_assistant_next_page (GTK_ASSISTANT(assistant));
}

static void
setup_signals (GfiProgress *progress)
{
	/* ASSISTANT */

	/* Connect to "prepare" signal to tell the wizard how to setup each
	 * page before it's loaded.
	 */
	g_signal_connect (assistant, "prepare",
			G_CALLBACK(prepare_cb), progress);

	g_signal_connect (assistant, "apply",
			G_CALLBACK(apply_cb), progress);

	g_signal_connect (assistant, "close",
			G_CALLBACK(close_cb), NULL);

	g_signal_connect (assistant, "cancel",
			G_CALLBACK(cancel_cb), NULL);

	g_signal_connect (assistant, "close-request",
			G_CALLBACK(close_request_cb), NULL);
	
	/* GFI PROGRESS */

	g_signal_connect (progress, "ready-to-confirm",
			G_CALLBACK(ready_to_confirm_cb), NULL);

	/* As the progress page's cancel button is greyed out, enable it when
	 * a transation commences.
	 */
	g_signal_connect (progress, "new-transaction",
			G_CALLBACK(new_transaction_cb), NULL);

	/* Allow the user to click click the apply button to proceed once
	 * that signal is emitted.
	 */
	g_signal_connect (progress, "ready-to-proceed",
			G_CALLBACK(ready_to_proceed_cb), NULL);

	/* Goto next page (ie, final page) when transaction is complete.
	 */
	g_signal_connect (progress, "transaction-done",
			G_CALLBACK(transaction_done_cb), NULL);
}

/* PUBLIC METHODS */

GtkWidget *
gfi_assistant_new (GfiProgress *progress)
{
	g_return_val_if_fail (GFI_IS_PROGRESS (progress), NULL);

	builder = gtk_builder_new_from_resource (RESOURCE_PREFIX "confirm-box.ui");
	gtk_builder_add_from_resource (builder, RESOURCE_PREFIX "progress-box.ui",
			NULL);
	gtk_builder_add_from_resource (builder, RESOURCE_PREFIX "success-box.ui",
			NULL);
	gtk_builder_add_from_resource (builder, RESOURCE_PREFIX "assistant.ui",
			NULL);

	GET_WIDGET (builder, assistant);
	GET_WIDGET (builder, confirm_box);
	GET_WIDGET (builder, progress_box);
	GET_WIDGET (builder, success_box);

	setup_signals (progress);

	return assistant;
}
