// -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-

//
//  Copyright (C) 2002-2004 Hiroyuki Ikezoe
//  Copyright (C) 2003 Takuro Ashie
//
//  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 2, 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, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include "kz-mozthumbnailcreator.h"

#include <gtkmozembed.h>
#include <gtkmozembed_internal.h>
#include <glib/gi18n.h>

#include "kz-mozthumbnailprogress.h"
#include "mozilla.h"
#include "utils.h"
#include "egg-pixbuf-thumbnail.h"
#include "kz-mozutils.h"
#include "MozillaPrivate.h"

#include <nsCOMPtr.h>
#include <nsIInterfaceRequestor.h>
#include <nsIInterfaceRequestorUtils.h>
#define MOZILLA_STRICT_API
#include <nsEmbedString.h>
#undef MOZILLA_STRICT_API
#include <nsIDOMDocument.h>
#include <nsIDocumentViewer.h>
#include <nsIWebBrowser.h>
#include <dom/nsIDOMNSDocument.h>
#include <docshell/nsIDocShell.h>
#include <nsIDOMWindow.h>
#include <nsISHistory.h>
#include <nsIHistoryEntry.h>
#include <nsISHEntry.h>
#include <nsIWebNavigation.h>
#include <nsIWebBrowserPrint.h>
#include <nsIWebBrowserSetup.h>
#include <nsIPrintSettings.h>
#include <nsIPrintSettingsService.h>
#include <nsISHistoryInternal.h>
#include <docshell/nsIDocShellTreeItem.h>
#include <nsIDocShellTreeOwner.h>
#include <nsTime.h>
#include <nsCWebBrowser.h>
#include <nsIDOM3Node.h>

typedef struct _KzMozThumbnailCreatorPrivate	KzMozThumbnailCreatorPrivate;
struct _KzMozThumbnailCreatorPrivate
{
	nsCOMPtr<nsIWebBrowser> mWebBrowser;
	nsCOMPtr<nsISHistory> mHistory;
	gboolean is_creating;
};
#define KZ_MOZ_THUMBNAIL_CREATOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_MOZ_THUMBNAIL_CREATOR, KzMozThumbnailCreatorPrivate))

static void kz_moz_thumbnail_creator_destroy       (GtkObject *object);
static void kz_moz_thumbnail_creator_realize       (GtkWidget *widget);
static void kz_moz_thumbnail_creator_unrealize     (GtkWidget *widget);
static void kz_moz_thumbnail_creator_net_stop      (GtkMozEmbed *embed);

static void kz_moz_thumbnail_creator_create_thumbnail   (KzMozThumbnailCreator *kzembed, const gchar *uri);

static KzMozThumbnailCreator *creator_single = NULL;

G_DEFINE_TYPE(KzMozThumbnailCreator, kz_moz_thumbnail_creator, GTK_TYPE_MOZ_EMBED)

static void
kz_moz_thumbnail_creator_class_init (KzMozThumbnailCreatorClass *klass)
{
	GObjectClass *gobject_class;
	GtkObjectClass *object_class;
	GtkWidgetClass *widget_class;
	GtkMozEmbedClass *moz_embed_class;

	gobject_class   = (GObjectClass *) klass;
	object_class    = (GtkObjectClass *) klass;
	widget_class    = (GtkWidgetClass *) klass;
	moz_embed_class = (GtkMozEmbedClass *) klass;

	// GtkObject signals
	object_class->destroy = kz_moz_thumbnail_creator_destroy;
 
	// widget class
	widget_class->realize         = kz_moz_thumbnail_creator_realize;
	widget_class->unrealize       = kz_moz_thumbnail_creator_unrealize;
 
	moz_embed_class->net_stop     = kz_moz_thumbnail_creator_net_stop;
	g_type_class_add_private (gobject_class, sizeof(KzMozThumbnailCreatorPrivate));
}


static void
kz_moz_thumbnail_creator_init (KzMozThumbnailCreator *creator)
{
	KzMozThumbnailCreatorPrivate *priv = KZ_MOZ_THUMBNAIL_CREATOR_GET_PRIVATE (creator);
	priv->is_creating = FALSE;
}

