/***************************************************************************
 *   Copyright (C) 2004 by Hans Oischinger                                 *
 *   hans.oischinger@kdemail.net                                           *
 *                                                                         *
 *   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 of the License, 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.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/
#include "svgparser.h"

// Core
#include "svgpathparserimpl.h"
#include "svghelper.h"

// Elements
#include "elements/documentelement.h"
#include "elements/vectorelement.h"
#include "elements/animateelement.h"
#include "elements/vectorpath.h"
#include "elements/animatedcolorattr.h"
#include "elements/colorattribute.h"
#include "elements/fillattribute.h"
#include "elements/strokeattribute.h"
#include "elements/animatedlengthattr.h"
#include "elements/lengthattribute.h"
#include "elements/transformlist.h"
#include "elements/animatedtransformlist.h"
#include "elements/matrix.h"
#include "elements/groupelement.h"
#include "elements/defselement.h"
#include "elements/useelement.h"
#include "elements/elementtreeiterator.h"
#include "elements/urireference.h"
#include "elements/animatednumber.h"
#include "elements/number.h"
#include "elements/timekeyedattribute.h"
#include "elements/gradient.h"
#include "elements/lineargradient.h"
#include "elements/radialgradient.h"
#include "elements/stopelement.h"
#include "elements/enumeration.h"
#include "elements/animatedenum.h"
#include "elements/timeattribute.h"

// Qt
#include <qcolor.h>
#include <qdom.h>
#include <qfile.h>
#include <qregexp.h>
#include <qstringlist.h>

// KDE
#include <kdebug.h>


SVGParser::SVGParser()
    : QObject(),
    m_rootDoc(0)
{}


SVGParser::~SVGParser()
{}


bool SVGParser::startDocument( QString path )
{
  QDomDocument doc( "mydocument" );
  QFile file( path );
  if ( !file.open( IO_ReadOnly ) )
  {
    return false;
    kdError() << "SVGParser::startDocument( " << path << " ) - Could not open file" << endl;
  }
  QString errorMsg;
  int errorLine;
  int errorColumn;
  if ( !doc.setContent( &file, &errorMsg, &errorLine, &errorColumn ) )
  {
    file.close();
    kdError() << "SVGParser::startDocument( " << path << " ) - Parse Error occured:" << endl;
    kdError() << "SVGParser::startDocument() -     " << errorMsg << endl;
    kdError() << "SVGParser::startDocument() -     in line " << errorLine << " col " << errorColumn<< endl;
    return false;
  }
  file.close();

  QDomElement docElem = doc.documentElement();

  if (docElem.tagName() == "svg")
  {
    m_rootDoc = new DocumentElement();
    unsigned int unitType;
    float valInSpecUnits = 0;
    valInSpecUnits = SVGHelper::extractLength(docElem.attributeNode("x").value(), unitType);
    m_rootDoc->x().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits);
    valInSpecUnits = SVGHelper::extractLength(docElem.attributeNode("y").value(), unitType);
    m_rootDoc->y().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits);
    valInSpecUnits = SVGHelper::extractLength(docElem.attributeNode("width").value(), unitType);
    m_rootDoc->width().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits);
    valInSpecUnits = SVGHelper::extractLength(docElem.attributeNode("height").value(), unitType);
    m_rootDoc->height().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits);

    //m_rootDoc->width().baseVal().setValue(docElem.attributeNode("width").value().toFloat());
    //m_rootDoc->height().baseVal().setValue(docElem.attributeNode("height").value().toFloat());
  }
  else
  {
    kdError() << "SVGParser::startDocument( " << path << " ) - DOM root element is not of type svg" << endl;
    return false;
  }

  QDomNode n = docElem.firstChild();
  parseElement(n, m_rootDoc, GroupElement());
  return true;
}

void SVGParser::parseElement(QDomNode n, ElementNode* parent, GroupElement inheritedStyle) const
{
  ElementNode* element = 0;
  GroupElement parentsInheritedStyle(inheritedStyle); // Copy inherited style for next() elements

  QDomElement e = n.toElement(); // try to convert the node to an element.
  if( !e.isNull() )
  {
    // kdDebug() << "SVGParser::parseElement() - Searching handler for <" << e.tagName() << "> tag " << endl; // the node really is an element.

    QString tagName = e.tagName().lower();
    if (e.tagName() == "rect")
    {
      element = createRectElement( e, inheritedStyle );
    }
    else if (tagName == "circle")
    {
      element = createCircleElement( e, inheritedStyle );
    }
    else if (tagName == "ellipse")
    {
      element = createEllipseElement( e, inheritedStyle );
    }
    else if (tagName == "line")
    {
      element = createLineElement( e, inheritedStyle );
    }
    else if (tagName == "path")
    {
      element = createPathElement( e, inheritedStyle );
    }
    else if (tagName == "polygon")
    {
      element = createPolygonElement( e, inheritedStyle );
    }
    else if (tagName == "animate" || tagName == "animatetransform" ||
             tagName == "animatecolor")
    {
      element = createAnimateElement( e );
    }
    else if (tagName == "use")
    {
      element = createUseElement( e, inheritedStyle );
    }
    else if (tagName == "g")
    {
      element = createGroupElement( e, inheritedStyle );
    }
    else if (tagName == "defs")
    {
      element = createDefsElement( e, inheritedStyle );
    }
    else if (tagName == "lineargradient" || tagName == "radialgradient" )
    {
      element = createGradientElement( e, inheritedStyle );
    }
    else if (tagName == "stop" )
    {
      element = createGradientStopElement( e, inheritedStyle );
    }
    else if (tagName == "desc" )
    {
      ; // Implemented as attribute of Element
    }
    else if (tagName == "title" )
    {
      ; // Implemented as attribute of Element
    }
    else
    {
      kdWarning() << "SVGParser::parseElement() - Error parsing tag <" << e.tagName() << "> - No matching handler defined" << endl; // the node really is an element.
    }

    if ( element )
    {
      // Set common things
      setCommonElementAttributes( e, element);
    }
  }

  if (!element)
    element = parent;   // TODO: make this safe
  else
    parent->appendChild( element );

  // Recursive iteration
  if (!n.firstChild().isNull())
    parseElement(n.firstChild(), element, inheritedStyle);

  if (!n.nextSibling().isNull())
    parseElement(n.nextSibling(), parent, parentsInheritedStyle);

}

ElementNode* SVGParser::createRectElement(QDomElement e, GroupElement& inheritedStyle) const
{
  VectorElement* element = new VectorElement();
  VectorPath &vecPath = element->vectorPath();
  if ( e.attributeNode("rx").isNull() && e.attributeNode("ry").isNull() )
    vecPath.setShapeMetaType( VectorPath::SHAPE_RECT );
  else
    vecPath.setShapeMetaType( VectorPath::SHAPE_RRECT );

  setStyleAttributes(element, e, inheritedStyle);

  unsigned int unitType;
  float valInSpecUnits = 0;
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("x").value(), unitType);
  vecPath.getX().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  vecPath.getX().baseVal().setValid( !e.attributeNode("x").isNull() );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("y").value(), unitType);
  vecPath.getY().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  vecPath.getY().baseVal().setValid( !e.attributeNode("y").isNull() );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("width").value(), unitType);
  vecPath.getWidth().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  vecPath.getWidth().baseVal().setValid( !e.attributeNode("width").isNull() );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("height").value(), unitType);
  vecPath.getHeight().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  vecPath.getHeight().baseVal().setValid( !e.attributeNode("height").isNull() );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("rx").value(), unitType);
  vecPath.getRX().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  vecPath.getRX().baseVal().setValid( !e.attributeNode("rx").isNull() );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("ry").value(), unitType);
  vecPath.getRY().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  vecPath.getRY().baseVal().setValid( !e.attributeNode("ry").isNull() );

  createTransformList(e, element->transformList());

  return element;
}

/**
 * parses a QDomElement's style attributes and transfers it to the VectorElement e
 */
