/*
 *  Copyright (C) 2010 Espial Group Inc. All rights reserved.
 *
 *  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.
 *
 */

#ifndef JSObjectBridge_h
#define JSObjectBridge_h

#include "JSArray.h"
#include "CallData.h"
#include "JSDestructibleObject.h"
#include "JSGlobalObject.h"
#include "PrivateName.h"
#include <wtf/text/WTFString.h>

namespace WebCore
{
class DOMWindow;
class DOMWrapperWorld;
class Element;
class Frame;
class File;
class Node;
class HTMLImageElement;
class HTMLObjectElement;
class JSDOMWindow;
class JSDOMWindowShell;
class JSHTMLObjectElement;
class WebGLTexture;
}

namespace JSC
{
class ArgList;
class ExecState;
class Identifier;
class JSFunction;
class JSGlobalObject;
class JSObject;
class JSString;
class MarkedArgumentBuffer;
class SlotVisitor;

namespace Bindings
{
	class RootObject;
}

ALWAYS_INLINE ExecState* JSGlobalObject::globalExec()
{
    return CallFrame::create(m_globalCallFrame + JSStack::CallFrameHeaderSize);
}
}

namespace WebCoreAPIs
{

class JSCustomInstance;

JSC::PrivateName createPrivateName();

JSC::JSValue get(JSC::JSValue, JSC::ExecState* exec, JSC::PropertyName propertyName);
JSC::JSValue get(JSC::JSValue, JSC::ExecState* exec, const String&);
JSC::JSValue get(JSC::JSValue, JSC::ExecState* exec, unsigned);
JSC::JSValue get(JSC::ExecState* exec, JSC::PropertySlot slot, JSC::PropertyName propertyName);

bool getDirect(JSC::JSObject*, JSC::ExecState* exec, const String&, JSC::PropertySlot&);
bool getDirect(JSC::JSObject*, JSC::ExecState* exec, JSC::PropertyName, JSC::PropertySlot&);
void putDirect(JSC::JSObject*, JSC::ExecState* exec, const String&, JSC::JSValue value, unsigned attributes);
void putDirect(JSC::JSObject*, JSC::JSGlobalData& globalData, JSC::PropertyName, JSC::JSValue value, unsigned attributes);

WebCore::Node* toNode(JSC::JSValue value);

WebCore::HTMLObjectElement* toHTMLObjectElement(JSC::JSValue thisValue);
WebCore::JSHTMLObjectElement* toJSHTMLObjectElement(JSC::JSValue thisValue);

#if ENABLE(WEBGL)
unsigned int toWebGLTextureObject(JSC::JSValue value);
#endif

typedef void (JSC_HOST_CALL *JSCustomDestroy)(void* customContext);

typedef JSC::EncodedJSValue (JSC_HOST_CALL *JSCustomCall)(JSC::ExecState* exec, void* customContext);
JSC::JSObject* createJSFunction(JSC::ExecState* exec, JSCustomCall customCall, JSCustomDestroy customDestroy = 0, void* customContext = 0);

typedef bool (JSC_HOST_CALL *JSCustomPut)(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&, void* customContext);
typedef bool (JSC_HOST_CALL *JSCustomPutByIndex)(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow, void* customContext);
typedef bool (JSC_HOST_CALL *JSCustomGet)(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&, void* customContext);
typedef bool (JSC_HOST_CALL *JSCustomGetByIndex)(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&, void* customContext);
typedef void (JSC_HOST_CALL *JSCustomVisitChildren)(JSCustomInstance* customInstance, JSC::SlotVisitor& visitor, void* customContext);

class JSCustomObjectConstructorData
{
public:
	JSCustomObjectConstructorData()
	: m_customPut(0)
	, m_customPutByIndex(0)
	, m_customGet(0)
	, m_customGetByIndex(0)
	, m_customDestroy(0)
	, m_customVisitor(0)
	, m_customContext(0)
	{
	}

