#ifndef SCRIPTHELPER_H
#define SCRIPTHELPER_H

#include <sstream>

#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif


BEGIN_AS_NAMESPACE

// Compare relation between two objects of the same type
int CompareRelation(asIScriptEngine *engine, void *lobj, void *robj, int typeId, int &result);

// Compare equality between two objects of the same type
int CompareEquality(asIScriptEngine *engine, void *lobj, void *robj, int typeId, bool &result);

// Compile and execute simple statements
// The module is optional. If given the statements can access the entities compiled in the module.
// The caller can optionally provide its own context, for example if a context should be reused.
int ExecuteString(asIScriptEngine *engine, const char *code, asIScriptModule *mod = 0, asIScriptContext *ctx = 0);

// Compile and execute simple statements with option of return value
// The module is optional. If given the statements can access the entitites compiled in the module.
// The caller can optionally provide its own context, for example if a context should be reused.
int ExecuteString(asIScriptEngine *engine, const char *code, void *ret, int retTypeId, asIScriptModule *mod = 0, asIScriptContext *ctx = 0);

// Write the registered application interface to a file for an offline compiler.
// The format is compatible with the offline compiler in /sdk/samples/asbuild/.
int WriteConfigToFile(asIScriptEngine *engine, const char *filename);

// Write the registered application interface to a text stream.
int WriteConfigToStream(asIScriptEngine *engine, std::ostream &strm); 
int ConfigEngineFromStream(asIScriptEngine *engine, std::istream &strm, const char *nameOfStream = "config");

// Print details of the script exception to the standard output
void PrintException(asIScriptContext *ctx, bool printStack = false);

// Determine traits of a type for registration of value types
// Relies on C++11 features so it can not be used with non-compliant compilers
#ifdef AS_CAN_USE_CPP11

END_AS_NAMESPACE
#include <type_traits>
BEGIN_AS_NAMESPACE

template<typename T>
asUINT GetTypeTraits()
{
	bool hasConstructor =  std::is_default_constructible<T>::value && !std::has_trivial_default_constructor<T>::value;
#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) 
	// http://stackoverflow.com/questions/12702103/writing-code-that-works-when-has-trivial-destructor-is-defined-instead-of-is
	bool hasDestructor = std::is_destructible<T>::value && !std::is_trivially_destructible<T>::value;
#else
	bool hasDestructor = std::is_destructible<T>::value && !std::has_trivial_destructor<T>::value;
#endif
	bool hasAssignmentOperator = std::is_copy_assignable<T>::value && !std::has_trivial_copy_assign<T>::value;
	bool hasCopyConstructor = std::is_copy_constructible<T>::value && !std::has_trivial_copy_constructor<T>::value;
	bool isFloat = std::is_floating_point<T>::value;
	bool isPrimitive = std::is_integral<T>::value || std::is_pointer<T>::value || std::is_enum<T>::value;
	bool isClass = std::is_class<T>::value;
	bool isArray = std::is_array<T>::value;

	if( isFloat )
		return asOBJ_APP_FLOAT;
	if( isPrimitive )
		return asOBJ_APP_PRIMITIVE;
	
	if( isClass )
	{
		asDWORD flags = asOBJ_APP_CLASS;
		if( hasConstructor )
			flags |= asOBJ_APP_CLASS_CONSTRUCTOR;
		if( hasDestructor )
			flags |= asOBJ_APP_CLASS_DESTRUCTOR;
		if( hasAssignmentOperator )
			flags |= asOBJ_APP_CLASS_ASSIGNMENT;
		if( hasCopyConstructor )
			flags |= asOBJ_APP_CLASS_COPY_CONSTRUCTOR;
		return flags;
	}

	if( isArray )
		return asOBJ_APP_ARRAY;

	// Unknown type traits
	return 0;
}

#endif // c++11

END_AS_NAMESPACE

#endif