bool SVGParser::setStyleAttributes(Stylable* element, QDomElement e, GroupElement& inheritedStyle) const
{
  // Work on the inherited style attributes
  FillAttribute& myFill = inheritedStyle.fill();
  StrokeAttribute& myStroke = inheritedStyle.stroke();

  // Retrieve CSS values
  if( !e.attributeNode("style").isNull() )
  {
    QStringList cssStyleList = QStringList::split(';', e.attributeNode("style").value() );
    QStringList::ConstIterator it = cssStyleList.begin();
    QStringList::ConstIterator end = cssStyleList.end();
    for(; it != end; ++it)
    {
      QStringList splitAttr = QStringList::split(':', (*it));
      splitAttr[0] = splitAttr[0].stripWhiteSpace().lower();
      //splitAttr[1] = splitAttr[1].stripWhiteSpace().lower();

      if (splitAttr[0] == "fill")
        parseFillPaint(myFill, splitAttr[1]);
      else if (splitAttr[0] == "stroke")
        parseStrokePaint(myStroke, splitAttr[1]);
      if( splitAttr[0] == "fill-opacity" )
        myFill.fillOpacity().baseVal().setValue( splitAttr[1].toDouble() );
      if( splitAttr[0] == "stroke-opacity" )
        myStroke.strokeOpacity().baseVal().setValue( splitAttr[1].toDouble() );
      if( splitAttr[0] == "stroke-width" )
        myStroke.strokeWidth().baseVal().setValue( splitAttr[1].toDouble() );
    }
  }

  // Non-CSS has precedence:
  if( !e.attributeNode("fill-opacity").isNull() )
    myFill.fillOpacity().baseVal().setValue(
      e.attributeNode("fill-opacity").value().toDouble() );
  if( !e.attributeNode("fill").isNull() )
    parseFillPaint(myFill, e.attributeNode("fill").value());
  if( !e.attributeNode("stroke-opacity").isNull() )
    myStroke.strokeOpacity().baseVal().setValue(
      e.attributeNode("stroke-opacity").value().toDouble() );
  if( !e.attributeNode("stroke-width").isNull() )
    myStroke.strokeWidth().baseVal().setValue(
      e.attributeNode("stroke-width").value().toDouble() );
  if( !e.attributeNode("stroke").isNull() )
    parseStrokePaint(myStroke, e.attributeNode("stroke").value());

  // Copy the (modified) inherited styles onto the current one
  element->fill() = inheritedStyle.fill();
  element->stroke() = inheritedStyle.stroke();

  return true;
}

