/*
    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 <kdebug.h>

#include <tqtimer.h>
#include <tqstringlist.h>
#include <tqdatetime.h>

#define USE_VALGRIND 0

#if USE_VALGRIND
#include <valgrind/calltree.h>
#endif

#include "SVGLength.h"

#include "SVGRectImpl.h"
#include "SVGAngleImpl.h"
#include "SVGShapeImpl.h"
#include "SVGPointImpl.h"
#include "SVGNumberImpl.h"
#include "SVGMatrixImpl.h"

#include "SVGEventImpl.h"
#include "SVGAElementImpl.h"
#include "SVGDocumentImpl.h"
#include "SVGViewSpecImpl.h"
#include "SVGTransformImpl.h"
#include "SVGSVGElementImpl.h"
#include "SVGAnimatedRectImpl.h"
#include "SVGTransformListImpl.h"
#include "SVGAnimatedLengthImpl.h"
#include "SVGAnimatedStringImpl.h"
#include "SVGPreserveAspectRatioImpl.h"
#include "SVGAnimatedTransformListImpl.h"
#include "SVGAnimatedPreserveAspectRatioImpl.h"

#include "CanvasItem.h"
#include "KSVGCanvas.h"

using namespace KSVG;

#include "SVGSVGElementImpl.lut.h"
#include "ksvg_scriptinterpreter.h"
#include "ksvg_bridge.h"
#include "ksvg_ecma.h"

SVGSVGElementImpl::SVGSVGElementImpl(DOM::ElementImpl *impl) : SVGContainerImpl(impl), SVGTestsImpl(), SVGLangSpaceImpl(), SVGExternalResourcesRequiredImpl(), SVGStylableImpl(this), SVGLocatableImpl(), SVGFitToViewBoxImpl(), SVGZoomAndPanImpl()
{
	KSVG_EMPTY_FLAGS

	m_x = new SVGAnimatedLengthImpl(LENGTHMODE_WIDTH, this);
	m_x->ref();

	m_y = new SVGAnimatedLengthImpl(LENGTHMODE_HEIGHT, this);
	m_y->ref();

	m_width = new SVGAnimatedLengthImpl(LENGTHMODE_WIDTH, this);
	m_width->ref();

	m_height = new SVGAnimatedLengthImpl(LENGTHMODE_HEIGHT, this);
	m_height->ref();

	m_viewport = SVGSVGElementImpl::createSVGRect();

	m_currentTranslate = SVGSVGElementImpl::createSVGPoint();

	m_currentView = new SVGViewSpecImpl();
	m_currentView->ref();

	m_currentScale = 1.0;

	m_useCurrentView = false;

	m_clip[0] = 0;
	m_clip[1] = 0;
	m_clip[2] = 0;
	m_clip[3] = 0;

	m_rootParentScreenCTM = 0;

	m_localMatrix = SVGSVGElementImpl::createSVGMatrix();
}

SVGSVGElementImpl::~SVGSVGElementImpl()
{
	if(m_x)
		m_x->deref();
	if(m_y)
		m_y->deref();
	if(m_width)
		m_width->deref();
	if(m_height)
		m_height->deref();
	if(m_viewport)
		m_viewport->deref();
	if(m_currentTranslate)
		m_currentTranslate->deref();
	if(m_currentView)
		m_currentView->deref();
	if(m_rootParentScreenCTM)
		m_rootParentScreenCTM->deref();
	if(m_localMatrix)
		m_localMatrix->deref();
}

bool SVGSVGElementImpl::isRootElement() const
{
	return ownerDoc()->rootElement() == this;
}

void SVGSVGElementImpl::setAttributes()
{
	SVGElementImpl::setAttributes();

	// Spec: if not specified, effect is as if a value of "0" were specified
	if(KSVG_TOKEN_NOT_PARSED(X))
		KSVG_SET_ALT_ATTRIBUTE(X, "0")

	// Spec: if not specified, effect is as if a value of "0" were specified
	if(KSVG_TOKEN_NOT_PARSED(Y))
		KSVG_SET_ALT_ATTRIBUTE(Y, "0")

	// Spec: If the attribute is not specified, the effect is as if a value of "100%" were specified.
	if(KSVG_TOKEN_NOT_PARSED(Width))
		KSVG_SET_ALT_ATTRIBUTE(Width, "100%")

	// Spec: If the attribute is not specified, the effect is as if a value of "100%" were specified.
	if(KSVG_TOKEN_NOT_PARSED(Height))
		KSVG_SET_ALT_ATTRIBUTE(Height, "100%")

	// Spec: The contentScriptType should default to "text/ecmascript".
	if(KSVG_TOKEN_NOT_PARSED(ContentScriptType))
		KSVG_SET_ALT_ATTRIBUTE(ContentScriptType, "text/ecmascript")

	// Spec: The contentStyleType should default to "text/css".
	if(KSVG_TOKEN_NOT_PARSED(ContentStyleType))
		KSVG_SET_ALT_ATTRIBUTE(ContentStyleType, "text/css")

	if(m_useCurrentView)
	{
		parseViewBox(m_currentView->viewBoxString().string());
		preserveAspectRatio()->baseVal()->parsePreserveAspectRatio(m_currentView->preserveAspectRatioString().string());
	}

	m_viewport->setX(x()->baseVal()->value());
	m_viewport->setY(y()->baseVal()->value());
	m_viewport->setWidth(width()->baseVal()->value());
	m_viewport->setHeight(height()->baseVal()->value());

	if(isRootElement() && ownerDoc()->parentImage() == 0)
	{
		if(ownerDoc()->canvas())
			ownerDoc()->canvas()->setViewportDimension(int(ceil(width()->baseVal()->value() * currentScale())), int(ceil(height()->baseVal()->value() * currentScale())));

		// Special case for outermost svg element:
		// We need to register our id manually, because
		// m_ownerSVGElement is 0 in SVGElementImpl::setAttributes (Niko)
		if(!id().isNull())
			addToIdMap(id().string(), this);		
	}
}

SVGAnimatedLengthImpl *SVGSVGElementImpl::x()
{
	return m_x;
}

SVGAnimatedLengthImpl *SVGSVGElementImpl::y()
{
	return m_y;
}

SVGAnimatedLengthImpl *SVGSVGElementImpl::width()
{
	return m_width;
}

SVGAnimatedLengthImpl *SVGSVGElementImpl::height()
{
	return m_height;
}

void SVGSVGElementImpl::setContentScriptType(const DOM::DOMString &contentScriptType)
{
	setAttribute("contentScriptType", contentScriptType);
}

DOM::DOMString SVGSVGElementImpl::contentScriptType() const
{
	return getAttribute("contentScriptType");
}

void SVGSVGElementImpl::setContentStyleType(const DOM::DOMString &contentStyleType)
{
	setAttribute("contentStyleType", contentStyleType);
}

DOM::DOMString SVGSVGElementImpl::contentStyleType() const
{
	return getAttribute("contentStyleType");
}

SVGRectImpl *SVGSVGElementImpl::viewport()
{
	return m_viewport;
}

SVGRectImpl *SVGSVGElementImpl::getBBox()
{
	SVGRectImpl *ret = new SVGRectImpl(getCTM()->qmatrix().invert().map(m_viewport->qrect()));
	ret->ref();
	return ret;
}

float SVGSVGElementImpl::pixelUnitToMillimeterX() const
{
	return ownerDoc()->screenPixelsPerMillimeterX();
}

float SVGSVGElementImpl::pixelUnitToMillimeterY() const
{
	return ownerDoc()->screenPixelsPerMillimeterY();
}

float SVGSVGElementImpl::screenPixelToMillimeterX() const
{
	return pixelUnitToMillimeterX();
}

float SVGSVGElementImpl::screenPixelToMillimeterY() const
{
	return pixelUnitToMillimeterY();
}

void SVGSVGElementImpl::setUseCurrentView(bool useCurrentView)
{
	m_useCurrentView = useCurrentView;
}

bool SVGSVGElementImpl::useCurrentView() const
{
	return m_useCurrentView;
}

SVGViewSpecImpl *SVGSVGElementImpl::currentView() const
{
	return m_currentView;
}

void SVGSVGElementImpl::setCurrentScale(float currentScale)
{
	if( m_currentScale != currentScale )
	{
		m_currentScale = currentScale;
		invalidateCachedMatrices();

		if(hasEventListener(SVGEvent::ZOOM_EVENT, true))
			dispatchEvent(SVGEvent::ZOOM_EVENT, false, false);
	}
}

float SVGSVGElementImpl::currentScale() const
{
	return m_currentScale;
}

void SVGSVGElementImpl::setCurrentTranslate(const TQPoint &p)
{
	if(m_currentTranslate->x() != p.x() || m_currentTranslate->y() != p.y())
	{
		m_currentTranslate->setX(p.x());
		m_currentTranslate->setY(p.y());
		invalidateCachedMatrices();
		if(hasEventListener(SVGEvent::SCROLL_EVENT, true))
			dispatchEvent(SVGEvent::SCROLL_EVENT, false, false);
	}
}

SVGPointImpl *SVGSVGElementImpl::currentTranslate()
{
	return m_currentTranslate;
}

unsigned long SVGSVGElementImpl::suspendRedraw(unsigned long)
{
	return 0;
}

void SVGSVGElementImpl::unsuspendRedraw(unsigned long)
{
}

void SVGSVGElementImpl::unsuspendRedrawAll()
{
}

void SVGSVGElementImpl::forceRedraw()
{
#if USE_VALGRIND
	CALLTREE_ZERO_STATS();
#endif

	TQTime timer;
	timer.start();

	if(ownerDoc() && ownerDoc()->canvas())
		ownerDoc()->canvas()->update();

	kdDebug(26000) << "forceRedraw in " << timer.elapsed()/1000.0 << " seconds" << endl;

#if USE_VALGRIND
	CALLTREE_DUMP_STATS();
#endif
}

void SVGSVGElementImpl::pauseAnimations()
{
	if(!ownerDoc()->timeScheduler()->animationsPaused())
		ownerDoc()->timeScheduler()->toggleAnimations();
}

void SVGSVGElementImpl::unpauseAnimations()
{
	if(ownerDoc()->timeScheduler()->animationsPaused())
		ownerDoc()->timeScheduler()->toggleAnimations();
}

bool SVGSVGElementImpl::animationsPaused()
{
	return ownerDoc()->timeScheduler()->animationsPaused();
}

float SVGSVGElementImpl::getCurrentTime() const
{
	return ownerDoc()->timeScheduler()->elapsed();
}

void SVGSVGElementImpl::setCurrentTime(float)
{
}

DOM::NodeList SVGSVGElementImpl::getIntersectionList(SVGRectImpl *, SVGElementImpl *)
{
	// TODO : implement me
	return DOM::NodeList();
}

DOM::NodeList SVGSVGElementImpl::getEnclosureList(SVGRectImpl *rect, SVGElementImpl */*referenceElement*/)
{
	DOM::NodeList list;

	DOM::Node node = firstChild();
	for(; !node.isNull(); node = node.nextSibling())
	{
		SVGElementImpl *element = ownerDoc()->getElementFromHandle(node.handle());
		SVGShapeImpl *shape = dynamic_cast<SVGShapeImpl *>(element);
		if(shape)
		{
			if(shape->isContainer())
				// TODO : pass it on to container::getEnclosureList() which should return a NodeList
				kdDebug() << "!shape" << endl;
			else
			{
				// TODO : add the shape to list if the test succeeds
				SVGRectImpl *current = shape->getBBox();
				if(rect->qrect().contains(current->qrect(), true))
					kdDebug() << "shape : " << element->nodeName().string() << " is fully enclosed" << endl;

				current->deref();
			}
		}
	}

	return list;
}

