/***************************************************************************
  
  gbx_class.c
  
  (c) Benoît Minisini <benoit.minisini@gambas-basic.org>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.
  
  This program 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 General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  MA 02110-1301, USA.
  
***************************************************************************/

#define __GBX_CLASS_C

#include "gb_common.h"
#include "gb_common_buffer.h"
#include "gb_common_case.h"
#include "gb_alloc.h"
#include "gb_error.h"
#include "gb_limit.h"

#include <ctype.h>

#include "gb_buffer.h"
#include "gb_file.h"
#include "gbx_type.h"
#include "gbx_exec.h"
#include "gbx_debug.h"
#include "gb_magic.h"
#include "gbx_stream.h"
#include "gbx_struct.h"
#include "gbx_string.h"
#include "gbx_object.h"
#include "gbx_variant.h"
#include "gbx_number.h"
#include "gbx_c_array.h"
#include "gbx_jit.h"

#include "gambas.h"

#include "gbx_class.h"

//#define DEBUG_COMP 1
//#define DEBUG_LOAD 1
//#define DEBUG_DESC 1

// We are exiting...
bool CLASS_exiting = FALSE;

// Global class table
static TABLE _global_table;
// List of all classes
static CLASS *_classes = NULL;
// First created class (must be 'Class' class)
static CLASS *_first = NULL;
// If set, do not create a new class, but register that class in the global table
static CLASS *_global_class = NULL;


#ifdef DEBUG
static CLASS *Class;
#endif


void CLASS_init(void)
{
	TABLE_create_static(&_global_table, sizeof(CLASS_SYMBOL), TF_IGNORE_CASE);

	CLASS_init_native();
}


TABLE *CLASS_get_table(void)
{
	return &_global_table;
}

CLASS *CLASS_get_list(void)
{
	return _classes;
}

static void exit_class(CLASS *class, bool native)
{
	//CLASS_DESC *desc;

	/*if (!class->ready && !CLASS_is_virtual(class))
		printf("WARNING: class %s was never loaded.\n", class->name);*/

	#if DEBUG_LOAD
		fprintf(stderr, "Exiting class %s...\n", class->name);
	#endif

	/* Est-ce que _exit marche pour les classes gambas ? */

	if (CLASS_is_native(class) != native)
		return;

	if (!CLASS_is_loaded(class))
		return;

	EXEC_public(class, NULL, "_exit", 0);
}

static void unload_class(CLASS *class)
{
	#if DEBUG_LOAD
		fprintf(stderr, "Unloading class %s...\n", class->name);
	#endif

	if (class->is_struct)
	{
		FREE(&class->load->dyn);
		if (class->debug)
			FREE(&class->load->global);
		FREE(&class->load);
		FREE(&class->data);
	}
	else if (!CLASS_is_native(class))
	{
		#ifdef OS_64BITS

			if (class->load)
			{
				FREE(&class->load->desc);
				FREE(&class->load->cst);
				FREE(&class->load->class_ref);
				FREE(&class->load->unknown);
				FREE(&class->load->event);
				FREE(&class->load->ext);
				FREE(&class->load->local);
				FREE(&class->load->array);

				if (class->debug)
				{
					int i;
					FUNCTION *func;

					for (i = 0; i < class->load->n_func; i++)
					{
						func = &class->load->func[i];
						FREE(&func->debug->local);
					}

					FREE(&class->load->global);
					FREE(&class->load->debug);
				}

				FREE(&class->load->func);
			}

		#endif

		if (class->load)
			FREE(&class->load->prof);

		FREE(&class->load);
		//if (!class->mmapped)
		FREE(&class->data);
	}
	else
	{
		FREE(&class->signature);
		FREE(&class->data);
	}

	if (class->free_event)
		FREE(&class->event);

	FREE(&class->stat);

	FREE(&class->table);
	FREE(&class->sort);

	class->ready = FALSE;
	class->loaded = FALSE;
}

#define SWAP_FIELD(_v, _s, _d, _f) (_v = _s->_f, _s->_f = _d->_f, _d->_f = _v)

static CLASS *class_keep_global(CLASS *class)
{
	CLASS_DESC_SYMBOL *cds;
	CLASS *parent;
	const char *name;
	char *old_name;
	CLASS *old_class;
	int len;
	CLASS swap;
	char *swap_name;
	//bool swap_free_name;
	int nprefix;
	int i;

	name = class->name;
	len = strlen(name);

	parent = class->parent;
	if (parent)
	{
		for (nprefix = 0;; nprefix++)
		{
			if (parent->name[nprefix] != '^')
				break;
		}
		nprefix++;
	}
	else
		nprefix = 1;
	
	ALLOC(&old_name, len + nprefix + 1);
	for (i = 0; i < nprefix; i++)
		old_name[i] = '^';
	strcpy(&old_name[i], name);

	old_class = CLASS_find_global(old_name);
	
	FREE(&old_name);

	swap = *class;
	*class = *old_class;
	*old_class = swap;

	SWAP_FIELD(swap_name, class, old_class, name);
	//SWAP_FIELD(swap_free_name, class, old_class, free_name);
	SWAP_FIELD(parent, class, old_class, next);
	SWAP_FIELD(i, class, old_class, count);
	SWAP_FIELD(i, class, old_class, ref);

	for (i = 0; i < old_class->n_desc; i++)
	{
		cds = &old_class->table[i];
		if (cds->desc && cds->desc->method.class == class)
			cds->desc->method.class = old_class;
	}

	CLASS_inheritance(class, old_class);
	return class;
}