void SVGParser::parseFillPaint(FillAttribute& fill, QString paint) const
{
  paint = paint.stripWhiteSpace();
  if (paint.lower() == "none")
    fill.setFillType(FillAttribute::FILL_NONE);
  else if ( paint.lower().startsWith("url(") )
  {
    // Referencing Paintserver
    QString href = paint.mid(4, paint.find(')') - 4);
    URIReference *paintServer = createRefAttribute( href, false );
    if (!paintServer)
    {
      kdWarning() << "SVGParser::parseFillPaint() - Could not find referenced element for href: " << href << endl;
    }
    else
    {
      // kdDebug() << "SVGParser::parseFillPaint() - Detected PaintServer Reference: " << paintServer->getURI() << endl;
      fill.ref() = *paintServer;
      fill.setFillType( FillAttribute::FILL_REF );
    }
  }
  else
  {
    ColorAttribute* cAttr = SVGHelper::parseColorString( paint );
    if ( !cAttr )
    {
      kdWarning() << "SVGParser::parseFillPaint() - Could not create ColorAttribute!" << endl;
      return;
    }
    fill.color().baseVal() = *cAttr;
    fill.setFillType(FillAttribute::FILL_SOLID);
    delete cAttr;
  }
}

void SVGParser::parseStrokePaint(StrokeAttribute& stroke, QString paint) const
{
  paint = paint.stripWhiteSpace();
  if (paint.lower() == "none")
    stroke.setStrokeType(StrokeAttribute::STROKE_NONE);
  else if ( paint.lower().startsWith("url(") )
  {
    // Referencing Paintserver
    QString href = paint.mid(4, paint.find(')') - 4);
    URIReference *paintServer = createRefAttribute( href, false );
    if (!paintServer)
    {
      kdWarning() << "SVGParser::parseStrokePaint() - Could not find referenced element for href: " << href << endl;
    }
    else
    {
      kdDebug() << "SVGParser::parseFillparseStrokePaint() - Detected PaintServer Reference: " << paintServer->getURI() << endl;
      stroke.ref() = *paintServer;
      stroke.setStrokeType( StrokeAttribute::STROKE_REF );
    }
  }
  else
  {
    ColorAttribute* cAttr = SVGHelper::parseColorString( paint );
    if ( !cAttr )
    {
      kdWarning() << "SVGParser::parseStrokePaint() - Could not create ColorAttribute!" << endl;
      return;
    }
    stroke.color().baseVal() = *cAttr;
    stroke.setStrokeType(StrokeAttribute::STROKE_SOLID);
    delete cAttr;
  }
}

ElementNode* SVGParser::createCircleElement(QDomElement e, GroupElement& inheritedStyle) const
{
  VectorElement* element = new VectorElement();
  VectorPath &vecPath = element->vectorPath();
  vecPath.setShapeMetaType( VectorPath::SHAPE_CIRCLE );

  setStyleAttributes(element, e, inheritedStyle);

  unsigned int unitType;
  float valInSpecUnits = 0;
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("cx").value(), unitType);
  vecPath.getX().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("cy").value(), unitType);
  vecPath.getY().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("r").value(), unitType);
  vecPath.getRadius().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );

  createTransformList(e, element->transformList());

  return element;
}

