/*
 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2026 the Claws Mail Team
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#include "claws-features.h"
#endif

#include <unistd.h>
#include <sys/wait.h>

#include <glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <gtk/gtkx.h>
#include <gdk/gdkx.h>

#include "common/claws.h"
#include "common/version.h"
#include "plugin.h"
#include "file-utils.h"
#include "utils.h"
#include "mimeview.h"
#include "addr_compl.h"

#include "dillo_prefs.h"

#define PLUGIN_NAME (_("Dillo HTML Viewer"))

typedef struct _DilloViewer DilloViewer;

struct _DilloViewer
{
	MimeViewer	 mimeviewer;
	GtkWidget	*widget;	
	GtkWidget	*socket;
	gchar		*filename;
	GPid		 dillo_pid;
};

static MimeViewerFactory dillo_viewer_factory;

static GtkWidget *dillo_get_widget(MimeViewer *_viewer)
{
	DilloViewer *viewer = (DilloViewer *) _viewer;

	debug_print("dillo_get_widget\n");

	return GTK_WIDGET(viewer->widget);
}

static gboolean socket_destroy_cb(GObject *object, gpointer data)
{
	DilloViewer *viewer = (DilloViewer *) data;
	debug_print("Destroyed dillo socket %p\n", viewer->socket);
	viewer->socket = NULL;
	return FALSE;
}

static gboolean load_images(DilloViewer *viewer)
{
	MessageView *messageview = ((MimeViewer *)viewer)->mimeview 
					? ((MimeViewer *)viewer)->mimeview->messageview 
					: NULL;
	MsgInfo *msginfo = NULL;
	gchar *ab_folderpath = NULL;

	if (messageview == NULL)
		return FALSE;
	
	msginfo = messageview->msginfo;
	
	if (msginfo == NULL)
		return FALSE;

	/* don't load remote images, period. */
	if (dillo_prefs.local)
		return FALSE;
	
	/* don't do whitelisting -> load images */
	if (!dillo_prefs.whitelist_ab)
		return TRUE;

	if (*dillo_prefs.whitelist_ab_folder != '\0' &&
	    strcasecmp(dillo_prefs.whitelist_ab_folder, _("Any")) != 0)
		ab_folderpath = dillo_prefs.whitelist_ab_folder;

	start_address_completion(ab_folderpath);

	/* do whitelisting -> check sender */
	if (found_in_addressbook(msginfo->from)) {
		end_address_completion();
		return TRUE;
	}
	
	end_address_completion();
	return FALSE;
}

static void dillo_show_mimepart(MimeViewer *_viewer,
				const gchar *infile,
				MimeInfo *partinfo)
{
	DilloViewer *viewer = (DilloViewer *) _viewer;
	GdkWindow *gdkwin;

	debug_print("dillo_show_mimepart\n");

	if (viewer->filename != NULL) {
		claws_unlink(viewer->filename);
		g_free(viewer->filename);
		viewer->filename = NULL;
	}

#ifdef GDK_WINDOWING_X11
	if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
		debug_print("dillo viewer only works on X11\n");
		return;
	}
#else
	debug_print("dillo viewer only works on X11\n");
	return;
#endif

	viewer->filename = procmime_get_tmp_file_name(partinfo);

	if (!(procmime_get_part(viewer->filename, partinfo) < 0)) {
		gchar *cmd = NULL;

		if (viewer->socket)
			gtk_widget_destroy(viewer->socket);

		viewer->socket = gtk_socket_new();
		debug_print("Adding dillo socket %p\n", viewer->socket);

		gtk_container_add(GTK_CONTAINER(viewer->widget), viewer->socket);
		gtk_widget_realize(viewer->socket);
		gtk_widget_show(viewer->socket);

		g_signal_connect(G_OBJECT(viewer->socket), "destroy",
				G_CALLBACK(socket_destroy_cb), viewer);

		gdkwin = gtk_widget_get_window(viewer->socket);

		/* Wait valid XID before spawning Dillo */
		guint xid = GDK_WINDOW_XID(gdkwin);
		int retries = 5;
		while (xid == 0 && retries > 0) {
			g_usleep(10000); /* 10 ms */
			xid = GDK_WINDOW_XID(gdkwin);
			retries--;
		}
		if (xid == 0) {
			g_warning("Failed to get valid XID for Dillo socket, giving up");
			return;
		}

		gchar *dillo_path = g_find_program_in_path("dillo");
		debug_print("dillo path: %s\n", dillo_path);

		if (dillo_path == NULL) {
			g_warning("Cannot find 'dillo' in $PATH");
			return;
		}

		cmd = g_strdup_printf("%s %s%s-x %d \"%s\"", dillo_path,
				(!load_images(viewer) ? "-l " : ""),
				(dillo_prefs.full    ? "-f " : ""),
				(gint) GDK_WINDOW_XID(gdkwin),
				viewer->filename);
		debug_print("cmd: %s\n", cmd);
		g_free(dillo_path);

		/* Spawn Dillo asynchronously */
		GPid pid = 0;
		GError *error = NULL;
		gchar **argv = NULL;
		gint argc = 0;

		if (!g_shell_parse_argv(cmd, &argc, &argv, &error)) {
			g_warning("Failed to parse command line for Dillo: %s",
				(error != NULL) ? error->message : "unknown");
			if (error != NULL)
				g_error_free(error);
		} else {
			if (!g_spawn_async(NULL, argv, NULL, 0, NULL, NULL,
					&pid, &error)) {
				g_warning("Failed to spawn Dillo: %s",
					(error != NULL) ? error->message : "unknown");
				if (error != NULL)
					g_error_free(error);
			} else {
				debug_print("dillo_pid: %d\n", pid);
				viewer->dillo_pid = pid;
			}
			g_strfreev(argv);
		}
		g_free(cmd);

	}
}