CLASS *class_replace_global(CLASS *class)
{
	char *old_name;
	CLASS *new_class;
	char *swap_name;
	//bool swap_free_name;
	int index;
	CLASS_SYMBOL *csym;
	const char *name = class->name;
	int len = strlen(class->name);
	
	if (!CLASS_is_loaded(class))
		return class;

	if (class->count == 0)
		return class_keep_global(class);

	//fprintf(stderr, "class_replace_global: %s already has %d objects!\n", class->name, class->count);
	
	ALLOC(&old_name, len + 2);
	old_name[0] = '^';
	strcpy(&old_name[1], name);
	
	new_class = CLASS_find_global(old_name);
	
	if (TABLE_find_symbol(&_global_table, name, len, &index))
  {
		csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index);
		csym->class = new_class;
	}

	if (TABLE_find_symbol(&_global_table, old_name, len + 1, &index))
  {
		csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index);
		csym->class = class;
	}

	FREE(&old_name);
	
	SWAP_FIELD(swap_name, class, new_class, name);
	//SWAP_FIELD(swap_free_name, class, new_class, free_name);

	CLASS_inheritance(new_class, class);
	
	CLASS_update_global(class, new_class);
	
	//fprintf(stderr, "class_replace_global: %s -> %s\n", class->name, new_class->name);
	
	return new_class;
}

static void release_class(CLASS *class)
{
	#if DEBUG_LOAD
	fprintf(stderr, "release_class: %s\n", class->name);
	#endif
	OBJECT_release(class, NULL);
	class->exit = TRUE;
}

void CLASS_clean_up(bool silent)
{
	int n, nc, nb;
	//CLASS_SYMBOL *csym;
	CLASS *class;
	bool circ = FALSE;

	#if DEBUG_LOAD
	fprintf(stderr, "\n------------------- CLASS_clean_up -------------------\n\n");
	#endif

	#if DEBUG_LOAD
	fprintf(stderr, "Freeing auto-creatable objects...\n");
	#endif

	CLASS_exiting = TRUE;

	// Free automatic instances

	for (class = _classes; class; class = class->next)
	{
		#if DEBUG_LOAD
		if (class->instance)
			fprintf(stderr, "Freeing instance of %p %s\n", class, class->name);
		#endif
		OBJECT_UNREF(class->instance);
	}

	// Count how many classes should be freed

	nc = 0;

	for (class = _classes; class; class = class->next)
	{
		if (!CLASS_is_native(class) && CLASS_is_loaded(class))
		{
			#if DEBUG_LOAD
			fprintf(stderr, "Must free: %p (%s) %s [%d]\n", class, class->component ? class->component->name : "-", class->name, class->count);
			#endif
			nc++;
		}
	}

	#if DEBUG_LOAD
	fprintf(stderr, "Must free %d classes...\n", nc);
	#endif

	#if DEBUG_LOAD
	fprintf(stderr, "Calling _exit on loaded classes...\n");
	#endif

	for (class = _classes; class; class = class->next)
		exit_class(class, FALSE);

	#if DEBUG_LOAD
	fprintf(stderr, "Freeing classes with no instance...\n");
	#endif

	// Free classes having no instance

	n = 0;
	for(;;)
	{
		nb = n;

		for (class = _classes; class; class = class->next)
		{
			if (class->count == 0 && !CLASS_is_native(class) && CLASS_is_loaded(class) && !class->exit)
			{
				release_class(class);
				n++;
			}
		}

		if (n == nb) // nothing could be done
			break;
	}

	#if DEBUG_LOAD
	fprintf(stderr, "Calling _exit on native classes...\n");
	#endif

	for (class = _classes; class; class = class->next)
		exit_class(class, TRUE);
	
	#if DEBUG_LOAD
	fprintf(stderr, "Freeing other classes...\n");
	#endif

	// Remaining objects are circular references
	// Everything is forced to be freed

	if (n < nc)
	{
		if (!silent)
		{
			for (class = _classes; class; class = class->next)
			{
				if (!CLASS_is_native(class) && CLASS_is_loaded(class) && !class->exit && (!class->array_class || class->array_class->count == 0)) // && !class->astruct_class)
				{
					if (!silent && class->count)
					{
						if (!circ)
						{
							ERROR_warning("circular references detected:");
							circ = TRUE;
						}
						ERROR_warning("% 5d %s\n", class->count, class->name);
					}
				}
			}
		}
			
		for(;;)
		{
			nb = n;

			for (class = _classes; class; class = class->next)
			{
				if (!CLASS_is_native(class) && CLASS_is_loaded(class) && !class->exit && (!class->array_class || class->array_class->count == 0)) // && !class->astruct_class)
				{
					release_class(class);
					n++;
				}
			}

			if (n == nb) // nothing could be done
				break;
		}
	}
}


void CLASS_exit()
{
	CLASS *class, *next;
	int i;

	#if DEBUG_LOAD
	fprintf(stderr, "Unloading classes...\n");
	#endif

	for (class = _classes; class; class = class->next)
		unload_class(class);

	#if DEBUG_LOAD
	fprintf(stderr, "Destroying classes...\n");
	#endif

	class = _classes;
	while (class)
	{
		next = class->next;
		FREE(&class);
		class = next;
	}

	for (i = 0; i < TABLE_count(&_global_table); i++)
		IFREE(TABLE_get_symbol(&_global_table, i)->name);
	
	TABLE_delete_static(&_global_table);
}


CLASS *CLASS_look_do(const char *name, int len, bool global)
{
	CLASS_SYMBOL *csym;
	ARCHIVE *arch = NULL;
	int index;

	if (len < 0)
		len = strlen(name);

	#if DEBUG_COMP
	fprintf(stderr, "CLASS_look: %.*s in %s", len, name, global ? "global" : "local");
	#endif

	//if (CP && CP->component && CP->component->archive)
	if (!global && !ARCHIVE_get_current(&arch))
	{
		if (TABLE_find_symbol(arch->classes, name, len, &index))
		{
			#if DEBUG_COMP
			fprintf(stderr, " -> in %s\n", arch->name ? arch->name : "main");
			#endif
			csym = (CLASS_SYMBOL *)TABLE_get_symbol(arch->classes, index);
			return csym->class;
		}
	}

	if (TABLE_find_symbol(&_global_table, name, len, &index))
	{
		#if DEBUG_COMP
		fprintf(stderr, " -> %d in global\n", index);
		#endif
		csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index);
		return csym->class;
	}
	else
	{
		#if DEBUG_COMP
		fprintf(stderr, " -> ?\n");
		#endif
		return NULL;
	}
}

