/* %Z%%M% %I% %E% */

/*
 * Copyright (c) 1992, 1993, 1995 by Wayne C. Gramlich.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * for any purpose is hereby granted without fee provided that the above
 * copyright notice and this permission are retained.  The author makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */

/* This file contains the code to implement the needs table: */

#ifndef FILE_DEFS_H
#include "file_defs.h"
#endif

#ifndef GEN_EXPORTS_H
#include "gen_exports.h"
#endif

#ifndef HEAP_EXPORTS_H
#include "heap_exports.h"
#endif

#ifndef NEED_DEFS_H
#include "need_defs.h"
#endif

#ifndef LINT_H
#include "lint.h"
#endif

#ifndef STR_EXPORTS_H
#include "str_exports.h"
#endif

#ifndef TABLE_EXPORTS_H
#include "table_exports.h"
#endif

#ifndef TYPE_DEFS_H
#include "type_defs.h"
#endif

#ifndef UNIX_ASSERT_H
#include "unix_assert.h"
#endif

#ifndef UNIX_UNISTD_H
#include "unix_unistd.h"
#endif

#ifndef VECTOR_DEFS_H
#include "vector_defs.h"
#endif

LOCAL int	need_entry_equal(Need_entry, Need_entry);
LOCAL int	need_entry_hash(Need_entry);
LOCAL Need_entry need_entry_object_create(Str, Type_ref, Type_ref, Heap);
LOCAL Need_entry need_entry_routine_create(Str, Type_ref, Type_proto, Heap);
LOCAL void	need_entrys_needs_gen(Vec(Need_entry), Str, Type_ref, int, Gen);
LOCAL void	need_entrys_typedef_gen(Vec(Need_entry),
					Str, Type_ref, int, Gen);
LOCAL Need_entry need_table_object_lookup(Need_table, Str, Type_ref);
LOCAL Need_entry need_table_routine_lookup(Need_table, Str, Type_ref);

/*
 * need_entry_equal(need_entry1, need_entry2)
 *	This routine will return 1 if "need_entry1" is equal to "need_entry2"
 *	and 0 otherwise.
 */
LOCAL int
need_entry_equal(
	Need_entry	need_entry1,
	Need_entry	need_entry2)
{
	return strequal(need_entry1->name, need_entry2->name) &&
	       type_ref_equal(need_entry1->type_ref,
			      need_entry2->type_ref);
}

/*
 * need_entry_hash(need_entry)
 *	This routine will return a hash for "need_entry".
 */
LOCAL int
need_entry_hash(
	Need_entry	need_entry)
{
	return strhash(need_entry->name) +
	       type_ref_hash(need_entry->type_ref);
}

/*
 * need_entry_create(need_entry_type, name, type_ref, heap)
 *	This routine will create and return a new need entry.
 */
LOCAL Need_entry
need_entry_create(
	Need_entry_type	need_entry_type,
	Str		name,
	Type_ref	type_ref,
	Heap		heap)
{
	Need_entry	need_entry;

	need_entry = heap_allocate(heap, Need_entry);
	need_entry->type = need_entry_type;
	need_entry->name = name;
	need_entry->type_ref = type_ref;
	return need_entry;
}

/*
 * need_entry_object_create(name, type_ref, actual_type_ref, heap)
 *	This routine will create and return a a new need entry object.
 */
LOCAL Need_entry
need_entry_object_create(
	Str		name,
	Type_ref	type_ref,
	Type_ref	actual_type_ref,
	Heap		heap)
{
	Need_entry	need_entry;

	need_entry = need_entry_create(Need_entry_object,
				       name, type_ref, heap);
	need_entry->value.object = actual_type_ref;
	return need_entry;
}

/*
 * need_entry_routine_create(name, type_ref, type_proto, heap)
 *	This routine will create and return a a new need entry object.
 */
