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

#include <kdebug.h>

#include "SVGPathSegArcImpl.h"
#include "SVGAngleImpl.h"

using namespace KSVG;

#include "SVGPathSegArcImpl.lut.h"
#include "ksvg_scriptinterpreter.h"
#include "ksvg_bridge.h"

static void getArcSlopes(bool relative, double curx, double cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag, double *pStartSlope, double *pEndSlope)
{
	double sin_th, cos_th;
	double a00, a01, a10, a11;
	double x0, y0, x1, y1, xc, yc;
	double d, sfactor, sfactor_sq;
	double th0, th1, th_arc;
	int i, n_segs;

	sin_th = sin(angle * (M_PI / 180.0));
	cos_th = cos(angle * (M_PI / 180.0));

	double dx;

	if(!relative)
		dx = (curx - x) / 2.0;
	else
		dx = -x / 2.0;

	double dy;
		
	if(!relative)
		dy = (cury - y) / 2.0;
	else
		dy = -y / 2.0;
		
	double _x1 =  cos_th * dx + sin_th * dy;
	double _y1 = -sin_th * dx + cos_th * dy;
	double Pr1 = r1 * r1;
	double Pr2 = r2 * r2;
	double Px = _x1 * _x1;
	double Py = _y1 * _y1;

	// Spec : check if radii are large enough
	double check = Px / Pr1 + Py / Pr2;
	if(check > 1)
	{
		r1 = r1 * sqrt(check);
		r2 = r2 * sqrt(check);
	}

	a00 = cos_th / r1;
	a01 = sin_th / r1;
	a10 = -sin_th / r2;
	a11 = cos_th / r2;

	x0 = a00 * curx + a01 * cury;
	y0 = a10 * curx + a11 * cury;

	if(!relative)
		x1 = a00 * x + a01 * y;
	else
		x1 = a00 * (curx + x) + a01 * (cury + y);
		
	if(!relative)
		y1 = a10 * x + a11 * y;
	else
		y1 = a10 * (curx + x) + a11 * (cury + y);

	/* (x0, y0) is current point in transformed coordinate space.
	   (x1, y1) is new point in transformed coordinate space.

	   The arc fits a unit-radius circle in this space.
     */

	d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);

	sfactor_sq = 1.0 / d - 0.25;

	if(sfactor_sq < 0)
		sfactor_sq = 0;

	sfactor = sqrt(sfactor_sq);

	if(sweepFlag == largeArcFlag)
		sfactor = -sfactor;

	xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
	yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);

	/* (xc, yc) is center of the circle. */
	th0 = atan2(y0 - yc, x0 - xc);
	th1 = atan2(y1 - yc, x1 - xc);

	th_arc = th1 - th0;
	if(th_arc < 0 && sweepFlag)
		th_arc += 2 * M_PI;
	else if(th_arc > 0 && !sweepFlag)
		th_arc -= 2 * M_PI;

	n_segs = (int) (int) ceil(fabs(th_arc / (M_PI * 0.5 + 0.001)));

	for(int step = 0; step < 2; step++)
	{
		i = step == 0 ? 0 : n_segs - 1;

		double sin_th, cos_th;
		double a00, a01, a10, a11;
		double x1, y1, x2, y2, x3, y3;
		double t;
		double th_half;

		double _th0 = th0 + i * th_arc / n_segs;
		double _th1 = th0 + (i + 1) * th_arc / n_segs;

		sin_th = sin(angle * (M_PI / 180.0));
		cos_th = cos(angle * (M_PI / 180.0));

		/* inverse transform compared with rsvg_path_arc */
		a00 = cos_th * r1;
		a01 = -sin_th * r2;
		a10 = sin_th * r1;
		a11 = cos_th * r2;

		th_half = 0.5 * (_th1 - _th0);
		t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
		x1 = xc + cos(_th0) - t * sin(_th0);
		y1 = yc + sin(_th0) + t * cos(_th0);
		x3 = xc + cos(_th1);
		y3 = yc + sin(_th1);
		x2 = x3 + t * sin(_th1);
		y2 = y3 - t * cos(_th1);

		double bezX1 = a00 * x1 + a01 * y1;
		double bezY1 = a10 * x1 + a11 * y1;
		double bezX2 = a00 * x2 + a01 * y2;
		double bezY2 = a10 * x2 + a11 * y2;
		double bezX = a00 * x3 + a01 * y3;
		double bezY = a10 * x3 + a11 * y3;

		if(step == 0)
			*pStartSlope = SVGAngleImpl::todeg(atan2(bezY1 - cury, bezX1 - curx));
		else
			*pEndSlope = SVGAngleImpl::todeg(atan2(bezY - bezY2, bezX - bezX2));
	}
}

