 /***************************************************************************
  
  gbc_info.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 __GBC_INFO_C

#include "gb_common.h"
#include "gb_error.h"
#include "gb_file.h"
#include "gb_str.h"
#include "gb_buffer.h"

#include "gbc_compile.h"
#include "gbc_class.h"
#include "gb_arch.h"

#include "gbc_info.h"


static bool _info = FALSE;

static TABLE *_components;
static TABLE *_classes;
static INFO_DESC *_desc;

static int _array_class_index = -1;

static const char *_desc_array_type_name = NULL;
static int _desc_array_type_len = 0;

static char *_desc_name = NULL;


//-------------------------------------------------------------------------


static void load_project_class(INFO_CLASS *iclass);
static int search_class(const char *name, int len, bool error);

#define get_class(_index) ((INFO_CLASS *)TABLE_get_symbol(_classes, _index))


//-------------------------------------------------------------------------


void INFO_init(bool info)
{
	_info = info;
	
	TABLE_create(&_components, sizeof(INFO_COMPONENT), TF_IGNORE_CASE);

	if (info)
	{
		TABLE_create(&_classes, sizeof(INFO_CLASS), TF_IGNORE_CASE);
		ARRAY_create(&_desc);
	}
}


static void free_component(INFO_COMPONENT *comp)
{
	STR_free(comp->sym.name);
	BUFFER_delete(&comp->info);
}


static void free_class(INFO_CLASS *class)
{
	if (class->alloc_name)
		STR_free(class->sym.name);
	if (class->info)
		BUFFER_delete(&class->info);
	
	TABLE_delete(&class->symbols);
}


void INFO_exit(void)
{
	int i;
	
	if (_info)
	{
		for (i = 0; i < TABLE_count(_classes); i++)
			free_class(get_class(i));
		
		TABLE_delete(&_classes);
		
		ARRAY_delete(&_desc);
	
		STR_free(_desc_name);
	}
	
	for (i = 0; i < TABLE_count(_components); i++)
		free_component((INFO_COMPONENT *)TABLE_get_symbol(_components, i));
	
	TABLE_delete(&_components);
}


static char *read_line(char **data, int *plen)
{
	char *p = *data;
	char *line = p;
	int len = 0;
	
	if (*p == 0)
	{
		*plen = -1;
		return NULL;
	}
	
	while (*p)
	{
		if (*p++ == '\n')
			break;
		len++;
	}
	
	*plen = len;
	*data = p;
	return line;
}


INFO_DESC *INFO_get_desc(int desc_index)
{
	if (desc_index < 0 || desc_index >= ARRAY_count(_desc))
		return NULL;
	else
		return &_desc[desc_index];
}


const char *INFO_get_name_from_desc(INFO_DESC *idesc)
{
	char *name;
	int len;
	
	if (!idesc)
		return "";
	
	name = idesc->desc;
	STR_free(_desc_name);
	_desc_name = read_line(&name, &len);
	_desc_name = STR_copy_len(_desc_name, len);
	return _desc_name;
}


static int declare_class(const char *name, int len, bool from_project)
{
	int ind;
	INFO_CLASS *iclass;
	bool alloc = FALSE;
	
	if (from_project)
	{
		if (!TABLE_find_symbol(_classes, name, len, &ind))
		{
			name = STR_copy_len(name, len);
			alloc = TRUE;
		}
	}
	
	ind = TABLE_add_symbol(_classes, name, len);
	iclass = get_class(ind);
	
	if (!iclass->symbols)
		TABLE_create(&iclass->symbols, sizeof(INFO_SYMBOL), TF_IGNORE_CASE);
	
	iclass->alloc_name = alloc;
	if (from_project)
		iclass->load_info = TRUE;
	
	if (COMP_verbose)
		fprintf(stderr, "declare_class: %.*s\n", len, name);
	
	return ind;
}


static void copy_class_symbols(INFO_CLASS *idest, INFO_CLASS *isrc)
{
	TABLE *dest;
	TABLE *src;
	int count;
	
	if (idest->inherited)
		return;
	
	idest->inherited = TRUE;
	
	dest = idest->symbols;
	src = isrc->symbols;
	
	count = TABLE_count(src);
	
	if (count == 0)
		return;
	
	ARRAY_add_many(&dest->symbol, count);
	ARRAY_add_many(&dest->sort, count);
	
	memcpy(dest->symbol, src->symbol, count * TABLE_symbol_size(dest));
	memcpy(dest->sort, src->sort, count * sizeof(ushort));
}


static INFO_CLASS *find_class(const char *name, int len)
{
	int ind;
	
	if (TABLE_find_symbol(_classes, name, len, &ind))
		return get_class(ind);
	else
		return NULL;
}


static void ignore_help(char **p)
{
	int len;
	
	while (**p == '\'')
		read_line(p, &len);
}


static void add_symbol(INFO_CLASS *iclass, const char *name, int len, char *desc)
{
	INFO_SYMBOL *isym;
	int ind;
	
	if (COMP_verbose) fprintf(stderr, "add symbol %.*s\n", len, name);

	//fprintf(stderr, "add symbol: %p: %d [%d] %.*s\n", iclass, TABLE_count(iclass->symbols), TABLE_symbol_size(iclass->symbols), len, name);
	ind = TABLE_add_symbol(iclass->symbols, name, len);
	//TABLE_print(iclass->symbols, FALSE);
	
	isym = (INFO_SYMBOL *)TABLE_get_symbol(iclass->symbols, ind);
	((INFO_DESC *)ARRAY_add_void(&_desc))->desc = desc;
	isym->desc = ARRAY_count(_desc) - 1;
}


static void terminate_class(INFO_CLASS *iclass)
{
	int ind;
	
	if (TABLE_find_symbol(iclass->symbols, "_unknown", 8, &ind))
		iclass->has_unknown = TRUE;
	
	if (COMP_verbose)
	{
		fprintf(stderr, "[%.*s]\n", iclass->sym.len, iclass->sym.name);
		TABLE_print(iclass->symbols, TRUE);
	}
}


static void parse_info(char *info, INFO_CLASS *iclass)
{
	char *p = info;
	int len;
	char *line;
	int todo = 0;
	INFO_CLASS *iparent;
	char *mark;
	
	if (iclass)
	{
		line = read_line(&p, &len); // class name
		line = read_line(&p, &len); // parent
		if (len > 0)
		{
			iparent = get_class(search_class(line, len, TRUE));
			if (iparent)
			{
				load_project_class(iparent);
				copy_class_symbols(iclass, iparent);
				
				//fprintf(stderr, "copy_class_symbols: %p <<< %p\n", iclass, iparent);
				//TABLE_print(iclass->symbols, TRUE);
			}
			else
				THROW("Unknown parent class");
		}
		
		line = read_line(&p, &len); // flags
		ignore_help(&p);
	}
	
	for(;;)
	{
		line = read_line(&p, &len);
		if (!line)
		{
			if (todo)
			{
				p = info;
				todo = 0;
				continue;
			}
			
			break;
		}
		
		if (*line == '#' || *line == '-')
		{
			if (iclass)
				terminate_class(iclass);
			
			mark = line;
			
			if (*mark == '-')
			{
				line = read_line(&p, &len); // parent
				iclass = NULL;
			}
			else
			{
				if (iclass)
				{
					if (COMP_verbose)
					{
						fprintf(stderr, "[%.*s]\n", iclass->sym.len, iclass->sym.name);
						TABLE_print(iclass->symbols, TRUE);
					}
				}
				
				iclass = get_class(declare_class(&line[1], len - 1, FALSE));
				
				line = read_line(&p, &len); // parent
				
				if (len > 0 && iclass)
				{
					if (len == 1 && *line == '$')
					{
						// structure, no inheritance
					}
					else
					{
						if (COMP_verbose) fprintf(stderr, "%.*s -> %.*s\n", iclass->sym.len, iclass->sym.name, len, line);
						iparent = find_class(line, len);
						if (iparent && iparent->parsed)
						{
							copy_class_symbols(iclass, iparent);
							if (COMP_verbose) TABLE_print(iclass->symbols, TRUE);
							
							//fprintf(stderr, "copy_class_symbols: %p <<< %p\n", iclass, iparent);
							//TABLE_print(iclass->symbols, TRUE);
						}
						else
						{
							todo++;
							iclass = NULL;
							if (COMP_verbose) fprintf(stderr, "...later (%d)\n", todo);
						}
					}
				}
				
				if (iclass)
				{
					iclass->parsed = TRUE;
					*mark = '-';
				}
			}
			
			line = read_line(&p,  &len); // flags
			ignore_help(&p);
			continue;
		}
		
		if (iclass)
			add_symbol(iclass, line, len, line);
		
		line = read_line(&p, &len); // kind
		line = read_line(&p, &len); // type
		line = read_line(&p, &len); // signature
		
		ignore_help(&p);
	}

	if (iclass)
		terminate_class(iclass);
}


static INFO_COMPONENT *add_component(const char *name, bool library)
{
	INFO_COMPONENT *comp;
	int count;
	int len;
	int ind;

	count = TABLE_count(_components);
	if (count >= MAX_COMPONENT)
		THROW("Too many components");
	
	len = strlen(name);
	
	TABLE_new_symbol = FALSE;
	ind = TABLE_add_symbol(_components, STR_copy_len(name, len), len);
	if (!TABLE_new_symbol)
		return NULL;
	
	comp = (INFO_COMPONENT *)TABLE_get_symbol(_components, ind);
	comp->library = library;
	BUFFER_create(&comp->info);

	return comp;
}


static void add_classes_from_memory(char *p, size_t size)
{
	char *pe = p + size;
	char *p2;
	int len;

	for(;;)
	{
		if (p >= pe)
			break;

		p2 = p;
		while (p2 < pe && *p2 != '\n')
			p2++;

		if (p2 >= pe)
			break;

		len = p2 - p;
		/*if (len > 2 && p[len - 1] == '?')
			len--;*/

		COMPILE_add_class(p, len);

		p = p2 + 1;
	}
}


