/*
 * Copyright (C) 2007 Holger Hans Peter Freyther
 *
 * 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.  Neither the name of Apple, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS 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 APPLE 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 "config.h"

#include "webkitgtkpage.h"
#include "webkitgtk-marshal.h"
#include "webkitgtkprivate.h"

#include "NotImplemented.h"
#include "ChromeClientGdk.h"
#include "ContextMenuClientGdk.h"
#include "EditorClientGdk.h"
#include "EventHandler.h"
#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "GraphicsContext.h"
#include "InspectorClientGdk.h"
#include "FrameLoader.h"
#include "FrameView.h"
#include "PlatformKeyboardEvent.h"
#include "PlatformWheelEvent.h"
#include "SubstituteData.h"


using namespace WebCore;
using namespace WebKitGtk;

extern "C" {

enum {
    /* normal signals */
    LOAD_STARTED,
    LOAD_PROGRESS_CHANGED,
    LOAD_FINISHED,
    TITLE_CHANGED,
    HOVERING_OVER_LINK,
    STATUS_BAR_TEXT_CHANGED,
    ICOND_LOADED,
    SELECTION_CHANGED,
    LAST_SIGNAL
};

static guint webkit_gtk_page_signals[LAST_SIGNAL] = { 0, };

G_DEFINE_TYPE(WebKitGtkPage, webkit_gtk_page, GTK_TYPE_LAYOUT)

static gboolean webkit_gtk_page_expose_event(GtkWidget* widget, GdkEventExpose* event)
{
    Frame* frame = core(getFrameFromPage(WEBKIT_GTK_PAGE(widget)));
    GdkRectangle clip;
    gdk_region_get_clipbox(event->region, &clip);
    cairo_t* cr = gdk_cairo_create(event->window);
    GraphicsContext ctx(cr);
    ctx.setGdkExposeEvent(event);
    if (frame->renderer()) {
        if (frame->view()->needsLayout())
            frame->view()->layout();
        IntRect rect(clip.x, clip.y, clip.width, clip.height);
        frame->view()->paint(&ctx, rect);
    }
    cairo_destroy(cr);

    return FALSE;
}

static gboolean webkit_gtk_page_key_event(GtkWidget* widget, GdkEventKey* event)
{
    Frame* frame = core(getFrameFromPage(WEBKIT_GTK_PAGE(widget)));
    frame->eventHandler()->keyEvent(PlatformKeyboardEvent(event));
    return FALSE;
}

static gboolean webkit_gtk_page_button_event(GtkWidget* widget, GdkEventButton* event)
{
    Frame* frame = core(getFrameFromPage(WEBKIT_GTK_PAGE(widget)));

    if (event->type == GDK_BUTTON_RELEASE)
        frame->eventHandler()->handleMouseReleaseEvent(PlatformMouseEvent(event));
    else
        frame->eventHandler()->handleMousePressEvent(PlatformMouseEvent(event));

    return FALSE;
}

static gboolean webkit_gtk_page_motion_event(GtkWidget* widget, GdkEventMotion* event)
{
    Frame* frame = core(getFrameFromPage(WEBKIT_GTK_PAGE(widget)));
    frame->eventHandler()->mouseMoved(PlatformMouseEvent(event));
    return FALSE;
}

static gboolean webkit_gtk_page_scroll_event(GtkWidget* widget, GdkEventScroll* event)
{
    Frame* frame = core(getFrameFromPage(WEBKIT_GTK_PAGE(widget)));

    PlatformWheelEvent wheelEvent(event);
    frame->eventHandler()->handleWheelEvent(wheelEvent);
    return FALSE;
}

static void webkit_gtk_page_size_allocate(GtkWidget* widget, GtkAllocation* allocation)
{
    GTK_WIDGET_CLASS(webkit_gtk_page_parent_class)->size_allocate(widget,allocation);

    Frame* frame = core(getFrameFromPage(WEBKIT_GTK_PAGE(widget)));
    frame->view()->resize(allocation->width, allocation->height);
    frame->forceLayout();
    frame->view()->adjustViewSize();
    frame->sendResizeEvent();
}