SVGPathSegArcAbsImpl::SVGPathSegArcAbsImpl() : SVGPathSegImpl()
{
	KSVG_EMPTY_FLAGS
}

SVGPathSegArcAbsImpl::~SVGPathSegArcAbsImpl()
{
}

void SVGPathSegArcAbsImpl::setX(double x)
{
	m_x = x;
}

double SVGPathSegArcAbsImpl::x() const
{
	return m_x;
}

void SVGPathSegArcAbsImpl::setY(double y)
{
	m_y = y;
}

double SVGPathSegArcAbsImpl::y() const
{
	return m_y;
}

void SVGPathSegArcAbsImpl::setR1(double r1)
{
	m_r1 = r1;
}

double SVGPathSegArcAbsImpl::r1() const
{
	return m_r1;
}

void SVGPathSegArcAbsImpl::setR2(double r2)
{
	m_r2 = r2;
}

double SVGPathSegArcAbsImpl::r2() const
{
	return m_r2;
}

void SVGPathSegArcAbsImpl::setAngle(double angle)
{
	m_angle = angle;
}

double SVGPathSegArcAbsImpl::angle() const
{
	return m_angle;
}

void SVGPathSegArcAbsImpl::setLargeArcFlag(bool largeArcFlag)
{
	m_largeArcFlag = largeArcFlag;
}

bool SVGPathSegArcAbsImpl::largeArcFlag() const
{
	return m_largeArcFlag;
}

void SVGPathSegArcAbsImpl::setSweepFlag(bool sweepFlag)
{
	m_sweepFlag = sweepFlag;
}

bool SVGPathSegArcAbsImpl::sweepFlag() const
{
	return m_sweepFlag;
}

void SVGPathSegArcAbsImpl::getDeltasAndSlopes(double curx, double cury, double *pDx, double *pDy, double *pStartSlope, double *pEndSlope) const
{
	double dx = x() - curx;
	double dy = y() - cury;
	double startSlope;
	double endSlope;
	getArcSlopes(false, curx, cury, angle(), x(), y(), r1(), r2(), largeArcFlag(), sweepFlag(), &startSlope, &endSlope);
	*pDx = dx;
	*pDy = dy;
	*pStartSlope = startSlope;
	*pEndSlope = endSlope;
}

// Ecma stuff

/*
@namespace KSVG
@begin SVGPathSegArcAbsImpl::s_hashTable 11
 x					SVGPathSegArcAbsImpl::X						DontDelete
 y					SVGPathSegArcAbsImpl::Y						DontDelete
 r1					SVGPathSegArcAbsImpl::R1					DontDelete
 r2					SVGPathSegArcAbsImpl::R2					DontDelete
 angle				SVGPathSegArcAbsImpl::Angle					DontDelete
 largeArcFlag		SVGPathSegArcAbsImpl::LargeArcFlag			DontDelete
 sweepFlag			SVGPathSegArcAbsImpl::SweepFlag				DontDelete
@end
*/

Value SVGPathSegArcAbsImpl::getValueProperty(ExecState *, int token) const
{
    switch(token)
    {
        case X:
			return Number(x());
        case Y:
			return Number(y());
        case R1:
			return Number(r1());
        case R2:
			return Number(r2());
        case Angle:
			return Number(angle());
		case LargeArcFlag:
			return Boolean(largeArcFlag());
		case SweepFlag:
			return Boolean(sweepFlag());
		default:
			kdWarning() << "Unhandled token in " << k_funcinfo << " : " << token << endl;
			return Undefined();
	}
}

void SVGPathSegArcAbsImpl::putValueProperty(ExecState *exec, int token, const Value &value, int)
{
	switch(token)
	{
		case X:
			m_x = value.toNumber(exec);
			break;
		case Y:
			m_y = value.toNumber(exec);
			break;
		case R1:
			m_r1 = value.toNumber(exec);
			break;
		case R2:
			m_r2 = value.toNumber(exec);
			break;
		case Angle:
			m_angle = value.toNumber(exec);
			break;
		case LargeArcFlag:
			m_largeArcFlag = value.toBoolean(exec);
			break;
		case SweepFlag:
			m_sweepFlag = value.toBoolean(exec);
			break;
		default:
			kdWarning() << "Unhandled token in " << k_funcinfo << " : " << token << endl;
	}
}