static bool add_classes_from_file(const char *path)
{
	FILE *f;
	char line[256];
	int len;

	f = fopen(path, "r");

	if (!f)
		return TRUE;

	for(;;)
	{
		if (COMPILE_read_line(f, line, sizeof(line)))
			break;

		len = strlen(line);
		/*if (len > 2 && line[len - 1] == '?')
			len--;*/

		COMPILE_add_class(line, len);
	}
	
	fclose(f);
	return FALSE;
}


void INFO_load_component(const char *name)
{
	INFO_COMPONENT *comp;
	char *path;
	
	comp = add_component(name, FALSE);
	if (!comp)
	{
		if (COMP_verbose)
			fprintf(stderr, "Ignore already loaded component: %s\n", name);
		return;
	}
	
	if (COMP_verbose)
		fprintf(stderr, "Loading information from component: %s\n", name);
	
	path = (char *)FILE_cat(COMP_info_path, name, NULL);
	strcat(path, ".list");
	
	if (add_classes_from_file(path))
	{
		// Do not raise an error if a component self-reference is not found
		if (strcmp(name, COMP_project_name))
			//fprintf(stderr, "warning: cannot read component list file: %s.list\n", name);
			THROW("Component information file not found: &1", name);
	}

	if (_info)
	{
		if (!strcmp(name, COMP_project_name))
		{
			if (COMP_verbose)
				fprintf(stderr, "Do not load information of a component having the same name as the current project\n");
			return;
		}
	
		path = (char *)FILE_cat(COMP_info_path, name, NULL);
		strcat(path, ".info");
		
		BUFFER_load_file(&comp->info, path);
		BUFFER_add_char(&comp->info, 0);

		if (COMP_verbose) fprintf(stderr, "================ parsing: %s\n", path);
		parse_info(comp->info, NULL);
	}
}