static void dillo_clear_viewer(MimeViewer *_viewer)
{
	DilloViewer *viewer = (DilloViewer *) _viewer;
	int status;

	debug_print("dillo_clear_viewer\n");

	if (viewer->filename) {
		if (claws_unlink(viewer->filename) < 0) {
			g_warning("Failed to remove file: %s", viewer->filename);
		}
		g_free(viewer->filename);
		viewer->filename = NULL;
	}

        if (viewer->dillo_pid > 0) {
                debug_print("Killing Dillo PID %d\n", viewer->dillo_pid);
                kill(viewer->dillo_pid, SIGTERM);
		waitpid(viewer->dillo_pid, &status, 0);
                g_spawn_close_pid(viewer->dillo_pid);
                viewer->dillo_pid = 0;
        }

	if (viewer->socket)
		gtk_widget_destroy(viewer->socket);
}

static void dillo_destroy_viewer(MimeViewer *_viewer)
{
	DilloViewer *viewer = (DilloViewer *) _viewer;

	debug_print("dillo_destroy_viewer\n");

	if (viewer->socket)
		gtk_widget_destroy(viewer->socket);

	g_object_unref(GTK_WIDGET(viewer->widget));
	if (viewer->filename) {
		claws_unlink(viewer->filename);
		g_free(viewer->filename);
	}
    	g_free(viewer);
}

static MimeViewer *dillo_viewer_create(void)
{
	DilloViewer *viewer;

	debug_print("dillo_viewer_create\n");
	
	viewer = g_new0(DilloViewer, 1);
	viewer->mimeviewer.factory = &dillo_viewer_factory;
	viewer->mimeviewer.get_widget = dillo_get_widget;
	viewer->mimeviewer.show_mimepart = dillo_show_mimepart;
	viewer->mimeviewer.clear_viewer = dillo_clear_viewer;
	viewer->mimeviewer.destroy_viewer = dillo_destroy_viewer;	
	viewer->widget = gtk_event_box_new();

	gtk_widget_show(viewer->widget);
	g_object_ref(viewer->widget);

	viewer->filename = NULL;

	return (MimeViewer *) viewer;
}

static gchar *content_types[] = 
	{"text/html", NULL};

static MimeViewerFactory dillo_viewer_factory =
{
	content_types,	
	0,

	dillo_viewer_create,
};

gint plugin_init(gchar **error)
{
	gchar *dillo_path = NULL;
	if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
				VERSION_NUMERIC, PLUGIN_NAME, error))
  		return -1;

	if ((dillo_path = g_find_program_in_path("dillo")) == NULL) {
		*error = g_strdup(_("Can't find the dillo executable in PATH. Is it installed?"));
		return -1;
	}
	g_free(dillo_path);

        dillo_prefs_init();

	mimeview_register_viewer_factory(&dillo_viewer_factory);

	return 0;	
}

gboolean plugin_done(void)
{
	mimeview_unregister_viewer_factory(&dillo_viewer_factory);

        dillo_prefs_done();
	return TRUE;
}

const gchar *plugin_name(void)
{
	return PLUGIN_NAME;
}

const gchar *plugin_desc(void)
{
	return _("This plugin renders HTML mail using the Dillo "
		"web browser.\n"
	        "\n"
	        "Options can be found in '/Configuration/Preferences/Plugins/Dillo Browser'.");
}

const gchar *plugin_type(void)
{
	return "GTK3";
}

const gchar *plugin_licence(void)
{
	return "GPL3+";
}

const gchar *plugin_version(void)
{
	return VERSION;
}

struct PluginFeature *plugin_provides(void)
{
	static struct PluginFeature features[] = 
		{ {PLUGIN_MIMEVIEWER, "text/html"},
		  {PLUGIN_NOTHING, NULL}};
	return features;
}