bool SVGSVGElementImpl::checkIntersection(SVGElementImpl *element, SVGRectImpl *rect)
{
	SVGShapeImpl *shape = dynamic_cast<SVGShapeImpl *>(element);
	if(!shape)
		return false;

	SVGRectImpl *current = shape->getBBox();
	bool result = rect->qrect().intersects(current->qrect());
	current->deref();
	return result;
}

bool SVGSVGElementImpl::checkEnclosure(SVGElementImpl *element, SVGRectImpl *rect)
{
	SVGShapeImpl *shape = dynamic_cast<SVGShapeImpl *>(element);
	if(!shape)
		return false;

	SVGRectImpl *current = shape->getBBox();
	bool result = rect->qrect().contains(current->qrect());
	current->deref();
	return result;
}

void SVGSVGElementImpl::deSelectAll()
{
}

SVGNumberImpl *SVGSVGElementImpl::createSVGNumber()
{
	// Spec: Creates an SVGNumber object outside of any document
	// trees. The object is initialized to a value of zero. 
	SVGNumberImpl *ret = new SVGNumberImpl();
	ret->ref();
	return ret;
}

SVGLengthImpl *SVGSVGElementImpl::createSVGLength()
{
	// Spec: Creates an SVGLength object outside of any document
	// trees. The object is initialized to the value of 0 user units. 
	SVGLengthImpl *ret = new SVGLengthImpl();
	ret->ref();
	return ret;
}

