/*
 * Functions to generate anonymous Ada types
 */
#include <sys/types.h>
#include "ansi.h"
#include "host.h"
#include "files.h"
#include "hash.h"
#include "il.h"
#include "nodeop.h"
#include "allocate.h"
#include "types.h"
#include "stab.h"
#include "ada_name.h"
#include "anonymous.h"
#include "config.h"
#include "type_util.h"

extern int auto_package;
extern file_pos_t yypos;

#undef TYPE_HASH_MAX
#define TYPE_HASH_MAX	256		/* Anonymous type hash table size */

#undef NULL
#define NULL			0

static typeinfo_t *anonymous_types[TYPE_HASH_MAX];
static int anonymous_ord[MAX_UNIQ_FNAMES];

symbol_t *anonymous_function_pointer;

/*
 * Use ordinal for anonymous type names
 */
int
next_anonymous_ord()
{
	int file_ord;

	if (auto_package) {
		file_ord = FILE_ORD(yypos);
		assert(file_ord < num_files());
		return anonymous_ord[file_ord]++;
	}

	return anonymous_ord[0]++;
}

/*
 * don't generate names ending in _t_t
 */
static int
ends_in(str,postfix)
	char *str, *postfix;
{
	for (; *str; str++) {
		if (*str == *postfix && strcmp(str+1,postfix+1) == 0) {
			return 1;
		}
	}
	return 0;
}

/*
 * look for type to Anonymous type hash table
 */
static typeinfo_t*
find_anonymous_type(typ)
	typeinfo_t *typ;
{
	typeinfo_t *t;

	assert(typ != NULL);
	assert(typ->type_hash != 0);

	t = anonymous_types[typ->type_hash & (TYPE_HASH_MAX-1)];
	for (; t; t = t->type_anonymous_list) {
		if (equal_types(t, typ)) {
			return t;
		}
	}

	return NULL;
}

/*
 * add type to Anonymous type hash table
 */
static void
store_anonymous_type(typ)
	typeinfo_t *typ;
{
	int index;
	index = typ->type_hash & (TYPE_HASH_MAX-1);
	assert(typ->type_anonymous_list == NULL);
	typ->type_anonymous_list = anonymous_types[index];
	anonymous_types[index] = typ;
}

static symbol_t*
add_anonymous_builtin(typ, c_name, ada_name)
	typeinfo_t *typ;
	char *c_name, *ada_name;
{
	symbol_t *basetype;

	typ->_anonymous = 1;

	basetype = new_sym();
	basetype->intrinsic = 1;
	typ->type_base = basetype;

	store_anonymous_type(typ);

	basetype->sym_kind = type_symbol;
	basetype->sym_type = typ;

	basetype->sym_ident = new_node(_Ident, c_name);
	basetype->sym_ada_name = ada_name;
	basetype->gened = 1;

	return basetype;
}

static typeinfo_t*
create_type(typ, n, mod1, mod2, mod3)
	typeinfo_t *typ;
	int n;
	int mod1, mod2, mod3;
{
	switch (n) {
	case 3: typ = concat_types(typ, typeof_typemod(mod3));
	case 2: typ = concat_types(typ, typeof_typemod(mod2));
	case 1: typ = concat_types(typ, typeof_typemod(mod1));
	}

	return typeof_typespec(typ);
}

static typeinfo_t*
typeof_long_long(want_unsigned)
	int want_unsigned;
{
	typeinfo_t *typ;
	typ = (want_unsigned) ? typeof_typemod(TYPEMOD_UNSIGNED) : NULL;
	return create_type(typ, 2, TYPEMOD_LONG, TYPEMOD_LONG, 0);
}

static typeinfo_t*
typeof_long(want_unsigned)
	int want_unsigned;
{
	typeinfo_t *typ;
	typ = (want_unsigned) ? typeof_typemod(TYPEMOD_UNSIGNED) : NULL;
	return create_type(typ, 1, TYPEMOD_LONG, 0, 0);
}