CLASS *CLASS_find_do(const char *name, int len, bool global)
{
	CLASS_SYMBOL *csym;
	CLASS *class;
	int index;
	ARCHIVE *arch = NULL;

	if (len < 0)
		len = strlen(name);

#if DEBUG_LOAD
		fprintf(stderr, "CLASS_find: %.*s (%d)\n", len, name, global);
#endif

	class = CLASS_look_do(name, len, global);
	if (class)
	{
		#if DEBUG_LOAD
			fprintf(stderr, "--> %p\n", class);
		#endif
		return class;
	}

	//if (CP && CP->component && CP->component->archive)
	if (!global && !ARCHIVE_get_current(&arch))
	{
		index = TABLE_add_symbol(arch->classes, name, len);
		#if DEBUG_LOAD || DEBUG_COMP
			fprintf(stderr, "Not found -> creating new one in %s\n", arch->name ? arch->name : "main");
		#endif
		csym = (CLASS_SYMBOL *)TABLE_get_symbol(arch->classes, index);
	}
	else
	{
		global = TRUE;
		index = TABLE_add_symbol(&_global_table, name, len);
		#if DEBUG_LOAD || DEBUG_COMP
			fprintf(stderr, "Not found -> creating new one in global\n");
		#endif
		csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index);
	}

	if (_global_class)
		class = _global_class;
	else
	{
		ALLOC_ZERO(&class, sizeof(CLASS));
		class->next = _classes;
		_classes = class;
	}
	
	class->ref++;
	csym->class = class;

	ALLOC(&csym->sym.name, len + 1);
	strcpy(csym->sym.name, name);

	if (!class->name)
		class->name = csym->sym.name;

	// The first class must be the Class class!
	if (_first == NULL)
		_first = class;
	class->class = _first;

	class->global = global;
	
	if (arch)
		class->component = arch->current_component;

	return class;
}


int CLASS_count(void)
{
	return TABLE_count(&_global_table);
}


CLASS *CLASS_get(const char *name)
{
	CLASS *class = CLASS_find(name);

	if (!CLASS_is_loaded(class) && !class->in_load)
		CLASS_load(class);

	return class;
}


CLASS *CLASS_find_export(const char *name, const char *global)
{
	CLASS *class;

	if (name)
	{
		class = CLASS_find(name);
		_global_class = class;
		CLASS_find_global(global);
		_global_class = NULL;
	}
	else
		class = CLASS_find_global(global);
	
	return class;
}

bool CLASS_inherits(const CLASS *class, const CLASS *parent)
{
	for(;;)
	{
		class = class->parent;

		if (class == NULL)
			return FALSE;

		if (class == parent)
			return TRUE;
	}
}


int CLASS_find_symbol_with_prefix(CLASS *class, const char *name, char prefix)
{
	return SYMBOL_find(class->table, class->sort, class->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE, name, strlen(name), prefix);
}


CLASS_DESC_SYMBOL *CLASS_get_symbol(CLASS *class, const char *name)
{
	int index;

	index = CLASS_find_symbol(class, name);
	if (index == NO_SYMBOL)
		return NULL;
	else
		return &class->table[index];
}


CLASS_DESC *CLASS_get_symbol_desc(CLASS *class, const char *name)
{
	CLASS_DESC_SYMBOL *cds = CLASS_get_symbol(class, name);

	if (cds == NULL)
		return NULL;
	else
		return cds->desc;
}


static short CLASS_get_symbol_index_kind(CLASS *class, const char *name, int kind, int kind2, TYPE type, bool error)
{
	CLASS_DESC *desc;
	int index;

	index = CLASS_find_symbol(class, name);
	if (index != NO_SYMBOL)
	{
		desc = CLASS_get_desc(class, index);
		if (desc)
			if ((CLASS_DESC_get_type(desc) == kind) || (CLASS_DESC_get_type(desc) == kind2))
			{
				if (type == T_ANY || desc->method.type == type)
					return (short)index;
				
				if (error)
					THROW(E_SPEC, name, class->name);
			}
	}

	return (short)NO_SYMBOL;
}


CLASS_DESC *CLASS_get_symbol_desc_kind(CLASS *class, const char *name, int kind, int kind2, TYPE type)
{
	short index;

	index = CLASS_get_symbol_index_kind(class, name, kind, kind2, type, TRUE);
	if (index == NO_SYMBOL)
		return NULL;
	else
		return CLASS_get_desc(class, index);
}


CLASS_DESC_EVENT *CLASS_get_event_desc(CLASS *class, const char *name)
{
	int index;
	CLASS_DESC *cd;

	index = CLASS_find_symbol_with_prefix(class, name, ':');
	if (index == NO_SYMBOL)
		return NULL;

	cd = class->table[index].desc;

	if (CLASS_DESC_get_type(cd) != CD_EVENT)
		return NULL;

	return &cd->event;
}