KzMozThumbnailCreator *
kz_moz_thumbnail_creator_new (void)
{
	KzMozThumbnailCreator *creator = KZ_MOZ_THUMBNAIL_CREATOR(g_object_new(KZ_TYPE_MOZ_THUMBNAIL_CREATOR, NULL));
	
	gtk_moz_embed_load_url(GTK_MOZ_EMBED(creator), "about:blank");
	return creator;
}

KzMozThumbnailCreator *
kz_moz_thumbnail_creator_get_instance (void)
{
	if (!creator_single)
		creator_single = kz_moz_thumbnail_creator_new();
	else
		g_object_ref(creator_single);

	return creator_single;
}

static void
kz_moz_thumbnail_creator_destroy (GtkObject *object)
{
	if (GTK_OBJECT_CLASS(kz_moz_thumbnail_creator_parent_class)->destroy)
		GTK_OBJECT_CLASS(kz_moz_thumbnail_creator_parent_class)->destroy(object);
}


static void
kz_moz_thumbnail_creator_realize (GtkWidget *widget)
{
	KzMozThumbnailCreator *creator = KZ_MOZ_THUMBNAIL_CREATOR(widget);
	if (GTK_WIDGET_CLASS(kz_moz_thumbnail_creator_parent_class)->realize)
		GTK_WIDGET_CLASS(kz_moz_thumbnail_creator_parent_class)->realize(widget);
	
	KzMozThumbnailCreatorPrivate *priv = KZ_MOZ_THUMBNAIL_CREATOR_GET_PRIVATE (creator);
	gtk_moz_embed_get_nsIWebBrowser(GTK_MOZ_EMBED(widget),
					getter_AddRefs(priv->mWebBrowser));
#if 0	
	nsCOMPtr<nsIWebBrowserSetup> setup;
	setup = do_QueryInterface(creator->priv->mWebBrowser);
	setup->SetProperty(nsIWebBrowserSetup::SETUP_ALLOW_PLUGINS, PR_FALSE);
#endif
}


static void
kz_moz_thumbnail_creator_unrealize (GtkWidget *widget)
{
	KzMozThumbnailCreator *creator = KZ_MOZ_THUMBNAIL_CREATOR(widget);

	if (GTK_WIDGET_CLASS(kz_moz_thumbnail_creator_parent_class)->unrealize)
		GTK_WIDGET_CLASS(kz_moz_thumbnail_creator_parent_class)->unrealize(widget);
}


typedef struct _SiteInfo
{
	KzMozThumbnailCreator *creator;
	gchar *uri;
} SiteInfo;

static gboolean
idle_create_thumbnail (gpointer data)
{
	SiteInfo *info = (SiteInfo*)data;

	KzMozThumbnailCreator *creator = KZ_MOZ_THUMBNAIL_CREATOR(info->creator);

	kz_moz_thumbnail_creator_create_thumbnail(creator, 
						  info->uri);
	
	g_free(info->uri);
	g_free(info);
	return FALSE;
}