SVGAngleImpl *SVGSVGElementImpl::createSVGAngle()
{
	// Spec: Creates an SVGAngle object outside of any document
	// trees. The object is initialized to the value 0 degrees (unitless). 
	SVGAngleImpl *ret = new SVGAngleImpl();
	ret->ref();
	return ret;
}

SVGPointImpl *SVGSVGElementImpl::createSVGPoint()
{
	// Spec: Creates an SVGPoint object outside of any document
	// trees. The object is initialized to the point (0,0) in the user coordinate system. 
	SVGPointImpl *ret = new SVGPointImpl();
	ret->ref();
	return ret;
}

SVGMatrixImpl *SVGSVGElementImpl::createSVGMatrix()
{
	// Spec: Creates an SVGMatrix object outside of any document
	// trees. The object is initialized to the identity matrix.
	SVGMatrixImpl *ret = new SVGMatrixImpl(TQWMatrix(1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F));
	ret->ref();
	return ret;
}

SVGRectImpl *SVGSVGElementImpl::createSVGRect()
{
	// Spec: Creates an SVGRect object outside of any document
	// trees. The object is initialized such that all values are set to 0 user units. 
	SVGRectImpl *ret = new SVGRectImpl();
	ret->ref();
	return ret;
}

SVGTransformImpl *SVGSVGElementImpl::createSVGTransform()
{
	// Spec: Creates an SVGTransform object outside of any document
	// trees. The object is initialized to an identity matrix transform (SVG_TRANSFORM_MATRIX). 
	SVGTransformImpl *transform = createSVGTransformFromMatrix(createSVGMatrix());

	// createSVGMatrix already ref's the matrix, the SVGTransformImpl->setMatrix
	// call also does this, prevent non deleting of the object by deref'ing (Niko)
	transform->matrix()->deref();

	return transform;
}