	JSCustomObjectConstructorData(JSCustomPut customPut, JSCustomPutByIndex customPutByIndex, JSCustomGet customGet, JSCustomGetByIndex customGetByIndex,
			JSCustomDestroy customDestroy, void* customContext, JSCustomVisitChildren customVisitor)
	: m_customPut(customPut)
	, m_customPutByIndex(customPutByIndex)
	, m_customGet(customGet)
	, m_customGetByIndex(customGetByIndex)
	, m_customDestroy(customDestroy)
	, m_customVisitor(customVisitor)
	, m_customContext(customContext)
	{
	}

public:
	JSCustomPut m_customPut;
	JSCustomPutByIndex m_customPutByIndex;
	JSCustomGet m_customGet;
	JSCustomGetByIndex m_customGetByIndex;
	JSCustomDestroy m_customDestroy;
	JSCustomVisitChildren m_customVisitor;
	void* m_customContext;
};

class JSCustomInstance : public JSC::JSDestructibleObject
{
private:
class Data
{
	WTF_MAKE_FAST_ALLOCATED;

	friend class JSCustomInstance;

public:
	ALWAYS_INLINE ~Data()
	{
		if(m_customDestroy)
			m_customDestroy(m_customContext);
	}

private:
	ALWAYS_INLINE Data()
	: m_customPut(0)
	, m_customPutByIndex(0)
	, m_customGet(0)
	, m_customGetByIndex(0)
	, m_customDestroy(0)
	, m_customVisitor(0)
	, m_customContext(0)
	{
	}

	ALWAYS_INLINE Data(const JSCustomObjectConstructorData& constructorData)
	: m_customPut(constructorData.m_customPut)
	, m_customPutByIndex(constructorData.m_customPutByIndex)
	, m_customGet(constructorData.m_customGet)
	, m_customGetByIndex(constructorData.m_customGetByIndex)
	, m_customDestroy(constructorData.m_customDestroy)
	, m_customVisitor(constructorData.m_customVisitor)
	, m_customContext(constructorData.m_customContext)
	{
	}

	ALWAYS_INLINE bool put(JSC::JSObject* object, JSC::ExecState* exec, JSC::PropertyName propertyName, JSC::JSValue value, JSC::PutPropertySlot& slot)
	{
		if(m_customPut)
			return m_customPut(object, exec, propertyName, value, slot, m_customContext);

		return false;
	}

	ALWAYS_INLINE bool putByIndex(JSC::JSObject* object, JSC::ExecState* exec, unsigned propertyName, JSC::JSValue value, bool shouldThrow)
	{
		if(m_customPutByIndex)
			return m_customPutByIndex(object, exec, propertyName, value, shouldThrow, m_customContext);

		return false;
	}

	ALWAYS_INLINE bool get(JSC::JSObject* object, JSC::ExecState* exec, JSC::PropertyName propertyName, JSC::PropertySlot& slot)
	{
		if(m_customGet)
			return m_customGet(object, exec, propertyName, slot, m_customContext);

		return false;
	}

	ALWAYS_INLINE bool getByIndex(JSC::JSObject* object, JSC::ExecState* exec, unsigned propertyName, JSC::PropertySlot& slot)
	{
		if(m_customGetByIndex)
			return m_customGetByIndex(object, exec, propertyName, slot, m_customContext);

		return false;
	}

	ALWAYS_INLINE void visitChildren(JSCustomInstance* customInstance, JSC::SlotVisitor& visitor)
	{
		if(m_customVisitor)
			m_customVisitor(customInstance, visitor, m_customContext);
	}

	JSCustomPut m_customPut;
	JSCustomPutByIndex m_customPutByIndex;
	JSCustomGet m_customGet;
	JSCustomGetByIndex m_customGetByIndex;
	JSCustomDestroy m_customDestroy;
	JSCustomVisitChildren m_customVisitor;
	void* m_customContext;
};

public:
	typedef JSC::JSDestructibleObject Base;

	~JSCustomInstance();

	DECLARE_INFO;

    static JSC::Structure* createStructure(JSC::JSGlobalData& globalData, JSC::JSGlobalObject* globalObject, JSC::JSValue proto);