char *CLASS_DESC_get_signature(CLASS_DESC *cd)
{
	char *res = NULL;
	TYPE *sign;
	int i, n;
	TYPE type;

	switch (CLASS_DESC_get_type(cd))
	{
		case CD_METHOD:
		case CD_STATIC_METHOD:

			sign = cd->method.signature;
			n = cd->method.npmax;
			break;

		case CD_EVENT:

			sign = cd->event.signature;
			n = cd->event.npmax;
			break;

		case CD_EXTERN:

			sign = cd->ext.signature;
			n = cd->ext.npmax;
			break;

		default:

			return NULL;
	}

	for (i = 0; i < n; i++)
	{
		type = sign[i];
		res = STRING_add(res, TYPE_to_string(type), 0);
		if (TYPE_is_object(type))
			res = STRING_add_char(res, ';');
	}

	return res;
}

// NOTE: The _free method can be called during a conversion, so we must save the EXEC structure

static void error_CLASS_free(void *object, const EXEC_GLOBAL *save)
{
	EXEC = *save;
	((OBJECT *)object)->ref = 0;
	OBJECT_release(OBJECT_class(object), object);
}

void CLASS_free(void *object)
{
	CLASS *class = OBJECT_class(object);
	EXEC_GLOBAL save;
	
	if (!class->has_free)
	{
		OBJECT_release(class, object);
		return;
	}
	
	save = EXEC;

	ON_ERROR_2(error_CLASS_free, object, &save)
	{
		((OBJECT *)object)->ref = 1; // Prevents anybody from freeing the object!
		EXEC_special_inheritance(SPEC_FREE, class, object, 0, TRUE);
		if (((OBJECT *)object)->ref != 1)
			THROW(E_FREEREF);
		error_CLASS_free(object, &save);
	}
	END_ERROR
}

#if DEBUG_REF
void CLASS_ref(void *object)
{
	char *name;

	((OBJECT *)object)->ref++;

	if (OBJECT_class(object) == FREE_MARK)
		name = "*ALREADY FREED*";
	else
		name = OBJECT_class(object)->name;

	#if DEBUG_MEMORY
	fprintf(stderr, "%s: %s: ref(%s <%d>) -> %ld\n", OBJECT_ref_where,
		DEBUG_get_current_position(),
		name, GET_ALLOC_ID(object), ((OBJECT *)object)->ref);
	#else
	fprintf(stderr, "%s: %s: ref(%s %p) -> %ld\n", OBJECT_ref_where,
		DEBUG_get_current_position(),
		name, object, ((OBJECT *)object)->ref);
	#endif
	fflush(stdout);
}

bool CLASS_unref(void *ob, bool can_free)
{
	char *name;

	OBJECT *object = (OBJECT *)ob;

	if (OBJECT_class(object) == FREE_MARK)
		name = "*ALREADY FREED*";
	else
		name = OBJECT_class(object)->name;

	#if DEBUG_MEMORY
	if (object->ref <= 0)
		fprintf(stderr, "*** <%d> REF = %ld !\n", GET_ALLOC_ID(object), object->ref);

	fprintf(stderr, "%s: %s: unref(%s <%d>) -> %ld\n", OBJECT_ref_where,
		DEBUG_get_current_position(),
		name, GET_ALLOC_ID(object), object->ref - 1);
	#else
	if (object->ref <= 0)
		fprintf(stderr, "*** %p REF = %ld !\n", object, object->ref);

	fprintf(stderr, "%s, %s: unref(%s %p) -> %ld\n", OBJECT_ref_where,
		DEBUG_get_current_position(),
		name, object, object->ref - 1);
	#endif
	fflush(stdout);

	/*if (strcmp(OBJECT_class(object)->name, "Class") == 0)
		fprintf(stderr, "Class ?\n");*/

	if ((--(object->ref) <= 0) && can_free)
	{
		#if DEBUG_MEMORY
		fprintf(stderr, "FREE <%d> !\n", GET_ALLOC_ID(object));
		#else
		fprintf(stderr, "FREE %p !\n", object);
		#endif
		CLASS_free(object);
		return TRUE;
	}

	return FALSE;
}
#endif

void CLASS_do_nothing()
{
}

int CLASS_return_zero()
{
	return 0;
}

static CLASS *_sorted_class;

static int partition(CLASS_DESC_SYMBOL *cds, ushort *sym, const int start, const int end)
{
	int pos = start;
	short pivot = sym[start];
	int i;
	ushort val;
	int len;
	const char *s1, *s2;
	int result;

	for (i = start + 1; i <= end; i++)
	{
		len = cds[sym[i]].len;
		result = len - cds[pivot].len;

		if (result > 0)
			continue;

		if (result == 0)
		{
#if TABLE_USE_KEY
			result = cds[sym[i]].key - cds[pivot].key;
			if (result > 0)
				continue;
			else if (result == 0)
#endif
			{
				s1 = cds[sym[i]].name;
				s2 = cds[pivot].name;

				while (len)
				{
					result = tolower(*s1++) - tolower(*s2++);
					if (result)
						break;
					len--;
				}

				if (result == 0)
				{
					if (*cds[pivot].name != '.')
						ERROR_panic("Symbol '%s' declared twice in class '%s'\n", cds[sym[i]].name, _sorted_class->name);
				}
				if (result >= 0)
					continue;
			}
		}

		pos++; // incrémente compteur cad la place finale du pivot
		//echanger(tableau, compteur, i); // élément positionné
		val = sym[pos];
		sym[pos] = sym[i];
		sym[i] = val;
	}

	//echanger(tableau, compteur, debut); // le pivot est placé
	val = sym[pos];
	sym[pos] = sym[start];
	sym[start] = val;

	return pos; // et sa position est retournée
}

static void my_qsort(CLASS_DESC_SYMBOL *cds, ushort *sym, const int start, const int end)
{
   if (start < end) // cas d'arrêt pour la récursivité
   {
      int pivot = partition(cds, sym, start, end); // division du tableau
      my_qsort(cds, sym, start, pivot - 1); // trie partie1
      my_qsort(cds, sym, pivot + 1, end); // trie partie2
   }
}