SVGTransformImpl *SVGSVGElementImpl::createSVGTransformFromMatrix(SVGMatrixImpl *mat)
{
	// Spec: Creates an SVGTransform object outside of any document
	// trees. The object is initialized to the given matrix transform (i.e., SVG_TRANSFORM_MATRIX). 
	SVGTransformImpl *ret = new SVGTransformImpl();
	ret->setMatrix(mat);
	ret->ref();
	return ret;
}

SVGElementImpl *SVGSVGElementImpl::getElementById(const DOM::DOMString &elementId)
{
	return m_map[elementId.string()];
}

void SVGSVGElementImpl::addToIdMap(const TQString &id, SVGElementImpl *obj)
{
	m_map.insert(id, obj);
}

SVGMatrixImpl *SVGSVGElementImpl::getCTM()
{
	return viewBoxToViewTransform(width()->baseVal()->value(), height()->baseVal()->value());
}

const SVGMatrixImpl *SVGSVGElementImpl::localMatrix()
{
	// TODO: only update the matrix when needed and just return m_localMatrix

	m_localMatrix->reset();

	if(ownerSVGElement() == 0)
	{
		if(m_rootParentScreenCTM != 0)
			m_localMatrix->copy(m_rootParentScreenCTM);

		// We're the outermost svg element.
		// Put the zoom scale and translate into the matrix.
		m_localMatrix->translate(currentTranslate()->x(), currentTranslate()->y());
		m_localMatrix->scale(currentScale());
	}

	// Apply viewport translation.
	m_localMatrix->translate(x()->baseVal()->value(), y()->baseVal()->value());

	// And viewbox.
	SVGMatrixImpl *viewboxMatrix = viewBoxToViewTransform(width()->baseVal()->value(), height()->baseVal()->value());

	m_localMatrix->multiply(viewboxMatrix);
	viewboxMatrix->deref();

	return m_localMatrix;
}

void SVGSVGElementImpl::setClip(const TQString &clip)
{
	// TODO : this routine should probably be shared between all classes that establish new viewports (Rob)
	if(!clip.startsWith("rect(") || !clip.endsWith(")"))
		return;

	TQString work = clip.mid(5, clip.length() - 6);
	TQStringList substrings = TQStringList::split(',', clip);
	TQStringList::ConstIterator it = substrings.begin();

	if(m_clip[0])
		m_clip[0]->deref();
	m_clip[0] = SVGSVGElementImpl::createSVGLength();

	if(*it != "auto")
		m_clip[0]->setValueAsString(*it);
	++it;

	if(m_clip[1])
		m_clip[1]->deref();
	m_clip[1] = SVGSVGElementImpl::createSVGLength();

	if(*it != "auto")
		m_clip[1]->setValueAsString(*it);
	++it;

	if(m_clip[2])
		m_clip[2]->deref();
	m_clip[2] = SVGSVGElementImpl::createSVGLength();

	if(*it != "auto")
		m_clip[2]->setValueAsString(*it);
	++it;

	if(m_clip[3])
		m_clip[3]->deref();
	m_clip[3] = SVGSVGElementImpl::createSVGLength();

	if(*it != "auto")
		m_clip[3]->setValueAsString(*it);
}