	static bool put(JSC::JSCell* cell, JSC::ExecState* exec, JSC::PropertyName propertyName, JSC::JSValue value, JSC::PutPropertySlot& slot);

	static bool putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow);

    static bool getOwnPropertySlot(JSC::JSObject*, JSC::ExecState* exec, JSC::PropertyName propertyName, JSC::PropertySlot& slot);

    static bool getOwnPropertySlotByIndex(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&);

    static void destroy(JSC::JSCell* cell);

    static void visitChildren(JSC::JSCell* cell, JSC::SlotVisitor& visitor);

    ALWAYS_INLINE JSCustomInstance(JSC::ExecState* exec);
    ALWAYS_INLINE JSCustomInstance(JSC::ExecState* exec, const JSCustomObjectConstructorData& constructorData);

    void finishCreation(JSC::JSGlobalData& globalData);

    void* customContext() { return m_private->m_customContext; }

protected:
    static const unsigned StructureFlags = JSC::OverridesGetOwnPropertySlot | JSC::InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | Base::StructureFlags;

private:
    OwnPtr<Data> m_private;
};

void append(JSC::SlotVisitor& visitor, JSC::JSValue v);

JSC::JSObject* createJSError(JSC::ExecState* exec, const String& message);

JSC::JSObject* createJSObject(JSC::ExecState* exec, const JSCustomObjectConstructorData& constructorData = JSCustomObjectConstructorData());

ALWAYS_INLINE JSC::JSObject* createJSObject(JSC::ExecState* exec, JSCustomPut customPut, JSCustomGet customGet, JSCustomDestroy customDestroy = 0, void* customContext = 0)
{
	JSCustomObjectConstructorData constructorData(customPut, 0, customGet, 0, customDestroy, customContext, 0);
	return createJSObject(exec, constructorData);
}

JSCustomInstance* asJSCustomInstance(JSC::JSValue value);

typedef JSC::EncodedJSValue (JSC_HOST_CALL *JSCustomConstruct)(JSC::ExecState* exec, void* customContext);
JSC::JSObject* createJSConstructor(JSC::ExecState* exec, JSCustomConstruct customConstruct, JSCustomDestroy customDestroy, void* customContext = 0);

class JSArrayBridge
{
public:
	static JSC::JSArray* create(JSC::ExecState* exec);

    static void push(JSC::JSArray*, JSC::ExecState*, JSC::JSValue);
    static void setLength(JSC::JSArray*, JSC::ExecState* exec, unsigned);
};


JSC::JSValue newDateInstance(JSC::ExecState* exec, double d);

JSC::JSValue newDateInstance(
		JSC::ExecState* exec,
		int year,	/* specified as an number, i.e. 2010 */
		int month, 	/* specified as a number (0-11), i.e. 5 */
		int day,  	/* day of the month specified as a number (1-31), i.e. 18 */
		int hour, 	/* Hour specified as a number (0-23), i.e. 13 */
		int minute, /* specified as a number (0-59), i.e. 34 */
		int second, /* specified as a number (0-59), i.e. 23 */
		int ms		/* milli-second specified as a number (0-999), i.e. 240 */
		);

bool parseDateInstance(
		JSC::ExecState* exec,
		JSC::JSValue value,
		int& year,	/* specified as an number, i.e. 2010 */
		int& month,	/* specified as a number (0-11), i.e. 5 */
		int& day,  	/* day of the month specified as a number (1-31), i.e. 18 */
		int& hour, 	/* Hour specified as a number (0-23), i.e. 13 */
		int& minute,/* specified as a number (0-59), i.e. 34 */
		int& second,/* specified as a number (0-59), i.e. 23 */
		int& ms		/* milli-second specified as a number (0-999), i.e. 240 */
		);

bool isJSArray(JSC::JSValue v);

JSC::JSValue newJSNaN();
double Inf();

JSC::JSValue toPrimitive(JSC::ExecState*, JSC::JSValue);

JSC::JSValue toJS(JSC::ExecState*, WebCore::Frame*, RefPtr<WebCore::HTMLImageElement>);