void CLASS_sort(CLASS *class)
{
	ushort *sym;
	ushort i;

	if (!class->n_desc)
		return;

	SYMBOL_compute_keys(class->table, class->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE);

	_sorted_class = class;

	ALLOC(&sym, sizeof(ushort) * class->n_desc);

	for (i = 0; i < class->n_desc; i++)
		sym[i] = i;

	//qsort(sym, class->n_desc, sizeof(ushort), (int (*)(const void *, const void *))sort_desc);
	my_qsort(class->table, sym, 0, class->n_desc - 1);

	class->sort = sym;

	#if DEBUG_DESC
	{
		SYMBOL *s;

		fprintf(stderr, "\nSORT %s\n", class->name);

		for (i = 0; i < class->n_desc; i++)
		{
			s = (SYMBOL *)&class->table[sym[i]];

			fprintf(stderr, "[%d] %.*s (%d)\n", i, (int)s->len, s->name, sym[i]);
		}
		fputc('\n', stderr);
	}
	#endif
}

void CLASS_inheritance(CLASS *class, CLASS *parent)
{
	if (class->parent != NULL)
		THROW_CLASS(class, "Multiple inheritance", "");

	class->parent = parent;
	parent->has_child = TRUE;

	TRY
	{
		CLASS_load(class->parent);
	}
	CATCH
	{
		THROW_CLASS(class, "Cannot load parent class: ", STRING_new_temp_zero(ERROR_current->info.msg));
	}
	END_TRY

	if (!class->check)
	{
		class->check = class->parent->check;
		class->must_check = class->parent->must_check;
	}
	
	if (!class->has_free)
		class->has_free = class->parent->has_free;
	
	// CREATE STATIC is inherited, but not CREATE PRIVATE

	if (parent->auto_create)
		class->auto_create = TRUE;
	
	class->is_array = parent->is_array;
	class->is_collection = parent->is_collection;

	if (!class->array_type)
		class->array_type = parent->array_type;

	//fprintf(stderr, "CLASS_inheritance: %s %s\n", class->name, class->auto_create ? "AUTO CREATE" : "");
}



const char *CLASS_DESC_get_type_name(const CLASS_DESC *desc)
{
	switch (desc->gambas.val3._int[1])
	{
		case CD_PROPERTY_ID: return desc->gambas.val2 < 0 ? "r" : desc->gambas.val1 < 0 ? "w" : "p";
		case CD_VARIABLE_ID: return "v";
		case CD_METHOD_ID: return "m";
		case CD_STATIC_PROPERTY_ID: return desc->gambas.val2 < 0 ? "R" : desc->gambas.val1 < 0 ? "W" : "P";
		case CD_STATIC_VARIABLE_ID: return "V";
		case CD_STATIC_METHOD_ID: return "M";
		case CD_CONSTANT_ID: return "C";
		case CD_EVENT_ID: return ":";
		case CD_EXTERN_ID: return "X";
		default:
			//fprintf(stderr, "CLASS_DESC_get_type_name: %s: %ld\n", desc->gambas.name, desc->gambas.val4);
			return "?";
	}
}

static bool check_signature(char type, const CLASS_DESC *desc, const CLASS_DESC *pdesc)
{
	TYPE *sd, *sp;
	int nsd, nsp;

	if (!TYPE_are_compatible(desc->property.type, pdesc->property.type))
		return TRUE;

	switch (type)
	{
		case CD_METHOD:
		case CD_STATIC_METHOD:

			sd = desc->method.signature;
			nsd = desc->method.npmax;
			sp = pdesc->method.signature;
			nsp = pdesc->method.npmax;
			break;

		case CD_EVENT:

			sd = desc->event.signature;
			nsd = desc->event.npmax;
			sp = pdesc->event.signature;
			nsp = pdesc->event.npmax;
			break;

		case CD_EXTERN:

			sd = desc->ext.signature;
			nsd = desc->ext.npmax;
			sp = pdesc->ext.signature;
			nsp = pdesc->ext.npmax;
			break;
			
		case CD_VARIABLE:
		case CD_STATIC_VARIABLE:
			
			return TRUE;

		default:

			return FALSE;
	}

	return TYPE_compare_signature(sd, nsd, sp, nsp, TRUE);
}