TQRect SVGSVGElementImpl::clip()
{
	// Get viewport in user coordinates.
	TQRect v(0, 0, m_viewport->qrect().width(), m_viewport->qrect().height());

	SVGMatrixImpl *ctm = getCTM();
	TQRect r = ctm->qmatrix().invert().mapRect(v);
	ctm->deref();

	if(m_clip[0])
		r.setTop(static_cast<int>(r.top() + m_clip[0]->value()));
	if(m_clip[1])
		r.setRight(static_cast<int>(r.right() - m_clip[1]->value()));
	if(m_clip[2])
		r.setBottom(static_cast<int>(r.bottom() - m_clip[2]->value()));
	if(m_clip[3])
		r.setLeft(static_cast<int>(r.left() + m_clip[3]->value()));

	return r;
}

void SVGSVGElementImpl::setRootParentScreenCTM(SVGMatrixImpl *screenCTM)
{
	if(m_rootParentScreenCTM != 0)
		m_rootParentScreenCTM->deref();

	m_rootParentScreenCTM = screenCTM;
	screenCTM->ref();
}

bool SVGSVGElementImpl::prepareMouseEvent(const TQPoint &p, const TQPoint &a, SVGMouseEventImpl *mev)
{
	// mop: central bool var which turns to true once the current "mouseover" element has been found
	bool ret = false, dorerender = false;
	SVGElementImpl *elem = 0;
	
	SVGMatrixImpl *ctm = getCTM();
	TQPoint userA = ctm->qmatrix().invert().map(a);
	ctm->deref();

	// Just check the lastTarget once (mop)
	if(ownerDoc()->lastTarget())
	{
		elem = ownerDoc()->lastTarget();
		ret = elem->prepareMouseEvent(p, userA, mev);

		// mop: only proceed if element is not the current element anymore. rest is done in the lower part
		if(!ret)
		{
			if(elem->hasEventListener(SVGEvent::MOUSEOUT_EVENT, false))
			{
				dorerender = true;
				elem->setMouseOver(false);
				elem->dispatchMouseEvent(SVGEvent::MOUSEOUT_EVENT, true, true, 0, mev->screenX(), mev->screenY(), mev->clientX(), mev->clientY(), mev->ctrlKey(), mev->altKey(), mev->shiftKey(), mev->metaKey(), mev->button(), elem);
			}

			if(elem->hasEventListener(SVGEvent::DOMFOCUSOUT_EVENT, false) && elem->focus())
			{
				dorerender = true;
				elem->setFocus(false);
				elem->dispatchEvent(SVGEvent::DOMFOCUSOUT_EVENT, true, true);
			}
			
			// mop: unset last target once we left it
			ownerDoc()->setLastTarget(0);
		}
	}

	// mop: DAMN...logic doesn't work :(
	// we cant use the results of the above check because something like this
	//  _________
	// | ____    |
	// ||____|   |
	// |_________|
	//
	// wouldn't work because even if the mousepointer would be in the inner rect it would still be inside the bbox
	// of the outer (assuming that the outer is the lastTarget). :(
	ret = false;

	// mop: just check for a node until the element where the mouse is over is found
	// mop: "ret" can be set from the above stuff too...
	//      find the element here and do the event stuff in the lower part..much cleaner
	CanvasItemList hits = ownerDoc()->canvas()->collisions(p, true);
	for(CanvasItemList::Iterator it = hits.begin(); it != hits.end(); ++it)
	{
		elem = (*it)->element();
		if(elem)
		{
			// Check if mouse is over a certain shape...
			// mop: once an element has been found check eventlisteners and leave immediately
			ret = elem->prepareMouseEvent(p, userA, mev);
			if(ret) break;
		}
	}

	// mop: has an element been found?
	if(ret)
	{
		int events = mev->target()->getEventListeners(false);

		// Dispatch mousemove, mousedown, mouseup and mouseclick events
		bool cancel = (mev->id() != SVGEvent::MOUSEMOVE_EVENT);

		if(events & 1 << mev->id())
		{
			mev->target()->dispatchMouseEvent(mev->id(), true, cancel, 0, mev->screenX(), mev->screenY(), mev->clientX(), mev->clientY(), mev->ctrlKey(), mev->altKey(), mev->shiftKey(), mev->metaKey(), mev->button(), elem);
			dorerender = true; // mop: if it has the event then rerender
		}

		// If a mouse "moves" over a shape, it's also "over" the shape
		if(mev->id() == SVGEvent::MOUSEMOVE_EVENT)
		{
			mev->target()->setMouseOver(true);
			if(events & 1 << SVGEvent::MOUSEOVER_EVENT)
			{
				mev->target()->dispatchMouseEvent(SVGEvent::MOUSEOVER_EVENT, true, cancel, 0, mev->screenX(), mev->screenY(), mev->clientX(), mev->clientY(), mev->ctrlKey(), mev->altKey(), mev->shiftKey(), mev->metaKey(), mev->button(), elem);
				dorerender = true;
			}

		}

		// Also send an domactivate + focusin event on mouseup
		bool dolinktest = true;
		if(mev->id() == SVGEvent::MOUSEUP_EVENT)
		{
			mev->target()->setFocus(true);

			if(events & 1 << SVGEvent::CLICK_EVENT)
			{
				dolinktest = mev->target()->dispatchEvent(SVGEvent::CLICK_EVENT, true, true);
				dorerender = true;
			}

			if(events & 1 << SVGEvent::DOMACTIVATE_EVENT)
			{
				mev->target()->dispatchEvent(SVGEvent::DOMACTIVATE_EVENT, true, true);
				dorerender = true;
			}

			if(events & 1 << SVGEvent::DOMFOCUSIN_EVENT)
			{
				mev->target()->dispatchEvent(SVGEvent::DOMFOCUSIN_EVENT, true, true);
				dorerender = true;
			}
		}

		// Hyperlink support
		SVGAElementImpl* link=0;
		if(dolinktest && !mev->defaultPrevented())
		{
			link = SVGAElementImpl::getLink(elem);
			if(link)
			{
				mev->setURL(link->href()->baseVal());
				emit ownerDoc()->gotURL(link->target()->baseVal().string());
			}
		}

		// The mouse is over a shape, so we have a target..we need to register that for a mouseout
		ownerDoc()->setLastTarget(mev->target());
	}

	// mop: all events may trigger changed style, add elements etc. this is definately needed :(
	if(dorerender)
		ownerDoc()->rerender();

	return dorerender; // mop: some kind of event has been found and executed
}

