/*
 * URI entry
 *
 * Copyright (C) 2017  Patrick McDermott
 *
 * This file is part of Marquee.
 *
 * Marquee is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Marquee is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Marquee.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "uri-entry.h"

#include <glib.h>
#include <gtk/gtk.h>
#include <webkit2/webkit2.h>

#include "../../web-view.h"

struct _MqUriEntry {
	GtkToolItem    parent_instance;
	MqWebView     *web_view;
	GtkWidget     *uri_entry;
	gchar         *hovered_link_uri;
	PangoAttrList *hovered_link_style;
	gboolean       load_failed;
};

enum {
	PROP_WEB_VIEW = 1,
	PROP_URI,
	N_PROPERTIES
};

static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,};

struct _MqUriEntryClass {
	GtkToolItemClass parent_class;
};

G_DEFINE_TYPE(MqUriEntry, mq_uri_entry, GTK_TYPE_TOOL_ITEM)

static void
load_changed_cb(MqWebView *web_view, WebKitLoadEvent load_event,
	MqUriEntry *uri_entry)
{
	const gchar *uri;

	switch (load_event) {
		case WEBKIT_LOAD_STARTED:
		case WEBKIT_LOAD_REDIRECTED:
		case WEBKIT_LOAD_COMMITTED:
			uri = mq_web_view_get_uri(web_view);
			gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri);
			gtk_entry_set_attributes(GTK_ENTRY(uri_entry->uri_entry),
				NULL);
			break;
		case WEBKIT_LOAD_FINISHED:
			gtk_entry_set_progress_fraction(
				GTK_ENTRY(uri_entry->uri_entry), 0.0);
			break;
		default:
			g_assert_not_reached();
			break;
	}
}

static gboolean
load_failed_cb(WebKitWebView G_GNUC_UNUSED *web_view,
	WebKitLoadEvent G_GNUC_UNUSED load_event,
	gchar G_GNUC_UNUSED *failing_uri, GError G_GNUC_UNUSED *error,
	MqUriEntry *uri_entry)
{
	uri_entry->load_failed = TRUE;
	return FALSE;
}

/* TODO: <Esc> key in URI bar should reset to URI of current hovered link, if
 * any, even if different from uri_entry->hovered_link_uri. */
static void
mouse_target_changed_cb(MqWebView G_GNUC_UNUSED *web_view,
	WebKitHitTestResult *hit_test_result, guint G_GNUC_UNUSED modifiers,
	MqUriEntry *uri_entry)
{
	const gchar *uri;

	uri = mq_web_view_get_uri(web_view);
	if (webkit_hit_test_result_context_is_link(hit_test_result)) {
		if (gtk_widget_has_focus(uri_entry->uri_entry)) {
		} else if (uri_entry->hovered_link_uri &&
				g_strcmp0(gtk_entry_get_text(
					GTK_ENTRY(uri_entry->uri_entry)),
				uri_entry->hovered_link_uri) != 0) {
			/* The user has edited the hovered link URI in the URI
			 * bar. */
		} else if (g_strcmp0(gtk_entry_get_text(
					GTK_ENTRY(uri_entry->uri_entry)), uri) ==
				0) {
			g_free(uri_entry->hovered_link_uri);
			uri_entry->hovered_link_uri = g_strdup(
				webkit_hit_test_result_get_link_uri(
					hit_test_result));
			gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry),
				uri_entry->hovered_link_uri);
			gtk_entry_set_attributes(GTK_ENTRY(uri_entry->uri_entry),
				uri_entry->hovered_link_style);
		} else if (gtk_entry_get_attributes(
					GTK_ENTRY(uri_entry->uri_entry)) ==
				uri_entry->hovered_link_style) {
			/* The URI bar's text differs from the Web view's URI
			 * because the mouse was already targeting a different
			 * link. */
			g_free(uri_entry->hovered_link_uri);
			uri_entry->hovered_link_uri = g_strdup(
				webkit_hit_test_result_get_link_uri(
					hit_test_result));
			gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry),
				uri_entry->hovered_link_uri);
		}
	} else if (gtk_entry_get_attributes(GTK_ENTRY(uri_entry->uri_entry)) ==
				uri_entry->hovered_link_style) {
		if (gtk_widget_has_focus(uri_entry->uri_entry)) {
		} else if (g_strcmp0(gtk_entry_get_text(
						GTK_ENTRY(uri_entry->uri_entry)),
				uri_entry->hovered_link_uri) == 0) {
			/* The user hasn't edited the hovered link URI in the
			 * URI bar. */
			g_free(uri_entry->hovered_link_uri);
			uri_entry->hovered_link_uri = NULL;
			gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri);
			gtk_entry_set_attributes(GTK_ENTRY(uri_entry->uri_entry),
				NULL);
		}
	}
}

