/****************************************************************************
**
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
**
** This file is part of the QSA of the Qt Toolkit.
**
** For QSA Commercial License Holders (non-open source):
** 
** Licensees holding a valid Qt Script for Applications (QSA) License Agreement
** may use this file in accordance with the rights, responsibilities and
** obligations contained therein.  Please consult your licensing agreement or
** contact sales@trolltech.com if any conditions of the Licensing Agreement are
** not clear to you.
** 
** Further information about QSA licensing is available at:
** http://www.trolltech.com/products/qsa/licensing.html or by contacting
** info@trolltech.com.
** 
** 
** For Open Source Edition:  
** 
** This file may be used 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.  Please review the
** following information to ensure GNU General Public Licensing requirements
** will be met:  http://www.trolltech.com/products/qt/opensource.html 
** 
** If you are unsure which license is appropriate for your use, please review
** the following information:
** http://www.trolltech.com/products/qsa/licensing.html or contact the 
** sales department at sales@trolltech.com.

**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#ifndef QSOBJECT_H
#define QSOBJECT_H

#include "qsglobal.h"

#include <QString>
#include <QVariant>
#include <QSharedData>

class QSList;
class QSClass;
class QSBooleanClass;
class QSForInNode;
class QSEngine;
class QSEnv;

/**
   * Error codes.
   */
enum ErrorType { NoError = 0,
		 GeneralError,
		 EvalError,
		 RangeError,
		 ReferenceError,
		 SyntaxError,
		 TypeError,
		 URIError,
		 ThrowError };

enum ValueType {
    TypeValue,
    TypeShared,
    TypeClass,
    TypeOther
};

struct QSFakeQString {
public:
    struct Data {
        QBasicAtomic ref;
        int alloc, size;
        ushort *data;
        ushort clean : 1;
        ushort simpletext : 1;
        ushort righttoleft : 1;
        ushort asciiCache : 1;
        ushort reserved : 12;
        ushort array[1];
    };
    Data *d;
};

QSFakeQString::Data *qsa_qstring_to_data(const QString &str);
QString qsa_qstring_from_data(QSFakeQString::Data *data);


// Reggie: This is a really ugly hack to get QSA link on windows
double NaN();
double Inf();
#define NaN NaN()
#define Inf Inf()

// forward declarations
class Imp;
class QSShared;
struct QSProperty;
typedef QMap<QString, QSProperty> QSPropertyMap;
class PropList;
class QSMember;

union Value {
    int i;
    double d;
    bool b;
    QSFakeQString::Data *str;
    QSShared *sh;
    QSClass *cl;
    void *other;
};

/**
   * @short Main base class for every Qt Script object.
   */


class QSElementNode;
class QSObject;

class QSObject {
    friend class QSStringClass;
    friend class QSEnv;
public:
    /**
     * Constructor.
     */
    QSObject();
    QSObject(const QSClass *c);
    QSObject(const QSClass *c, QSShared *s);
    /**
     * Copy constructor.
     */
    QSObject(const QSObject &);

    /*
     * Assignment operator
     */
    QSObject& operator=(const QSObject &);

    bool operator==(const QSObject &a) const;

    /**
     * Destructor.
     */
    ~QSObject();
    bool isValid() const;
    void invalidate() { clss = 0; }
    /**
     * @return True if this objects is of any other value than Undefined.
     */
    bool isDefined() const;
    /**
     * @return the type of the object. One of the @ref Type enums.
     */
    QString typeName() const;

    ValueType valueType() const;

    /*! Return the object's most specific type
     */
    inline const QSClass *objectType() const;
    void setType(QSClass *c);

    bool isA(const QSClass *c) const;
    /**
     * Check whether object is of a certain type. Allows checking of
     * host objects, too.
     * @param type name (Number, Boolean etc.)
     */
    bool isA(const char *s) const;

    bool isObject() const;
    bool isNull() const;
    bool isUndefined() const;
    bool isNumber() const;
    bool isString() const;
    bool isBoolean() const;
    bool isFunction() const;
    bool isPrimitive() const;

    bool equals(const QSObject &other) const;
    bool strictEquals(const QSObject &other) const;
    QSCompareResult compareTo(const QSObject &other) const;

    /*!
     * Returns the environment this object belongs to.
     */

    QSEnv *env() const;
    /**
     * Examine the inheritance structure of this object.
     * @param t Name of the base class.
     * @return True if object is of type t or a derived from such a type.
     */
    //     bool derivedFrom(const char *s) const;

    /**
     * @return Conversion to primitive type (Undefined, Boolean, Number
     * or String)
     * @param preferred Optional hint. Either StringType or NumberType.
     */
    QSObject toPrimitive(const QSClass *preferred=0) const; // ECMA 9.1
    /**
     * @return Conversion to Boolean type.
     */
    bool toBoolean() const; // ECMA 9.2
    /**
     * @return Conversion to Number type.
     */
    double toNumber() const; // ECMA 9.3
    QVariant toVariant(QVariant::Type t) const;
    /**
     * @return Conversion to double. 0.0 if conversion failed.
     */
    double round() const;
    /**
     * @return Conversion to Number type containing an integer value.
     */
    int toInteger() const; // ECMA 9.4
    /**
     * @return Conversion to signed integer value.
     */
    int toInt32() const; // ECMA 9.5
    /**
     * @return Conversion to unsigned integer value.
     */
    unsigned int toUInt32() const; // ECMA 9.6
    /**
     * @return Conversion to unsigned short value.
     */
    unsigned short toUInt16() const; // ECMA 9.7
    /**
     * @return Conversion to String type.
     */
    QString toString() const; // ECMA 9.8