ElementNode* SVGParser::createEllipseElement(QDomElement e, GroupElement& inheritedStyle) const
{
  VectorElement* element = new VectorElement();
  VectorPath &vecPath = element->vectorPath();
  vecPath.setShapeMetaType( VectorPath::SHAPE_ELLIPSE );

  setStyleAttributes(element, e, inheritedStyle);

  unsigned int unitType;
  float valInSpecUnits = 0;
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("cx").value(), unitType);
  vecPath.getX().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("cy").value(), unitType);
  vecPath.getY().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("rx").value(), unitType);
  vecPath.getRadiusX().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("ry").value(), unitType);
  vecPath.getRadiusY().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );

  createTransformList(e, element->transformList());

  return element;
}

ElementNode* SVGParser::createLineElement(QDomElement e, GroupElement& inheritedStyle) const
{
  VectorElement* element = new VectorElement();
  VectorPath &vecPath = element->vectorPath();
  vecPath.setShapeMetaType( VectorPath::SHAPE_LINE );

  setStyleAttributes(element, e, inheritedStyle);

  unsigned int unitType;
  float valInSpecUnits = 0;
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("x1").value(), unitType);
  vecPath.getX1().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("y1").value(), unitType);
  vecPath.getY1().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("x2").value(), unitType);
  vecPath.getX2().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
  valInSpecUnits = SVGHelper::extractLength(e.attributeNode("y2").value(), unitType);
  vecPath.getY2().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );

  createTransformList(e, element->transformList());

  return element;
}

ElementNode* SVGParser::createPathElement(QDomElement e, GroupElement& inheritedStyle) const
{
  VectorElement* element = new VectorElement();
  VectorPath &vecPath = element->vectorPath();
  vecPath.setShapeMetaType( VectorPath::SHAPE_PATH ); // TODO: or polyline? what's the diff?

  setStyleAttributes(element, e, inheritedStyle);

  SVGPathParserImpl pathParser;
  if ( !e.attributeNode("d").isNull() )
    pathParser.parseSVG_D(e.attributeNode("d").value(), element, true);
  else if ( !e.attributeNode("points").isNull() )
    pathParser.parseSVG_PolyPoints( e.attributeNode("points").value(), element, true);

  createTransformList(e, element->transformList());

  return element;
}

ElementNode* SVGParser::createPolygonElement(QDomElement e, GroupElement& inheritedStyle) const
{
  VectorElement* element = new VectorElement();
  VectorPath &vecPath = element->vectorPath();
  vecPath.setShapeMetaType( VectorPath::SHAPE_POLYGON ); // TODO: or polyline? what's the diff?

  setStyleAttributes(element, e, inheritedStyle);

  SVGPathParserImpl pathParser;
  if ( !e.attributeNode("d").isNull() )
    pathParser.parseSVG_D(e.attributeNode("d").value(), element);
  else if ( !e.attributeNode("points").isNull() )
    pathParser.parseSVG_PolyPoints( e.attributeNode("points").value(), element);

  createTransformList(e, element->transformList());

  return element;
}

ElementNode* SVGParser::createUseElement(QDomElement e, GroupElement& inheritedStyle) const
{
  UseElement* element = new UseElement();
  setStyleAttributes(element, e, inheritedStyle);

  QString href = e.attributeNode("xlink:href").value();
  if (href.left(1)=="#")
    element->ref().setURI( href );
  // TODO: external links?

  ElementNode *refElem = m_rootDoc->findElementByRef( href );
  element->ref().setRefElement( refElem );

  if ( refElem == 0 )
  {
    kdWarning() << "SVGParser::createUseElement() - Referenced element " << href << " does not exist" << endl;
    delete element;
    element=0;
    return 0;
  }

  // Check for Transformations
  createTransformList(e, element->transformList());

  if ( !e.attributeNode("x").isNull() || !e.attributeNode("y").isNull() )
  {
    // Create the x,y Transformation
    unsigned int unitTypeX;
    float valInSpecUnitsX = 0;
    unsigned int unitTypeY;
    float valInSpecUnitsY = 0;

    valInSpecUnitsX = SVGHelper::extractLength(e.attributeNode("x").value(), unitTypeX);
    valInSpecUnitsY = SVGHelper::extractLength(e.attributeNode("y").value(), unitTypeY);

    LengthAttribute lengthX(valInSpecUnitsX, unitTypeX);
    LengthAttribute lengthY(valInSpecUnitsY, unitTypeY);

    Transform *translate = new Transform();
    translate->setTranslate( lengthX, lengthY );
    element->transformList().baseVal().append( translate );
  }

  // TODO: handle width and height here... according to the standard this should be a <svg width... height..>

  return element;
}