void CLASS_make_description(CLASS *class, const CLASS_DESC *desc, int n_desc, int *first)
{
	static const char *nonher[] = { "_new", "_free", "_init", "_exit", "_lang", NULL };

	int ind;
	int i, j;
	const char *name;
	const char **pnonher;
	CLASS *parent;
	char type, parent_type;
	CLASS_DESC_SYMBOL *cds;
	bool check;

	#if DEBUG_DESC
	fprintf(stderr, "\n---- %s\n", class->name);
	#endif

	// Compute number of public descriptions

	class->n_desc = n_desc;
	if (class->parent)
		class->n_desc += class->parent->n_desc;

	// Make the description symbol table

	if (class->n_desc)
		ALLOC(&class->table, sizeof(CLASS_DESC_SYMBOL) * class->n_desc);

	i = 0;

	if (class->parent && class->parent->n_desc)
	{
		for (j = 0; j < class->parent->n_desc; j++)
		{
			class->table[i] = class->parent->table[j];
			i++;
		}

		for (j = 0; j < n_desc; j++)
		{
			if (CLASS_is_native(class))
			{
				name = &(desc[j].gambas.name[1]);
				type = CLASS_DESC_get_type(&desc[j]);
				//ptype = (const char*)desc[j].gambas.type;
				//fprintf(stderr, "%s -> ", ptype);
				//desc[j].gambas.type = TYPE_from_string(&ptype);
				//fprintf(stderr, "%p\n", (void *)desc[j].gambas.type);
			}
			else
			{
				name = desc[j].gambas.name;
				type = *CLASS_DESC_get_type_name(&desc[j]);
			}

			//fprintf(stderr, "%s.%s\n", class->name, name);

			// An inherited symbol has two or more entries in the table. Only the first one
			// will be used, and so it must point at the new description, not the inherited one.
			check = FALSE;
			parent = class;
			while ((parent = parent->parent))
			{
				ind = CLASS_find_symbol(parent, name);
				if (ind == NO_SYMBOL)
					continue;

				cds = &parent->table[ind];

				// The parent class public symbols of non-native classes were replaced by the symbol kind returned by CLASS_DESC_get_type_name()
				// Only the first inheritance level is tested against signature compatibility

				if (cds->desc && !check)
				{
					parent_type = CLASS_DESC_get_type(cds->desc);

					if (parent_type != type)
					{
						#if DEBUG_DESC
						fprintf(stderr, "type = '%c' parent_type = '%c'\n", type, parent_type);
						#endif
						THROW(E_OVERRIDE, CLASS_get_name(parent), cds->name, CLASS_get_name(class));
					}

					if (!CLASS_is_native(class) && strcasecmp(name, "_new"))
					{
						//fprintf(stderr, "check_signature: %s\n", name);
						if (check_signature(type, &desc[j], cds->desc))
							THROW(E_OVERRIDE, CLASS_get_name(parent), cds->name, CLASS_get_name(class));
					}

					check = TRUE;
				}

				cds = &class->table[ind];

				#if DEBUG_DESC
				fprintf(stderr, "%s: [%d] (%p %d) := (%p %d)\n", name, ind, cds->desc, cds->desc ? cds->desc->gambas.val1 : 0, &desc[j], desc[j].gambas.val1);
				#endif

				cds->desc = (CLASS_DESC *)&desc[j];
				cds->name = ".";
				cds->len = 1;

				/*if (!desc[j].gambas.val1 && index(CD_CALL_SOMETHING_LIST, type) != NULL)
				{
					//#if DEBUG_DESC
					fprintf(stderr, "CLASS_make_description: '%s.%s' gambas.val1: %ld -> %ld\n", class->name, desc[j].gambas.name, desc[j].gambas.val1, class->parent->table[ind].desc->gambas.val1);
					//#endif
					((CLASS_DESC *)desc)[j].gambas.val1 = class->parent->table[ind].desc->gambas.val1;
				}*/
			}
		}

		for (pnonher = nonher; *pnonher; pnonher++)
		{
			ind = CLASS_find_symbol(class->parent, *pnonher);
			if (ind != NO_SYMBOL)
			{
				cds = &class->table[ind];
				cds->desc = NULL;
				cds->name = ".";
				cds->len = 1;
			}
		}
	}

	*first = i;

	for (j = 0; j < n_desc; j++, i++)
	{
		class->table[i].desc = (CLASS_DESC *)&desc[j];
		name = desc[j].gambas.name;

		/* On saute le caractère de type de symbole */
		if (CLASS_is_native(class))
		{
			name++;
			if (*name == '!')
				name++;
		}

		class->table[i].name = (char *)name;
		class->table[i].len = strlen(name);
	}
	
	#if DEBUG_DESC
	for (i = 0; i < class->n_desc; i++)
	{
		cds = &class->table[i];
		fprintf(stderr, "%d: %.*s %p\n", i, cds->len, cds->name, cds->desc);
	}
	#endif
}

/* 'all' means that size_dynamic is the entire size of the object, not just the size of the data */
/* And check if the class is a stream */

void CLASS_calc_info(CLASS *class, int n_event, int size_dynamic, bool all, int size_static)
{
	// If the class is native and static, then size_dynamic == 0. But if we want to inherit
	// the static class, and make the inherited class dynamic, then class->off_event must
	// start after the object header. So we fix size_dynamic accordingly.

	if (all && size_dynamic == 0)
		size_dynamic = sizeof(OBJECT);

	if (class->parent)
	{
		if (all)
			class->off_event = size_dynamic;
		else
			class->off_event = class->parent->off_event + size_dynamic;

		class->n_event = class->parent->n_event + n_event;
	}
	else
	{
		if (all)
			class->off_event = size_dynamic;
		else
			class->off_event = sizeof(OBJECT) + size_dynamic;

		class->n_event = n_event;
	}

	if (class->n_event)
		class->size = class->off_event + sizeof(OBJECT_EVENT) + class->n_event * sizeof(ushort);
	else
		class->size = class->off_event;

	class->size_stat = size_static;
	if (size_static)
		ALLOC_ZERO(&class->stat, class->size_stat);
	else
		class->stat = NULL;

	class->is_stream = (class == CLASS_Stream) || (class->parent && class->parent->is_stream);

	class->is_simple = class->parent == NULL && !class->is_virtual && !class->must_check;
	if (class->parent)
		class->parent->is_simple = FALSE;
}


void CLASS_make_event(CLASS *class, int *first)
{
	int i, ev;

	*first = 0;

	if (class->n_event == 0)
		return;

	if (!CLASS_is_native(class))
	{
		if (class->parent == NULL)
		{
			class->event = class->load->event;
			class->free_event = FALSE;
			return;
		}
	}

	ALLOC_ZERO(&class->event, sizeof(CLASS_EVENT) * class->n_event);

	if (class->parent != NULL)
	{
		ev = class->parent->n_event;

		for (i = 0; i < ev; i++)
			class->event[i] = class->parent->event[i];
	}
	else
		ev = 0;

	class->free_event = TRUE;

	*first = ev;
}


int CLASS_get_inheritance(CLASS *class, CLASS **her)
{
	int nher = 0;

	while ((nher < MAX_INHERITANCE) && (class != NULL))
	{
		*her++ = class;
		nher++;
		class = class->parent;
	}

	return nher;
}

