/*
    Copyright (C) 2001-2003 KSVG Team
    This file is part of the KDE project

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include <krun.h>
#include <kdebug.h>
#include <tdeaction.h>
#include <tdefiledialog.h>
#include <tdelocale.h>
#include <tdetempfile.h>
#include <ksimpleconfig.h>
#include <tdeaboutapplication.h>

#include "ksvg_widget.h"
#include "ksvg_factory.h"
#include "ksvg_plugin.moc"

#include "KSVGCanvas.h"
#include "KSVGLoader.h"
#include "CanvasFactory.h"
#include "DocumentFactory.h"

#include "SVGDocument.h"
#include "SVGWindowImpl.h"
#include "SVGZoomAndPan.h"
#include "SVGDocumentImpl.h"
#include "SVGSVGElementImpl.h"

#include <tqimage.h>

using namespace KSVG;

#define DEFAULT_WIDTH 400
#define DEFAULT_HEIGHT 400
#define ZOOM_FACTOR 1.2

struct KSVGPlugin::Private
{
	KSVGWidget *window;
	KSVGPluginBrowserExtension *extension;

	TDEAction *zoomInAction;
	TDEAction *zoomOutAction;
	TDEAction *zoomResetAction;
	TDEAction *stopAnimationsAction;
	TDEAction *viewSourceAction;
	TDEAction *viewMemoryAction;
	TDEAction *aboutApp;
	TDEAction *saveToPNG;
	TDEToggleAction *fontKerningAction;
	TDEToggleAction *progressiveAction;
	TDESelectAction *renderingBackendAction;

	TQString description;

	TQPoint panPoint;
	float zoomFactor;

	SVGDocumentImpl *doc;
	KSVGCanvas *canvas;
	TQPixmap *backgroundPixmap;
	TDEAboutApplication *aboutKSVG;

	unsigned int width;
	unsigned int height;
};

KSVGPlugin::KSVGPlugin(TQWidget *wparent, const char *, TQObject *parent, const char *name, unsigned int width, unsigned int height) : KParts::ReadOnlyPart(parent, name)
{
	kdDebug(26003) << "KSVGPlugin::KSVGPlugin" << endl;
	setInstance(KSVGPluginFactory::instance());

	ksvgd = new KSVGPlugin::Private();

	ksvgd->width = width;
	ksvgd->height = height;

	ksvgd->zoomFactor = 1.0;

	ksvgd->doc = 0;

	ksvgd->window = new KSVGWidget(this, wparent, "Rendering Widget");
	connect(ksvgd->window, TQ_SIGNAL(browseURL(const TQString &)), this, TQ_SLOT(browseURL(const TQString &)));
	ksvgd->window->show();

	KParts::Part::setWidget(ksvgd->window);

	ksvgd->extension = new KSVGPluginBrowserExtension(this);

	ksvgd->backgroundPixmap = new TQPixmap(width > 0 ? width : DEFAULT_WIDTH, height > 0 ? height : DEFAULT_HEIGHT);
	ksvgd->backgroundPixmap->fill();

	ksvgd->canvas = KSVG::CanvasFactory::self()->loadCanvas(width > 0 ? width : DEFAULT_WIDTH, height > 0 ? height : DEFAULT_HEIGHT);
	if(!ksvgd->canvas)
		return;

	ksvgd->canvas->setup(ksvgd->backgroundPixmap, ksvgd->window);

	ksvgd->zoomInAction = KStdAction::zoomIn(this, TQ_SLOT(slotZoomIn()), actionCollection());
	ksvgd->zoomOutAction = KStdAction::zoomOut(this, TQ_SLOT(slotZoomOut()), actionCollection());
	ksvgd->zoomResetAction =  new TDEAction(i18n("Zoom &Reset"), "viewmag", this, TQ_SLOT(slotZoomReset()), actionCollection(), "zoom_reset");
	ksvgd->stopAnimationsAction = new TDEAction(i18n("&Stop Animations"), "process-stop", Key_Escape, this, TQ_SLOT(slotStop()), actionCollection(), "stop_anims");
	ksvgd->viewSourceAction = new TDEAction(i18n("View &Source"), "text-x-generic-template", Key_F6, this, TQ_SLOT(slotViewSource()), actionCollection(), "view_source");
	ksvgd->viewMemoryAction = new TDEAction(i18n("View &Memory"), "text-x-generic-template", Key_F7, this, TQ_SLOT(slotViewMemory()), actionCollection(), "view_memory");
	ksvgd->saveToPNG = new TDEAction(i18n("Save to PNG..."), "save", 0, this, TQ_SLOT(slotSaveToPNG()), actionCollection(), "save_to_png");
//	ksvgd->aboutApp = KStdAction::aboutApp(this, TQ_SLOT(slotAboutKSVG()), actionCollection());//, "KSVG");
	ksvgd->aboutApp = new TDEAction(i18n("About KSVG"), "image-svg+xml", 0, this, TQ_SLOT(slotAboutKSVG()), actionCollection(), "help_about_app");
	ksvgd->fontKerningAction = new TDEToggleAction(i18n("Use Font &Kerning"), "zoom-fit-best", Key_F8, this, TQ_SLOT(slotFontKerning()), actionCollection(), "font_kerning");
	ksvgd->progressiveAction = new TDEToggleAction(i18n("Use &Progressive Rendering"), "", Key_F9, this, TQ_SLOT(slotProgressiveRendering()), actionCollection(), "progressive");

	KSimpleConfig config("ksvgpluginrc", true);
	config.setGroup("Rendering");
	ksvgd->fontKerningAction->setChecked(config.readBoolEntry("FontKerning", true));
	ksvgd->progressiveAction->setChecked(config.readBoolEntry("ProgressiveRendering", true));

	ksvgd->renderingBackendAction = new TDESelectAction(i18n("Rendering &Backend"), 0, this, TQ_SLOT(slotRenderingBackend()), actionCollection(), "rendering_backend");

	TQStringList items;
	TQPtrList<KSVG::CanvasInfo> canvasList = KSVG::CanvasFactory::self()->canvasList();
	TQPtrListIterator<KSVG::CanvasInfo> it(canvasList);
	KSVG::CanvasInfo *canvasInfo = 0;
	while((canvasInfo = it.current()) != 0)
	{
		items << canvasInfo->name;
		++it;
	}

	ksvgd->renderingBackendAction->setItems(items);
	ksvgd->renderingBackendAction->setCurrentItem(KSVG::CanvasFactory::self()->itemInList(ksvgd->canvas));

	ksvgd->aboutKSVG = new TDEAboutApplication(KSVGPluginFactory::instance()->aboutData(), wparent);

	setXMLFile("ksvgplugin.rc");
}

KSVGPlugin::~KSVGPlugin()
{
	kdDebug(26003) << "KSVGPlugin::~KSVGPlugin" << endl;

	if(ksvgd->doc && ksvgd->doc->rootElement())
		ksvgd->doc->rootElement()->pauseAnimations();

	KSVG::CanvasFactory::self()->cleanup();
	delete ksvgd->extension;

	if(ksvgd->doc)
		ksvgd->doc->detach();

	delete ksvgd->canvas;
	delete ksvgd->backgroundPixmap;
	delete ksvgd;
}

SVGDocumentImpl *KSVGPlugin::docImpl()
{
	return ksvgd->doc;
}

SVGDocument KSVGPlugin::document()
{
	return SVGDocument(ksvgd->doc);
}

void KSVGPlugin::reset()
{
	if(ksvgd->canvas)
		ksvgd->canvas->reset();

	ksvgd->zoomFactor = 1;
	ksvgd->window->reset();
	ksvgd->panPoint = TQPoint(0, 0);
}

bool KSVGPlugin::openURL(const KURL &url)
{
	m_url = url;

	if(!url.prettyURL().isEmpty())
	{
		reset();

		DocumentFactory *docFactory = DocumentFactory::self();

		// when embedded, stick to passed width and height
		ksvgd->doc = docFactory->requestDocumentImpl((ksvgd->width > 0 && ksvgd->height > 0));

		ksvgd->doc->attach(ksvgd->canvas);
		ksvgd->doc->addToDocumentDict(ksvgd->doc->handle(), ksvgd->doc);
		ksvgd->doc->setReferrer(ksvgd->extension->urlArgs().metaData()["referrer"]);

		connect(ksvgd->doc, TQ_SIGNAL(finishedParsing(bool, const TQString &)), this, TQ_SLOT(slotParsingFinished(bool, const TQString &)));
		connect(ksvgd->doc, TQ_SIGNAL(finishedRendering()), this, TQ_SLOT(slotRenderingFinished()));
		connect(ksvgd->doc, TQ_SIGNAL(gotDescription(const TQString &)), this, TQ_SLOT(slotSetDescription(const TQString &)));
		connect(ksvgd->doc, TQ_SIGNAL(gotTitle(const TQString &)), this, TQ_SLOT(slotSetTitle(const TQString &)));
		connect(ksvgd->doc, TQ_SIGNAL(gotURL(const TQString &)), this, TQ_SLOT(slotGotURL(const TQString &)));
		connect(ksvgd->window, TQ_SIGNAL(redraw(const TQRect &)), this, TQ_SLOT(slotRedraw(const TQRect &)));

		ksvgd->backgroundPixmap->fill();
		bitBlt(ksvgd->window, 0, 0, ksvgd->backgroundPixmap, 0, 0, ksvgd->backgroundPixmap->width(), ksvgd->backgroundPixmap->height());
		ksvgd->zoomFactor = 1;

		emit started(0);
		ksvgd->doc->open(url);
		emit completed();
	}
	else
		return false;

	return true;
}

void KSVGPlugin::browseURL(const TQString &url)
{
	ksvgd->doc->rootElement()->pauseAnimations();
	KParts::URLArgs args;
	args.frameName = "_self";
	emit ksvgd->extension->openURLRequest(KURL(m_url, url), args);
}

void KSVGPlugin::slotRedraw(const TQRect &r)
{
	if(ksvgd->window->width() != ksvgd->backgroundPixmap->width() ||
	   ksvgd->window->height() != ksvgd->backgroundPixmap->height())
	{
		ksvgd->backgroundPixmap->resize(ksvgd->window->width(), ksvgd->window->height());

		if(ksvgd->doc && ksvgd->doc->canvas())
		{
			ksvgd->doc->canvas()->resize(ksvgd->window->width(), ksvgd->window->height());
			ksvgd->doc->canvas()->blit();
		}
	}

	bitBlt(ksvgd->window, r.x(), r.y(), ksvgd->backgroundPixmap, r.x(), r.y(), r.width(), r.height());
}

void KSVGPlugin::slotViewSource()
{
	KTempFile temp;
	*temp.textStream() << KSVGLoader::getUrl(m_url, true) << endl;
	KRun::runURL(KURL(temp.name()), "text/plain", true);
}

void KSVGPlugin::slotViewMemory()
{
	KTempFile temp;
	*temp.textStream() << ksvgd->doc->window()->printNode(*ksvgd->doc).string() << endl;
	KRun::runURL(KURL(temp.name()), "text/plain", true);
}

void KSVGPlugin::slotFontKerning()
{
	KSimpleConfig config("ksvgpluginrc", false);
	config.setGroup("Rendering");
	config.writeEntry("FontKerning", ksvgd->fontKerningAction->isChecked());

	if(ksvgd->doc)
	{
		SVGSVGElementImpl *root = ksvgd->doc->rootElement();
		if(!root)
			return;

		ksvgd->doc->canvas()->fontContext()->setKerning(ksvgd->fontKerningAction->isChecked());
		update();
	}
}

void KSVGPlugin::slotProgressiveRendering()
{
	KSimpleConfig config("ksvgpluginrc", false);
	config.setGroup("Rendering");
	config.writeEntry("ProgressiveRendering", ksvgd->progressiveAction->isChecked());
}

void KSVGPlugin::slotRenderingBackend()
{
	KSimpleConfig config("ksvgpluginrc", false);
	config.setGroup("Canvas");
	config.writeEntry("ActiveCanvas", KSVG::CanvasFactory::self()->internalNameFor(ksvgd->renderingBackendAction->currentText()));
	config.sync();
	KSVG::CanvasFactory::self()->deleteCanvas(ksvgd->canvas);
	ksvgd->canvas = KSVG::CanvasFactory::self()->loadCanvas(ksvgd->width > 0 ? ksvgd->width : DEFAULT_WIDTH, ksvgd->height > 0 ? ksvgd->height : DEFAULT_HEIGHT);
	if(!ksvgd->canvas)
		return;

	ksvgd->canvas->setup(ksvgd->backgroundPixmap, ksvgd->window);
	openURL(m_url);
}

void KSVGPlugin::slotAboutKSVG()
{
	ksvgd->aboutKSVG->show();
}

void KSVGPlugin::slotStop()
{
	if(ksvgd->doc->rootElement()->animationsPaused())
		ksvgd->doc->rootElement()->unpauseAnimations();
	else
		ksvgd->doc->rootElement()->pauseAnimations();
}

void KSVGPlugin::slotParsingFinished(bool error, const TQString &errorDesc)
{
	emit completed();

	if(error)
	{
		kdDebug(26003) << "Finished with error : " << errorDesc << endl;
		emit setStatusBarText(errorDesc);
	}
	else
		kdDebug(26003) << "Finished without errors!" << endl;
}

void KSVGPlugin::slotRenderingFinished()
{
	bitBlt(ksvgd->window, 0, 0, ksvgd->backgroundPixmap, 0, 0, ksvgd->canvas->width(), ksvgd->canvas->height());
}

void KSVGPlugin::slotZoomIn()
{
	ksvgd->zoomFactor *= ZOOM_FACTOR;
	update();
}

void KSVGPlugin::slotZoomOut()
{
	ksvgd->zoomFactor *= (1.0 / ZOOM_FACTOR);
	update();
}

void KSVGPlugin::slotZoomReset()
{
	ksvgd->zoomFactor = 1.0;
	update();
}

void KSVGPlugin::slotSaveToPNG()
{
	if(ksvgd && ksvgd->backgroundPixmap)
	{
		TQImage img = ksvgd->backgroundPixmap->convertToImage();
		TQString filename = KFileDialog::getSaveFileName();
		if(!filename.isEmpty())
			img.save(filename, "PNG");
	}
}

void KSVGPlugin::setPanPoint(const TQPoint &translate)
{
	ksvgd->panPoint = translate;
	update();
}

void KSVGPlugin::update()
{
	if(ksvgd->doc)
	{
		SVGSVGElementImpl *root = ksvgd->doc->rootElement();
		if(!root || root->zoomAndPan() != SVG_ZOOMANDPAN_MAGNIFY)
			return;

		ksvgd->backgroundPixmap->fill();

		bool zoomChanged = ksvgd->zoomFactor != root->currentScale();
		root->setCurrentScale(ksvgd->zoomFactor);
		root->setCurrentTranslate(ksvgd->panPoint);

		ksvgd->doc->syncCachedMatrices();

		if(zoomChanged)
			ksvgd->doc->canvas()->update(ksvgd->zoomFactor);
		else
			ksvgd->doc->canvas()->update(ksvgd->panPoint);

		// Fixes drawing glitches
		slotRedraw(TQRect(0, 0, ksvgd->backgroundPixmap->width(), ksvgd->backgroundPixmap->height()));
	}
}

void KSVGPlugin::slotSetDescription(const TQString &desc)
{
	ksvgd->description = desc;
	emit setStatusBarText(i18n("Description: %1").arg(desc));
}

void KSVGPlugin::slotSetTitle(const TQString &title)
{
	emit setWindowCaption(title);
}

void KSVGPlugin::slotGotURL(const TQString &text)
{
	if(text.isNull() && !ksvgd->description.isEmpty())
		emit setStatusBarText(i18n("Description: %1").arg(ksvgd->description));
	else
		emit setStatusBarText(text);
}