LOCAL Need_entry
need_entry_routine_create(
	Str		name,
	Type_ref	type_ref,
	Type_proto	type_proto,
	Heap		heap)
{
	Need_entry	need_entry;

	need_entry = need_entry_create(Need_entry_routine,
				       name, type_ref, heap);
	need_entry->value.routine = type_proto;
	return need_entry;
}

/*
 * need_entrys_needs_gen(need_entrys, name, type_ref, parameterized, gen)
 *	This routine will generate a vector of need entry records for
 *	"name"@"type_ref" from "need_entrys" to "gen".
 */
LOCAL void
need_entrys_needs_gen(
	Vec(Need_entry)	need_entrys,
	Str		name,
	Type_ref	type_ref,
	int		parameterized,
	Gen		gen)
{
	Need_entry	need_entry;
	Str		need_name;
	Type_ref	need_type_ref;
	Type_refs	parameters;
	int		parameters_size;
	int		size;
	Str		type_name;

	size = vec_size(Need_entry, need_entrys);
	VEC_LOOP(Need_entry, need_entrys, need_entry) {
		parameters = need_entry->type_ref->parameters;
		if (!type_refs_is_empty(parameters)) {
			type_refs_gen(parameters, gen);
		}
	}
	type_name = parameterized ? "block" : "static";
	gen_out(gen, "need___entry %s__%s__%s__needs[%d] = {\n",
		type_ref->name, name, type_name, size);
	VEC_LOOP(Need_entry, need_entrys, need_entry) {
		need_name = need_entry->name;
		need_type_ref = need_entry->type_ref;
		parameters = need_type_ref->parameters;
		parameters_size = type_refs_size(parameters);
		gen_out(gen, "%\t%\", %\", ",
			1, need_name, need_type_ref->name);
		if (parameters_size == 0) {
			gen_out(gen, "0, (type___reference **)0,\n");
		} else {
			gen_out(gen, "%d, %M__type__references,\n",
				parameters_size, parameters);
		}
		switch (need_entry->type) {
		    case Need_entry_object:
			gen_out(gen,
			    "%\t0, (int)(&((%s__%s__%s__type *)0)->%m__%s),\n",
				2, type_ref->name, name,
				type_name, need_type_ref,
				strequal(need_name, "??") ?
					 "_initial" : need_name);
			break;
		    case Need_entry_routine:
			gen_out(gen,
			    "%\t1, (int)(&((%s__%s__%s__type *)0)->%m__%s),\n",
				2, type_ref->name, name, type_name,
				need_type_ref, need_name);
			break;
		    default:
			assert_fail();
		}
	}
	gen_out(gen, "};\n");
}

/*
 * need_entrys_typedef_gen(need_entrys, name, type_ref, parameterized, gen)
 *	This routine will output a typedef for "need_entrys" to "gen".
 */
LOCAL void
need_entrys_typedef_gen(
	Vec(Need_entry)	need_entrys,
	Str		name,
	Type_ref	type_ref,
	int		parameterized,
	Gen		gen)
{
	Need_entry	need_entry;

	gen_out(gen, "typedef struct {\n");
	VEC_LOOP(Need_entry, need_entrys, need_entry) {
		switch (need_entry->type) {
		    case Need_entry_routine:
		      {
			Type_proto	type_proto;

			type_proto = need_entry->value.routine;
			gen_out(gen, "%\t%r___type %m__%s;\n", 1,
				type_proto->type_ref,
				need_entry->type_ref,
				need_entry->name);
			break;
		      }
		    case Need_entry_object:
			gen_out(gen, "%\tvoid *%m__%s;\n", 1,
				need_entry->type_ref,
				strequal(need_entry->name, "??") ?
						"_initial" : need_entry->name);
			break;
		    default:
			assert_fail();
		}
	}
	gen_out(gen, "} %s__%s__%s__type;\n", type_ref->name, name,
		parameterized ? "block" : "static");
}

/*
 * need_table_create(routine, name, type_ref, type_tables, heap)
 *	This routine will create and return a table of need entrys.
 */