ElementNode* SVGParser::createAnimateElement(QDomElement e) const
{
  AnimateElement* animElement = 0;

  /**
   * Now check which type of animation we have here
   */
  if ( e.attributeNode("attributeName").value() == "transform" )
    animElement = createAnimateTransformElement(e);
  else   // We assume Length element
    animElement = createAnimateLengthElement(e);

  /*
    else if ( e.attributeNode("attributeName").value() == "fill" )
    {
      //TODO: make this by-compatible
      targetAttr = &(static_cast<VectorElement*>(targetElement))->fill().color();
      fromAttr = new ColorAttribute( QColor( e.attributeNode("from").value() ) );
      toAttr = new ColorAttribute( QColor( e.attributeNode("to").value() ) );
    }
  */
  if ( animElement )
  {
    // Begin
    // TODO: according to the spec begin can be a list of values...
    animElement->getStartTime().setValue(
      SVGHelper::parseClockValue(e.attributeNode("begin").value()) );

    // Duration
    if ( e.attributeNode("dur").isNull() )
      animElement->getSimpleDuration().setValue(-1);
    else
      animElement->getSimpleDuration().setValue(
        SVGHelper::parseClockValue(e.attributeNode("dur").value()) );

    //     // Repeat Duration   // TODO: Implement
    //     if ( !e.attributeNode("dur").isNull() )
    //       animElement->setRepeatDuration(
    //         SVGHelper::parseClockValue(e.attributeNode("repeatDur").value()));

    // Fill
    if ( e.attributeNode("fill").value() == "freeze" )
      animElement->setFill( AnimateElement::FILL_FREEZE );
    else if ( e.attributeNode("fill").value() == "remove" )
      animElement->setFill( AnimateElement::FILL_REMOVE );

  }
  return animElement;
}

ElementNode* SVGParser::createGroupElement(QDomElement e, GroupElement& inheritedStyle) const
{
  GroupElement* element = new GroupElement();
  createTransformList(e, element->transformList());
  setStyleAttributes(element, e, inheritedStyle);

  return element;
}

ElementNode* SVGParser::createDefsElement(QDomElement e, GroupElement& inheritedStyle) const
{
  DefsElement* element = new DefsElement();
  createTransformList(e, element->transformList());   // No need for transform here I think... whatever!
  setStyleAttributes(element, e, inheritedStyle);

  QDomNodeList descList = e.elementsByTagName("desc");
  if ( !e.elementsByTagName("desc").item(0).isNull() )
  {
    QDomElement descElem = descList.item(0).toElement();
    if( !e.isNull() )
      element->setDesc( descElem.text() );
  }

  QDomNodeList titleList = e.elementsByTagName("title");
  if ( !e.elementsByTagName("title").item(0).isNull() )
  {
    QDomElement titleElem = titleList.item(0).toElement();
    if( !e.isNull() )
      element->setTitle( titleElem.text() );
  }

  return element;
}

void SVGParser::createTransformList(QDomElement e, AnimatedTransformList& aList) const
{
  SVGHelper::parseTransformAttribute(&aList.baseVal(), e.attributeNode("transform").value());
}



void SVGParser::setCommonElementAttributes(QDomElement domElement, ElementNode* element) const
{
  if (!domElement.attributeNode("id").isNull())
    element->setId(domElement.attributeNode("id").value());

  QDomNodeList descList = domElement.elementsByTagName("desc");
  if ( !domElement.elementsByTagName("desc").item(0).isNull() )
  {
    QDomElement descElem = descList.item(0).toElement();
    element->setDesc( descElem.text() );
  }

  QDomNodeList titleList = domElement.elementsByTagName("title");
  if ( !domElement.elementsByTagName("title").item(0).isNull() )
  {
    QDomElement testElem = titleList.item(0).toElement();
    element->setTitle( testElem.text() );
  }

  // This is only important for visible elements but we do the check anyway
  if( !domElement.attributeNode("display").isNull() )
    if ( domElement.attributeNode("display").value() == "none" )
      element->setState( Element::hidden );
  if( !domElement.attributeNode("visibility").isNull() )
    if ( domElement.attributeNode("visibility").value() == "hidden" )
      element->setState( Element::hidden );
}