static typeinfo_t*
typeof_short(want_unsigned)
	int want_unsigned;
{
	typeinfo_t *typ;
	typ = (want_unsigned) ? typeof_typemod(TYPEMOD_UNSIGNED) : NULL;
	return create_type(typ, 1, TYPEMOD_SHORT, 0, 0);
}

/*
 * Define some builtin/intrinsic anonymous types
 */
void
init_anonymous_types()
{
	static int initialized = 0;

	if (initialized) return;

	initialized = 1;

	/* Builin: signed char* */
	add_anonymous_builtin(add_pointer_type(set_signed_type(typeof_char())),
						  "%% builin *signed char %%",
						  TYPE_NAMEOF_CHAR_POINTER);


	/* Builin: unsigned char* */
	add_anonymous_builtin(add_pointer_type(set_unsigned_type(typeof_char())),
						  "%% builin *unsigned char %%",
						  TYPE_NAMEOF_UCHAR_POINTER);


	/* Builin: void* */
	add_anonymous_builtin(add_pointer_type(typeof_void()),
						  "%% builin *void %%",
						  TYPE_NAMEOF_VOID_POINTER);

	/* Builin: int (*)() */
	anonymous_function_pointer = 
	add_anonymous_builtin(add_function_type(typeof_int(0)),
						  "%% int (*)() %%",
						  TYPE_NAMEOF_FUNCTION_POINTER);

#ifdef SIZEOF_LONG_LONG
	add_anonymous_builtin(add_pointer_type(typeof_long_long(0)),
						  "%% long long int * %%",
						  TYPE_NAMEOF_SIGNED_LONG_LONG_POINTER);

	add_anonymous_builtin(add_pointer_type(typeof_long_long(1)),
						  "%% unsigned long long int * %%",
						  TYPE_NAMEOF_UNSIGNED_LONG_LONG_POINTER);
#endif

	/* builtin: long int* */
	add_anonymous_builtin(add_pointer_type(typeof_long(0)),
						  "%% long int * %%",
						  TYPE_NAMEOF_SIGNED_LONG_POINTER);

	/* builtin: unsigned long int* */
	add_anonymous_builtin(add_pointer_type(typeof_long(1)),
						  "%% long unsigned int * %%",
						  TYPE_NAMEOF_UNSIGNED_LONG_POINTER);

	/* builtin: int* */
	add_anonymous_builtin(add_pointer_type(typeof_int(0)),
						  "%% int * %%",
						  TYPE_NAMEOF_SIGNED_INT_POINTER);

	/* builtin: int* */
	add_anonymous_builtin(add_pointer_type(typeof_int(1)),
						  "%% unsigned int * %%",
						  TYPE_NAMEOF_UNSIGNED_INT_POINTER);

	/* builtin: short* */
	add_anonymous_builtin(add_pointer_type(typeof_short(0)),
						  "%% short int * %%",
						  TYPE_NAMEOF_SIGNED_SHORT_POINTER);

	/* builtin: unsigned short* */
	add_anonymous_builtin(add_pointer_type(typeof_short(1)),
						  "%% short unsigned int * %%",
						  TYPE_NAMEOF_UNSIGNED_SHORT_POINTER);

	/* builtin: float* */
	add_anonymous_builtin(add_pointer_type(typeof_float()),
						  "%% int * %%",
						  TYPE_NAMEOF_FLOAT_POINTER);

	/* builtin: double* */
	add_anonymous_builtin(add_pointer_type(typeof_double()),
						  "%% int * %%",
						  TYPE_NAMEOF_DOUBLE_POINTER);

	/* builtin: string */
	add_anonymous_builtin(add_array_type(set_signed_type(typeof_char()), 1),
						  "%% builin char[] %%",
						  TYPE_NAMEOF_STRING);

	/* builtin: ustring */
	add_anonymous_builtin(add_array_type(set_unsigned_type(typeof_char()), 1),
						  "%% builin char[] %%",
						  TYPE_NAMEOF_USTRING);
}

/*
 * Given "vector_of_c.signed_int" produce "vector_of_c_signed_int"
 */
rm_selectors(s)
	char *s;
{
	for (; *s; s++) {
		if (*s == '.') *s = '_';
	}
}