Need_table
need_table_create(
	Routine		routine,
	Str		name,
	Type_ref	type_ref,
	Type_tables	type_tables,
	Heap		heap)
{
	Need_table	need_table;

	need_table = heap_allocate(heap, Need_table);
	need_table->block_entrys = vec_create(Need_entry, heap);
	need_table->heap = heap;
	need_table->key = heap_allocate(heap, Need_entry);
	need_table->name = name;
	need_table->routine = routine;
	need_table->static_entrys = vec_create(Need_entry, heap);
	need_table->table = table_create(Need_entry, Need_entry, 10,
					 need_entry_equal, need_entry_hash,
					 (Need_entry)0, heap);
	need_table->type_ref = type_ref;
	need_table->type_tables = type_tables;
	return need_table;
}

/*
 * need_table_typedef_gen(need_table, gen)
 *	This routine will output the typedef's from "need_table" to "gen".
 */
void
need_table_typedef_gen(
	Need_table	need_table,
	Gen		gen)
{
	Vec(Need_entry)	block_entrys;
	int		block_size;
	Str		name;
	Vec(Need_entry)	static_entrys;
	int		static_size;
	Type_ref	type_ref;

	name = need_table->name;
	type_ref = need_table->type_ref;

	static_entrys = need_table->static_entrys;
	static_size = vec_size(Need_entry, static_entrys);
	if (static_size > 0) {
		need_entrys_typedef_gen(static_entrys, name, type_ref, 0, gen);
		gen_out(gen, "%s__%s__static__type %s__%s__static;\n",
			type_ref->name, name, type_ref->name, name);
	}

	block_entrys = need_table->block_entrys;
	block_size = vec_size(Need_entry, block_entrys);
	if (block_size > 0) {
		need_entrys_typedef_gen(block_entrys, name, type_ref, 1, gen);
	}
	if (static_size + block_size > 0) {
		gen_out(gen, "\n");
	}
}

/*
 * need_table_data_gen(need_table, routine, gen)
 *	This routine will generate all of the need entry record from
 *	"need_table" to "gen".
 */
int
need_table_data_gen(
	Need_table	need_table,
	Gen		gen)
{
	Vec(Need_entry)	block_entrys;
	int		block_size;
	Str		name;
	Vec(Need_entry)	static_entrys;
	int		static_size;
	Type_ref	type_ref;
	Str		type_name;
	
	name = need_table->name;
	type_ref = need_table->type_ref;
	type_name = type_ref->name;

	static_entrys = need_table->static_entrys;
	block_entrys = need_table->block_entrys;

	static_size = vec_size(Need_entry, static_entrys);
	block_size = vec_size(Need_entry, block_entrys);

	if (static_size + block_size == 0) {
		return 0;
	}

	if (!vec_empty(Need_entry, static_entrys)) {
		need_entrys_needs_gen(static_entrys, name, type_ref, 0, gen);
	}
	if (!vec_empty(Need_entry, block_entrys)) {
		need_entrys_needs_gen(block_entrys, name, type_ref, 1, gen);
	}

	gen_out(gen, "routine___params %s__%s__routine__params = {\n",
		type_name, name);
	gen_out(gen, "%\t%d,\n", 1, static_size);
	if (static_size == 0) {
		gen_out(gen, "%\t(need___entry *)0,\n", 1);
		gen_out(gen, "%\t(void *)0,\n", 1);
	} else {
		gen_out(gen, "%\t%s__%s__static__needs,\n", 1, type_name, name);
		gen_out(gen, "%\t(void *)&%s__%s__static,\n",
			1, type_name, name);
	}
	gen_out(gen, "%\t%d,\n", 1, block_size);
	if (block_size == 0) {
		gen_out(gen, "%\t(need___entry *)0,\n", 1);
		gen_out(gen, "%\t0,\n", 1);
	} else {
		gen_out(gen, "%\t%s__%s__block__needs,\n", 1, type_name, name);
		gen_out(gen, "%\tsizeof(%s__%s__block__type),\n",
			1, type_name, name);
	}
	gen_out(gen, "};\n");

	gen_out(gen, "\n");
	return 1;
}

