/****************************************************************************
** $Id: qsregexp_object.cpp  beta1   edited Dec 17 19:08 $
**
** 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 "qsregexp_object.h"
#include "qsoperations.h"
#include "qsenv.h"
#include "qstypes.h"
#include "qsinternal.h"
#include "qsarray_object.h"
#include <qregexp.h>

class QSRegExpShared : public QSWritable
{
public:
    QSRegExpShared( const QString &pat ) : reg( pat ) { }
    QRegExp reg;
};

QSRegExpClass::QSRegExpClass( QSClass *b )
    : QSWritableClass( b )
{
    addMember( "exec", QSMember( &exec ) );
    addMember( "test", QSMember( &test ) );
    addMember( "toString", QSMember( &QSRegExpClass::toStringScript ) );
    addMember( "search", QSMember( &search ) );
    addMember( "searchRev", QSMember( &searchRev ) );
    addMember( "exactMatch", QSMember( &exactMatch ) );
    addMember( "pos", QSMember( &pos ) );
    addMember( "cap", QSMember( &cap ) );
}

/*!
 * Convenience function that retrieves a pointer to the QRegExp
 * object contained in \a obj. Only to be used if \a obj is of
 * type RegExp.
 */

QRegExp *QSRegExpClass::regExp( const QSObject *obj )
{
    Q_ASSERT( obj->typeName() == "RegExp" );
    return &((QSRegExpShared*)obj->shVal())->reg;
}

/*! \reimp */
QSObject QSRegExpClass::cast( const QSList &args ) const
{
    return construct( args );
}

QSObject QSRegExpClass::construct( const QSList &args ) const
{
    // ### regexp arguments
    QString p = args.isEmpty() ? QString( "" ) : args[0].toString();
    QString flags = args[1].toString();

    bool global = (flags.find("g") >= 0);
    bool ignoreCase = (flags.find("i") >= 0);
    bool multiline = (flags.find("m") >= 0);
    // ### throw an error on invalid flags

    QSRegExpShared *sh = new QSRegExpShared( p ); // ### apply flags
    QSObject obj( this, sh );

    obj.put( "global", QSBoolean( global ) );
    obj.put( "ignoreCase", QSBoolean( ignoreCase ) );
    obj.put( "multiline", QSBoolean( multiline ) );

    obj.put( "source", QSString( p ) );
    obj.put( "lastIndex", QSNumber( 0 ), DontDelete | DontEnum );

    return obj;
}

bool QSRegExpClass::hasProperty( const QSObject *obj, const QString &p ) const
{
    return p == "exec" || p == "test" || p == "toString" || p == "valid" ||
	   p == "empty" || p == "matchedLength" || p == "capturedTexts" ||
	   p == "search" || p == "searchRev" || "exactMatch" || p == "pos" ||
	   p == "cap" || QSClass::hasProperty( obj, p );
}

QSObject QSRegExpClass::get( const QSObject *obj, const QString &p ) const
{
    QRegExp *re = regExp( obj );
    if ( p == "valid" )
	return QSBoolean( re->isValid() );
    else if ( p == "empty" )
	return QSBoolean( re->isEmpty() );
    else if ( p == "matchedLength" )
	return QSNumber( re->matchedLength() );
    else if ( p == "capturedTexts" ) {
 	QSArray array;
 	QStringList ct = re->capturedTexts();
 	QStringList::ConstIterator it = ct.begin();
 	int i = 0;
 	for ( ; it != ct.end(); ++it, ++i )
 	    array.put( QString::number( i ), QSString( *it ) );
	array.put( "length", QSNumber( i ) );
 	return array;
    }
    return QSUndefined();
}

bool QSRegExpClass::hasStaticProperty( const QString &p ) const
{
    return ( p.length() == 2 && p[0] == '$' && p[1].isDigit() ) ||
	   ( p.length() == 3 && p[0] == '$' && p[1].isDigit() &&
	     p[2].isDigit() );
}

QSObject QSRegExpClass::getStatic( const QString &p ) const
{
    if ( ( p.length() == 2 && p[0] == '$' && p[1].isDigit() ) ||
	 ( p.length() == 3 && p[0] == '$' && p[1].isDigit() &&
	   p[2].isDigit() ) ) {
	int i = p.mid( 1 ).toInt();
	if ( i < (int)lastCaptures.count() )
	    return QSString( lastCaptures[ i ] );
	else
	    return QSString( "" );
    }

    return QSUndefined();
}

QString QSRegExpClass::toString( const QSObject *obj ) const
{
    return "/" + regExp( obj )->pattern() + "/";
}

QSObject QSRegExpClass::exec( QSObject *obj, const QSList &args )
{
    QRegExp *re = regExp( obj );
    QString s = args[0].toString();
    uint length = s.length();
    int i = obj->get( "lastIndex" ).toInt32();
    QSObject tmp = obj->get( "global" );
    if ( !tmp.toBoolean() )
	i = 0;
    if ( i < 0 || i > (int)length ) {
	obj->put( "lastIndex", 0 );
	return QSNull();
    }
    i = re->search( s, i );
    obj->env()->regexpClass()->lastCaptures = re->capturedTexts();
    // ### complete
    return QSString( re->cap(0) );
}

QSObject QSRegExpClass::test( QSObject *obj, const QSList &args )
{
    QRegExp *re = regExp( obj );
    QString s = args[0].toString();
    uint length = s.length();
    int i = obj->get( "lastIndex" ).toInt32();
    QSObject tmp = obj->get( "global" );
    if ( !tmp.toBoolean() )
	i = 0;
    if ( i < 0 || i >(int) length ) {
	obj->put( "lastIndex", 0 );
	return QSBoolean( FALSE );
    }
    i = re->search( s, i );
    obj->env()->regexpClass()->lastCaptures = re->capturedTexts();
    return QSBoolean( i >= 0 );
}

QSObject QSRegExpClass::toStringScript( QSObject *obj, const QSList & )
{
    return QSString( "/" + regExp( obj )->pattern() + "/" );
}

QSObject QSRegExpClass::search( QSObject *obj, const QSList &args )
{
    int start = args.size() >= 2 ? args[1].toInteger() : 0;
    return QSNumber( regExp( obj )->search( args[0].toString(), start ) );
}

QSObject QSRegExpClass::searchRev( QSObject *obj, const QSList &args )
{
    int start = args.size() >= 2 ? args[1].toInteger() : 0;
    return QSNumber( regExp( obj )->searchRev( args[0].toString(), start ) );
}

QSObject QSRegExpClass::exactMatch( QSObject *obj, const QSList &args )
{
    return QSBoolean( regExp( obj )->exactMatch( args[0].toString() ) );
}

QSObject QSRegExpClass::pos( QSObject *obj, const QSList &args )
{
    return QSNumber( regExp( obj )->pos( args.size() >= 1 ?
					 args[0].toInteger() : 0 ) );
}

QSObject QSRegExpClass::cap( QSObject *obj, const QSList &args )
{
    return QSString( regExp( obj )->cap( args.size() >= 1 ?
					 args[0].toInteger() : 0 ) );
}