// Ecma stuff

/*
@namespace KSVG
@begin SVGSVGElementImpl::s_hashTable 23
 x							SVGSVGElementImpl::X						DontDelete|ReadOnly
 y							SVGSVGElementImpl::Y						DontDelete|ReadOnly
 width						SVGSVGElementImpl::Width					DontDelete|ReadOnly
 height						SVGSVGElementImpl::Height					DontDelete|ReadOnly
 viewport					SVGSVGElementImpl::Viewport					DontDelete|ReadOnly
 contentScriptType			SVGSVGElementImpl::ContentScriptType		DontDelete
 contentStyleType			SVGSVGElementImpl::ContentStyleType			DontDelete
 pixelUnitToMillimeterX 	SVGSVGElementImpl::PixelUnitToMillimeterX	DontDelete|ReadOnly
 pixelUnitToMillimeterY 	SVGSVGElementImpl::PixelUnitToMillimeterY	DontDelete|ReadOnly
 screenPixelToMillimeterX 	SVGSVGElementImpl::ScreenPixelToMillimeterX DontDelete|ReadOnly
 screenPixelToMillimeterY 	SVGSVGElementImpl::ScreenPixelToMillimeterY DontDelete|ReadOnly
 useCurrentView				SVGSVGElementImpl::UseCurrentView			DontDelete
 currentScale				SVGSVGElementImpl::CurrentScale				DontDelete
 currentTranslate			SVGSVGElementImpl::CurrentTranslate			DontDelete|ReadOnly
 onunload					SVGSVGElementImpl::OnUnload					DontDelete
 onerror					SVGSVGElementImpl::OnError					DontDelete
 onresize					SVGSVGElementImpl::OnResize					DontDelete
 onzoom						SVGSVGElementImpl::OnZoom					DontDelete
 onscroll					SVGSVGElementImpl::OnScroll					DontDelete
@end
@namespace KSVG
@begin SVGSVGElementImplProto::s_hashTable 29
 createSVGNumber					SVGSVGElementImpl::CreateSVGNumber				DontDelete|Function 0
 createSVGLength					SVGSVGElementImpl::CreateSVGLength				DontDelete|Function 0
 createSVGAngle						SVGSVGElementImpl::CreateSVGAngle				DontDelete|Function 0
 createSVGPoint						SVGSVGElementImpl::CreateSVGPoint				DontDelete|Function 0
 createSVGMatrix					SVGSVGElementImpl::CreateSVGMatrix				DontDelete|Function 0
 createSVGRect						SVGSVGElementImpl::CreateSVGRect				DontDelete|Function 0
 createSVGTransform					SVGSVGElementImpl::CreateSVGTransform			DontDelete|Function 0
 createSVGTransformFromMatrix		SVGSVGElementImpl::CreateSVGTransformFromMatrix	DontDelete|Function 1
 suspendRedraw						SVGSVGElementImpl::SuspendRedraw				DontDelete|Function 1
 unsuspendRedraw					SVGSVGElementImpl::UnsuspendRedraw				DontDelete|Function 1
 unsuspendRedrawAll					SVGSVGElementImpl::UnsuspendRedrawAll			DontDelete|Function 0
 forceRedraw						SVGSVGElementImpl::ForceRedraw					DontDelete|Function 0
 pauseAnimations					SVGSVGElementImpl::PauseAnimations				DontDelete|Function 0
 unpauseAnimations					SVGSVGElementImpl::UnpauseAnimations			DontDelete|Function 0
 animationsPaused					SVGSVGElementImpl::AnimationsPaused				DontDelete|Function 0
 getCurrentTime						SVGSVGElementImpl::GetCurrentTime				DontDelete|Function 0
 setCurrentTime						SVGSVGElementImpl::SetCurrentTime				DontDelete|Function 1
 getIntersectionList				SVGSVGElementImpl::GetIntersectionList			DontDelete|Function 2
 getEnclosureList					SVGSVGElementImpl::GetEnclosureList				DontDelete|Function 2
 checkIntersection					SVGSVGElementImpl::CheckIntersection			DontDelete|Function 2
 checkEnclosure						SVGSVGElementImpl::CheckEnclosure				DontDelete|Function 2
 deselectAll						SVGSVGElementImpl::DeselectAll					DontDelete|Function 0
 getElementById						SVGSVGElementImpl::GetElementById				DontDelete|Function 1
@end
*/