static void webkit_gtk_page_realize(GtkWidget* widget)
{
    GTK_WIDGET_CLASS(webkit_gtk_page_parent_class)->realize(widget);

    gdk_window_set_events(GTK_LAYOUT(widget)->bin_window,
                          (GdkEventMask)(GDK_EXPOSURE_MASK
                            | GDK_BUTTON_PRESS_MASK
                            | GDK_BUTTON_RELEASE_MASK
                            | GDK_POINTER_MOTION_MASK
                            | GDK_KEY_PRESS_MASK
                            | GDK_KEY_RELEASE_MASK
                            | GDK_BUTTON_MOTION_MASK
                            | GDK_BUTTON1_MOTION_MASK
                            | GDK_BUTTON2_MOTION_MASK
                            | GDK_BUTTON3_MOTION_MASK));
}

static void webkit_gtk_page_set_scroll_adjustments(GtkLayout* layout, GtkAdjustment* hadj, GtkAdjustment* vadj)
{
    FrameView* view = core(getFrameFromPage(WEBKIT_GTK_PAGE(layout)))->view();
    view->setGtkAdjustments(hadj, vadj);
}

static WebKitGtkFrame* webkit_gtk_page_real_create_frame(WebKitGtkPage*, WebKitGtkFrame* parent, WebKitGtkFrameData*)
{
    notImplemented();
    return 0;
}

static WebKitGtkPage* webkit_gtk_page_real_create_page(WebKitGtkPage*)
{
    notImplemented();
    return 0;
}

static WEBKIT_GTK_NAVIGATION_REQUEST_RESPONSE webkit_gtk_page_real_navigation_requested(WebKitGtkPage*, WebKitGtkFrame* frame, WebKitGtkNetworkRequest*)
{
    notImplemented();
    return WEBKIT_GTK_ACCEPT_NAVIGATION_REQUEST;
}

static gchar* webkit_gtk_page_real_choose_file(WebKitGtkPage*, WebKitGtkFrame*, const gchar* old_name)
{
    notImplemented();
    return g_strdup(old_name);
}

static void webkit_gtk_page_real_java_script_alert(WebKitGtkPage*, WebKitGtkFrame*, const gchar*)
{
    notImplemented();
}

static gboolean webkit_gtk_page_real_java_script_confirm(WebKitGtkPage*, WebKitGtkFrame*, const gchar*)
{
    notImplemented();
    return FALSE;
}

/**
 * WebKitGtkPage::java_script_prompt
 *
 * @return: NULL to cancel the prompt
 */
static gchar* webkit_gtk_page_real_java_script_prompt(WebKitGtkPage*, WebKitGtkFrame*, const gchar*, const gchar* defaultValue)
{
    notImplemented();
    return g_strdup(defaultValue);
}

static void webkit_gtk_page_real_java_script_console_message(WebKitGtkPage*, const gchar*, unsigned int, const gchar*)
{
    notImplemented();
}

static void webkit_gtk_page_finalize(GObject* object)
{
    webkit_gtk_page_stop_loading(WEBKIT_GTK_PAGE(object));

    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(WEBKIT_GTK_PAGE(object));
    //delete pageData->page; - we don't have a DragClient yet, so it is crashing here
    delete pageData->settings;
    g_object_unref(pageData->mainFrame);
    delete pageData->userAgent;

    G_OBJECT_CLASS(webkit_gtk_page_parent_class)->finalize(object);
}

