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

// Core
#include "svgpathparserimpl.h"

// Elements
#include "elements/lengthattribute.h"
#include "elements/transformlist.h"
#include "elements/matrix.h"
#include "elements/colorattribute.h"

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

// KDE
#include <kdebug.h>

SVGHelper::SVGHelper()
{}

SVGHelper::~SVGHelper()
{}

LengthAttribute* SVGHelper::createLengthAttr(QString s)
{
  unsigned int unitType;
  float valInSpecUnits = extractLength(s, unitType);
  return new LengthAttribute(valInSpecUnits, unitType);
}

/**
 * Returns the Length attribute contained in s with it's type
 */
float SVGHelper::extractLength(QString s, unsigned int& unitType)
{
  if(s.isEmpty())
    return 0.0f;

  double convNum = 0;
  const char *start = s.latin1();
  const char *end = SVGPathParser::getNumber(start, convNum);

  if(uint(end - start) < s.length())
  {
    if(s.endsWith(LengthAttribute::LengthUnitText[LengthAttribute::SVG_LENGTHTYPE_PX]))
      unitType = LengthAttribute::SVG_LENGTHTYPE_PX;
    else if(s.endsWith(LengthAttribute::LengthUnitText[LengthAttribute::SVG_LENGTHTYPE_CM]))
      unitType = LengthAttribute::SVG_LENGTHTYPE_CM;
    else if(s.endsWith(LengthAttribute::LengthUnitText[LengthAttribute::SVG_LENGTHTYPE_PC]))
      unitType = LengthAttribute::SVG_LENGTHTYPE_PC;
    else if(s.endsWith(LengthAttribute::LengthUnitText[LengthAttribute::SVG_LENGTHTYPE_MM]))
      unitType = LengthAttribute::SVG_LENGTHTYPE_MM;
    else if(s.endsWith(LengthAttribute::LengthUnitText[LengthAttribute::SVG_LENGTHTYPE_IN]))
      unitType = LengthAttribute::SVG_LENGTHTYPE_IN;
    else if(s.endsWith(LengthAttribute::LengthUnitText[LengthAttribute::SVG_LENGTHTYPE_PT]))
      unitType = LengthAttribute::SVG_LENGTHTYPE_PT;
  }
  else
    unitType = LengthAttribute::SVG_LENGTHTYPE_PX;

  return convNum;
};

/**
 * Parses the contents of SVG's transform attribute
 */
void SVGHelper::parseTransformAttribute(TransformList *list, const QString &transform)
{
  // Split string for handling 1 transform statement at a time
  QStringList subtransforms = QStringList::split(')', transform);
  QStringList::ConstIterator it = subtransforms.begin();
  QStringList::ConstIterator end = subtransforms.end();
  for(; it != end; ++it)
  {
    QStringList subtransform = QStringList::split('(', (*it));

    subtransform[0] = subtransform[0].stripWhiteSpace().lower();
    if(subtransform[0].startsWith(";") || subtransform[0].startsWith(","))
      subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);

    Transform *t = new Transform();
    setTransform( t, subtransform[0], subtransform[1] );

    list->append(t);
  }
}

/**
 * parses a List of numbers like "1.43 0 1123.2"
 * and writes the output into params
 */
void SVGHelper::splitSVGNumberList(QStringList& params, QString& text)
{
  QRegExp reg("([-]?\\d*\\.?\\d+(?:e[-]?\\d+)?)");
  int pos = 0;
  text = text.simplifyWhiteSpace();

  while(pos >= 0)
  {
    pos = reg.search(text, pos);
    if(pos != -1)
    {
      params += reg.cap(1);
      pos += reg.matchedLength();
    }
  }
}