KSVG_IMPLEMENT_PROTOTYPE("SVGSVGElement", SVGSVGElementImplProto, SVGSVGElementImplProtoFunc)

Value SVGSVGElementImpl::getValueProperty(ExecState *exec, int token) const
{
	KSVG_CHECK_ATTRIBUTE

	switch(token)
	{
		case X:
			if(!attributeMode)
				return m_x->cache(exec);
			else
				return Number(m_x->baseVal()->value());
		case Y:
			if(!attributeMode)
				return m_y->cache(exec);
			else
				return Number(m_y->baseVal()->value());
		case Width:
			if(!attributeMode)
				return m_width->cache(exec);
			else
				return Number(m_width->baseVal()->value());
		case Height:
			if(!attributeMode)
				return m_height->cache(exec);
			else
				return Number(m_height->baseVal()->value());
		case Viewport:
			return m_viewport->cache(exec);
		case ContentScriptType:
			return String(contentScriptType().string());
		case ContentStyleType:
			return String(contentStyleType().string());
		case PixelUnitToMillimeterX:
			return Number(pixelUnitToMillimeterX());
		case PixelUnitToMillimeterY:
			return Number(pixelUnitToMillimeterY());
		case ScreenPixelToMillimeterX:
			return Number(screenPixelToMillimeterX());
		case ScreenPixelToMillimeterY:
			return Number(screenPixelToMillimeterY());
		case UseCurrentView:
			return Boolean(useCurrentView());
		case CurrentScale:
			return Number(currentScale());
		case CurrentTranslate:
			return m_currentTranslate->cache(exec);
		default:
			kdWarning() << "Unhandled token in " << k_funcinfo << " : " << token << endl;
			return Undefined();
	}
}

