/* $Id$ */
/* Copyright (c) 2015-2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Surfer */
/* 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, version 3 of the License.
 *
 * 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/>. */
/* TODO:
 * - add a flag to disable network access */



#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <locale.h>
#include <libintl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <Desktop.h>
#include "ghtml.h"
#include <System.h>
#include "../config.h"
#define _(string) gettext(string)
#define N_(string) string

/* constants */
#ifndef PROGNAME_HTMLAPP
# define PROGNAME_HTMLAPP	"htmlapp"
#endif
#ifndef PREFIX
# define PREFIX			"/usr/local"
#endif
#ifndef DATADIR
# define DATADIR		PREFIX "/share"
#endif
#ifndef LOCALEDIR
# define LOCALEDIR		DATADIR "/locale"
#endif


/* htmlapp */
/* private */
/* types */
typedef struct _Surfer
{
	guint source;
	char const ** p;

	/* widgets */
	GtkIconTheme * icontheme;
	GtkWidget * window;
	GtkWidget * vbox;
	GtkWidget * view;

	/* find */
	GtkWidget * fi_dialog;
	GtkWidget * fi_text;
	GtkWidget * fi_case;
	GtkWidget * fi_back;
	GtkWidget * fi_wrap;

	/* about */
	GtkWidget * ab_window;
} HTMLApp;


/* prototypes */
static HTMLApp * _htmlapp_new(void);
void _htmlapp_delete(HTMLApp * htmlapp);

static int _htmlapp_open(HTMLApp * htmlapp, char const * url);
static int _htmlapp_open_dialog(HTMLApp * htmlapp);

static int _error(char const * message, int ret);
static int _usage(void);

/* callbacks */
static void _htmlapp_on_close(gpointer data);
static gboolean _htmlapp_on_closex(gpointer data);
static void _htmlapp_on_find(gpointer data);
static void _htmlapp_on_fullscreen(gpointer data);
static void _htmlapp_on_open(gpointer data);


/* constants */
static const DesktopAccel _htmlapp_accel[] =
{
	{ G_CALLBACK(_htmlapp_on_close), GDK_CONTROL_MASK, GDK_KEY_W },
	{ G_CALLBACK(_htmlapp_on_find), GDK_CONTROL_MASK, GDK_KEY_F },
	{ G_CALLBACK(_htmlapp_on_fullscreen), 0, GDK_KEY_F11 },
	{ G_CALLBACK(_htmlapp_on_open), GDK_CONTROL_MASK, GDK_KEY_O },
	{ NULL, 0, 0 }
};


/* functions */
/* HTMLApp */
/* htmlapp_new */
static HTMLApp * _htmlapp_new(void)
{
	HTMLApp * htmlapp;
	GtkAccelGroup * group;
	GtkWidget * vbox;

	if((htmlapp = surfer_new(NULL)) == NULL)
		return NULL;
	htmlapp->source = 0;
	htmlapp->p = NULL;
	/* widgets */
	htmlapp->icontheme = gtk_icon_theme_get_default();
	/* window */
	group = gtk_accel_group_new();
	htmlapp->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_add_accel_group(GTK_WINDOW(htmlapp->window), group);
#ifndef EMBEDDED
	gtk_window_set_default_size(GTK_WINDOW(htmlapp->window), 640, 480);
#else
	gtk_window_set_default_size(GTK_WINDOW(htmlapp->window), 240, 480);
#endif
#if GTK_CHECK_VERSION(2, 6, 0)
	gtk_window_set_icon_name(GTK_WINDOW(htmlapp->window), "web-browser");
#endif
	gtk_window_set_title(GTK_WINDOW(htmlapp->window), _("HTML Application"));
	g_signal_connect_swapped(htmlapp->window, "delete-event", G_CALLBACK(
				_htmlapp_on_closex), htmlapp);
#if GTK_CHECK_VERSION(3, 0, 0)
	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
#else
	vbox = gtk_vbox_new(FALSE, 0);
#endif
	htmlapp->vbox = vbox;
	desktop_accel_create(_htmlapp_accel, htmlapp, group);
	/* view */
	htmlapp->view = ghtml_new(htmlapp);
	ghtml_set_enable_javascript(htmlapp->view, FALSE);
	gtk_box_pack_start(GTK_BOX(vbox), htmlapp->view, TRUE, TRUE, 0);
	gtk_container_add(GTK_CONTAINER(htmlapp->window), vbox);
	gtk_widget_grab_focus(htmlapp->view);
	gtk_widget_show_all(htmlapp->window);
	htmlapp->fi_dialog = NULL;
	htmlapp->ab_window = NULL;
	return htmlapp;
}