void INFO_load_library(const char *path, bool ref)
{
	INFO_COMPONENT *comp;
	ARCH *arch;
	ARCH_FIND find;
	const char *name;
	char *rpath = NULL;
	
	comp = add_component(path, TRUE);
	if (!comp)
	{
		if (COMP_verbose)
			fprintf(stderr, "Ignore already loaded library: %s\n", path);
		return;
	}
			
	if (COMP_verbose)
		fprintf(stderr, "Loading information from library: %s\n", path);
	
	if (*path == ':')
	{
		name = &path[1];

		rpath = STR_cat(FILE_cat(COMP_lib_path, name, NULL), ".gambas", NULL);
		if (!FILE_exist(rpath))
		{
			STR_free(rpath);
			rpath = STR_cat(FILE_cat(COMP_root, "lib/gambas" GAMBAS_VERSION_STRING, name, NULL), ".gambas", NULL);
		}

		if (!FILE_exist(rpath))
			path = NULL;
		else
			path = rpath;
	}
	else
	{
		name = path;
		if (!FILE_exist(path))
			path = NULL;
	}

	if (path)
	{
		arch = ARCH_open(path);

		if (!ARCH_find(arch, ".list", 0, &find))
			add_classes_from_memory(&arch->addr[find.pos], find.len);
		
		if (_info && !ARCH_find(arch, ".info", 0, &find))
		{
			BUFFER_add(&comp->info, &arch->addr[find.pos], find.len);
			BUFFER_add_char(&comp->info, 0);
			
			parse_info(comp->info, NULL);
		}

		ARCH_close(arch);
	}
	else
		ERROR_warning((ref ? "cannot find reference: %s" : "cannot find library: %s"), name);

	if (rpath)
		STR_free(rpath);
}