    /*
      Gets a qualified name in this object. E.g. if name is
      a.b.c, c is returned based on this.a.b.c
    */
    QSObject getQualified(const QString &name) const;

    // Properties
    /**
     * The internal [[Get]] method.
     * @return The value of property p.
     */
    QSObject get(const QString &p) const;
    /**
     * The internal [[HasProperty]] method.
     * @param p Property name.
     * @return Boolean value indicating whether the object already has a
     * member with the given name p.
     */
    bool hasProperty(const QString &p) const;
    void put(const QString &p, const QSObject& v);
    /**
     * The internal [[Delete]] method. Removes the specified property from
     * the object.
     * @param p Property name.
     * @return True if the property was deleted successfully or didn't exist
     * in the first place. False if the DontDelete attribute was set.
     */
    bool deleteProperty(const QString &p);
    /**
     * Convenience function for adding a Number property constructed from
     * a double value.
     */
    void put(const QString &p, double d);
    /**
     * Convenience function for adding a Number property constructed from
     * an integer value.
     */
    void put(const QString &p, int i);
    /**
     * Convenience function for adding a Number property constructed from
     * an unsigned integer value.
     */
    void put(const QString &p, unsigned int u);
    bool isExecutable() const;
    /**
     * Execute function implemented via the @ref Function::execute() method.
     *
     * Note: check availability via @ref implementsCall() beforehand.
     * @param thisV Object serving as the 'this' value.
     * @param args Pointer to the list of arguments or null.
     * @return Result of the function call.
     */
    QSObject executeCall(QSObject *thisV,
			  const QSList *args);

    QSObject execute(const QSList &args);

    QSObject invoke(const QSMember &mem, const QSList &args);

    QSObject fetchValue(const QSMember &mem);
    void write(const QSMember &mem, const QSObject &val);

    void mark();

    const QSClass * resolveMember(const QString &name, QSMember *mem, const QSClass *owner=0, int *offset=0) const;

#ifdef QSDEBUG_MEM
    /**
     * @internal
     */
    static int count;
#endif
    int iVal() const { return val.i; }
    void setVal(int v) { val.i = v; }

    double dVal() const { return val.d; }
    void setVal(double v) { val.d = v; }

    bool bVal() const { return val.b; }
    void setVal(bool v) { val.b = v; }

    QString sVal() const;
    void setVal(const QString &v);

    void *oVal() const { return val.other; }
    void setVal(void *v) { val.other = v; }

    QSShared *shVal() const { return val.sh; }
    void setVal(QSShared *s);

    QString debugString() const;
protected:
    const QSClass *clss;

private:
#if QS_DEBUG_MEM >= 1
    void checkShared() const;
#endif

    Value val;
}; // end of QSObject

inline QString QSObject::sVal() const {
    return qsa_qstring_from_data(val.str);
}

inline void QSObject::setVal(const QString &v)
{
    val.str = qsa_qstring_to_data(v);
    val.str->ref.ref();
}

inline void QSObject::setVal(QSShared *s)
{
#if QS_DEBUG_MEM >= 1
    checkShared();
#endif
    val.sh = s;
}

class QSShared {
    friend class QSEnv;
public:
    QSShared() : count(1), next(0), prev(0) { }
    virtual ~QSShared() { }
    virtual void invalidate() { }
    bool isConnected() const { return next!=0 || prev!=0; }

    void ref() { ++count; }
    bool deref() { return !--count; }
    int count;

private:
    QSShared *next;
    QSShared *prev;
};

class QSWritable : public QSShared {
public:
    QSWritable();
    ~QSWritable();

    void setProperty(const QString &n, const QSProperty &p);
    QSProperty *reference(const QString &n) const;
    bool hasProperty(const QString &n) const;
    QSPropertyMap *properties() const { return props; }

    void invalidate();

private:
    QSPropertyMap *props;
};

struct QSProperty {
    QSProperty() { }
    QSProperty(const QSObject &o) : object(o) { }
    void invalidate() { object.invalidate(); }
    QSObject object;
};

class QSEngineImp;
/**
 * The Global object represents the global namespace. It holds the native
 * objects like String and functions like eval().
   *
   * It also serves as a container for variables created by the user, i.e.
   * the statement
   * <pre>
   *   var i = 2;
   * </pre>
   * will basically perform a Global::current().put("i", Number(2)); operation.
   *
   * @short Unique global object containing initial native properties.
   */
class Global : public QSObject {
    friend class QSEngineImp;
public:
private:
    Global(QSEngine *e);
    void init();
    void clear();
    QSEngine *eng;
};

inline const QSClass *QSObject::objectType() const
{
    Q_ASSERT(clss);
    return clss;
}

#endif