/* htmlapp_delete */
void _htmlapp_delete(HTMLApp * htmlapp)
{
	if(htmlapp->source != 0)
		g_source_remove(htmlapp->source);
	if(htmlapp->ab_window != NULL)
		gtk_widget_destroy(htmlapp->ab_window);
	if(htmlapp->fi_dialog != NULL)
		gtk_widget_destroy(htmlapp->fi_dialog);
	gtk_widget_destroy(htmlapp->window);
	surfer_delete(htmlapp);
}


/* useful */
/* htmlapp_open */
static int _htmlapp_open(HTMLApp * htmlapp, char const * url)
{
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, url);
#endif
	if(url == NULL)
		return _htmlapp_open_dialog(htmlapp);
	ghtml_load_url(htmlapp->view, url);
	return 0;
}


/* htmlapp_open_dialog */
static void _open_dialog_on_choose_activate(GtkWidget * widget, gint arg1,
		gpointer data);

static int _htmlapp_open_dialog(HTMLApp * htmlapp)
{
	int ret;
	GtkWidget * dialog;
	GtkWidget * vbox;
	GtkWidget * hbox;
	GtkWidget * entry;
	GtkWidget * widget;
	GtkFileFilter * filter;
	char const * url = NULL;

	dialog = gtk_dialog_new_with_buttons(_("Open page..."),
			GTK_WINDOW(htmlapp->window),
			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
#if GTK_CHECK_VERSION(2, 14, 0)
	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
#else
	vbox = GTK_DIALOG(dialog)->vbox;
#endif
	gtk_box_set_spacing(GTK_BOX(vbox), 4);
#if GTK_CHECK_VERSION(3, 0, 0)
	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
#else
	hbox = gtk_hbox_new(FALSE, 4);
#endif
	widget = gtk_label_new(_("Filename: "));
	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
	entry = gtk_entry_new();
	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
	/* file chooser */
	widget = gtk_file_chooser_dialog_new(_("Open file..."),
			GTK_WINDOW(dialog), GTK_FILE_CHOOSER_ACTION_OPEN,
			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
	filter = gtk_file_filter_new();
	gtk_file_filter_set_name(filter, _("HTML files"));
	gtk_file_filter_add_mime_type(filter, "text/html");
	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(widget), filter);
	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(widget), filter);
	filter = gtk_file_filter_new();
	gtk_file_filter_set_name(filter, _("Text files"));
	gtk_file_filter_add_mime_type(filter, "text/plain");
	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(widget), filter);
	filter = gtk_file_filter_new();
	gtk_file_filter_set_name(filter, _("All files"));
	gtk_file_filter_add_pattern(filter, "*");
	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(widget), filter);
	g_signal_connect(widget, "response", G_CALLBACK(
				_open_dialog_on_choose_activate), entry);
	widget = gtk_file_chooser_button_new_with_dialog(widget);
	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
	gtk_widget_show_all(vbox);
	if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
		url = gtk_entry_get_text(GTK_ENTRY(entry));
	gtk_widget_hide(dialog);
	if(url == NULL || strlen(url) == 0)
		ret = -1;
	else
		ret = _htmlapp_open(htmlapp, url);
	gtk_widget_destroy(dialog);
	return ret;
}

static void _open_dialog_on_choose_activate(GtkWidget * widget, gint arg1,
		gpointer data)
{
	GtkWidget * entry = data;

	if(arg1 != GTK_RESPONSE_ACCEPT)
		return;
	gtk_entry_set_text(GTK_ENTRY(entry), gtk_file_chooser_get_filename(
				GTK_FILE_CHOOSER(widget)));
}


/* callbacks */
/* htmlapp_on_close */
static void _htmlapp_on_close(gpointer data)
{
	HTMLApp * htmlapp = data;

	gtk_widget_hide(htmlapp->window);
	gtk_main_quit();
}


/* htmlapp_on_closex */
static gboolean _htmlapp_on_closex(gpointer data)
{
	HTMLApp * htmlapp = data;

	_htmlapp_on_close(htmlapp);
	return TRUE;
}


/* htmlapp_on_find */
static void _htmlapp_on_find(gpointer data)
{
	HTMLApp * htmlapp = data;

	surfer_find(htmlapp, NULL);
}


/* htmlapp_on_fullscreen */
static void _htmlapp_on_fullscreen(gpointer data)
{
	HTMLApp * htmlapp = data;
	GdkWindow * window;
	gboolean fullscreen;

#if GTK_CHECK_VERSION(2, 14, 0)
	window = gtk_widget_get_window(htmlapp->window);
#else
	window = htmlapp->window->window;
#endif
	fullscreen = (gdk_window_get_state(window)
			& GDK_WINDOW_STATE_FULLSCREEN)
		== GDK_WINDOW_STATE_FULLSCREEN;
	surfer_set_fullscreen(htmlapp, !fullscreen);
}


