/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#ifndef __OBJFW_RUNTIME_H__
#define __OBJFW_RUNTIME_H__
#include <stdint.h>

#ifndef __has_feature
# define __has_feature(x) 0
#endif

#if __has_feature(objc_arc)
# define OBJC_BRIDGE __bridge
#else
# define OBJC_BRIDGE
#endif

typedef struct objc_class *Class;
typedef struct objc_object *id;
typedef const struct objc_selector *SEL;
typedef signed char BOOL;
typedef id (*IMP)(id, SEL, ...);

struct objc_class {
	Class isa;
	Class superclass;
	const char *name;
	unsigned long version;
	unsigned long info;
	unsigned long instance_size;
	struct objc_ivar_list *ivars;
	struct objc_method_list *methodlist;
	struct objc_sparsearray *dtable;
	Class *subclass_list;
	void *sibling_class;
	struct objc_protocol_list *protocols;
	void *gc_object_type;
	unsigned long abi_version;
	void *ivar_offsets;
	struct objc_property_list *properties;
};

enum objc_abi_class_info {
	OBJC_CLASS_INFO_CLASS	    = 0x001,
	OBJC_CLASS_INFO_METACLASS   = 0x002,
	OBJC_CLASS_INFO_NEW_ABI	    = 0x010,
	OBJC_CLASS_INFO_SETUP	    = 0x100,
	OBJC_CLASS_INFO_LOADED	    = 0x200,
	OBJC_CLASS_INFO_DTABLE	    = 0x400,
	OBJC_CLASS_INFO_INITIALIZED = 0x800
};

struct objc_object {
	Class isa;
};

struct objc_selector {
	uintptr_t uid;
	const char *types;
};

struct objc_super {
	id self;
	Class class;
};

struct objc_method {
	struct objc_selector sel;
	IMP imp;
};

struct objc_method_list {
	struct objc_method_list *next;
	unsigned int count;
	struct objc_method methods[1];
};

struct objc_category {
	const char *category_name;
	const char *class_name;
	struct objc_method_list *instance_methods;
	struct objc_method_list *class_methods;
	struct objc_protocol_list *protocols;
};

struct objc_ivar {
	const char *name;
	const char *type;
	unsigned offset;
};

struct objc_ivar_list {
	unsigned count;
	struct objc_ivar ivars[1];
};

#define OBJC_PROPERTY_READONLY	0x01
#define OBJC_PROPERTY_GETTER	0x02
#define OBJC_PROPERTY_ASSIGN	0x04
#define OBJC_PROPERTY_READWRITE	0x08
#define OBJC_PROPERTY_RETAIN	0x10
#define OBJC_PROPERTY_COPY	0x20
#define OBJC_PROPERTY_NONATOMIC	0x40
#define OBJC_PROPERTY_SETTER	0x80

struct objc_property {
	const char *name;
	unsigned char attributes;
	BOOL synthesized;
	struct {
		const char *name;
		const char *type;
	} getter, setter;
};

struct objc_property_list {
	unsigned count;
	struct objc_property_list *next;
	struct objc_property properties[1];
};

#ifdef __OBJC__
@interface Protocol
{
@public
#else
typedef struct {
#endif
	Class isa;
	const char *name;
	struct objc_protocol_list *protocol_list;
	struct objc_abi_method_description_list *instance_methods;
	struct objc_abi_method_description_list *class_methods;
#ifdef __OBJC__
}
@end
#else
} Protocol;
#endif

struct objc_protocol_list {
	struct objc_protocol_list *next;
	long count;
	Protocol *list[1];
};

#define Nil (Class)0
#define nil (id)0
#define YES (BOOL)1
#define NO  (BOOL)0

typedef void (*objc_uncaught_exception_handler)(id);

extern SEL sel_registerName(const char*);
extern const char* sel_getName(SEL);
extern BOOL sel_isEqual(SEL, SEL);
extern Class objc_get_class(const char*);
extern Class objc_lookup_class(const char*);
extern const char* class_getName(Class);
extern Class class_getSuperclass(Class);
extern BOOL class_isKindOfClass(Class, Class);
extern unsigned long class_getInstanceSize(Class);
extern BOOL class_respondsToSelector(Class, SEL);
extern BOOL class_conformsToProtocol(Class, Protocol*);
extern IMP class_getMethodImplementation(Class, SEL);
extern IMP class_replaceMethod(Class, SEL, IMP, const char*);
extern const char* objc_get_type_encoding(Class, SEL);
extern IMP objc_msg_lookup(id, SEL);
extern IMP objc_msg_lookup_super(struct objc_super*, SEL);
extern const char* protocol_getName(Protocol*);
extern BOOL protocol_isEqual(Protocol*, Protocol*);
extern BOOL protocol_conformsToProtocol(Protocol*, Protocol*);
extern void objc_thread_add(void);
extern void objc_thread_remove(void);
extern void objc_exit(void);
extern objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(
    objc_uncaught_exception_handler);
extern IMP (*objc_forward_handler)(id, SEL);
extern id objc_autorelease(id);
extern void* objc_autoreleasePoolPush(void);
extern void objc_autoreleasePoolPop(void*);
extern id _objc_rootAutorelease(id);

static inline Class
object_getClass(id obj_)
{
	struct objc_object *obj = (OBJC_BRIDGE struct objc_object*)obj_;

	return obj->isa;
}

static inline Class
object_setClass(id obj_, Class cls)
{
	struct objc_object *obj = (OBJC_BRIDGE struct objc_object*)obj_;
	Class old = obj->isa;

	obj->isa = cls;

	return old;
}

static inline const char*
object_getClassName(id obj)
{
	return class_getName(object_getClass(obj));
}

static inline BOOL
class_isMetaClass(Class cls)
{
	return (cls->info & OBJC_CLASS_INFO_METACLASS);
}

#undef OBJC_BRIDGE

#endif
