/*
    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 Steet, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include <math.h>

#include <kdebug.h>

#include "angle.h"
#include "matrix.h"

Matrix::Matrix()
{
}

Matrix::Matrix(QWMatrix mat)
{
  m_mat = mat;
}

Matrix::Matrix(double a, double b, double c, double d, double e, double f)
{
  m_mat.setMatrix(a, b, c, d, e, f);
}

Matrix::~Matrix()
{}

bool Matrix::operator==( const Matrix &other )
{
  return ( m_mat == other.qmatrix() );
}

bool Matrix::operator!=( const Matrix &other )
{
  return !(*this==other);
}

void Matrix::setA(const double &a)
{
  m_mat.setMatrix(a, m_mat.m12(), m_mat.m21(), m_mat.m22(), m_mat.dx(), m_mat.dy());
}

double Matrix::a() const
{
  return m_mat.m11();
}

void Matrix::setB(const double &b)
{
  m_mat.setMatrix(m_mat.m11(), b, m_mat.m21(), m_mat.m22(), m_mat.dx(), m_mat.dy());
}

double Matrix::b() const
{
  return m_mat.m12();
}

void Matrix::setC(const double &c)
{
  m_mat.setMatrix(m_mat.m11(), m_mat.m12(), c, m_mat.m22(), m_mat.dx(), m_mat.dy());
}

double Matrix::c() const
{
  return m_mat.m21();
}

void Matrix::setD(const double &d)
{
  m_mat.setMatrix(m_mat.m11(), m_mat.m12(), m_mat.m21(), d, m_mat.dx(), m_mat.dy());
}

double Matrix::d() const
{
  return m_mat.m22();
}

void Matrix::setE(const double &e)
{
  m_mat.setMatrix(m_mat.m11(), m_mat.m12(), m_mat.m21(), m_mat.m22(), e, m_mat.dy());
}

double Matrix::e() const
{
  return m_mat.dx();
}

void Matrix::setF(const double &f)
{
  m_mat.setMatrix(m_mat.m11(), m_mat.m12(), m_mat.m21(), m_mat.m22(), m_mat.dx(), f);
}

double Matrix::f() const
{
  return m_mat.dy();
}

void Matrix::copy(const Matrix& other)
{
  m_mat.setMatrix(other.m_mat.m11(), other.m_mat.m12(), other.m_mat.m21(), other.m_mat.m22(), other.m_mat.dx(), other.m_mat.dy());
}

Matrix *Matrix::postMultiply(const Matrix &secondMatrix)
{
  QWMatrix temp(secondMatrix.a(), secondMatrix.b(), secondMatrix.c(), secondMatrix.d(), secondMatrix.e(), secondMatrix.f());
  m_mat *= temp;
  return this;
}

Matrix Matrix::inverse() const
{
  Matrix mat(m_mat.invert());
  return mat;
}

Matrix *Matrix::invert()
{
  m_mat = m_mat.invert();
  return this;
}

Matrix *Matrix::postTranslate(const double &x,  const double &y)
{
  // Could optimise these.
  QWMatrix temp;
  temp.translate(x, y);
  m_mat *= temp;
  return this;
}

Matrix *Matrix::postScale(const double &scaleFactorX, const double &scaleFactorY)
{
  QWMatrix temp;
  temp.scale(scaleFactorX, scaleFactorY);
  m_mat *= temp;
  return this;
}

Matrix *Matrix::postRotate(const double &angle)
{
  QWMatrix temp;
  temp.rotate(angle);
  m_mat *= temp;
  return this;
}

Matrix *Matrix::postRotateFromVector(const double &x, const double &y)
{
  QWMatrix temp;
  temp.rotate(Angle::todeg(atan2(y, x)));
  m_mat *= temp;
  return this;
}

Matrix *Matrix::postFlipX()
{
  QWMatrix temp(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F);
  m_mat *= temp;
  return this;
}

Matrix *Matrix::postFlipY()
{
  QWMatrix temp(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F);
  m_mat *= temp;
  return this;
}

Matrix *Matrix::postSkewX(const double &angle)
{
  QWMatrix temp;
  temp.shear(tan(Angle::torad(angle)), 0.0F);
  m_mat *= temp;
  return this;
}

Matrix *Matrix::postSkewY(const double &angle)
{
  QWMatrix temp;
  temp.shear(0.0F, tan(Angle::torad(angle)));
  m_mat *= temp;
  return this;
}

Matrix *Matrix::multiply(const Matrix &secondMatrix)
{
  QWMatrix temp(secondMatrix.a(), secondMatrix.b(), secondMatrix.c(), secondMatrix.d(), secondMatrix.e(), secondMatrix.f());
  temp *= m_mat;
  m_mat = temp;
  return this;
}

Matrix *Matrix::translate(const double &x,  const double &y)
{
  m_mat.translate(x, y);
  return this;
}

Matrix *Matrix::scale(const double &scaleFactor)
{
  m_mat.scale(scaleFactor, scaleFactor);
  return this;
}

Matrix *Matrix::scaleNonUniform(const double &scaleFactorX, const double &scaleFactorY)
{
  m_mat.scale(scaleFactorX, scaleFactorY);
  return this;
}

Matrix *Matrix::rotate(const double &angle)
{
  m_mat.rotate(angle);
  return this;
}

Matrix *Matrix::rotateFromVector(const double &x, const double &y)
{
  m_mat.rotate(Angle::todeg(atan2(y, x)));
  return this;
}

Matrix *Matrix::flipX()
{
  m_mat.scale(-1.0f, 1.0f);
  return this;
}

Matrix *Matrix::flipY()
{
  m_mat.scale(1.0f, -1.0f);
  return this;
}

Matrix *Matrix::skewX(const double &angle)
{
  m_mat.shear(tan(Angle::torad(angle)), 0.0F);
  return this;
}

Matrix *Matrix::skewY(const double &angle)
{
  m_mat.shear(0.0F, tan(Angle::torad(angle)));
  return this;
}

void Matrix::setMatrix(QWMatrix mat)
{
  m_mat = mat;
}

QWMatrix &Matrix::qmatrix()
{
  return m_mat;
}

const QWMatrix &Matrix::qmatrix() const
{
  return m_mat;
}

void Matrix::reset()
{
  m_mat.reset();
}

void Matrix::debugPrint() const
{
  kdDebug() << a() << " "  << c() << " "  << e() << endl;
  kdDebug() << b() << " "  << d() << " "  << f() << endl;
}