/* htmlapp_on_open */
static void _htmlapp_on_open(gpointer data)
{
	HTMLApp * htmlapp = data;

	_htmlapp_open_dialog(htmlapp);
}


/* error */
static int _error(char const * message, int ret)
{
	fputs(PROGNAME_HTMLAPP ": ", stderr);
	perror(message);
	return ret;
}


/* usage */
static int _usage(void)
{
	fprintf(stderr, _("Usage: %s [-Jj] [URL]\n"
"  -J	Disable Javascript\n"
"  -j	Enable Javascript (default)\n"), PROGNAME_HTMLAPP);
	return 1;
}


/* public */
/* surfer */
/* essential */
/* surfer_new */
HTMLApp * surfer_new(char const * url)
{
	HTMLApp * htmlapp;

	if((htmlapp = object_new(sizeof(*htmlapp))) == NULL)
		return NULL;
	return htmlapp;
}


/* surfer_delete */
void surfer_delete(HTMLApp * htmlapp)
{
	object_delete(htmlapp);
}


/* accessors */
/* surfer_get_view */
GtkWidget * surfer_get_view(Surfer * surfer)
{
	/* FIXME remove from the API? */
	return surfer->view;
}


/* surfer_set_enable_javascript */
void surfer_set_enable_javascript(Surfer * surfer, gboolean enable)
{
	ghtml_set_enable_javascript(surfer->view, enable);
}


/* surfer_set_favicon */
void surfer_set_favicon(Surfer * surfer, GdkPixbuf * pixbuf)
{
	/* FIXME implement */
}


/* surfer_set_fullscreen */
void surfer_set_fullscreen(Surfer * surfer, gboolean fullscreen)
{
	HTMLApp * htmlapp = surfer;

	if(fullscreen)
		gtk_window_fullscreen(GTK_WINDOW(htmlapp->window));
	else
		gtk_window_unfullscreen(GTK_WINDOW(htmlapp->window));
}


/* surfer_set_location */
void surfer_set_location(Surfer * surfer, char const * location)
{
	/* FIXME implement? */
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, location);
#endif
}


/* surfer_set_progress */
void surfer_set_progress(Surfer * surfer, gdouble fraction)
{
	/* FIXME implement? */
}


/* surfer_set_security */
void surfer_set_security(Surfer * surfer, SurferSecurity security)
{
	/* FIXME implement? */
}


/* surfer_set_status */
void surfer_set_status(Surfer * surfer, char const * status)
{
	/* FIXME really implement? */
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, status);
#endif
}


/* surfer_set_title */
void surfer_set_title(Surfer * surfer, char const * title)
{
	HTMLApp * htmlapp = surfer;
	gchar * t;

	t = g_strdup_printf("%s%s%s", _("HTML Application"),
			(title != NULL) ? " - " : "",
			(title != NULL) ? title : "");
	gtk_window_set_title(GTK_WINDOW(htmlapp->window), t);
	g_free(t);
}


/* useful */
/* surfer_confirm */
int surfer_confirm(Surfer * surfer, char const * message, gboolean * confirmed)
{
	HTMLApp * htmlapp = surfer;
	int ret = 0;
	GtkWidget * dialog;
	int res;

	dialog = gtk_message_dialog_new((htmlapp != NULL)
			? GTK_WINDOW(htmlapp->window) : NULL,
			GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
#if GTK_CHECK_VERSION(2, 6, 0)
			"%s", _("Question"));
	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
#endif
			"%s", message);
	gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
	res = gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);
	if(res == GTK_RESPONSE_YES)
		*confirmed = TRUE;
	else if(res == GTK_RESPONSE_NO)
		*confirmed = FALSE;
	else
		ret = 1;
	return ret;
}


/* surfer_console_message */
void surfer_console_message(Surfer * surfer, char const * message,
		char const * source, long line)
{
	/* FIXME really implement */
	fprintf(stderr, "%s: %s:%ld: %s\n", PROGNAME_HTMLAPP, source, line,
			message);
}


/* surfer_copy */
void surfer_copy(Surfer * surfer)
{
	/* FIXME really implement */
	ghtml_copy(surfer->view);
}


/* surfer_download */
int surfer_download(Surfer * surfer, char const * url, char const * suggested)
{
	/* FIXME really implement */
	return 0;
}


/* surfer_error */
int surfer_error(Surfer * surfer, char const * message, int ret)
{
	HTMLApp * htmlapp = surfer;
	GtkWidget * dialog;

	dialog = gtk_message_dialog_new((htmlapp != NULL)
			? GTK_WINDOW(htmlapp->window) : NULL,
			GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
#if GTK_CHECK_VERSION(2, 6, 0)
			"%s", _("Error"));
	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
#endif
			"%s", (message != NULL) ? message : _("Unknown error"));
	gtk_window_set_title(GTK_WINDOW(dialog), _("Error"));
	gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);
	return ret;
}


