/*
 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
 *
 * 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 "config.h"
#include "SVGTransform.h"

#include "FloatConversion.h"
#include "FloatPoint.h"
#include "FloatSize.h"
#include "SVGAngle.h"
#include <wtf/MathExtras.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>

namespace WebCore {

SVGTransform::SVGTransform()
    : m_type(SVG_TRANSFORM_UNKNOWN)
    , m_angle(0)
{
}

SVGTransform::SVGTransform(SVGTransformType type, ConstructionMode mode)
    : m_type(type)
    , m_angle(0)
{
    if (mode == ConstructZeroTransform)
        m_matrix = AffineTransform(0, 0, 0, 0, 0, 0);
}

SVGTransform::SVGTransform(const AffineTransform& matrix)
    : m_type(SVG_TRANSFORM_MATRIX)
    , m_angle(0)
    , m_matrix(matrix)
{
}

void SVGTransform::setMatrix(const AffineTransform& matrix)
{
    m_type = SVG_TRANSFORM_MATRIX;
    m_angle = 0;
    m_matrix = matrix;
}

void SVGTransform::updateSVGMatrix()
{
    // The underlying matrix has been changed, alter the transformation type.
    // Spec: In case the matrix object is changed directly (i.e., without using the methods on the SVGTransform interface itself)
    // then the type of the SVGTransform changes to SVG_TRANSFORM_MATRIX.
    m_type = SVG_TRANSFORM_MATRIX;
    m_angle = 0;
}

void SVGTransform::setTranslate(float tx, float ty)
{
    m_type = SVG_TRANSFORM_TRANSLATE;
    m_angle = 0;

    m_matrix.makeIdentity();
    m_matrix.translate(tx, ty);
}

FloatPoint SVGTransform::translate() const
{
    return FloatPoint::narrowPrecision(m_matrix.e(), m_matrix.f());
}

void SVGTransform::setScale(float sx, float sy)
{
    m_type = SVG_TRANSFORM_SCALE;
    m_angle = 0;
    m_center = FloatPoint();

    m_matrix.makeIdentity();
    m_matrix.scaleNonUniform(sx, sy);
}

FloatSize SVGTransform::scale() const
{
    return FloatSize::narrowPrecision(m_matrix.a(), m_matrix.d());
}

void SVGTransform::setRotate(float angle, float cx, float cy)
{
    m_type = SVG_TRANSFORM_ROTATE;
    m_angle = angle;
    m_center = FloatPoint(cx, cy);

    // TODO: toString() implementation, which can show cx, cy (need to be stored?)
    m_matrix.makeIdentity();
    m_matrix.translate(cx, cy);
    m_matrix.rotate(angle);
    m_matrix.translate(-cx, -cy);
}

void SVGTransform::setSkewX(float angle)
{
    m_type = SVG_TRANSFORM_SKEWX;
    m_angle = angle;

    m_matrix.makeIdentity();
    m_matrix.skewX(angle);
}

void SVGTransform::setSkewY(float angle)
{
    m_type = SVG_TRANSFORM_SKEWY;
    m_angle = angle;

    m_matrix.makeIdentity();
    m_matrix.skewY(angle);
}

const String& SVGTransform::transformTypePrefixForParsing(SVGTransformType type)
{
    switch (type) {
    case SVG_TRANSFORM_UNKNOWN:
        return emptyString();
    case SVG_TRANSFORM_MATRIX: {
        static NeverDestroyed<String> matrixString(ASCIILiteral("matrix("));
        return matrixString;
    }
    case SVG_TRANSFORM_TRANSLATE: {
        static NeverDestroyed<String> translateString(ASCIILiteral("translate("));
        return translateString;
    }
    case SVG_TRANSFORM_SCALE: {
        static NeverDestroyed<String> scaleString(ASCIILiteral("scale("));
        return scaleString;
    }
    case SVG_TRANSFORM_ROTATE: {
        static NeverDestroyed<String> rotateString(ASCIILiteral("rotate("));
        return rotateString;
    }    
    case SVG_TRANSFORM_SKEWX: {
        static NeverDestroyed<String> skewXString(ASCIILiteral("skewX("));
        return skewXString;
    }
    case SVG_TRANSFORM_SKEWY: {
        static NeverDestroyed<String> skewYString(ASCIILiteral("skewY("));
        return skewYString;
    }
    }

    ASSERT_NOT_REACHED();
    return emptyString();
}

String SVGTransform::valueAsString() const
{
    const String& prefix = transformTypePrefixForParsing(m_type);
    switch (m_type) {
    case SVG_TRANSFORM_UNKNOWN:
        return prefix;
    case SVG_TRANSFORM_MATRIX: {
        StringBuilder builder;
        builder.append(prefix);
        builder.appendNumber(m_matrix.a());
        builder.append(' ');
        builder.appendNumber(m_matrix.b());
        builder.append(' ');
        builder.appendNumber(m_matrix.c());
        builder.append(' ');
        builder.appendNumber(m_matrix.d());
        builder.append(' ');
        builder.appendNumber(m_matrix.e());
        builder.append(' ');
        builder.appendNumber(m_matrix.f());
        builder.append(')');
        return builder.toString();
    }
    case SVG_TRANSFORM_TRANSLATE: {
        StringBuilder builder;
        builder.append(prefix);
        builder.appendNumber(m_matrix.e());
        builder.append(' ');
        builder.appendNumber(m_matrix.f());
        builder.append(')');
        return builder.toString();
    }
    case SVG_TRANSFORM_SCALE: {
        StringBuilder builder;
        builder.append(prefix);
        builder.appendNumber(m_matrix.xScale());
        builder.append(' ');
        builder.appendNumber(m_matrix.yScale());
        builder.append(')');
        return builder.toString();
    }
    case SVG_TRANSFORM_ROTATE: {
        double angleInRad = deg2rad(m_angle);
        double cosAngle = cos(angleInRad);
        double sinAngle = sin(angleInRad);
        float cx = narrowPrecisionToFloat(cosAngle != 1 ? (m_matrix.e() * (1 - cosAngle) - m_matrix.f() * sinAngle) / (1 - cosAngle) / 2 : 0);
        float cy = narrowPrecisionToFloat(cosAngle != 1 ? (m_matrix.e() * sinAngle / (1 - cosAngle) + m_matrix.f()) / 2 : 0);
        StringBuilder builder;
        builder.append(prefix);
        builder.appendNumber(m_angle);
        if (cx || cy) {
            builder.append(' ');
            builder.appendNumber(cx);
            builder.append(' ');
            builder.appendNumber(cy);
        }
        builder.append(')');
        return builder.toString();
    }
    case SVG_TRANSFORM_SKEWX:
    case SVG_TRANSFORM_SKEWY: {
        StringBuilder builder;
        builder.append(prefix);
        builder.appendNumber(m_angle);
        builder.append(')');
        return builder.toString();
    }
    }

    ASSERT_NOT_REACHED();
    return emptyString();
}

} // namespace WebCore