static void webkit_gtk_page_class_init(WebKitGtkPageClass* pageClass)
{
    g_type_class_add_private(pageClass, sizeof(WebKitGtkPagePrivate));


    /*
     * signals
     */
    /**
     * WebKitGtkPage::load-started
     * @page: the object on which the signal is emitted
     * @frame: the frame going to do the load
     *
     * When a WebKitGtkFrame begins to load this signal is emitted.
     */
    webkit_gtk_page_signals[LOAD_STARTED] = g_signal_new("load_started",
            G_TYPE_FROM_CLASS(pageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            WEBKIT_GTK_TYPE_FRAME);

    /**
     * WebKitGtkPage::load-progress-changed
     * @page: The WebKitGtkPage
     * @progress: Global progress
     */
    webkit_gtk_page_signals[LOAD_PROGRESS_CHANGED] = g_signal_new("load_progress_changed",
            G_TYPE_FROM_CLASS(pageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__INT,
            G_TYPE_NONE, 1,
            G_TYPE_INT);
    
    webkit_gtk_page_signals[LOAD_FINISHED] = g_signal_new("load_finished",
            G_TYPE_FROM_CLASS(pageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            WEBKIT_GTK_TYPE_FRAME);

    webkit_gtk_page_signals[TITLE_CHANGED] = g_signal_new("title_changed",
            G_TYPE_FROM_CLASS(pageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL,
            NULL,
            webkit_gtk_marshal_VOID__STRING_STRING,
            G_TYPE_NONE, 2,
            G_TYPE_STRING, G_TYPE_STRING);

    webkit_gtk_page_signals[HOVERING_OVER_LINK] = g_signal_new("hovering_over_link",
            G_TYPE_FROM_CLASS(pageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL,
            NULL,
            webkit_gtk_marshal_VOID__STRING_STRING,
            G_TYPE_NONE, 2,
            G_TYPE_STRING,
            G_TYPE_STRING);

    webkit_gtk_page_signals[STATUS_BAR_TEXT_CHANGED] = g_signal_new("status_bar_text_changed",
            G_TYPE_FROM_CLASS(pageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__STRING,
            G_TYPE_NONE, 1,
            G_TYPE_STRING);

    webkit_gtk_page_signals[ICOND_LOADED] = g_signal_new("icon_loaded",
            G_TYPE_FROM_CLASS(pageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__VOID,
            G_TYPE_NONE, 0);

    webkit_gtk_page_signals[SELECTION_CHANGED] = g_signal_new("selection_changed",
            G_TYPE_FROM_CLASS(pageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__VOID,
            G_TYPE_NONE, 0);


    /*
     * implementations of virtual methods
     */
    pageClass->create_frame = webkit_gtk_page_real_create_frame;
    pageClass->create_page = webkit_gtk_page_real_create_page;
    pageClass->navigation_requested = webkit_gtk_page_real_navigation_requested;
    pageClass->choose_file = webkit_gtk_page_real_choose_file;
    pageClass->java_script_alert = webkit_gtk_page_real_java_script_alert;
    pageClass->java_script_confirm = webkit_gtk_page_real_java_script_confirm;
    pageClass->java_script_prompt = webkit_gtk_page_real_java_script_prompt;
    pageClass->java_script_console_message = webkit_gtk_page_real_java_script_console_message;

    G_OBJECT_CLASS(pageClass)->finalize = webkit_gtk_page_finalize;

    GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(pageClass);
    widgetClass->realize = webkit_gtk_page_realize;
    widgetClass->expose_event = webkit_gtk_page_expose_event;
    widgetClass->key_press_event = webkit_gtk_page_key_event;
    widgetClass->key_release_event = webkit_gtk_page_key_event;
    widgetClass->button_press_event = webkit_gtk_page_button_event;
    widgetClass->button_release_event = webkit_gtk_page_button_event;
    widgetClass->motion_notify_event = webkit_gtk_page_motion_event;
    widgetClass->scroll_event = webkit_gtk_page_scroll_event;
    widgetClass->size_allocate = webkit_gtk_page_size_allocate;

    GTK_LAYOUT_CLASS(pageClass)->set_scroll_adjustments = webkit_gtk_page_set_scroll_adjustments;
}

static void webkit_gtk_page_init(WebKitGtkPage* page)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(WEBKIT_GTK_PAGE(page));
    pageData->page = new Page(new ChromeClientGdk(page), new ContextMenuClientGdk, new EditorClientGdk(page), 0, new InspectorClientGdk);

    Settings* settings = pageData->page->settings();
    settings->setLoadsImagesAutomatically(true);
    settings->setMinimumFontSize(5);
    settings->setMinimumLogicalFontSize(5);
    settings->setShouldPrintBackgrounds(true);
    settings->setJavaScriptEnabled(true);
    settings->setDefaultFixedFontSize(14);
    settings->setDefaultFontSize(14);
    settings->setSerifFontFamily("Times New Roman");
    settings->setSansSerifFontFamily("Arial");
    settings->setFixedFontFamily("Courier");
    settings->setStandardFontFamily("Arial");

    GTK_WIDGET_SET_FLAGS(page, GTK_CAN_FOCUS);
    pageData->mainFrame = WEBKIT_GTK_FRAME(webkit_gtk_frame_new(page));
}

GtkWidget* webkit_gtk_page_new(void)
{
    WebKitGtkPage* page = WEBKIT_GTK_PAGE(g_object_new(WEBKIT_GTK_TYPE_PAGE, NULL));

    return GTK_WIDGET(page);
}

void webkit_gtk_page_set_settings(WebKitGtkPage* page, WebKitGtkSettings* settings)
{
    notImplemented();
}

WebKitGtkSettings* webkit_gtk_page_get_settings(WebKitGtkPage* page)
{
    notImplemented();
    return 0;
}

void webkit_gtk_page_go_backward(WebKitGtkPage* page)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(page);
    WebKitGtkFramePrivate* frameData = WEBKIT_GTK_FRAME_GET_PRIVATE(pageData->mainFrame);
    frameData->frame->loader()->goBackOrForward(-1);
}

void webkit_gtk_page_go_forward(WebKitGtkPage* page)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(page);
    WebKitGtkFramePrivate* frameData = WEBKIT_GTK_FRAME_GET_PRIVATE(pageData->mainFrame);
    frameData->frame->loader()->goBackOrForward(1);
}

gboolean webkit_gtk_page_can_go_backward(WebKitGtkPage* page)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(page);
    WebKitGtkFramePrivate* frameData = WEBKIT_GTK_FRAME_GET_PRIVATE(pageData->mainFrame);
    return frameData->frame->loader()->canGoBackOrForward(-1);
}

gboolean webkit_gtk_page_can_go_forward(WebKitGtkPage* page)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(page);
    WebKitGtkFramePrivate* frameData = WEBKIT_GTK_FRAME_GET_PRIVATE(pageData->mainFrame);
    return frameData->frame->loader()->canGoBackOrForward(1);
}

void webkit_gtk_page_open(WebKitGtkPage* page, const gchar* url)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(page);
    WebKitGtkFramePrivate* frameData = WEBKIT_GTK_FRAME_GET_PRIVATE(pageData->mainFrame);

    DeprecatedString string = DeprecatedString::fromUtf8(url);
    frameData->frame->loader()->load(ResourceRequest(KURL(string)));
}

void webkit_gtk_page_reload(WebKitGtkPage* page)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(page);
    WebKitGtkFramePrivate* frameData = WEBKIT_GTK_FRAME_GET_PRIVATE(pageData->mainFrame);
    frameData->frame->loader()->reload();
}

void webkit_gtk_page_load_string(WebKitGtkPage* page, const gchar* content, const gchar* contentMimeType, const gchar* contentEncoding, const gchar* baseUrl)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(page);
    WebKitGtkFramePrivate* frameData = WEBKIT_GTK_FRAME_GET_PRIVATE(pageData->mainFrame);
    
    KURL url(DeprecatedString::fromUtf8(baseUrl));
    RefPtr<SharedBuffer> sharedBuffer = new SharedBuffer(strdup(content), strlen(content));    
    SubstituteData substituteData(sharedBuffer.release(), String(contentMimeType), String(contentEncoding), KURL("about:blank"), url);

    frameData->frame->loader()->load(ResourceRequest(url), substituteData);
}

void webkit_gtk_page_load_html_string(WebKitGtkPage* page, const gchar* content, const gchar* baseUrl)
{
    webkit_gtk_page_load_string(page, content, "text/html", "UTF-8", baseUrl);
}

void webkit_gtk_page_stop_loading(WebKitGtkPage* page)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(page);
    WebKitGtkFramePrivate* frameData = WEBKIT_GTK_FRAME_GET_PRIVATE(pageData->mainFrame);
    
    if (FrameLoader* loader = frameData->frame->loader())
        loader->stopAllLoaders();
        
}

WebKitGtkFrame* webkit_gtk_page_get_main_frame(WebKitGtkPage* page)
{
    WebKitGtkPagePrivate* pageData = WEBKIT_GTK_PAGE_GET_PRIVATE(page);
    return pageData->mainFrame;
}
}