static void
net_stop_proccess (KzMozThumbnailCreator *creator)
{
	KzMozThumbnailCreatorPrivate *priv = KZ_MOZ_THUMBNAIL_CREATOR_GET_PRIVATE (creator);

        nsCOMPtr<nsIDocShellTreeItem> browserAsItem;
        browserAsItem = do_QueryInterface(priv->mWebBrowser);
        if (!browserAsItem) return;

        // get the owner for that item
        nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
        browserAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
        if (!treeOwner) return;

        // get the primary content shell as an item
        nsCOMPtr<nsIDocShellTreeItem> contentItem;
        treeOwner->GetPrimaryContentShell(getter_AddRefs(contentItem));
        if (!contentItem) return;

        // QI that back to a docshell
        nsCOMPtr<nsIDocShell> DocShell;
        DocShell = do_QueryInterface(contentItem);
	
        nsCOMPtr<nsIContentViewer> contentViewer;
        nsresult rv = DocShell->GetContentViewer(getter_AddRefs(contentViewer));
        if (!NS_SUCCEEDED(rv) || !contentViewer) return;

	nsCOMPtr<nsIDOMDocument> domDoc;
        contentViewer->GetDOMDocument(getter_AddRefs(domDoc));

	//get the last modification time
	nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(domDoc);
	nsEmbedString value;
	nsDoc->GetLastModified(value);
	nsEmbedCString cValue;
	NS_UTF16ToCString(value,
			  NS_CSTRING_ENCODING_UTF8, cValue);
	nsTime lm (cValue.get(), PR_TRUE);
	GTime last_modified;
	LL_DIV(last_modified,
	       NS_STATIC_CAST(PRTime, lm), PR_USEC_PER_SEC);

	nsCOMPtr<nsIDOM3Node> domnode = do_QueryInterface(domDoc);
     	if(!domnode) return;

	nsEmbedString spec;
	domnode->GetBaseURI(spec);

	nsEmbedCString sURI;
	NS_UTF16ToCString(spec,
			  NS_CSTRING_ENCODING_UTF8, sURI);

	if (!g_ascii_strcasecmp(sURI.get(), "about:blank"))
		return;

	if ((!last_modified || (thumbnail_get_last_modified(sURI.get()) < last_modified)))
	{
		SiteInfo *info = g_new0(SiteInfo, 1);
		info->creator = creator;
		info->uri = g_strdup(sURI.get());
		g_idle_add(idle_create_thumbnail, (gpointer)info);
	}
}


static void
kz_moz_thumbnail_creator_net_stop (GtkMozEmbed *embed)
{
	KzMozThumbnailCreator *creator = KZ_MOZ_THUMBNAIL_CREATOR(embed);

	g_return_if_fail(KZ_IS_MOZ_THUMBNAIL_CREATOR(creator));

	if (((GtkMozEmbedClass *)kz_moz_thumbnail_creator_parent_class)->net_stop)
		((GtkMozEmbedClass *)kz_moz_thumbnail_creator_parent_class)->net_stop(embed);

	net_stop_proccess(creator);
}

static void
kz_moz_thumbnail_creator_create_thumbnail (KzMozThumbnailCreator *creator, const gchar *uri)
{
	nsresult rv;
	nsCOMPtr<nsIPrintSettings> options;

	KzMozThumbnailCreatorPrivate *priv = KZ_MOZ_THUMBNAIL_CREATOR_GET_PRIVATE (creator);

	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(priv->mWebBrowser, &rv));
	if (NS_FAILED(rv) || !print)
	{
		priv->is_creating = FALSE;
		return;
	}

	if (!MozillaPrivate::CreatePrintSettings(getter_AddRefs(options)))
	{
		priv->is_creating = FALSE;
		return;
	}
	
	gchar *thumb_filename;
	thumb_filename = egg_pixbuf_get_thumb_filename(uri,
						       EGG_PIXBUF_THUMB_LARGE);
	gchar *ps_name;	
	ps_name = g_strconcat(thumb_filename, ".ps", NULL);
	g_free(thumb_filename);

	options->SetPrintRange(nsIPrintSettings::kRangeSpecifiedPageRange);
	options->SetStartPageRange(1);
	options->SetEndPageRange(1);
	options->SetMarginTop(0.0);
	options->SetMarginLeft(0.0);
	options->SetMarginBottom(0.0);
	options->SetMarginRight(0.0);
	nsEmbedString printer;
	NS_CStringToUTF16(nsEmbedCString("PostScript/default"),
			  NS_CSTRING_ENCODING_UTF8, printer);
	options->SetPrinterName(printer.get());
	nsEmbedString tmp;
	NS_CStringToUTF16(nsEmbedCString(""), NS_CSTRING_ENCODING_UTF8, tmp);
	options->SetHeaderStrLeft(tmp.get());
	options->SetHeaderStrCenter(tmp.get());
	options->SetHeaderStrRight(tmp.get());
	options->SetFooterStrLeft(tmp.get());
	options->SetFooterStrCenter(tmp.get());
	options->SetFooterStrRight(tmp.get());
	options->SetPaperSize(nsIPrintSettings::kPaperSizeNativeData);

	NS_CStringToUTF16(nsEmbedCString("Letter"),
			  NS_CSTRING_ENCODING_UTF8, tmp);
	options->SetPaperName(tmp.get());
	NS_CStringToUTF16(nsEmbedCString(ps_name),
			  NS_CSTRING_ENCODING_UTF8, tmp);
	
	options->SetToFileName(tmp.get());
	options->SetPrintToFile(PR_TRUE);	
	options->SetPrintInColor(PR_TRUE);
	options->SetOrientation(nsIPrintSettings::kLandscapeOrientation);
	options->SetPrintBGImages(PR_TRUE);
	options->SetPrintBGColors(PR_TRUE);
	options->SetPrintFrameType(nsIPrintSettings::kFramesAsIs);
	options->SetShowPrintProgress(PR_FALSE);
	options->SetShrinkToFit(PR_TRUE);
	options->SetNumCopies(1);
	options->SetPrintSilent(PR_TRUE);

	PRBool printing = PR_TRUE;

	KzMozThumbnailProgressListener *aProgress = new KzMozThumbnailProgressListener(ps_name, creator);
	g_free(ps_name);
	
