#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include "host.h"
#include "ansi.h"
#include "files.h"
#include "units.h"
#include "allocate.h"
#include "vendor.h"
#include "config.h"

extern int auto_package;

#undef NULL
#define NULL			0

/* from limits.h */
#ifndef PATH_MAX
#define PATH_MAX		1024
#endif

#ifndef MAX_NEST
#define MAX_NEST		64		/* Arbitrary limit */
#endif

FILE *cur_unit_fd;

static ada_unit_t *table[MAX_UNIQ_FNAMES];
static int cur_unit;

static int nesting_table[MAX_NEST];
static int nest_level;

int
current_unit()
{
	if (! auto_package) return 0;

	assert(cur_unit < num_files());
	return cur_unit;
}

static void
set_reference(ref, ord)
	unit_ref_t ref;
	int ord;
{
	int index;
	int bit;

	index = ord / BITS_PER_INT;
	assert(index < UNIT_SET_SIZE);

	bit = 1 << (ord % BITS_PER_INT);

	ref[index] |= bit;
}

static void
clear_reference(ref, ord)
	unit_ref_t ref;
	int ord;
{
	int index;
	int bit;

	index = ord / BITS_PER_INT;
	assert(index < UNIT_SET_SIZE);

	bit = 1 << (ord % BITS_PER_INT);

	ref[index] &= ~bit;
}

static int
is_referenced(ref, ord)
	unit_ref_t ref;
	int ord;
{
	int index;
	int bit;

	index = ord / BITS_PER_INT;
	assert(index < UNIT_SET_SIZE);

	bit = 1 << (ord % BITS_PER_INT);

	return (ref[index] & bit) != 0;
}

static void
ref_merge(r1, r2)
	unit_ref_t r1, r2;
{
	int i;
	for (i = 0; i < UNIT_SET_SIZE; i++) {
		r1[i] |= r2[i];
	}
}	

static void
merge_direct_refs()
{
	ada_unit_t *unit, *ref;
	int last, i, j;

	if (! auto_package) return;

	last = num_files();

	for (i = 0; i < last; i++) {
		unit = table[i];
		assert(unit != NULL);
		for (j = 0; j < last; j++) {
			if (j != i && is_referenced(unit->direct_ref, j)) {
				ref = table[j];
				assert(ref != NULL);
				ref_merge(unit->direct_ref, ref->direct_ref);
			}
		}

		/* Don't let unit "i" reference itself */
		clear_reference(unit->direct_ref, i);
		clear_reference(unit->unit_ref, i);
	}
}

static void
merge_refs()
{
	ada_unit_t *unit, *ref;
	int last, i, j;

	if (! auto_package) return;

	last = num_files();

	for (i = 0; i < last; i++) {
		unit = table[i];
		assert(unit != NULL);
		for (j = 0; j < last; j++) {
			if (j != i && is_referenced(unit->unit_ref, j)) {
				ref = table[j];
				assert(ref != NULL);
				ref_merge(unit->unit_ref, ref->unit_ref);
			}
		}

		/* Don't let unit "i" reference itself */
		clear_reference(unit->direct_ref, i);
		clear_reference(unit->unit_ref, i);
	}
}

void
unit_start_gen()
{
	cur_unit_fd = stdout;
#if 0
	merge_direct_refs();
#else
	merge_refs();
#endif
}

static char*
gen_unit_name(path)
	char *path;
{
	char buf[PATH_MAX];
	char res[PATH_MAX];
	char *p;

	if (!strncmp(path, "/usr/include/", 13)) {
		p = buf;
		path = &path[13];
	}
	else {
		char *last = path;
		char *s;
		for (s = path; *s; s++) {
			if (*s == '/') {
				last = s;
			}
		}

		path = last;
		p = buf;
	}

	for (; *path; path++) {
		switch (*path) {
		  case '.':
#ifdef HEADER_POSTFIX
			if (path[1] == 'h' && path[2] == 0) {
				strcpy(p, HEADER_POSTFIX);
				path++;
				break;
			}
#endif
		  case '/':
			*p++ = '_';
			break;
		  default:
			*p++ = *path;
			break;
		}
	}

	*p = 0;

	make_ada_identifier(buf, res);
	return new_string(res);
}

static void
initialize_unit(ord)
	int ord;
{
	char buf[PATH_MAX];
	ada_unit_t *unit;

	assert(table[ord] == NULL);

	unit = allocate(sizeof(ada_unit_t));

	unit->src_path = file_name_from_ord(ord);
	assert(unit->src_path != NULL);

	unit->unit_name = gen_unit_name(unit->src_path);
	assert(unit->unit_name != NULL);

	strcpy(buf, "bindings/");
	strcat(buf, unit->unit_name);

	switch (ada_compiler) {
	  case GNAT:
		strcat(buf, ".ads");
		break;
	  case VADS:
		strcat(buf, ".a");
		break;
	  case Rational:
		strcat(buf, ".1.ada");
		break;
	  default:
		strcat(buf, ".ada");
		break;
	}

	unit->unit_path = new_string(buf);

	unit->initialized = 1;

	table[ord] = unit;
}