static int
is_matrix(typ)
	typeinfo_t *typ;
{
	int count;

	if (! is_array(typ)) return 0;

	count = 1;
	for (typ = typ->type_next; is_array(typ); typ = typ->type_next) {
		count++;
	}

	/* if even number of indices then assume matrix */
	return (count & 1) == 0;
}

static void
force_ada_name(sym)
	symbol_t *sym;
{
	if (sym->sym_ada_name == NULL) {
		assert(sym->sym_ident != NULL);
		assert(sym->sym_ident->node_kind == _Ident);
		assert(sym->sym_ident->node.id.name != NULL);
		sym->sym_ada_name =
			ada_name(sym->sym_ident->node.id.name,
					 FILE_ORD(sym->sym_def));
	}
}

/*
 * Here we go.  We may need to generate an anonymous type
 * for the input type.  An example is when "struct foo *a;" is
 * encountered we'll need to gen "type a_sruct_foo_t is ...".
 */
symbol_t*
get_anonymous_type(typ)
	typeinfo_t *typ;
{
	typeinfo_t *anonymous_type;
	symbol_t *basetype;
	ident_case_t icase;
	char buf[1024];

	assert(typ != NULL);

	if (typ->type_next != NULL && typ->type_next->type_base == NULL) {
		if (is_function_pointer(typ)) {
			typ->type_base = anonymous_function_pointer;
			return typ->type_base;
		}
	}

	assert(typ->type_next == NULL || typ->type_next->type_base != NULL);

	assert(typ->type_hash != 0);
	assert(typ->type_anonymous_list == NULL);

	anonymous_type = find_anonymous_type(typ);
	if (anonymous_type != NULL) {
		basetype = anonymous_type->type_base;
		assert(basetype != NULL);
		typ->type_base = basetype;
		return basetype;
	}

	if (is_access(typ)) {
		typeinfo_t *ptype;

		ptype = typ->type_next;
		assert(ptype != NULL);

		basetype = ptype->type_base;
		assert(basetype != NULL);
		assert(basetype->gened);

		force_ada_name(basetype);
		strcpy(buf, ACCESS_TYPE_PREFIX);
		if (ptype->_unsigned != basetype->sym_type->_unsigned) {
			if (ptype->_unsigned) {
				strcat(buf, "unsigned_");
			} else {
				strcat(buf, "signed_");
			}
		}
		strcat(buf, basetype->sym_ada_name);
		icase = id_case(basetype->sym_ada_name);
	} else if (is_array(typ)) {
		typeinfo_t *elem_typ;
		char *prefix = VECTOR_TYPE_PREFIX;

		elem_typ = typ->type_next;
		assert(elem_typ != NULL);

		if (is_array(elem_typ) && !is_matrix(elem_typ)) { /* matrix */
			prefix = MATRIX_TYPE_PREFIX;
			elem_typ = elem_typ->type_next;
			assert(elem_typ != NULL);
		}

		basetype = elem_typ->type_base;
		assert(basetype != NULL);
		assert(basetype->gened);

		force_ada_name(basetype);
		sprintf(buf, "%s%s", prefix, basetype->sym_ada_name);
		icase = id_case(basetype->sym_ada_name);
	} else {
		sprintf(buf, "%s%d", OTHER_TYPE_PREFIX,
				next_anonymous_ord());
		icase = Lower;
	}

	rm_selectors(buf);

	if (! ends_in(basetype->sym_ada_name, TYPE_POSTFIX)) {
		strcat(buf, TYPE_POSTFIX);
	}

	id_format(buf, icase);

	basetype = new_sym();
	typ->type_base = basetype;

	typ = copy_type(typ);
	typ->_anonymous = 1;
	store_anonymous_type(typ);

	basetype->sym_kind = type_symbol;
	basetype->sym_type = typ;
	basetype->sym_def = yypos;

	basetype->sym_ident = new_node(_Ident, new_string(buf));
	basetype->sym_ada_name =
		ada_name(basetype->sym_ident->node.id.name, FILE_ORD(yypos));
	
	return basetype;
}



syntax highlighted by Code2HTML, v. 0.9.1