/****************************************************************************
** $Id: quickcoordobjects.cpp  beta1   edited Dec 12 14:24 $
**
** Copyright (C) 2001-2002 Trolltech AS.  All rights reserved.
**
** This file is part of the Qt Script for Applications framework (QSA).
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding a valid QSA Beta Evaluation Version license may use
** this file in accordance with the QSA Beta Evaluation Version License
** Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about QSA Commercial License Agreements.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
*****************************************************************************/

#include "quickcoordobjects.h"
#include "qstypes.h"
#include "qsfunction.h"
#include "qsenv.h"
#include <qpoint.h>
#include <qsize.h>
#include <qrect.h>
#include <assert.h>
#include <quickinterpreter.h>

class PointShared : public QSShared {
public:
    PointShared( const QPoint &p ) : point( p ) { }
    QPoint point;
};

QSPointClass::QSPointClass( QSClass *b )
    : QSClass( b )
{
    addMember( "x", QSMember( QSMember::Custom, 0, AttributePublic ) );
    addMember( "y", QSMember( QSMember::Custom, 1, AttributePublic ) );
}

QPoint *QSPointClass::point( const QSObject *obj ) const
{
    assert( obj->objectType() == this );
    return &((PointShared*)obj->shVal())->point;
}

QSObject QSPointClass::fetchValue( const QSObject *obj,
				   const QSMember &mem ) const
{
    if ( mem.type() == QSMember::Custom ) {
	switch ( mem.idx ) {
	case 0:
	    return QSNumber( point( obj )->x() );
	case 1:
	    return QSNumber( point( obj )->y() );
	default:
	    qFatal( "QSPointClass::fetchValue: unhandled case" );
	    return QSUndefined();
	}
    }
    return QSClass::fetchValue( obj, mem );
}

void QSPointClass::write( QSObject *objPtr, const QSMember &mem,
			  const QSObject &val ) const
{
    if ( mem.type() == QSMember::Custom ) {
	switch ( mem.idx ) {
	case 0:
	    point( objPtr )->setX( val.toInteger() );
	    break;
	case 1:
	    point( objPtr )->setY( val.toInteger() );
	    break;
	default:
	    qDebug( "QSPointClass::write() Unhandled case" );
	}
    } else {
	QSClass::write( objPtr, mem, val );
    }
}

QString QSPointClass::toString( const QSObject *obj ) const
{
    QPoint *p = point( obj );
    return QString( "(%1, %2)" ).arg( p->x() ).arg( p->y() );
}

QString QSPointClass::debugString( const QSObject *obj ) const
{
    QPoint *p = point( obj );
    return QString( "{x=%1:Number,y=%2:Number}" ).arg( p->x() ).arg( p->y() );
}

QSObject QSPointClass::construct( const QSList &args ) const
{
    if ( args.size() == 1 ) {
	if ( args[ 0 ].objectType() == this )
	    return args[ 0 ];
    } else if ( args.size() == 2 ) {
	return construct( QPoint( args[ 0 ].toInteger(),
				  args[ 1 ].toInteger() ) );
    }

    return construct( QPoint() );
}

QSObject QSPointClass::construct( const QPoint &p ) const
{
    return QSObject( this, new PointShared( p ) );
}

int QSPointClass::isEqual( const QSObject &a, const QSObject &b ) const
{
    if ( !b.isA( this ) )
	return FALSE;
    return *point( &a ) == *point( &b );
}

//////////////////////////////////////////////////////////////////////////

class SizeShared : public QSShared {
public:
    SizeShared( const QSize &s ) : size( s ) { }
    QSize size;
};

static QSObject transpose( QSObject * objPtr, const QSList & )
{
    qDebug( "transpose called..." );
    ( (QSSizeClass*) objPtr->objectType() )->size( objPtr )->transpose();
    return QSUndefined();
}

QSSizeClass::QSSizeClass( QSClass *b ) : QSClass(b)
{
    addMember( "width",  QSMember( QSMember::Custom, 0, AttributePublic ) );
    addMember( "height", QSMember( QSMember::Custom, 1, AttributePublic ) );
    addMember( "transpose", QSMember( &transpose ) );
}

QSize *QSSizeClass::size( const QSObject *obj ) const
{
    assert( obj->objectType() == this );
    return &((SizeShared*)obj->shVal())->size;
}

QSObject QSSizeClass::fetchValue( const QSObject *objPtr,
				  const QSMember &mem ) const
{
    if( mem.type()==QSMember::Custom ) {
	switch( mem.idx ) {
	case 0:
	    return QSNumber( size( objPtr )->width() );
	case 1:
	    return QSNumber( size( objPtr )->height() );
	default:
	    qFatal( "QSSizeClass::fetchValue, unhandled case" );
	    return QSUndefined();
	}
    }
    return QSClass::fetchValue( objPtr, mem );
}