/**
 * Get the target Element
 */
URIReference* SVGParser::createRefAttribute( const QString& uri, bool referenceParentIfInvalid ) const
{
  URIReference *ref = 0;
  ElementNode* targetElement = 0;

  if ( !uri.isEmpty() )
  {
    targetElement = m_rootDoc->findElementByRef( uri );
    if (!targetElement)
    {
      kdWarning() << "SVGHelper::createAnimateElement() - Referenced element " << uri << " does not exist" << endl;
      return 0;
    }

    ref = new URIReference(0,referenceParentIfInvalid);
    ref->setURI(uri);
    ref->setRefElement( targetElement );
  }
  else
  {
    // If undefined set refID to QString::null
    ref = new URIReference(0,referenceParentIfInvalid);
  }

  return ref;
}

AnimateElement * SVGParser::createAnimateTransformElement( QDomElement e ) const
{
  URIReference* ref = 0;
  ref = createRefAttribute( e.attributeNode("xlink:href").value(), true );
  if (!ref)
  {
    kdWarning() << "SVGHelper::createAnimateTransformElement() - Could not find referenced element!!!" << endl;
    return 0;
  }

  // Create the AnimateTransformElement
  Attribute* targetAttr = ref->getRefElement()->getAttrByName( e.attributeNode("attributeName").value() );

  if (!targetAttr)
  {
    kdWarning() << "SVGHelper::createAnimateTransformElement() - Could not find referenced Attribute!!!" << endl;
    return 0;
  }
  AnimatedTransformList* concreteTargetAttr = dynamic_cast<AnimatedTransformList*>(targetAttr);
  if ( !concreteTargetAttr )
  {
    kdWarning() << "SVGHelper::createAnimateTransformElement() - Referenced Attribute is not of expected type AnimatedTransformList!!!" << endl;
    return 0;
  }

  AnimateTransformElement* animTransElement = new AnimateTransformElement( *ref, concreteTargetAttr );
  delete ref;
  Transform fromAttr;
  Transform byAttr;
  Transform toAttr;

  // Set it's from, by and to values
  if ( !e.attributeNode("type").isNull() )
  {
    QString fromStr, byStr, toStr;

    if ( !e.attributeNode("values").isNull() )
    {
      QStringList values = QStringList::split ( ";", e.attributeNode("values").value() );

      if ( !e.attributeNode("keyTimes").isNull() )
      {
        // handle KeyTimes
        QStringList timeValues = QStringList::split ( ";", e.attributeNode("keyTimes").value() );
        if ( timeValues.count() != values.count() )
        {
          kdWarning() << "SVGHelper::createAnimateTransformElement() - Syntax error: Different numbers of KeyTimes and values!" << endl;
          return 0;
        }
        for (unsigned int i=0; i<values.count(); ++i)
        {
          Transform *trans = new Transform();
          TimekeyedTransform *keyTrans = new TimekeyedTransform(timeValues[i].toDouble(), trans);
          SVGHelper::setTransform(&keyTrans->attribute(), e.attributeNode("type").value(), values[i] );
          animTransElement->values().append(keyTrans);
        }
      }
      else
      {
        // No KeyTimes... Values are from, by, to
        fromStr = values[0];
        byStr = values[1];
        toStr = values[2];
      }
    }
    else
    {
      fromStr = e.attributeNode("from").value();
      toStr = e.attributeNode("to").value();
      byStr = e.attributeNode("by").value();
    }

    if (fromStr.isNull() && animTransElement->values().isEmpty())
    {
      kdWarning() << "SVGHelper::createAnimateTransformElement() - No From Attr or values list given" << endl;
      return 0;
    }
    SVGHelper::setTransform(&fromAttr, e.attributeNode("type").value(), fromStr );
    if (!byStr.isNull())
      SVGHelper::setTransform(&byAttr, e.attributeNode("type").value(), byStr );
    if (!toStr.isNull())
      SVGHelper::setTransform(&toAttr, e.attributeNode("type").value(), toStr );
    if ( toStr.isNull() && byStr.isNull() && animTransElement->values().count() < 2 )
    {
      kdWarning() << "SVGHelper::createAnimateTransformElement() - No To or By Attr given" << endl;
      return 0;
    }
  }
  else
  {
    kdWarning() << "SVGHelper::createAnimateLengthElement() - Invalid Animate Syntax" << endl;
    return 0;
  }

  /**
   * Finally set the animTransElement's from, by and to values
   */
  animTransElement->fromAttribute() = fromAttr;
  animTransElement->byAttribute() = byAttr;
  animTransElement->toAttribute() = toAttr;

  return animTransElement;
}