static void
dump_unit(ord)
	int ord;
{
	ada_unit_t *unit;

	unit = table[ord];
	assert(unit != NULL);

	printf("unit %d\n", ord);
	printf("\tsrc = %s\n", unit->src_path);
	printf("\tname = %s\n", unit->unit_name);
	printf("\tpath = %s\n", unit->unit_path);
	fflush(stdout);
}

void
unit_included(pos, nest)
	file_pos_t pos;
	int nest;
{
	ada_unit_t *unit;
	int ord;
	int uord;

	if (! auto_package) return;
	if (nest < 1) return;
	if (nest >= MAX_NEST) return;

	ord = FILE_ORD(pos);

	if (nest > nest_level) {
		uord = nesting_table[nest_level];
		unit = table[uord];
		assert(unit != NULL);
		set_reference(unit->direct_ref, ord);
		set_reference(unit->unit_ref, ord);
	}

	nest_level = nest;
	nesting_table[nest] = ord;
}

void
init_unit(pos)
	file_pos_t pos;
{
	int ord;

	if (! auto_package) return;

	ord = FILE_ORD(pos);
	if (table[ord] == NULL) {
		initialize_unit(ord);
	}
}

int
set_unit(ord)
	int ord;
{
	ada_unit_t *unit;

	if (! auto_package) return 0;

	assert(ord < num_files());

	unit = table[ord];
	assert(unit != NULL);

	mkdir("bindings", 0777);

	cur_unit = ord;

	cur_unit_fd = fopen(unit->unit_path, "w");
	if (cur_unit_fd == NULL) {
		syserr(unit->unit_path, 0);
		return 1;
	}

	inform(0, 0, "Generating %s", unit->unit_path);
	return 0;
}

void
unit_completed()
{
	if (auto_package) {
		fclose(cur_unit_fd);
		cur_unit = MAX_UNIQ_FNAMES + 1;
	}
}

void
unit_dependency(ord, dep)
	int ord, dep;
{
	ada_unit_t *unit;
	int last;

	if (! auto_package) return;

	last = num_files();

	assert(ord >= 0 && ord < last);
	assert(dep >= 0 && dep < last);

	unit = table[ord];
	assert(unit != NULL);

	set_reference(unit->unit_ref, dep);
}

char*
unit_name(ord)
	int ord;
{
	ada_unit_t *unit;

	if (! auto_package) return "y";

	assert(ord < num_files());

	unit = table[ord];
	assert(unit != NULL);

	assert(unit->unit_name != NULL);
	return unit->unit_name;
}

char*
include_path(ord)
	int ord;
{
	ada_unit_t *unit;

	if (! auto_package) return NULL;

	assert(ord < num_files());

	unit = table[ord];
	assert(unit != NULL);

	assert(unit->src_path != NULL);
	return unit->src_path;
}

char*
cur_unit_name()
{
	return unit_name(current_unit());
}

char*
cur_unit_source()
{
	ada_unit_t *unit;
	int ord;

	if (! auto_package) return NULL;

	ord = current_unit();
	assert(ord < num_files());

	unit = table[ord];
	assert(unit != NULL);

	assert(unit->src_path != NULL);
	return unit->src_path;
}

char*
cur_unit_path()
{
	ada_unit_t *unit;
	int ord;

	if (! auto_package) return NULL;

	ord = current_unit();
	assert(ord < num_files());

	unit = table[ord];
	assert(unit != NULL);

	assert(unit->unit_path != NULL);
	return unit->unit_path;
}

int
nth_ref_unit_ord(n)
	int n;
{
	ada_unit_t *unit;
	int count, i, last;

	if (auto_package) {
		unit = table[current_unit()];
		assert(unit != NULL);

		last = num_files();

		for (count = 0, i = 0; i < last; i++) {
			if (is_referenced(unit->unit_ref, i)) {
				if (count == n) {
					return i;
				}
				count++;
			}
		}
	}

	return -1;
}

int
nth_direct_ref_unit_ord(n)
	int n;
{
	ada_unit_t *unit;
	int count, i, last;
	int cur;

	if (auto_package) {
		cur = current_unit();
		unit = table[cur];
		assert(unit != NULL);

		last = num_files();

		for (count = 0, i = 0; i < last; i++) {
			if (cur != i && is_referenced(unit->direct_ref, i)) {
				if (count == n) {
					return i;
				}
				count++;
			}
		}
	}

	return -1;
}


syntax highlighted by Code2HTML, v. 0.9.1