//	print->ExitPrintPreview();
	/* it is dangerous. */
	while (printing)
	{
		print->GetDoingPrintPreview(&printing);
		if (!printing)
		{
			print->Print(options, aProgress);
			break;
		}

		while (gtk_events_pending())
			gtk_main_iteration();
	}
}

void
kz_moz_thumbnail_creator_create_next (KzMozThumbnailCreator *creator)
{
	g_return_if_fail(KZ_IS_MOZ_THUMBNAIL_CREATOR(creator));

	KzMozThumbnailCreatorPrivate *priv = KZ_MOZ_THUMBNAIL_CREATOR_GET_PRIVATE (creator);
	nsresult rv;
        if (!priv->mWebBrowser) return;
	
	nsCOMPtr<nsIWebNavigation> ContentNav = do_QueryInterface (priv->mWebBrowser,
								   &rv);
	if (!ContentNav) return;

	gtk_moz_embed_stop_load(GTK_MOZ_EMBED(creator));

	nsCOMPtr<nsISHistory> SessionHistory;
	rv = ContentNav->GetSessionHistory (getter_AddRefs (SessionHistory));

	PRInt32 count, index;
	SessionHistory->GetCount (&count);
	SessionHistory->GetIndex (&index);	

	// remove previous page
	if (count > 2)
		SessionHistory->PurgeHistory(index);
	
	PRBool exist_next;
	ContentNav->GetCanGoForward(&exist_next);

	if (exist_next)
		ContentNav->GoForward();
	else
	{
		priv->is_creating = FALSE;
		gtk_moz_embed_load_url(GTK_MOZ_EMBED(creator), "about:blank");
	}
}

void
kz_moz_thumbnail_creator_append_queue (KzMozThumbnailCreator *creator,
				       gpointer data)
{
	g_return_if_fail(KZ_IS_MOZ_THUMBNAIL_CREATOR(creator));
	nsresult rv;
	KzMozThumbnailCreatorPrivate *priv = KZ_MOZ_THUMBNAIL_CREATOR_GET_PRIVATE (creator);
        if (!priv->mWebBrowser) return;
	
	nsCOMPtr<nsIWebNavigation> ContentNav = do_QueryInterface (priv->mWebBrowser,
								   &rv);
	if (!ContentNav) return;

	nsCOMPtr<nsISHistory> SessionHistory;
	rv = ContentNav->GetSessionHistory (getter_AddRefs (SessionHistory));
	nsCOMPtr<nsISHistoryInternal> hi = do_QueryInterface (SessionHistory);
	if (!hi) return;

	nsIHistoryEntry *he = (nsIHistoryEntry*) data;
	nsCOMPtr<nsISHEntry> she = do_QueryInterface (he);
	if (!she) return;
	
	nsCOMPtr<nsISHEntry> dest_she;
	she->Clone(getter_AddRefs(dest_she));
	
	rv = hi->AddEntry (dest_she, PR_TRUE);
	if (NS_FAILED(rv)) return;

	if (priv->is_creating) return;

	priv->is_creating = TRUE;

	PRInt32 index;
	SessionHistory->GetIndex (&index);	
	ContentNav->GotoIndex(index);
}