void *CLASS_auto_create(CLASS *class, int nparam)
{
	void *ob = class->instance;

	//fprintf(stderr, ">>> CLASS_auto_create: %s (%p)\n", class->name, ob);

	// We automatically release invalid automatic instances

	if (ob)
	{
		if (OBJECT_is_valid(ob))
		{
			RELEASE_MANY(SP, nparam);
			//fprintf(stderr, "<<< CLASS_auto_create: %s (%p): valid=1\n", class->name, ob);
			return ob;
		}

		OBJECT_UNREF(class->instance);
		class->instance = NULL;
	}

	/*fprintf(stderr, "CLASS_auto_create: create %s\n", class->name);*/

	OBJECT_create_and_set(&class->instance, class, NULL, NULL, nparam);
	//ob = class->instance;
	//OBJECT_REF(ob, "CLASS_auto_create");

	//fprintf(stderr, "<<< CLASS_auto_create: %s (%p) valid=%d\n", class->name, ob, OBJECT_is_valid(ob));

	return class->instance;
}

//#define SET_OPTIONAL_OPERATOR(_class, _op, _func) if (!CLASS_has_operator(_class, _op)) (_class)->operators[_op] = (EXEC_no_operator##_func)

void CLASS_search_special(CLASS *class)
{
	static int _operator_strength = 0;
	int sym;
	int offset;

	class->special[SPEC_NEW] = CLASS_get_symbol_index_kind(class, "_new", CD_METHOD, 0, T_VOID, TRUE);
	class->special[SPEC_FREE] = CLASS_get_symbol_index_kind(class, "_free", CD_METHOD, 0, T_VOID, TRUE);
	class->special[SPEC_GET] = CLASS_get_symbol_index_kind(class, "_get", CD_METHOD, CD_STATIC_METHOD, T_ANY, TRUE);
	class->special[SPEC_PUT] = CLASS_get_symbol_index_kind(class, "_put", CD_METHOD, CD_STATIC_METHOD, T_VOID, TRUE);
	class->special[SPEC_FIRST] = CLASS_get_symbol_index_kind(class, "_first", CD_METHOD, CD_STATIC_METHOD, T_VOID, TRUE);
	class->special[SPEC_NEXT] = CLASS_get_symbol_index_kind(class, "_next", CD_METHOD, CD_STATIC_METHOD, T_ANY, TRUE);
	class->special[SPEC_CALL] = CLASS_get_symbol_index_kind(class, "_call", CD_METHOD, CD_STATIC_METHOD, T_ANY, TRUE);
	class->special[SPEC_UNKNOWN] = CLASS_get_symbol_index_kind(class, "_unknown", CD_METHOD, CD_STATIC_METHOD, T_ANY, TRUE);
	class->special[SPEC_PROPERTY] = CLASS_get_symbol_index_kind(class, "_property", CD_METHOD, CD_STATIC_METHOD, T_BOOLEAN, TRUE);
	class->special[SPEC_COMPARE] = CLASS_get_symbol_index_kind(class, "_compare", CD_METHOD, 0, T_INTEGER, TRUE);
	class->special[SPEC_ATTACH] = CLASS_get_symbol_index_kind(class, "_attach", CD_METHOD, 0, T_VOID, TRUE);
	class->special[SPEC_READY] = CLASS_get_symbol_index_kind(class, "_ready", CD_METHOD, 0, T_VOID, TRUE);
	class->special[SPEC_READ] = CLASS_get_symbol_index_kind(class, "_read", CD_METHOD, 0, T_VOID, TRUE);
	class->special[SPEC_WRITE] = CLASS_get_symbol_index_kind(class, "_write", CD_METHOD, 0, T_VOID, TRUE);
	class->special[SPEC_INVALID] = CLASS_get_symbol_index_kind(class, "_invalid", CD_VARIABLE, 0, T_BOOLEAN, TRUE);

	sym = CLASS_get_symbol_index_kind(class, "_@_convert", CD_CONSTANT, 0, T_ANY, FALSE);
	if (sym != NO_SYMBOL)
	{
		class->has_convert = TRUE;
		class->convert = CLASS_get_desc(class, sym)->constant.value._pointer;
	}

	sym = CLASS_get_symbol_index_kind(class, "_@_operator", CD_CONSTANT, 0, T_ANY, FALSE);
	if (sym != NO_SYMBOL)
	{
		class->has_operators = TRUE;
		class->operators = CLASS_get_desc(class, sym)->constant.value._pointer;
		_operator_strength++;
		CLASS_set_operator_strength(class, _operator_strength);
	}

	if (class->special[SPEC_NEXT] != NO_SYMBOL)
		class->enum_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_NEXT])) == CD_STATIC_METHOD;
	if (class->special[SPEC_UNKNOWN] != NO_SYMBOL)
		class->unknown_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_UNKNOWN])) == CD_STATIC_METHOD;
	if (class->special[SPEC_PROPERTY] != NO_SYMBOL)
		class->property_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_PROPERTY])) == CD_STATIC_METHOD;
	if (class->special[SPEC_FREE] != NO_SYMBOL)
		class->has_free = TRUE;
	
	if (class->special[SPEC_INVALID] != NO_SYMBOL)
	{
		if (class->must_check && class->check != OBJECT_check_valid)
			THROW_CLASS(class, "Validation method already implemented", "");
		class->must_check = TRUE;
		class->check = OBJECT_check_valid;
		offset = CLASS_get_desc(class, class->special[SPEC_INVALID])->variable.offset;
		if (offset > 32767)
			THROW_CLASS(class, "Validation variable must be declared first", "");
		class->special[SPEC_INVALID] = (short)offset;
	}

	// Some flags

	class->init_dynamic = class->load && class->load->func && (class->load->func[FUNC_INIT_DYNAMIC].code[0] & 0xFF00) != C_RETURN;
	class->new_method = class->parent || class->init_dynamic || class->special[SPEC_NEW] != NO_SYMBOL;
	class->ready_method = class->special[SPEC_READY] != NO_SYMBOL;
}