static void load_project_class(INFO_CLASS *iclass)
{
	char *path;
	char *name;
	
	if (!iclass->load_info)
		return;
	
	name = STR_upper_len(iclass->sym.name, iclass->sym.len);
	
	path = (char *)FILE_cat(COMP_dir, ".desc", name, NULL);
	strcat(path, ".info");
	
	BUFFER_create(&iclass->info);
	BUFFER_load_file(&iclass->info, path);
	BUFFER_add_char(&iclass->info, 0);
	
	if (COMP_verbose) fprintf(stderr, "================ parsing: %s\n", path);
		
	iclass->load_info = FALSE;
	parse_info(iclass->info, iclass);
}


void INFO_add_project_class(const char *name)
{
	int len = strlen(name);
	COMPILE_add_class(name, len);
	if (_info)
		declare_class(name, len, TRUE);
}


#if 0
void INFO_add_structure(int structure_index)
{
	CLASS *class = JOB->class;
	CLASS_STRUCT *structure;
	INFO_CLASS *iclass;
	CLASS_SYMBOL *sym;
	int i;
	VARIABLE *field;
	
	structure = &class->structure[structure_index];
	sym = CLASS_get_symbol(class, structure->index);
	
	iclass = declare_class(sym->symbol.name, sym->symbol.len, TRUE);
	
	if (TABLE_count(iclass->symbols))
	{
		// Should check that the declaration is the same.
		return;
		//TABLE_delete(&iclass->symbols);
		//TABLE_create(&iclass->symbols, sizeof(SYMBOL), TF_IGNORE_CASE);
	}
		
	for (i = 0; i < structure->nfield; i++)
	{
		field = &structure->field[i];
		sym = CLASS_get_symbol(class, field->index);
		if (COMP_verbose) fprintf(stderr, "add symbol %.*s\n", sym->symbol.len, sym->symbol.name);
		TABLE_add_symbol(iclass->symbols, sym->symbol.name, sym->symbol.len);
	}
}
#endif