double SVGHelper::parseClockValue(const QString &data)
{
  QString parse = data.stripWhiteSpace();
  QString debugOutput = "parseClockValue(" + parse + ") -> ";

  if(parse == "indefinite") // Saves some time...
    ;

  double result;

  int doublePointOne = parse.find(':');
  int doublePointTwo = parse.find(':', doublePointOne + 1);

  if(doublePointOne != -1 && doublePointTwo != -1) // Spec: "Full clock values"
  {
    unsigned int hours = parse.mid(0, 2).toUInt();
    unsigned int minutes = parse.mid(3, 2).toUInt();
    unsigned int seconds = parse.mid(6, 2).toUInt();
    unsigned int milliseconds = 0;

    result = (3600 * hours) + (60 * minutes) + seconds;

    if(parse.find('.') != -1)
    {
      QString temp = parse.mid(9, 2);
      milliseconds = temp.toUInt();
      result += (milliseconds * (1 / pow(10.0, temp.length())));
    }
  }
  else if(doublePointOne != -1 && doublePointTwo == -1) // Spec: "Partial clock values"
  {
    unsigned int minutes = parse.mid(0, 2).toUInt();
    unsigned int seconds = parse.mid(3, 2).toUInt();
    unsigned int milliseconds = 0;

    result = (60 * minutes) + seconds;

    if(parse.find('.') != -1)
    {
      QString temp = parse.mid(6, 2);
      milliseconds = temp.toUInt();
      result += (milliseconds * (1 / pow(10.0, temp.length())));
    }
  }
  else // Spec: "Timecount values"
  {
    int dotPosition = parse.find('.');

    if(parse.endsWith("h"))
    {
      if(dotPosition == -1)
        result = parse.mid(0, parse.length() - 1).toUInt() * 3600;
      else
      {
        result = parse.mid(0, dotPosition).toUInt() * 3600;
        QString temp = parse.mid(dotPosition + 1, parse.length() - dotPosition - 2);
        result += (3600.0 * temp.toUInt()) * (1 / pow(10.0, temp.length()));
      }
    }
    else if(parse.endsWith("min"))
    {
      if(dotPosition == -1)
        result = parse.mid(0, parse.length() - 3).toUInt() * 60;
      else
      {
        result = parse.mid(0, dotPosition).toUInt() * 60;
        QString temp = parse.mid(dotPosition + 1, parse.length() - dotPosition - 4);
        result += (60.0 * temp.toUInt()) * (1 / pow(10.0, temp.length()));
      }
    }
    else if(parse.endsWith("ms"))
    {
      if(dotPosition == -1)
        result = parse.mid(0, parse.length() - 2).toUInt() / 1000.0;
      else
      {
        result = parse.mid(0, dotPosition).toUInt() / 1000.0;
        QString temp = parse.mid(dotPosition + 1, parse.length() - dotPosition - 3);
        result += (temp.toUInt() / 1000.0) * (1 / pow(10.0, temp.length()));
      }
    }
    else if(parse.endsWith("s"))
    {
      if(dotPosition == -1)
        result = parse.mid(0, parse.length() - 1).toUInt();
      else
      {
        result = parse.mid(0, dotPosition).toUInt();
        QString temp = parse.mid(dotPosition + 1, parse.length() - dotPosition - 2);
        result += temp.toUInt() * (1 / pow(10.0, temp.length()));
      }
    }
    else
      result = parse.toDouble();
  }

  kdDebug() << debugOutput << result << endl;
  return result;
}

void SVGHelper::setTransform(Transform *transform, const QString type, const QString values)
{
  if (values.isNull() || type.isNull())
  {
    kdWarning() << "SVGHelper::setTransform() - Null Values or Type argument given" << endl;
    return;
  }

  QString myValues(values);
  QStringList params;
  SVGHelper::splitSVGNumberList(params, myValues);

  if ( type.lower() == "rotate" )
  {
    // kdDebug() << "SVGHelper::setTransform() - " << params[0].toDouble() << " " << params[1].toDouble() << " " << params[2].toDouble() << endl;
    if(params.count() == 3)
      transform->setRotate(params[0].toDouble(),
                           params[1].toDouble(),
                           params[2].toDouble());
    else
      transform->setRotate(params[0].toDouble(), 0, 0);
  }
  else if(type.lower() == "translate")
  {
    if(params.count() == 2)
      transform->setTranslate(params[0].toDouble(), params[1].toDouble());
    else    // Spec : if only one param given, assume 2nd param to be 0
      transform->setTranslate(params[0].toDouble(), 0);
  }
  else if(type.lower() == "scale")
  {
    if(params.count() == 2)
      transform->setScale(params[0].toDouble(), params[1].toDouble());
    else    // Spec : if only one param given, assume uniform scaling
      transform->setScale(params[0].toDouble(), params[0].toDouble());
  }
  else if(type.lower() == "skewx")
    transform->setSkewX(params[0].toDouble());
  else if(type.lower() == "skewy")
    transform->setSkewY(params[0].toDouble());
  else if(type.lower() == "matrix")
  {
    if(params.count() >= 6)
    {
      Matrix ret(params[0].toDouble(),
                 params[1].toDouble(),
                 params[2].toDouble(),
                 params[3].toDouble(),
                 params[4].toDouble(),
                 params[5].toDouble());
      transform->setMatrix(ret);
    }
  }

}

ColorAttribute * SVGHelper::parseColorString( const QString & color )
{
  ColorAttribute* cAttr = 0;
  if ( color.lower().startsWith("rgb(") )
  {
    QStringList params;
    QString rgb = color.mid( 4, color.find(")") - 4 );
    SVGHelper::splitSVGNumberList(params, rgb );
    cAttr = new ColorAttribute( params[0].toInt(),
                                params[1].toInt(),
                                params[2].toInt() );
  }
  else if ( color.length() ==4 && color.startsWith("#") )
  {
    cAttr = new ColorAttribute( QString("#").append(color.mid(1,1)).append(color.mid(1,1)).append(color.mid(2,1)).append(color.mid(2,1)).append(color.mid(3,1)).append(color.mid(3,1)) );
  }
  else
    cAttr = new ColorAttribute( color );

  return cAttr;
}