SVGPathSegArcRelImpl::SVGPathSegArcRelImpl() : SVGPathSegImpl()
{
	KSVG_EMPTY_FLAGS
}

SVGPathSegArcRelImpl::~SVGPathSegArcRelImpl()
{
}

void SVGPathSegArcRelImpl::setX(double x)
{
	m_x = x;
}

double SVGPathSegArcRelImpl::x() const
{
	return m_x;
}

void SVGPathSegArcRelImpl::setY(double y)
{
	m_y = y;
}

double SVGPathSegArcRelImpl::y() const
{
	return m_y;
}

void SVGPathSegArcRelImpl::setR1(double r1)
{
	m_r1 = r1;
}

double SVGPathSegArcRelImpl::r1() const
{
	return m_r1;
}

void SVGPathSegArcRelImpl::setR2(double r2)
{
	m_r2 = r2;
}

double SVGPathSegArcRelImpl::r2() const
{
	return m_r2;
}

void SVGPathSegArcRelImpl::setAngle(double angle)
{
	m_angle = angle;
}

double SVGPathSegArcRelImpl::angle() const
{
	return m_angle;
}

void SVGPathSegArcRelImpl::setLargeArcFlag(bool largeArcFlag)
{
	m_largeArcFlag = largeArcFlag;
}

bool SVGPathSegArcRelImpl::largeArcFlag() const
{
	return m_largeArcFlag;
}

void SVGPathSegArcRelImpl::setSweepFlag(bool sweepFlag)
{
	m_sweepFlag = sweepFlag;
}

bool SVGPathSegArcRelImpl::sweepFlag() const
{
	return m_sweepFlag;
}

void SVGPathSegArcRelImpl::getDeltasAndSlopes(double curx, double cury, double *pDx, double *pDy, double *pStartSlope, double *pEndSlope) const
{
	double dx = x();
	double dy = y();
	double startSlope;
	double endSlope;
	getArcSlopes(true, curx, cury, angle(), x(), y(), r1(), r2(), largeArcFlag(), sweepFlag(), &startSlope, &endSlope);
	*pDx = dx;
	*pDy = dy;
	*pStartSlope = startSlope;
	*pEndSlope = endSlope;
}

// Ecma stuff

/*
@namespace KSVG
@begin SVGPathSegArcRelImpl::s_hashTable 11
 x				SVGPathSegArcRelImpl::X						DontDelete
 y				SVGPathSegArcRelImpl::Y						DontDelete
 r1				SVGPathSegArcRelImpl::R1					DontDelete
 r2				SVGPathSegArcRelImpl::R2					DontDelete
 angle			SVGPathSegArcRelImpl::Angle					DontDelete
 largeArcFlag	SVGPathSegArcRelImpl::LargeArcFlag			DontDelete
 sweepFlag		SVGPathSegArcRelImpl::SweepFlag				DontDelete
@end
*/

Value SVGPathSegArcRelImpl::getValueProperty(ExecState *, int token) const
{
    switch(token)
    {
        case X:
			return Number(x());
        case Y:
			return Number(y());
        case R1:
			return Number(r1());
        case R2:
			return Number(r2());
        case Angle:
			return Number(angle());
		case LargeArcFlag:
			return Boolean(largeArcFlag());
		case SweepFlag:
			return Boolean(sweepFlag());
		default:
			kdWarning() << "Unhandled token in " << k_funcinfo << " : " << token << endl;
			return Undefined();
	}
}

void SVGPathSegArcRelImpl::putValueProperty(ExecState *exec, int token, const Value &value, int)
{
	switch(token)
	{
		case X:
			m_x = value.toNumber(exec);
			break;
		case Y:
			m_y = value.toNumber(exec);
			break;
		case R1:
			m_r1 = value.toNumber(exec);
			break;
		case R2:
			m_r2 = value.toNumber(exec);
			break;
		case Angle:
			m_angle = value.toNumber(exec);
			break;
		case LargeArcFlag:
			m_largeArcFlag = value.toBoolean(exec);
			break;
		case SweepFlag:
			m_sweepFlag = value.toBoolean(exec);
			break;
		default:
			kdWarning() << "Unhandled token in " << k_funcinfo << " : " << token << endl;
	}
}

// vim:ts=4:noet