/* surfer_find */
#include "../src/common/find.c"


/* surfer_go_back */
gboolean surfer_go_back(Surfer * surfer)
{
	return ghtml_go_back(surfer->view);
}


/* surfer_go_forward */
gboolean surfer_go_forward(Surfer * surfer)
{
	return ghtml_go_forward(surfer->view);
}


/* surfer_open */
void surfer_open(Surfer * surfer, char const * url)
{
}


/* surfer_open_tab */
void surfer_open_tab(Surfer * surfer, char const * url)
{
	surfer_open(surfer, url);
}


/* surfer_prompt */
int surfer_prompt(Surfer * surfer, char const * message,
		char const * default_value, char ** value)
{
	HTMLApp * htmlapp = surfer;
	int ret = 0;
	GtkWidget * dialog;
	GtkWidget * vbox;
	GtkWidget * entry;
	int res;

	dialog = gtk_message_dialog_new((htmlapp != NULL)
			? GTK_WINDOW(htmlapp->window) : NULL,
			GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
#if GTK_CHECK_VERSION(2, 6, 0)
			"%s", _("Question"));
	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
#endif
			"%s", message);
	gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
#if GTK_CHECK_VERSION(2, 14, 0)
	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
#else
	vbox = GTK_DIALOG(dialog)->vbox;
#endif
	entry = gtk_entry_new();
	if(default_value != NULL)
		gtk_entry_set_text(GTK_ENTRY(entry), default_value);
	gtk_widget_show(entry);
	gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
	if((res = gtk_dialog_run(GTK_DIALOG(dialog))) == GTK_RESPONSE_OK)
		*value = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
	if(res != GTK_RESPONSE_OK || value == NULL)
		ret = 1;
	gtk_widget_destroy(dialog);
	return ret;
}


/* surfer_print */
void surfer_print(Surfer * surfer)
{
	ghtml_print(surfer->view);
}


/* surfer_refresh */
void surfer_refresh(Surfer * surfer)
{
	ghtml_refresh(surfer->view);
}


/* surfer_resize */
void surfer_resize(Surfer * surfer, gint width, gint height)
{
	HTMLApp * htmlapp = surfer;

	gtk_window_resize(GTK_WINDOW(htmlapp->window), width, height);
}


/* surfer_save_dialog */
void surfer_save_dialog(Surfer * surfer)
{
}


/* surfer_select_all */
void surfer_select_all(Surfer * surfer)
{
	ghtml_select_all(surfer->view);
}


/* surfer_show_console */
void surfer_show_console(Surfer * surfer, gboolean show)
{
}


/* surfer_show_location */
void surfer_show_location(Surfer * surfer, gboolean show)
{
}


/* surfer_show_menubar */
void surfer_show_menubar(Surfer * surfer, gboolean show)
{
}


/* surfer_show_statusbar */
void surfer_show_statusbar(Surfer * surfer, gboolean show)
{
}


/* surfer_show_toolbar */
void surfer_show_toolbar(Surfer * surfer, gboolean show)
{
}


/* surfer_show_window */
void surfer_show_window(Surfer * surfer, gboolean show)
{
}


/* surfer_view_source */
void surfer_view_source(Surfer * surfer)
{
}


/* surfer_warning */
void surfer_warning(Surfer * surfer, char const * message)
{
	fprintf(stderr, "%s: %s\n", PROGNAME_HTMLAPP, message);
}


/* htmlapp */
/* main */
int main(int argc, char * argv[])
{
	int o;
	HTMLApp * htmlapp;
	gboolean javascript = TRUE;

	if(setlocale(LC_ALL, "") == NULL)
		_error("setlocale", 1);
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
#if defined(WITH_GTKHTML) || defined(WITH_GTKTEXTVIEW) || defined(WITH_WEBKIT)
	if(g_thread_supported() == FALSE)
		g_thread_init(NULL);
#endif
	gtk_init(&argc, &argv);
	while((o = getopt(argc, argv, "jJ")) != -1)
		switch(o)
		{
			case 'J':
				javascript = FALSE;
				break;
			case 'j':
				javascript = TRUE;
				break;
			default:
				return _usage();
		}
	if(optind != argc && (optind + 1) != argc)
		return _usage();
	if((htmlapp = _htmlapp_new()) == NULL)
		return 2;
	surfer_set_enable_javascript(htmlapp, javascript);
	if(argv[optind] != NULL)
		_htmlapp_open(htmlapp, argv[optind]);
	else
		_htmlapp_open_dialog(htmlapp);
	gtk_main();
	_htmlapp_delete(htmlapp);
	return 0;
}