JSC::JSValue toJS(JSC::ExecState*, WebCore::Frame*, PassRefPtr<WebCore::File>);

JSC::JSValue toJS(JSC::ExecState*, WebCore::DOMWindow*);

bool toBoolean(JSC::ExecState*, JSC::JSValue);
int32_t toInt32(JSC::ExecState*, JSC::JSValue);
uint32_t toUInt32(JSC::ExecState*, JSC::JSValue);
double toInteger(JSC::ExecState*, JSC::JSValue);
double toNumber(JSC::ExecState*, JSC::JSValue);

ALWAYS_INLINE JSC::JSValue newJSNumber(double d) { return JSC::jsNumber(d); }
ALWAYS_INLINE JSC::JSValue newJSNumber(char i) { return JSC::jsNumber(i); }
ALWAYS_INLINE JSC::JSValue newJSNumber(unsigned char i) { return JSC::jsNumber(i); }
ALWAYS_INLINE JSC::JSValue newJSNumber(short i) { return JSC::jsNumber(i); }
ALWAYS_INLINE JSC::JSValue newJSNumber(unsigned short i) { return JSC::jsNumber(i); }
ALWAYS_INLINE JSC::JSValue newJSNumber(int i) { return JSC::jsNumber(i); }
ALWAYS_INLINE JSC::JSValue newJSNumber(unsigned i) { return JSC::jsNumber(i); }
ALWAYS_INLINE JSC::JSValue newJSNumber(long i) { return JSC::jsNumber(i); }
ALWAYS_INLINE JSC::JSValue newJSNumber(unsigned long i) { return JSC::jsNumber(i); }
ALWAYS_INLINE JSC::JSValue newJSNumber(long long i) { return JSC::jsNumber(i); }
ALWAYS_INLINE JSC::JSValue newJSNumber(unsigned long long i) { return JSC::jsNumber(i); }

JSC::JSString* newJSString(JSC::ExecState*, const char*);

JSC::JSValue newJSString(JSC::ExecState* exec, const WTF::String& s);

JSC::JSValue newJSSingleCharacterString(JSC::ExecState*, UChar);

void append(JSC::MarkedArgumentBuffer&, JSC::JSValue v);

WebCore::DOMWrapperWorld* mainThreadNormalWorld();
WebCore::JSDOMWindow* globalObject(WebCore::Frame*);

JSC::Bindings::RootObject* bindingRootObject(WebCore::Frame*);

#if ENABLE(MOTTO_TV)
Vector<String> getPropertyNames(JSC::ExecState* exec, JSC::JSValue v);
#endif

/**
 * dispatch event to "obj.a.b" with optional arguments
 */
bool dispatchEvent(WebCore::Frame*, const Vector<String, 2>&, const JSC::MarkedArgumentBuffer& args, JSC::JSValue& jsReturnValue);

bool dispatchEvent(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, JSC::JSValue listener, const JSC::MarkedArgumentBuffer& args, JSC::JSValue& jsReturnValue);

inline bool dispatchEvent(WebCore::Frame* frame, const Vector<String, 2>& objects, const JSC::MarkedArgumentBuffer& args)
{
	JSC::JSValue jsReturnValue;
	return dispatchEvent(frame, objects, args, jsReturnValue);
}

inline bool dispatchEvent(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, JSC::JSValue listener, const JSC::MarkedArgumentBuffer& args)
{
	JSC::JSValue jsReturnValue;
	return dispatchEvent(exec, globalObject, listener, args, jsReturnValue);
}

JSC::JSValue throwException(JSC::ExecState*, JSC::JSValue);
JSC::JSObject* throwException(JSC::ExecState*, JSC::JSObject*);

} // namespace WebCoreAPIs

namespace WebCore
{

class Document;

Document* getDocument(JSC::ExecState*, JSC::JSGlobalObject*);
String toStringWithNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null

JSC::JSValue getProperty(JSC::JSObject*, JSC::ExecState*, const String&);

} // namespace WebCore

#endif