CLASS *CLASS_check_global(CLASS *class)
{
	if (CLASS_is_loaded(class))
	{
		if (COMPONENT_current && class->component == COMPONENT_current)
			ERROR_panic("Class '%s' declared twice in the component '%s'.", CLASS_get_name(class), class->component->name);

		/*if (class->has_child)
			THROW(E_CLASS, class->name, "Overriding an already inherited class is forbidden", "");*/

		class = class_replace_global(class);
	}

	return class;
}


CLASS_DESC_METHOD *CLASS_get_special_desc(CLASS *class, int spec)
{
	short index = class->special[spec];

	if (index == NO_SYMBOL)
		return NULL;
	else
		return &CLASS_get_desc(class, index)->method;
}


CLASS_DESC_SYMBOL *CLASS_get_next_sorted_symbol(CLASS *class, int *index)
{
	CLASS_DESC_SYMBOL *old = NULL;
	CLASS_DESC_SYMBOL *cur;

	for(;;)
	{
		if (*index >= class->n_desc)
			return NULL;

		cur = &class->table[class->sort[*index]];
		if (*index > 0)
			old = &class->table[class->sort[*index - 1]];

		(*index)++;

		if (!cur->desc)
			continue;

		if (old && !TABLE_compare_ignore_case(cur->name, cur->len, old->name, old->len))
			continue;

		return cur;
	}
}


void CLASS_create_array_class(CLASS *class)
{
	void *save = TYPE_joker;
	char *name_joker;
	GB_DESC *desc;
	CLASS *array_type = (CLASS *)class->array_type;

	name_joker = STRING_new(class->name, strlen(class->name) - 2);

	if (!array_type)
	{
		array_type = class->global ? CLASS_find_global(name_joker) : CLASS_find(name_joker);
		class->array_type = (TYPE)array_type;
	}

	TYPE_joker = array_type;
	array_type->array_class = class;

	ALLOC(&desc, sizeof(GB_DESC) * ARRAY_TEMPLATE_NDESC);
	memcpy(desc, NATIVE_TemplateArray, sizeof(GB_DESC) * ARRAY_TEMPLATE_NDESC);
	((CLASS_DESC_GAMBAS *)desc)->name = class->name;

	CLASS_register_class(desc, class);

	class->is_array = TRUE;
	class->quick_array = CQA_ARRAY;
	class->data = (char *)desc;

	STRING_free(&name_joker);
	TYPE_joker = save;
}


CLASS *CLASS_get_array_class(CLASS *class)
{
	if (!class->array_class)
	{
		char name[strlen(class->name) + 3];
		strcpy(name, class->name);
		strcat(name, "[]");
		class->array_class = class->global ? CLASS_find_global(name) : CLASS_find(name);
		CLASS_create_array_class(class->array_class);
	}

	return class->array_class;
}


void CLASS_create_array_of_struct_class(CLASS *class)
{
	void *save = TYPE_joker;
	char *name_joker;
	GB_DESC *desc;
	CLASS *array_type = (CLASS *)class->array_type;

	//fprintf(stderr, "CLASS_create_array_class: create %s\n", class->name);

	name_joker = STRING_new(&class->name[1], strlen(class->name) - 3);

	if (!array_type)
	{
		array_type = class->global ? CLASS_find_global(name_joker) : CLASS_find(name_joker);
		class->array_type = (TYPE)array_type;
	}

	TYPE_joker = array_type;
	array_type->astruct_class = class;

	ALLOC(&desc, sizeof(GB_DESC) * ARRAY_OF_STRUCT_TEMPLATE_NDESC);
	memcpy(desc, NATIVE_TemplateArrayOfStruct, sizeof(GB_DESC) * ARRAY_OF_STRUCT_TEMPLATE_NDESC);
	((CLASS_DESC_GAMBAS *)desc)->name = class->name;

	CLASS_register_class(desc, class);

	class->is_array = TRUE;
	class->is_array_of_struct = TRUE;
	class->data = (char *)desc;

	STRING_free(&name_joker);
	TYPE_joker = save;
}


CLASS *CLASS_get_array_of_struct_class(CLASS *class)
{
	if (!class->astruct_class)
	{
		char name[strlen(class->name) + 4];
		sprintf(name, "$%s[]", class->name);
		class->astruct_class = class->global ? CLASS_find_global(name) : CLASS_find(name);
		CLASS_create_array_of_struct_class(class->astruct_class);
	}

	return class->astruct_class;
}

int CLASS_sizeof(CLASS *class)
{
	if (CLASS_is_struct(class))
		return class->size - sizeof(CSTRUCT);
	else
		return class->size - sizeof(OBJECT);
}

char *CLASS_get_name(CLASS *class)
{
	char *name = class->name;

	while (*name == '^')
		name++;

	return name;
}


CLASS *CLASS_find_load_from(const char *name, const char *from)
{
	COMPONENT *save;
	CLASS *save_cp;
	COMPONENT *comp = NULL;
	CLASS *class;
	
	comp = COMPONENT_find(from);
	
	save = COMPONENT_current;
	save_cp = CP;
	
	COMPONENT_current = comp;
	CP = NULL;

	class = CLASS_find(name);

	if (!CLASS_is_loaded(class) && !class->in_load)
		CLASS_load_without_init(class);

	COMPONENT_current = save;
	CP = save_cp;
	
	return class;
}


void CLASS_translation_must_be_reloaded(void)
{
	CLASS *class;

	for (class = _classes; class; class = class->next)
	{
		if (class->ready && !class->is_native)
			EXEC_public(class, NULL, "_lang", 0);
	}
}