void QSSizeClass::write( QSObject *objPtr, const QSMember &mem,
			 const QSObject &val ) const
{
    if( mem.type()==QSMember::Custom ) {
	switch( mem.idx ) {
	case 0:
	    size( objPtr )->setWidth( val.toInteger() );
	    break;
	case 1:
	    size( objPtr )->setHeight( val.toInteger() );
	    break;
	default:
	    qFatal( "QSSizeClass::write, unhandled case" );
	    break;
	}
    } else {
	QSClass::write( objPtr, mem, val );
    }
}

// QSObject QSSizeClass::invoke( QSObject * objPtr, const QSMember &mem,
// 			 const QSList &args ) const
// {
//     if( mem.type()==QSMember::Custom ) {
// 	if( mem.idx==2 ) {
// 	    size( objPtr )->transpose();
// 	}
// 	return QSUndefined();
//     } else {
// 	return QSClass::invoke( objPtr, mem, args );
//     }
// }

QString QSSizeClass::toString( const QSObject *obj ) const
{
    QSize *s = size( obj );
    return QString( "(%1, %2)" ).arg( s->width() ).arg( s->height() );
}

QString QSSizeClass::debugString( const QSObject *obj ) const
{
    QSize *s = size( obj );
    return QString( "{width=%1:Number,height=%2:Number}" )
	.arg( s->width() ).arg( s->height() );
}

QSObject QSSizeClass::construct( const QSList &args ) const
{
    if ( args.size() == 1 ) {
	if ( args[ 0 ].objectType() == this )
	    return args[ 0 ];
    } else if ( args.size() == 2 ) {
	return construct( QSize( args[ 0 ].toInteger(),
				 args[ 1 ].toInteger() ) );
    }

    return construct( QSize() );
}

QSObject QSSizeClass::construct( const QSize &s ) const
{
    return QSObject( this, new SizeShared( s ) );
}

//////////////////////////////////////////////////////////////////////////

class RectShared : public QSShared {
public:
    RectShared( const QRect &r ) : rect( r ) { }
    QRect rect;
};

QSRectClass::QSRectClass( QSClass * b ) : QSClass(b)
{
    addMember( "x",      QSMember( QSMember::Custom, 0, AttributePublic ) );
    addMember( "left",      QSMember( QSMember::Custom, 1, AttributePublic ) );
    addMember( "y",      QSMember( QSMember::Custom, 2, AttributePublic ) );
    addMember( "top",      QSMember( QSMember::Custom, 3, AttributePublic ) );
    addMember( "width",  QSMember( QSMember::Custom, 4, AttributePublic ) );
    addMember( "height", QSMember( QSMember::Custom, 5, AttributePublic ) );
    addMember( "right",      QSMember( QSMember::Custom, 6, AttributePublic ) );
    addMember( "bottom",      QSMember( QSMember::Custom, 7, AttributePublic ) );
    addMember( "center",      QSMember( QSMember::Custom, 8, AttributePublic ) );
    addMember( "isNull", QSMember( &isNull ) );
    addMember( "isEmpty", QSMember( &isNull ) );
    addMember( "contains", QSMember( &contains ) );
    addMember( "intersection", QSMember( &intersection ) );
    addMember( "union", QSMember( &union_ ) );
    addMember( "intersects", QSMember( &intersects ) );
    addMember( "normalize", QSMember( &normalize ) );
    addMember( "moveLeft", QSMember( &moveLeft ) );
    addMember( "moveRight", QSMember( &moveRight ) );
    addMember( "moveTop", QSMember( &moveTop ) );
    addMember( "moveBottom", QSMember( &moveBottom ) );
    addMember( "moveBy", QSMember( &moveBy ) );
}


QSObject QSRectClass::fetchValue( const QSObject *o,
				  const QSMember &mem ) const
{
    if( mem.type() == QSMember::Custom ) {
	switch( mem.idx ) {
	case 0:
	case 1:
	    return QSNumber( rect( o )->x() );
	case 2:
	case 3:
	    return QSNumber( rect( o )->y() );
	case 4:
	    return QSNumber( rect( o )->width() );
	case 5:
	    return QSNumber( rect( o )->height() );
	case 6:
	    return QSNumber( rect( o )->right() );
	case 7:
	    return QSNumber( rect( o )->bottom() );
	case 8:
	    return QuickInterpreter::self()->pointClass()->construct( rect( o )->center() );
	default:
	    qFatal( "QSRectClass::fetchValue: unhandled case" );
	    return QSUndefined();
	}
    }
    return QSClass::fetchValue( o, mem );
}


