/* ====================================================================
 * FTD4Linux Software License, Version 1.0
 *
 * Copyright (c) 2000-2004 The FTD4Linux Development Team.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        FTD4Linux Development Team (http://www.ftd4linux.2y.net/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "FTD" and "FTD4Linux" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact alguno@ftd4linux.2y.net.
 *
 * 5. Products derived from this software may not be called "FTD4Linux",
 *    nor may "FTD4Linux" appear in their name, without prior written
 *    permission of the FTD4Linux Development Team.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE FTD4LINUX DEVELOPMENT TEAM OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gtkhtml/gtkhtml.h>
#include <gtkhtml/gtkhtml-stream.h>

#include "ftd4linux.h"
#include "ftd_gui_browser.h"

static void load_finished(GtkWidget *caller, gpointer data);
static gboolean open_uri (GtkWidget *embed, const char *uri, gpointer data);

struct _url_request_data
{
    char *url;
    GtkHTML *html;
    GtkHTMLStream *stream;
};

static gpointer
url_requested_background(gpointer data)
{
    struct _url_request_data *request_data = (struct _url_request_data *) data;
    char *filename = NULL;
    int fd;
    int size;
    char buf[1024];
    char *tmp;
    FtdString *normal_filename = NULL, *orig_filename = NULL;
    FtdErrorCode retval;

    retval = ftd_http_curl_execute(request_data->url, NULL, FALSE, NULL, FALSE, &filename, TRUE);
    if (retval != ERROR_NONE) {
        gtk_widget_unref(GTK_WIDGET(request_data->html));
        g_free(request_data->url);
        g_free(request_data);

        gtk_html_stream_close(request_data->stream, GTK_HTML_STREAM_ERROR);
        return NULL;
    }

    if (filename == NULL) {
        gtk_widget_unref(GTK_WIDGET(request_data->html));
        g_free(request_data->url);
        g_free(request_data);

        gtk_html_stream_close(request_data->stream, GTK_HTML_STREAM_ERROR);

        return NULL;
    }

#ifdef WIN32
    fd = _open(filename, _O_RDONLY | _O_BINARY);
#else
    OPEN(fd, filename, O_RDONLY);
#endif

    if (fd == -1) {
        if (filename) {
            g_print("Unable to open %s\n", filename);
        }

        gtk_html_stream_close(request_data->stream, GTK_HTML_STREAM_ERROR);

        gtk_widget_unref(GTK_WIDGET(request_data->html));
        g_free(request_data->url);
        g_free(request_data);

        return;
    }

    while ((size = read(fd, buf, 1024)) > 0) {
        gtk_html_stream_write(request_data->stream, buf, size);
    }

    gtk_html_stream_close(request_data->stream, size == -1 ? GTK_HTML_STREAM_ERROR : GTK_HTML_STREAM_OK);

    close(fd);

    if (filename) {
        unlink(filename);
        g_free(filename);
    }

    gtk_widget_unref(GTK_WIDGET(request_data->html));
    g_free(request_data->url);
    g_free(request_data);
}

static void
url_requested (GtkHTML *html, const char *url, GtkHTMLStream *stream, gpointer data)
{
    char *filename = NULL;
    int fd;
    int size;
    char buf[1024];
    char *tmp;
    FtdString *normal_filename = NULL, *orig_filename = NULL;
    FtdGuiBrowserData *browser_data = (FtdGuiBrowserData *) data;

    // Moeten we de url van internet opvragen ?
    if (strstr(url, "http://")) {
        struct _url_request_data *request_data;
        GThread *thread;

        request_data = g_malloc0(sizeof(struct _url_request_data));
        request_data->url = g_strdup(url);
        request_data->stream = stream;
        request_data->html = html;
        gtk_widget_ref(GTK_WIDGET(html));

        thread = g_thread_create(url_requested_background, request_data, TRUE, NULL);
        if (!thread) {
            g_error("Aanmaken van thread mislukt\n");
        }

        browser_data->private_data = (gpointer) ftd_list_append((FtdList *) browser_data->private_data, thread);

        return;
    } else if (strstr(url, "file://")) {
        tmp = strstr(url, "file://");
        if (tmp) {
            tmp += 7;
#ifdef WIN32
            if (tmp[0] == '/') {
                tmp += 1;      // Er staat nog een extra / helemaal aan het begin
            }
#endif

            // Sommige javascript code maakt gebruik van url(file://bestandje)
            // filter een eventuele afsluitende )
            if (tmp[strlen(tmp) - 1] == ')') {
                tmp[strlen(tmp) - 1] = '\0';
            }

            // Verander %20 spaties in normale spaties
            orig_filename = ftd_string_new(tmp);
            normal_filename = ftd_string_replace(orig_filename, "%20", " ");
            ftd_string_free(orig_filename);
            tmp = ftd_string_value(normal_filename);
        }
    } else {
        tmp = (char *) url;
    }

    if (filename == NULL) {
#ifdef WIN32
        g_strdelimit(tmp, "/" , '\\');  // Verander alle / tekens in \ tekens
        OPEN(fd, tmp, O_RDONLY | O_BINARY);
#else
        OPEN(fd, tmp, O_RDONLY);
#endif
    }

    if (fd == -1) {
        g_print("Unable to open %s\n", tmp);
        gtk_html_stream_close(stream, GTK_HTML_STREAM_ERROR);
        return;
    }

    while ((size = read(fd, buf, 1024)) > 0) {
        gtk_html_stream_write(stream, buf, size);
    }

    gtk_html_stream_close(stream, size == -1 ? GTK_HTML_STREAM_ERROR : GTK_HTML_STREAM_OK);

    close(fd);

    if (normal_filename) {
        ftd_string_free(normal_filename);
    }
}

const char *
ftd_gui_browser_get_name(void)
{
    return "GtkHTML";
}

static void
copy_func(GtkWidget *caller, gpointer data)
{
    FtdGuiBrowserData *browser_data = (FtdGuiBrowserData *) data;

    gtk_html_copy(GTK_HTML(browser_data->private_widget));
}

static gboolean
button_release_func (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
    if (event->button == 3) {
        // Laad een contextmenu zien
        GtkWidget *menu = gtk_menu_new();
        GtkWidget *item;

        item = gtk_menu_item_new_with_label("Kopieren selectie");
        g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(copy_func), user_data);
        gtk_widget_show(item);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);

        gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());

        return TRUE;
    }

    return FALSE;
}

FtdGuiBrowserData *
ftd_gui_browser_new(GtkWidget *parent, gboolean is_paned)
{
    FtdGuiBrowserData *browser_data = (FtdGuiBrowserData *) g_malloc0(sizeof(FtdGuiBrowserData));

    browser_data->public_widget = gtk_scrolled_window_new (NULL, NULL);

    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (browser_data->public_widget),
                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    browser_data->private_widget = gtk_html_new();
    browser_data->private_data = NULL;      // Lijst met alle actieve threads welke afbeeldingen ophalen

    gtk_container_add(GTK_CONTAINER(browser_data->public_widget), browser_data->private_widget);

    if (is_paned) {
        gtk_paned_pack2 (GTK_PANED (parent), browser_data->public_widget, TRUE, FALSE);
    } else {
        gtk_container_add (GTK_CONTAINER (parent), browser_data->public_widget);
    }

    gtk_widget_show_all(browser_data->public_widget);

    g_signal_connect (browser_data->private_widget, "url_requested", G_CALLBACK(url_requested), browser_data);

    g_signal_connect_after (browser_data->private_widget, "load_done", G_CALLBACK(load_finished), browser_data);
    g_signal_connect (browser_data->private_widget, "link_clicked", G_CALLBACK(open_uri), browser_data);

    g_signal_connect (browser_data->private_widget, "button-release-event", G_CALLBACK(button_release_func), browser_data);

    return browser_data;
}

void
ftd_gui_browser_navigate(FtdGuiBrowserData *browser_data, const char *url)
{
    if (strncmp(url, "http://", 7) == 0) {
        FtdErrorCode retval;
        char *filename = NULL;
        gsize length;
        char *inhoud = NULL;

        retval = ftd_http_curl_execute(url, NULL, FALSE, NULL, FALSE, &filename, TRUE);

        if (retval != ERROR_NONE) {
            return;
        }

        g_file_get_contents(filename, &inhoud, &length, NULL);
        if (inhoud) {
            gtk_html_load_from_string(GTK_HTML(browser_data->private_widget), inhoud, length);
            g_free(inhoud);
        }

        unlink(filename);
        g_free(filename);
    } else {
        FtdString *inhoud = ftd_file_load(url, FALSE);
        if (inhoud) {
            gtk_html_load_from_string(GTK_HTML(browser_data->private_widget), ftd_string_value(inhoud), ftd_string_length(inhoud));
            ftd_string_free(inhoud);

//            gtk_widget_grab_focus(browser_data->private_widget);
        }
    }
}

void
ftd_gui_browser_destroy(FtdGuiBrowserData *browser_data)
{
    // Voorkom thread-problemen dus wacht eerst af totdat alle threads netjes klaar zijn
    FtdList *list = (FtdList *) browser_data->private_data;
    while (list) {
        GThread *thread = (GThread *) ftd_list_value(list);

        g_thread_join(thread);

        list = ftd_list_next(list);
    }
    ftd_list_free(browser_data->private_data);

    gtk_widget_destroy(browser_data->public_widget);
    g_free(browser_data);
}

// FIXME: Dit is echt een heel smerige oplossing die eigenlijk upstream opgelost dient te worden
// Deze functie wordt namelijk niet gedeclareerd in de .h files van GtkHTML
// Kan mogelijk problemen vooroorzaken bij platformen waarbij shared libraries alleen
// de voorgedefineerde API's exporteren (zoals Win32)
void
html_image_edit_set_url (gpointer image, const gchar *url);
HTMLObject *html_engine_get_object_by_id (HTMLEngine  *e, const gchar *id);

void
ftd_gui_browser_set_element_attribute(FtdGuiBrowserData *browser_data, const char *elementId, const char *attribute, const char *value)
{
    HTMLEngine *engine = GTK_HTML(browser_data->private_widget)->engine;
    HTMLObject *obj;

    obj = html_engine_get_object_by_id(engine, elementId);
    if (obj) {
        html_image_edit_set_url(obj, value);
    }
}

static void
load_finished(GtkWidget *caller, gpointer data)
{
    FtdGuiBrowserData *browser_data = (FtdGuiBrowserData *) data;

    if (browser_data->load_finished_cb) {
        browser_data->load_finished_cb(browser_data->public_widget, browser_data->load_finished_data);
    }
}

void
ftd_gui_browser_set_load_finished_cb(FtdGuiBrowserData *browser_data, void (*cb) (GtkWidget *caller, gpointer data), gpointer data)
{
    browser_data->load_finished_cb = cb;
    browser_data->load_finished_data = data;
}

static gboolean
open_uri (GtkWidget *embed, const char *uri, gpointer data)
{
    FtdGuiBrowserData *browser_data = (FtdGuiBrowserData *) data;

    if (browser_data->link_clicked_cb) {
        return browser_data->link_clicked_cb(browser_data->public_widget, uri, browser_data->link_clicked_data);
    }

    return TRUE;
}

void
ftd_gui_browser_set_link_clicked_cb(FtdGuiBrowserData *browser_data, gboolean (*cb) (GtkWidget *caller, const char *url, gpointer data), gpointer data)
{
    browser_data->link_clicked_cb = cb;
    browser_data->link_clicked_data = data;
}

GtkWidget *
ftd_gui_browser_get_widget(FtdGuiBrowserData *browser_data)
{
    return browser_data->public_widget;
}