/*
 * need_table_object_insert(need_table, name, type_ref, actual_type_ref)
 *	This routine will insert "name"@"type_ref" into "need_table"
 *	with an actual type of "actual_type_ref".
 */
void
need_table_object_insert(
	Need_table	need_table,
	Str		name,
	Type_ref	type_ref,
	Type_ref	actual_type_ref)
{
	Need_entry	need_entry;

	need_entry = need_table_object_lookup(need_table, name, type_ref);
	if (need_entry != (Need_entry)0) {
		/*XXX: Should be an error! */
		assert(need_entry->type == Need_entry_object);
		return;
	}
	need_entry = need_entry_object_create(name, type_ref,
					      actual_type_ref,
					      need_table->heap);
	assert(table_insert(Need_entry, Need_entry,
			    need_table->table, need_entry, need_entry) == 0);
	if (type_ref_contains_parameter(need_entry->type_ref)) {
		vec_append(Need_entry, need_table->block_entrys, need_entry);
	} else {
		vec_append(Need_entry, need_table->static_entrys, need_entry);
	}
}

/*
 * need_table_object_lookup(need_table, name, type_ref);
 *	This routine will return the need entry for "name"@"type_ref"
 *	from "need_table".  (Need_entry)0 is returned if "name"@"type_ref"
 *	is not in "need_table".
 */
LOCAL Need_entry
need_table_object_lookup(
	Need_table	need_table,
	Str		name,
	Type_ref	type_ref)
{
	Need_entry	need_entry;

	need_entry = need_table->key;
	need_entry->name = name;
	need_entry->type_ref = type_ref;
	need_entry = table_lookup(Need_entry, Need_entry,
				  need_table->table, need_entry);
	if (need_entry != (Need_entry)0) {
		/*XXX:Really should generate an error message! */
		assert(need_entry->type == Need_entry_object);
	}
	return need_entry;
}

/*
 * need_table_routine_insert(need_table, name, type_ref, type_proto)
 *	This routine will insert "name"@"type_ref" into "need_table"
 *	with a prototype of "type_proto".
 */
void
need_table_routine_insert(
	Need_table	need_table,
	Str		name,
	Type_ref	type_ref,
	Type_proto	type_proto)
{
	Need_entry	need_entry;

	need_entry = need_table_routine_lookup(need_table, name, type_ref);
	if (need_entry != (Need_entry)0) {
		/*XXX: Should be an error! */
		assert(need_entry->type == Need_entry_routine);
		return;
	}
	need_entry = need_entry_routine_create(name, type_ref,
					       type_proto, need_table->heap);
	assert(table_insert(Need_entry, Need_entry,
			    need_table->table, need_entry, need_entry) == 0);
	if (type_ref_contains_parameter(need_entry->type_ref)) {
		vec_append(Need_entry, need_table->block_entrys, need_entry);
	} else {
		vec_append(Need_entry, need_table->static_entrys, need_entry);
	}
	type_proto_needed(type_proto, need_table->type_tables);
}

/*
 * need_table_routine_lookup(need_table, name, type_ref);
 *	This routine will return the need entry for "name"@"type_ref"
 *	from "need_table".  (Need_entry)0 is returned if "name"@"type_ref"
 *	is not in "need_table".
 */
LOCAL Need_entry
need_table_routine_lookup(
	Need_table	need_table,
	Str		name,
	Type_ref	type_ref)
{
	Need_entry	need_entry;

	need_entry = need_table->key;
	need_entry->name = name;
	need_entry->type_ref = type_ref;
	need_entry = table_lookup(Need_entry, Need_entry,
				  need_table->table, need_entry);
	if (need_entry != (Need_entry)0) {
		/*XXX:Really should generate an error message! */
		assert(need_entry->type == Need_entry_routine);
	}
	return need_entry;
}