void QSRectClass::write( QSObject *o, const QSMember &mem,
			 const QSObject &val ) const
{
    if( mem.type() == QSMember::Custom ) {
	int i = val.toInteger();
	switch( mem.idx ) {
	case 0:
	case 1:
	    rect( o )->setX( i );
	    break;
	case 2:
	case 3:
	    rect( o )->setY( i );
	    break;
	case 4:
	    rect( o )->setWidth( i );
	    break;
	case 5:
	    rect( o )->setHeight( i );
	    break;
	case 6:
	    rect( o )->setRight( i );
	    break;
	case 7:
	    rect( o )->setBottom( i );
	    break;
	case 8:
	    break;
	default:
	    qFatal( "QSRectClass::write: unhandled case" );
	}
    } else {
	QSClass::write( o, mem, val );
    }
}


QRect *QSRectClass::rect( const QSObject *obj ) const
{
    assert( obj->objectType() == this );
    return &((RectShared*)obj->shVal())->rect;
}


QString QSRectClass::toString( const QSObject *obj ) const
{
    QRect *r = rect( obj );
    return QString( "(%1, %2, %3, %4)" ).arg( r->x() ).arg( r->y() )
	.arg( r->width() ).arg( r->height() );
}

QString QSRectClass::debugString( const QSObject *obj ) const
{
    QRect *r = rect( obj );
    return QString( "{x=%1:Number,y=%2:Number,width=%3,height=%3}" )
	.arg( r->x() ).arg( r->y() ).arg( r->width() ).arg( r->height() );
}

QSObject QSRectClass::construct( const QSList &args ) const
{
    if ( args.size() == 1 ) {
	if ( args[ 0 ].objectType() == this )
	    return args[ 0 ];
    } else if ( args.size() == 4 ) {
	return construct( QRect( args[ 0 ].toInteger(),
				 args[ 1 ].toInteger(),
				 args[ 2 ].toInteger(),
				 args[ 3 ].toInteger() ) );
    }

    return construct( QRect() );
}

QSObject QSRectClass::construct( const QRect &r ) const
{
    return QSObject( this, new RectShared( r ) );
}

int QSRectClass::isEqual( const QSObject &a, const QSObject &b ) const
{
    if ( !b.isA( this ) )
	return FALSE;
    return *rect( &a ) == *rect( &b );
}

extern void qsa_throw_error( const QString &message );

QSObject QSRectClass::isNull( QSObject *obj, const QSList & )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );
    return QSBoolean( r->isNull() );
}

QSObject QSRectClass::contains( QSObject *obj, const QSList &args )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );

    if ( args.size() < 1 || args.size() > 2 ) {
	qsa_throw_error( QString( "Rect.contains() called with %1 arguments. 1 or 2 arguments expected." ).
			 arg( args.size() ) );
	return QSUndefined();
    }

    if ( args.size() == 1 ) {
	QSObject a0 = args[0];
	if ( !a0.isA( "Point" ) ) {
	    qsa_throw_error( QString( "Rect.contains() called with an argument of type %1. "
				  "Type Point is expeced" ).
			     arg( a0.typeName() ) );
	    return QSUndefined();
	}

	return QSBoolean( r->contains( *( (QSPointClass*)a0.objectType() )->point( &a0 ) ) );
    } else {
	if ( !args[0].isA( "Number" ) || !args[1].isA( "Number" ) ) {
	    qsa_throw_error( QString( "Rect.contains() called with arguments of type %1 and %2. "
				  "Type Number and Number is expeced" ).
			     arg( args[0].typeName() ).arg( args[1].typeName() ) );
	    return QSUndefined();
	}
	return QSBoolean( r->contains( args[0].toInteger(),
				       args[1].toInteger() ) );
    }

    return QSUndefined();
}

QSObject QSRectClass::intersection( QSObject *obj, const QSList &args )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );

    if ( args.size() != 1 ) {
	qsa_throw_error( QString( "Rect.intersection() called with %1 arguments. 1 argument expected." ).
			 arg( args.size() ) );
	return QSUndefined();
    }

    QSObject a0 = args[0];
    if ( !a0.isA( cl ) ) {
	qsa_throw_error( QString( "Rect.intersection() called with an argument of type %1. "
				  "Type Rect is expeced" ).
			 arg( a0.typeName() ) );
	return QSUndefined();
    }

    return cl->construct( r->intersect( *cl->rect( &a0 ) ) );
}