AnimateElement* SVGParser::createAnimateLengthElement(QDomElement e) const
{
  URIReference* ref = 0;
  if ( !e.attributeNode("xlink:href").isNull() )
    ref = createRefAttribute( e.attributeNode("xlink:href").value(), true );
  if (!ref)
  {
    kdWarning() << "SVGHelper::createAnimateLengthElement() - Could not find referenced element!!!" << endl;
    return 0;
  }

  // Create the AnimateLengthElement
  AnimatedLength* refAttr = 0;
  refAttr = dynamic_cast<AnimatedLength*>(
              ref->getRefElement()->getAttrByName( e.attributeNode("attributeName").value() ) );
  AnimateLengthElement* animLengthElement = new AnimateLengthElement( *ref, refAttr );
  delete ref;
  LengthAttribute fromAttr;
  LengthAttribute byAttr;
  LengthAttribute toAttr;

  // Set it's from, by and to values
  if ( !e.attributeNode("attributeName").isNull() )
  {
    QString fromStr, byStr, toStr;

    fromStr = e.attributeNode("from").value();
    toStr = e.attributeNode("to").value();
    byStr = e.attributeNode("by").value();

    if (fromStr.isNull())
    {
      kdWarning() << "SVGHelper::createAnimateLengthElement() - No From Attr given" << endl;
      return 0;
    }
    unsigned int unitType;
    float valInSpecUnits = SVGHelper::extractLength(fromStr, unitType);
    fromAttr.newValueSpecifiedUnits( unitType, valInSpecUnits );
    // kdDebug() << "From: " << valInSpecUnits << " < Val Type > " << unitType << endl;

    if (!byStr.isNull())
    {
      unsigned int unitType;
      float valInSpecUnits = SVGHelper::extractLength(byStr, unitType);
      byAttr.newValueSpecifiedUnits( unitType, valInSpecUnits );
      // kdDebug() << "By: " << valInSpecUnits << " < Val Type > " << unitType << endl;
    }
    if (!toStr.isNull())
    {
      unsigned int unitType;
      float valInSpecUnits = SVGHelper::extractLength(toStr, unitType);
      toAttr.newValueSpecifiedUnits( unitType, valInSpecUnits );
      // kdDebug() << "To: " << valInSpecUnits << " < Val Type > " << unitType << endl;
    }
    if ( toStr.isNull() && byStr.isNull() )
    {
      kdWarning() << "SVGHelper::createAnimateLengthElement() - No To or By Attr given" << endl;
      return 0;
    }
  }
  else
  {
    kdWarning() << "SVGHelper::createAnimateLengthElement() - Invalid Animate Syntax" << endl;
    return 0;
  }

  /**
   * Finally set the animTransElement's from, by and to values
   */
  animLengthElement->fromAttribute() = fromAttr;
  animLengthElement->byAttribute() = byAttr;
  animLengthElement->toAttribute() = toAttr;

  return animLengthElement;
}