static int create_array_class(int index, const char *tname, int tlen)
{
	INFO_CLASS *itemplate;
	int ind;
	INFO_CLASS *iclass;
	char *name;
	int i;
	INFO_SYMBOL *isym;
	
	if (_array_class_index < 0)
		_array_class_index = search_class("*[]", 3, TRUE);
	
	itemplate = get_class(_array_class_index);
	
	ALLOC(&name, tlen + 3);
	memcpy(name, tname, tlen);
	memcpy(&name[tlen], "[]", 2);
	name[tlen + 2] = 0;
	
	ind = declare_class(name, tlen + 2, FALSE);
	
	iclass = get_class(ind);
	iclass->alloc_name = TRUE;
	iclass->parsed = TRUE;
	iclass->array_type = index + 1;
	
	for (i = 0; i < TABLE_count(itemplate->symbols); i++)
	{
		isym = (INFO_SYMBOL *)TABLE_get_symbol(itemplate->symbols, i);
		add_symbol(iclass, isym->sym.name, isym->sym.len, _desc[isym->desc].desc);
	}
	
	return ind;
}


static int search_class(const char *name, int len, bool error)
{
	int index;
	
	if (len < 0)
		len = strlen(name);
	
	if (!TABLE_find_symbol(_classes, name, len, &index))
	{
		if (len > 3 && name[len - 1] == ']' && name[len - 2] == '[')
		{
			index = search_class(name, len - 2, FALSE);
			if (index >= 0)
				return create_array_class(index, name, len - 2);
		}
		
		if (error)
		{
			char *temp = STR_copy_len(name, len);
			ERROR("No information found about class &1", temp);
			STR_free(temp);
			PROPAGATE();
		}
		
		index = -1;
	}
	
	return index;
}


TYPE INFO_get_object(const char *class_name)
{
	int ind;
	
	if (_info)
	{
		ind =  search_class(class_name, strlen(class_name), FALSE);
		if (ind >= 0)
			return TYPE_make(T_IOBJECT, ind, 0);
	}

	return TYPE_make_simple(T_OBJECT);
}


static void check_exist_structure_field(int structure_index, int symbol_index)
{
	CLASS_STRUCT *structure = &JOB->class->structure[structure_index];
	int i;
	VARIABLE *field;
	char *structure_name, *symbol_name;
	CLASS_SYMBOL *sym;
	
	/*sym = CLASS_get_symbol(JOB->class, symbol_index);
	fprintf(stderr, "check_exist_structure_field: %s %d / %.*s\n", JOB->class->name, structure_index, sym->symbol.len, sym->symbol.name);*/
	
	for (i = 0; i < structure->nfield; i++)
	{
		field = &structure->field[i];
		if (field->index == symbol_index)
			return;
	}

	sym = CLASS_get_symbol(JOB->class, structure->index);
	structure_name = STR_copy_len(sym->symbol.name, sym->symbol.len);
	
	sym = CLASS_get_symbol(JOB->class, symbol_index);
	symbol_name = STR_copy_len(sym->symbol.name, sym->symbol.len);
	
	ERROR("Unknown symbol '&1' in structure '&2'", symbol_name, structure_name);
	
	STR_free(structure_name);
	STR_free(symbol_name);
	
	PROPAGATE();
}


// Beware that this function can reallocate the _desc array!

static TYPE convert_type(char *desc, int len)
{
	short value = TV_NONE;
	TYPE_ID type_id;
	char *joker;
	int jlen;
	
	if (len == 0)
		return TYPE_make_simple(T_VOID);
	
	if (len == 1)
	{
		switch (*desc)
		{
			case 'b': type_id = T_BOOLEAN; break;
			case 'c': type_id = T_BYTE; break;
			case 'h': type_id = T_SHORT; break;
			case 'i': type_id = T_INTEGER; break;
			case 'l': type_id = T_LONG; break;
			case 'g': type_id = T_SINGLE; break;
			case 'f': type_id = T_FLOAT; break;
			case 'd': type_id = T_DATE; break;
			case 's': type_id = T_STRING; break;
			case 'p': type_id = T_POINTER; break;
			case 'v': type_id = T_VARIANT; break;
			case 'o': type_id = T_OBJECT; break;
			default: type_id = T_VARIANT;
		}
	}
	else if (*desc == 'i' && desc[1] == '{')
	{
		type_id = T_INTEGER;
	}
	else
	{
		type_id = T_IOBJECT;
		
		if (*desc == '*')
		{
			jlen = _desc_array_type_len + len - 1;
			ALLOC(&joker, jlen + 1);
			memcpy(joker, _desc_array_type_name, _desc_array_type_len);
			memcpy(&joker[_desc_array_type_len], desc + 1, len - 1);
			joker[jlen] = 0;
			
			value = search_class(joker, jlen, FALSE);
		}
		else
			value = search_class(desc, len, FALSE);
	}
	
	return TYPE_make(type_id, value, 0);
}