QSObject QSRectClass::union_( QSObject *obj, const QSList &args )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );

    if ( args.size() != 1 ) {
	qsa_throw_error( QString( "Rect.union() called with %1 arguments. 1 argument expected." ).
			 arg( args.size() ) );
	return QSUndefined();
    }

    QSObject a0 = args[0];
    if ( !a0.isA( cl ) ) {
	qsa_throw_error( QString( "Rect.union() called with an argument of type %1. "
				  "Type Rect is expeced" ).
			 arg( a0.typeName() ) );
	return QSUndefined();
    }

    return cl->construct( r->unite( *cl->rect( &a0 ) ) );
}

QSObject QSRectClass::intersects( QSObject *obj, const QSList &args )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );

    if ( args.size() != 1 ) {
	qsa_throw_error( QString( "Rect.intersects() called with %1 arguments. 1 argument expected." ).
			 arg( args.size() ) );
	return QSUndefined();
    }

    QSObject a0 = args[0];
    if ( !a0.isA( cl ) ) {
	qsa_throw_error( QString( "Rect.intersects() called with an argument of type %1. "
				  "Type Rect is expeced" ).
			 arg( a0.typeName() ) );
	return QSUndefined();
    }

    return QSBoolean( r->intersects( *cl->rect( &a0 ) ) );
}

QSObject QSRectClass::normalize( QSObject *obj, const QSList & )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );
    return cl->construct( r->normalize() );
}

QSObject QSRectClass::moveLeft( QSObject *obj, const QSList &args )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );

    if ( args.size() != 1 ) {
	qsa_throw_error( QString( "Rect.moveLeft() called with %1 arguments. 1 argument expected." ).
			 arg( args.size() ) );
	return QSUndefined();
    }

    if ( !args[0].isA( cl->env()->numberClass() ) ) {
	qsa_throw_error( QString( "Rect.moveLeft() called with an argument of type %1. "
				  "Type Number is expeced" ).
			 arg( args[0].typeName() ) );
	return QSUndefined();
    }

    r->moveLeft( args[0].toInteger() );
    return QSUndefined();
}

QSObject QSRectClass::moveRight( QSObject *obj, const QSList &args )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );

    if ( args.size() != 1 ) {
	qsa_throw_error( QString( "Rect.moveRight() called with %1 arguments. 1 argument expected." ).
			 arg( args.size() ) );
	return QSUndefined();
    }

    if ( !args[0].isA( cl->env()->numberClass() ) ) {
	qsa_throw_error( QString( "Rect.moveRight() called with an argument of type %1. "
				  "Type Number is expeced" ).
			 arg( args[0].typeName() ) );
	return QSUndefined();
    }

    r->moveRight( args[0].toInteger() );
    return QSUndefined();
}

QSObject QSRectClass::moveTop( QSObject *obj, const QSList &args )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );

    if ( args.size() != 1 ) {
	qsa_throw_error( QString( "Rect.moveTop() called with %1 arguments. 1 argument expected." ).
			 arg( args.size() ) );
	return QSUndefined();
    }

    if ( !args[0].isA( "Number" ) ) {
	qsa_throw_error( QString( "Rect.moveTop() called with an argument of type %1. "
				  "Type Number is expeced" ).
			 arg( args[0].typeName() ) );
	return QSUndefined();
    }

    r->moveTop( args[0].toInteger() );
    return QSUndefined();
}

QSObject QSRectClass::moveBottom( QSObject *obj, const QSList &args )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );

    if ( args.size() != 1 ) {
	qsa_throw_error( QString( "Rect.moveBottom() called with %1 arguments. 1 argument expected." ).
			 arg( args.size() ) );
	return QSUndefined();
    }

    if ( !args[0].isA( "Number" ) ) {
	qsa_throw_error( QString( "Rect.moveBottom() called with an argument of type %1. "
				  "Type Number is expeced" ).
			 arg( args[0].typeName() ) );
	return QSUndefined();
    }

    r->moveBottom( args[0].toInteger() );
    return QSUndefined();
}

QSObject QSRectClass::moveBy( QSObject *obj, const QSList &args )
{
    Q_ASSERT( obj->typeName() == "Rect" );
    QSRectClass *cl = (QSRectClass*)obj->objectType();
    QRect *r = cl->rect( obj );

    if ( args.size() != 2 ) {
	qsa_throw_error( QString( "Rect.moveBy() called with %1 arguments. 2 arguments expected." ).
			 arg( args.size() ) );
	return QSUndefined();
    }

    if ( !args[0].isA( "Number" ) || !args[1].isA( "Number" ) ) {
	qsa_throw_error( QString( "Rect.moveBy() called with arguments of type %1 and %2. "
				  "Type Number and Number are expeced" ).
			 arg( args[0].typeName() ).arg( args[1].typeName() ) );
	return QSUndefined();
    }

    r->moveBy( args[0].toInteger(), args[1].toInteger() );
    return QSUndefined();
}