static void
load_progress_cb(WebKitWebView *web_view, GParamSpec G_GNUC_UNUSED *param_spec,
	MqUriEntry *uri_entry)
{
	/*
	 * If loading fails, the WebKitWebView's "estimated-load-progress" is
	 * set to 1.0 after signals like "load-changed" and "load-failed" are
	 * emitted.  So the only way to avoid leaving behind a full progress bar
	 * after, for example, canceling a page load is to save a flag on a
	 * failed load and only update the progress bar if the flag is unset.
	 */
	if (uri_entry->load_failed) {
		uri_entry->load_failed = FALSE;
		return;
	}
	gtk_entry_set_progress_fraction(GTK_ENTRY(uri_entry->uri_entry),
		webkit_web_view_get_estimated_load_progress(web_view));
}

static void
uri_cb(MqWebView *web_view, GParamSpec G_GNUC_UNUSED *param_spec,
	MqUriEntry *uri_entry)
{
	const gchar *uri;

	uri = mq_web_view_get_uri(web_view);
	gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri);
	gtk_entry_set_attributes(GTK_ENTRY(uri_entry->uri_entry), NULL);
}

static void
uri_activate_cb(GtkEntry *entry, MqUriEntry *uri_entry)
{
	const gchar *uri;

	uri = gtk_entry_get_text(GTK_ENTRY(entry));

	mq_web_view_load_uri(uri_entry->web_view, uri);
}

static void
set_uri(MqUriEntry *uri_entry, const gchar *uri)
{
	if (uri) {
		gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri);
	}
}

static void
set_web_view(MqUriEntry *uri_entry, MqWebView *web_view)
{
	uri_entry->web_view = web_view;

	g_signal_connect(web_view, "load-changed",
		G_CALLBACK(load_changed_cb), uri_entry);
	g_signal_connect(web_view, "load-failed",
		G_CALLBACK(load_failed_cb), uri_entry);
	g_signal_connect(web_view, "mouse-target-changed",
		G_CALLBACK(mouse_target_changed_cb), uri_entry);
	g_signal_connect(web_view, "notify::estimated-load-progress",
		G_CALLBACK(load_progress_cb), uri_entry);
	g_signal_connect(web_view, "notify::rewritten-uri",
		G_CALLBACK(uri_cb), uri_entry);
}

static void
get_property(GObject *object, guint property_id, GValue *value,
	GParamSpec *param_spec)
{
	switch (property_id) {
		case PROP_URI:
			g_value_set_string(value, NULL);
			break;
		case PROP_WEB_VIEW:
			g_value_set_object(value, NULL);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id,
				param_spec);
			break;
	}
}

static void
set_property(GObject *object, guint property_id, const GValue *value,
	GParamSpec *param_spec)
{
	MqUriEntry *uri_entry;

	uri_entry = MQ_URI_ENTRY(object);

	switch (property_id) {
		case PROP_URI:
			set_uri(uri_entry, g_value_get_string(value));
			break;
		case PROP_WEB_VIEW:
			set_web_view(uri_entry, g_value_get_object(value));
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id,
				param_spec);
			break;
	}
}

static void
mq_uri_entry_class_init(MqUriEntryClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS(klass);

	object_class->get_property = get_property;
	object_class->set_property = set_property;

	obj_properties[PROP_URI] = g_param_spec_string(
		"uri",
		"URI",
		"The initial URI in the URI entry",
		"",
		G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
		G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
	obj_properties[PROP_WEB_VIEW] = g_param_spec_object(
		"web-view",
		"MqWebView",
		"The associated MqWebView instance",
		MQ_TYPE_WEB_VIEW,
		G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
		G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
	g_object_class_install_properties(object_class, N_PROPERTIES,
		obj_properties);
}

static void
mq_uri_entry_init(MqUriEntry *uri_entry)
{
	uri_entry->uri_entry = gtk_entry_new();
	gtk_entry_set_placeholder_text(GTK_ENTRY(uri_entry->uri_entry),
		"URI...");
	gtk_entry_set_icon_from_icon_name(GTK_ENTRY(uri_entry->uri_entry),
		GTK_ENTRY_ICON_PRIMARY, "text-html");
	gtk_entry_set_progress_fraction(GTK_ENTRY(uri_entry->uri_entry), 0.0);
	g_signal_connect(uri_entry->uri_entry, "activate",
		G_CALLBACK(uri_activate_cb), uri_entry);
	gtk_container_add(GTK_CONTAINER(uri_entry), uri_entry->uri_entry);
	gtk_tool_item_set_expand(GTK_TOOL_ITEM(uri_entry), TRUE);

	uri_entry->hovered_link_uri = NULL;

	/* URI bar hovered link style */
	uri_entry->hovered_link_style = pango_attr_list_new();
	pango_attr_list_insert(uri_entry->hovered_link_style,
		pango_attr_style_new(PANGO_STYLE_ITALIC));

	uri_entry->load_failed = FALSE;
}

GtkToolItem *
mq_uri_entry_new(MqWebView *web_view, const gchar *uri)
{
	return g_object_new(MQ_TYPE_URI_ENTRY,
		"web-view", web_view,
		"uri", uri,
		NULL);
}