ElementNode* SVGParser::createGradientElement(QDomElement e, GroupElement& inheritedStyle) const
{
  Gradient *retGradient = 0;
  if ( e.tagName().lower() == "lineargradient" )
  {
    LinearGradient* retLinGradient = new LinearGradient();
    retGradient = retLinGradient;

    unsigned int unitType = 0;
    float valInSpecUnits = 0;
    if ( !e.attributeNode("x1").isNull() )
    {
      valInSpecUnits = SVGHelper::extractLength(e.attributeNode("x1").value(), unitType);
      retLinGradient->getX1().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
    }
    if ( !e.attributeNode("y1").isNull() )
    {
      valInSpecUnits = SVGHelper::extractLength(e.attributeNode("y1").value(), unitType);
      retLinGradient->getY1().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
    }
    if ( !e.attributeNode("x2").isNull() )
    {
      valInSpecUnits = SVGHelper::extractLength(e.attributeNode("x2").value(), unitType);
      retLinGradient->getX2().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
    }
    if ( !e.attributeNode("y2").isNull() )
    {
      valInSpecUnits = SVGHelper::extractLength(e.attributeNode("y2").value(), unitType);
      retLinGradient->getY2().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
    }
  }
  else if ( e.tagName().lower() == "radialgradient" )
  {
    RadialGradient* retRadGradient = new RadialGradient();
    retGradient = retRadGradient;

    unsigned int unitType = 0;
    float valInSpecUnits = 0;
    if ( !e.attributeNode("cx").isNull() )
    {
      valInSpecUnits = SVGHelper::extractLength(e.attributeNode("cx").value(), unitType);
      retRadGradient->getCX().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
    }
    if ( !e.attributeNode("cy").isNull() )
    {
      valInSpecUnits = SVGHelper::extractLength(e.attributeNode("cy").value(), unitType);
      retRadGradient->getCY().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
    }
    if ( !e.attributeNode("r").isNull() )
    {
      valInSpecUnits = SVGHelper::extractLength(e.attributeNode("r").value(), unitType);
      retRadGradient->getR().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
    }
    if ( !e.attributeNode("fx").isNull() )
    {
      valInSpecUnits = SVGHelper::extractLength(e.attributeNode("fx").value(), unitType);
      retRadGradient->getFX().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
    }
    if ( !e.attributeNode("fy").isNull() )
    {
      valInSpecUnits = SVGHelper::extractLength(e.attributeNode("fy").value(), unitType);
      retRadGradient->getFY().baseVal().newValueSpecifiedUnits(unitType, valInSpecUnits );
    }
  }

  // Common Gradient attributes
  if (retGradient)
  {
    // inherited Gradients:
    QString href = e.attributeNode("xlink:href").value();
    if (href.left(1)=="#")
      retGradient->ref().setURI( href );

    ElementNode *refElem = m_rootDoc->findElementByRef( href );
    retGradient->ref().setRefElement( refElem );

    if ( !e.attributeNode("gradientUnits").isNull() )
    {
      if (e.attributeNode("gradientUnits").value().lower() == "userspaceonuse")
        retGradient->gradientUnits().baseVal().setValue( Gradient::GRADUNITS_USERSPACEONUSE );
      else if (e.attributeNode("gradientUnits").value().lower() == "objectboundingbox")
        retGradient->gradientUnits().baseVal().setValue( Gradient::GRADUNITS_OBJECTBOUNDINGBOX );
    }
    if ( !e.attributeNode("spreadMethod").isNull() )
    {
      if (e.attributeNode("spreadMethod").value().lower() == "pad")
        retGradient->spreadMethod().baseVal().setValue( Gradient::SPREADMETHOD_PAD );
      else if (e.attributeNode("spreadMethod").value().lower() == "reflect")
        retGradient->spreadMethod().baseVal().setValue( Gradient::SPREADMETHOD_REFLECT );
      else if (e.attributeNode("spreadMethod").value().lower() == "repeat")
        retGradient->spreadMethod().baseVal().setValue( Gradient::SPREADMETHOD_REPEAT );
    }

    // TransformList
    SVGHelper::parseTransformAttribute(
      &retGradient->gradientTransformList().baseVal(),
      e.attributeNode("gradientTransform").value());
  }

  return retGradient;
}

ElementNode * SVGParser::createGradientStopElement( QDomElement e, GroupElement & inheritedStyle ) const
{
  StopElement* stop = new StopElement();

  // Retrieve CSS values
  if( !e.attributeNode("style").isNull() )
  {
    QStringList cssStyleList = QStringList::split(';', e.attributeNode("style").value() );
    QStringList::ConstIterator it = cssStyleList.begin();
    QStringList::ConstIterator end = cssStyleList.end();
    for(; it != end; ++it)
    {
      QStringList splitAttr = QStringList::split(':', (*it));
      splitAttr[0] = splitAttr[0].stripWhiteSpace().lower();

      if (splitAttr[0] == "offset")
        stop->getOffset().baseVal() = Number( splitAttr[1] );
      else if (splitAttr[0] == "stop-color")
        stop->getStopColor().baseVal() = ColorAttribute( splitAttr[1] );
      else if( splitAttr[0] == "stop-opacity" )
        stop->getStopOpacity().baseVal() = Number( splitAttr[1] );
    }
  }

  // Non-css attributes
  if ( !e.attributeNode("offset").isNull() )
  {
    stop->getOffset().baseVal() = Number( e.attributeNode("offset").value() );
  }
  if ( !e.attributeNode("stop-color").isNull() )
  {
    stop->getStopColor().baseVal() = ColorAttribute( e.attributeNode("stop-color").value() );
  }
  if ( !e.attributeNode("stop-opacity").isNull() )
  {
    stop->getStopOpacity().baseVal() = Number( e.attributeNode("stop-opacity").value() );
  }

  return stop;
}