void SVGSVGElementImpl::putValueProperty(ExecState *exec, int token, const Value &value, int)
{
	switch(token)
	{
		case ContentScriptType:
			setContentScriptType(value.toString(exec).string());
			break;
		case ContentStyleType:
			setContentStyleType(value.toString(exec).string());
			break;
		case CurrentScale:
			m_currentScale = value.toNumber(exec);
			break;
		case X:
			x()->baseVal()->setValueAsString(value.toString(exec).qstring());
			break;
		case Y:
			y()->baseVal()->setValueAsString(value.toString(exec).qstring());
			break;
		case Width:
			width()->baseVal()->setValueAsString(value.toString(exec).qstring());
			break;
		case Height:
			height()->baseVal()->setValueAsString(value.toString(exec).qstring());
			break;
		case OnUnload:
			// Spec: only applicable to outermost 'svg' elements
			if(isRootElement())
				setEventListener(SVGEvent::UNLOAD_EVENT, ownerDoc()->createEventListener(value.toString(exec).qstring()));
			break;
		case OnError:
			setEventListener(SVGEvent::ERROR_EVENT, ownerDoc()->createEventListener(value.toString(exec).qstring()));
			break;
		case OnResize:
			// Spec: only applicable to outermost 'svg' elements
			if(isRootElement())
				setEventListener(SVGEvent::RESIZE_EVENT, ownerDoc()->createEventListener(value.toString(exec).qstring()));
			break;
		case OnZoom:
			// Spec: only applicable to outermost 'svg' elements
			if(isRootElement())
				setEventListener(SVGEvent::ZOOM_EVENT, ownerDoc()->createEventListener(value.toString(exec).qstring()));
			break;
		case OnScroll:
			// Spec: only applicable to outermost 'svg' elements
			if(isRootElement())
				setEventListener(SVGEvent::SCROLL_EVENT, ownerDoc()->createEventListener(value.toString(exec).qstring()));
			break;
		default:
			kdWarning() << k_funcinfo << "unhandled token " << token << endl;
	}
}

Value SVGSVGElementImplProtoFunc::call(ExecState *exec, Object &thisObj, const List &args)
{
	KSVG_CHECK_THIS(SVGSVGElementImpl)
	
	switch(id)
	{
		case SVGSVGElementImpl::CreateSVGNumber:
			return obj->createSVGNumber()->cache(exec);
		case SVGSVGElementImpl::CreateSVGLength:
			return obj->createSVGLength()->cache(exec);
		case SVGSVGElementImpl::CreateSVGAngle:
			return obj->createSVGAngle()->cache(exec);
		case SVGSVGElementImpl::CreateSVGPoint:
			return obj->createSVGPoint()->cache(exec);
		case SVGSVGElementImpl::CreateSVGMatrix:
			return obj->createSVGMatrix()->cache(exec);
		case SVGSVGElementImpl::CreateSVGRect:
			return obj->createSVGRect()->cache(exec);
		case SVGSVGElementImpl::CreateSVGTransform:
			return obj->createSVGTransform()->cache(exec);
		case SVGSVGElementImpl::CreateSVGTransformFromMatrix:
			return obj->createSVGTransformFromMatrix(static_cast<KSVGBridge<SVGMatrixImpl> *>(args[0].imp())->impl())->cache(exec);
		case SVGSVGElementImpl::GetElementById:
		{
			// Keep in sync with SVGDocumentImpl's version.
			Value ret;

			SVGElementImpl *element = obj->getElementById(args[0].toString(exec).string());

			if(element)
				ret = getDOMNode(exec, *element);
			else
			{
				element = obj->ownerDoc()->recursiveSearch(*(obj->ownerDoc()), args[0].toString(exec).string());
				if(!element)
					return Null();

				ret = getDOMNode(exec, *element);
			}

			return ret;
		}
		case SVGSVGElementImpl::GetCurrentTime:
			return Number(obj->getCurrentTime());
		case SVGSVGElementImpl::SetCurrentTime:
			obj->setCurrentTime(args[0].toNumber(exec));
			return Undefined();
		case SVGSVGElementImpl::DeselectAll:
			obj->deSelectAll();
			return Undefined();
		case SVGSVGElementImpl::PauseAnimations:
			obj->pauseAnimations();
			return Undefined();
		case SVGSVGElementImpl::UnpauseAnimations:
			obj->unpauseAnimations();
			return Undefined();
		case SVGSVGElementImpl::AnimationsPaused:
			return Boolean(obj->animationsPaused());
		default:
			kdWarning() << "Unhandled function id in " << k_funcinfo << " : " << id << endl;
			break;	
	}
	
	return Undefined();
}