static void convert_signature(INFO_DESC *idesc, const char *sign, int len)
{
	int i;
	char wait = 0;
	bool opt = FALSE;
	
	for (i = 0; i < len; i++)
	{
		if (wait)
		{
			if (sign[i] != wait)
				continue;
			wait = 0;
		}
		
		if (islower(sign[i]) || sign[i] == '*')
			idesc->npmax++;
		else if (isupper(sign[i]) || sign[i] == '_')
		{
			idesc->npmax++;
			wait = ';';
		}
		else if (sign[i] == '[')
		{
			idesc->npmin = idesc->npmax;
			opt = TRUE;
		}
		else if (sign[i] == '(')
			wait = ')';
		else if (sign[i] == '{')
			wait = '}';
		else if (sign[i] == '.')
		{
			opt = TRUE;
			idesc->npmax = 255;
		}
	}
	
	if (!opt)
		idesc->npmin = idesc->npmax;

	//fprintf(stderr, "%s: %.*s -> %d / %d\n", INFO_get_name_from_desc(idesc), len, sign, idesc->npmin, idesc->npmax);
}


static void convert_desc(INFO_CLASS *iclass, int desc_index)
{
	INFO_CLASS *iarray;
	INFO_DESC *idesc;
	char *desc;
	int len;
	char *line;
	char kind;
	bool is_static;
	TYPE type;
	
	idesc = &_desc[desc_index];
	if (idesc->parsed)
		return;
	
	if (iclass->array_type)
	{
		iarray = get_class(iclass->array_type - 1);
		_desc_array_type_name = iarray->sym.name;
		_desc_array_type_len = iarray->sym.len;
	}
	
	desc = idesc->desc;
	
	// name
	
	read_line(&desc, &len);
	
	// kind
	
	line = read_line(&desc, &len);
	
	kind = *line;
	
	is_static = kind == toupper(kind);
	
	switch (tolower(kind))
	{
		case 'c':
			kind = TK_CONST; break;
		case 'm':
			kind = TK_FUNCTION; break;
		case 'p': case 'r':
			kind = TK_PROPERTY; break;
		case 'v':
			kind = TK_VARIABLE; break;
		case ':':
			kind = TK_EVENT; break;
		case 'X':
			kind = TK_EXTERN; break;
		case 'E':
			kind = TK_ENUM; break;
		default:
			kind = TK_UNKNOWN; break;
	}
			
	// type
	
	line = read_line(&desc, &len); 
	
	type = convert_type(line, len);
	idesc = &_desc[desc_index];
	idesc->type = type;

	TYPE_set_kind(&idesc->type, kind);
	TYPE_set_flag(&idesc->type, TF_PUBLIC);
	if (is_static) TYPE_set_flag(&idesc->type, TF_STATIC);
	
	// signature
	
	line = read_line(&desc, &len);
	
	if (kind == TK_FUNCTION || kind == TK_EVENT)
		convert_signature(idesc, line, len);
	
	if (iclass->array_type)
	{
		_desc_array_type_name = NULL;
		_desc_array_type_len = 0;
	}
	
	idesc->parsed = TRUE;
}


int INFO_check_exist_symbol(TYPE type, int symbol_index)
{
	CLASS *class = JOB->class;
	int class_index = -1;
	SYMBOL *sym;
	int value;
	INFO_CLASS *iclass;
	int ind;
	bool found;
	
	if (!_info)
		return NO_SYMBOL;
	
	if (TYPE_get_id(type) == T_OBJECT || TYPE_get_id(type) == T_CLASS)
	{
		value = TYPE_get_value(type);
		
		if (value < 0)
		{
			switch(value)
			{
				case TV_ME:
					if (!TABLE_find_symbol(_classes, class->name, strlen(class->name), &class_index))
						THROW("No information found about class &1", class->name);
					break;
					
				case TV_SUPER:
					
				default:
					return NO_SYMBOL;
			}
		}
		else
		{
			CLASS_REF *class_ref = &class->class[value];
			
			sym = TABLE_get_symbol(class->table, class_ref->index);
			
			if (class_ref->structure)
			{
				check_exist_structure_field(class_ref->structure - 1, symbol_index);
				return NO_SYMBOL;
			}
			
			class_index = class_ref->info_index;
		
			if (class_index == 0)
			{
				sym = TABLE_get_symbol(class->table, class_ref->index);
				class_ref->info_index = class_index = search_class(sym->name, sym->len, TRUE);
			}
		}
	}
	else if (TYPE_get_id(type) == T_IOBJECT)
	{
		class_index = TYPE_get_value(type);
	}
	
	if (class_index < 0)
		return NO_SYMBOL;
	
	iclass = get_class(class_index);
	
	if (iclass->structure)
	{
		check_exist_structure_field(iclass->structure - 1, symbol_index);
		return NO_SYMBOL;
	}
	
	load_project_class(iclass);
	
	if (symbol_index == INFO_GET)
	{
		found = TABLE_find_symbol(iclass->symbols, "_get", 4, &ind);
	}
	else if (symbol_index == INFO_CALL)
	{
		found = TABLE_find_symbol(iclass->symbols, "_call", 5, &ind);
	}
	else
	{
		sym = TABLE_get_symbol(class->table, symbol_index);
		found = TABLE_find_symbol(iclass->symbols, sym->name, sym->len, &ind);
	}

	if (!found)
	{
		char *sym_name;
		char *class_name;
		
		if (iclass->has_unknown)
			return NO_SYMBOL;
		
		if (symbol_index == INFO_GET)
			sym_name = "_get";
		else if (symbol_index == INFO_CALL)
		{
			if (TYPE_get_id(type) == T_CLASS)
				return INFO_CONVERT;
			
			sym_name = "_call";
		}
		else
			sym_name = STR_copy_len(sym->name, sym->len);
		
		class_name = STR_copy_len(iclass->sym.name, iclass->sym.len);
		
		ERROR("Unknown symbol '&1' in class '&2'", sym_name, class_name);
		if (symbol_index >= 0)
			STR_free(sym_name);
		STR_free(class_name);
		PROPAGATE();
	}
	
	ind = ((INFO_SYMBOL *)TABLE_get_symbol(iclass->symbols, ind))->desc;
	convert_desc(iclass, ind);
	
	return ind;
}


void INFO_load_current_class(void)
{
	CLASS *class = JOB->class;
	int i;
	SYMBOL *sym;
	INFO_CLASS *iclass;
	const char *name;
	
	for (i = 0; i < ARRAY_count(class->use); i++)
	{
		name = TABLE_get_symbol_name(class->string, class->use[i]);
		if (*name == '/' || *name == ':')
			INFO_load_library(name, FALSE);
		else
			INFO_load_component(name);
	}
	
	if (_info)
	{
		for (i = 0; i < ARRAY_count(class->structure); i++)
		{
			sym = TABLE_get_symbol(class->table, class->structure[i].index);
			iclass = get_class(declare_class(sym->name, sym->len, TRUE));
			iclass->structure = i + 1;
		}
	}
}


void INFO_unload_current_class(void)
{
	CLASS *class = JOB->class;
	int i;
	CLASS_SYMBOL *sym;
	
	if (_info)
	{
		for (i = 0; i < ARRAY_count(class->structure); i++)
		{
			sym = CLASS_get_symbol(class, class->structure[i].index);
			find_class(sym->symbol.name, sym->symbol.len)->structure = 0;
		}
	}
}
